-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit a274c94
Showing
13 changed files
with
580 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
name: Test | ||
|
||
on: [push, pull_request] | ||
|
||
jobs: | ||
tests: | ||
runs-on: ubuntu-latest | ||
strategy: | ||
fail-fast: true | ||
matrix: | ||
php-versions: [ '7.4', '8.0' ] | ||
steps: | ||
- uses: actions/checkout@master | ||
- uses: shivammathur/setup-php@v2 | ||
with: | ||
php-version: ${{ matrix.php-versions }} | ||
- name: Install dependencies | ||
run: composer install --prefer-dist --no-interaction --no-ansi --no-progress | ||
- name: PHPUnit | ||
run: vendor/bin/phpunit |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
/vendor/ | ||
/composer.lock |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
Copyright (c) Vincent Huck | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is furnished | ||
to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all | ||
copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
THE SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
Weather Gradient | ||
================== | ||
|
||
Weather gradient is a small library allowing to determine the RGB color at a given value in an interval bounded by | ||
a combination of a minimum value and a color and a combination of a maximum value and a color. | ||
|
||
In addition to the minimum and maximum limits, it is possible to add as many thresholds as desired. | ||
|
||
<p align="center"> | ||
<img src="./doc/temperature-gradient.png" title="Weather Gradient example"> | ||
</p> | ||
|
||
## Documentation | ||
|
||
### Installation | ||
|
||
Use [Composer](http://getcomposer.org/) to install Weather Gradient in your project : | ||
|
||
```shell | ||
composer require "zhb/weather-gradient" | ||
``` | ||
|
||
### Usage | ||
|
||
```php | ||
$colors = [ | ||
0 => [59, 130, 246], // blue | ||
30 => [239, 68, 68], // red | ||
]; | ||
|
||
// create a gradient from given thresholds | ||
$gradient = Gradient::fromColors($colors); | ||
|
||
// get the RGB color at a specific gradient position | ||
$color = $gradient->colorAtGradientPosition(18); | ||
|
||
// print the color | ||
echo $color; | ||
|
||
// or get r, g, b values | ||
$r = $color->getR(); | ||
$g = $color->getG(); | ||
$b = $color->getB(); | ||
``` | ||
|
||
In addition to the Gradient class, you can use the Contrast::darkOrLight(array $rgb) to determine if a dark or light text fit the best with the given rgb color. | ||
|
||
```php | ||
// $bestColor will contain [255, 255, 255] (white) | ||
$bestColor = Contrast::darkOrLight($darkBlue = [85, 101, 242]); | ||
|
||
// $bestColor will contain [0, 0, 0] (black) | ||
$bestColor = Contrast::darkOrLight($lightBlue = [59, 130, 246]); | ||
``` | ||
|
||
### Examples | ||
|
||
A usage example can be found in [example](./example) folder. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
{ | ||
"name": "zhb/weather-gradient", | ||
"description": "Library for determining the color at a specific position in a bounded color gradient.", | ||
"keywords": ["colors", "gradient", "weather"], | ||
"type": "library", | ||
"license": "MIT", | ||
"authors": [ | ||
{ | ||
"name": "Vincent Huck", | ||
"email": "[email protected]", | ||
"homepage": "https://github.com/ZHB" | ||
} | ||
], | ||
"require": { | ||
"php": "^7.4 || ^8.0" | ||
}, | ||
"autoload": { | ||
"psr-4": { "Zhb\\WeatherGradient\\": "src/WeatherGradient"} | ||
}, | ||
"require-dev": { | ||
"phpunit/phpunit": "^9.5" | ||
} | ||
} |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
<?php | ||
|
||
/* | ||
* (c) ZHB <[email protected]> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
use Zhb\WeatherGradient\Gradient; | ||
|
||
require_once __DIR__.'/../vendor/autoload.php'; | ||
|
||
?> | ||
|
||
<html> | ||
<head> | ||
<title>Color gradient</title> | ||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/tailwindcss/2.2.6/tailwind.min.css" integrity="sha512-EYVjvPqURgm6pqtZxeqvlbZtnWjYmecnLS0QEedL51IUdaH0HXmSHjTKK7X1yWmiB3/5U1fwIv06ZDwLoo1LdA==" crossorigin="anonymous" referrerpolicy="no-referrer" /> | ||
</head> | ||
<body> | ||
<div class="my-5 mx-5"> | ||
<div class="flex flex-row text-xs space-y-0 space-x-4"> | ||
<div class="w-40 flex-shrink-0"> | ||
<div class="h-10 flex flex-col justify-center"> | ||
<div class="text-sm font-semibold text-gray-900">Temperature</div> | ||
<div> | ||
<code class="text-xs text-gray-500">-20 °C to 40 °C</code> | ||
</div> | ||
</div> | ||
</div> | ||
<div class="min-w-0 flex-1 grid grid-cols-10 gap-x-1 gap-y-3"> | ||
<?php | ||
$colors = [ | ||
-20 => [124, 58, 237], // purple | ||
-10 => [59, 130, 246], // blue | ||
0 => [239, 246, 255], // white | ||
20 => [252, 211, 77], // yellow | ||
35 => [239, 68, 68], // red | ||
10 => [16, 185, 129], // green | ||
40 => [236, 72, 153], // pink | ||
]; | ||
|
||
$gradient = Gradient::fromColors($colors); | ||
|
||
for ($temperature = -20; $temperature <= 40; ++$temperature) { | ||
$color = $gradient->colorAtGradientPosition($temperature); ?> | ||
<div class="space-y-1.5"> | ||
<div class="h-10 w-full rounded ring-1 ring-inset ring-black ring-opacity-0" style="background-color:<?php echo $color; ?>;"></div> | ||
<div class="px-0.5 md:flex md:justify-between md:space-x-2 2xl:space-x-0 2xl:block"> | ||
<div class="w-20 font-medium text-gray-900"><?php echo $temperature; ?> °C</div><div><?php echo $color; ?></div> | ||
</div> | ||
</div> | ||
<?php | ||
} ?> | ||
</div> | ||
</div> | ||
</div> | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
|
||
<phpunit | ||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd" | ||
bootstrap="./vendor/autoload.php" | ||
colors="true" | ||
> | ||
<testsuites> | ||
<testsuite name="all"> | ||
<directory>./tests</directory> | ||
</testsuite> | ||
</testsuites> | ||
</phpunit> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
<?php | ||
|
||
/* | ||
* This file is part of the Weather Gradient package. | ||
* | ||
* (c) Vincent Huck <[email protected]> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
namespace Zhb\WeatherGradient; | ||
|
||
class Contrast | ||
{ | ||
/** | ||
* Determines the brightness contrast ratio between a background and a foreground color. | ||
*/ | ||
private static function brightnessContrastRatio(array $backgroundColor, array $foregroundColor): string | ||
{ | ||
list($r1, $g1, $b1) = $backgroundColor; | ||
list($r2, $g2, $b2) = $foregroundColor; | ||
|
||
$l1 = 0.2126 * (($r1 / 255) ** 2.2) + 0.7152 * (($g1 / 255) ** 2.2) + 0.0722 * (($b1 / 255) ** 2.2); | ||
$l2 = 0.2126 * (($r2 / 255) ** 2.2) + 0.7152 * (($g2 / 255) ** 2.2) + 0.0722 * (($b2 / 255) ** 2.2); | ||
|
||
if ($l1 > $l2) { | ||
$ratio = ($l1 + 0.05) / ($l2 + 0.05); | ||
} else { | ||
$ratio = ($l2 + 0.05) / ($l1 + 0.05); | ||
} | ||
|
||
return $ratio; | ||
} | ||
|
||
/** | ||
* Return black or white hexadecimal color that fit best (best contrast ratio) with a given background color. | ||
*/ | ||
public static function darkOrLight(array $backgroundColor, array $color1 = [255, 255, 255], array $color2 = [0, 0, 0], int $ratioBreak = 5): string | ||
{ | ||
$ratio = self::brightnessContrastRatio($backgroundColor, $color2); | ||
|
||
$preferredColor = $ratio < $ratioBreak ? $color1 : $color2; | ||
|
||
return implode(',', $preferredColor); | ||
} | ||
} |
27 changes: 27 additions & 0 deletions
27
src/WeatherGradient/Exception/WeatherGradientException.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
<?php | ||
|
||
/* | ||
* This file is part of the Weather Gradient package. | ||
* | ||
* (c) Vincent Huck <[email protected]> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
namespace Zhb\WeatherGradient\Exception; | ||
|
||
use Throwable; | ||
|
||
class WeatherGradientException extends \Exception | ||
{ | ||
public static function missingOrInvalidNumberOfColors(Throwable $previous = null): self | ||
{ | ||
return new self('Gradient colors must contain at least two RGB colors.', 0, $previous); | ||
} | ||
|
||
public static function arrayColorsNotValid(Throwable $previous = null): self | ||
{ | ||
return new self('Colors must be an associative array with numeric keys only.', 0, $previous); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
<?php | ||
|
||
/* | ||
* This file is part of the Weather Gradient package. | ||
* | ||
* (c) Vincent Huck <[email protected]> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
namespace Zhb\WeatherGradient; | ||
|
||
use Zhb\WeatherGradient\Exception\WeatherGradientException; | ||
|
||
class Gradient | ||
{ | ||
private array $rgbColors = []; | ||
|
||
public function __construct(array $colors) | ||
{ | ||
$this->rgbColors = $colors; | ||
|
||
if (2 > $this->countColors()) { | ||
throw WeatherGradientException::missingOrInvalidNumberOfColors(); | ||
} | ||
|
||
if (!$this->isColorsValid($colors)) { | ||
throw WeatherGradientException::arrayColorsNotValid(); | ||
} | ||
|
||
ksort($this->rgbColors); | ||
} | ||
|
||
private function getRgbColors(): array | ||
{ | ||
return array_values($this->rgbColors); | ||
} | ||
|
||
private function getThresholds(): array | ||
{ | ||
return array_keys($this->rgbColors); | ||
} | ||
|
||
private function countColors(): int | ||
{ | ||
return \count($this->rgbColors); | ||
} | ||
|
||
private function isColorsValid(array $array): bool | ||
{ | ||
if ([] === $array) { | ||
return false; | ||
} | ||
|
||
$keys = $this->getThresholds(); | ||
|
||
// numeric keys only | ||
if ($keys !== array_filter($keys, 'is_int')) { | ||
return false; | ||
} | ||
|
||
return $keys !== range(0, \count($array) - 1); | ||
} | ||
|
||
/** | ||
* Retrieves the color at the given position of the gradient. | ||
*/ | ||
public function colorAtGradientPosition(int $position): RgbColor | ||
{ | ||
$gradientThresholds = $this->getThresholds(); | ||
$gradientRgbs = $this->getRgbColors(); | ||
$colorCount = $this->countColors(); | ||
|
||
$rgbColor = []; | ||
for ($i = 0; $i < $colorCount; ++$i) { | ||
if ($position >= $gradientThresholds[$i] && $position < ($gradientThresholds[$i + 1] ?? $gradientThresholds[$i])) { | ||
$rgb1 = $gradientRgbs[$i]; | ||
$rgb2 = $gradientRgbs[$i + 1]; | ||
|
||
for ($j = 0; $j < 3; ++$j) { | ||
$c = (max($rgb1[$j], $rgb2[$j]) - min($rgb1[$j], $rgb2[$j])) / (max($gradientThresholds[$i], $gradientThresholds[$i + 1]) - min($gradientThresholds[$i], $gradientThresholds[$i + 1])); | ||
|
||
if ($rgb1[$j] < $rgb2[$j]) { | ||
$rgbColor[] = (int) (max($rgb1[$j], $rgb2[$j]) - ((max($gradientThresholds[$i], $gradientThresholds[$i + 1]) - $position) * $c)); | ||
} else { | ||
$rgbColor[] = (int) (min($rgb1[$j], $rgb2[$j]) + ((max($gradientThresholds[$i], $gradientThresholds[$i + 1]) - $position) * $c)); | ||
} | ||
} | ||
} | ||
} | ||
|
||
if ($position <= $gradientThresholds[0]) { | ||
$rgbColor = $gradientRgbs[0]; | ||
} | ||
|
||
if ($position >= $gradientThresholds[$colorCount - 1]) { | ||
$rgbColor = $gradientRgbs[$colorCount - 1]; | ||
} | ||
|
||
return RgbColor::fromRgb($rgbColor); | ||
} | ||
|
||
public static function fromColors(array $colors): self | ||
{ | ||
return new self($colors); | ||
} | ||
} |
Oops, something went wrong.