* */ namespace ComboStrap; class ColorHsl { const CANONICAL = "color"; private $hue; private $saturation; /** * @var float|int */ private $lightness; /** * ColorHsl constructor. * @param $hue * @param $saturation * @param float|int $lightness */ public function __construct($hue, $saturation, $lightness) { $this->hue = $hue; $this->saturation = $saturation; $this->lightness = $lightness; } public static function createFromChannels(float $hue, float $saturation, float $lightness): ColorHsl { return new ColorHsl($hue, $saturation, $lightness); } public function getLightness() { return $this->lightness; } public function getSaturation() { return $this->saturation; } public function getHue() { return $this->hue; } /** * @throws ExceptionCompile */ public function setLightness(int $int): ColorHsl { if ($int < 0 || $int > 100) { throw new ExceptionCompile("Lightness should be between 0 and 100"); } $this->lightness = $int; return $this; } /** * @return ColorRgb Reference: * * Reference: * https://en.wikipedia.org/wiki/HSL_and_HSV#HSL_to_RGB * https://gist.github.com/brandonheyer/5254516 * @throws ExceptionCompile */ function toRgb(): ColorRgb { $lightness = $this->lightness / 100; $saturation = $this->saturation / 100; $hue = $this->hue; $chroma = (1 - abs(2 * $lightness - 1)) * $saturation; $x = $chroma * (1 - abs(fmod(($hue / 60), 2) - 1)); $m = $lightness - ($chroma / 2); if ($hue < 60) { $red = $chroma; $green = $x; $blue = 0; } else if ($hue < 120) { $red = $x; $green = $chroma; $blue = 0; } else if ($hue < 180) { $red = 0; $green = $chroma; $blue = $x; } else if ($hue < 240) { $red = 0; $green = $x; $blue = $chroma; } else if ($hue < 300) { $red = $x; $green = 0; $blue = $chroma; } else { $red = $chroma; $green = 0; $blue = $x; } $red = ($red + $m) * 255; $green = ($green + $m) * 255; $blue = ($blue + $m) * 255; /** * To the closest integer */ try { return ColorRgb::createFromRgbChannels( intval(round($red)), intval(round($green)), intval(round($blue)) ); } catch (ExceptionCompile $e) { // should not happen but yeah, who knows // and because there is no safe constructor, no safe default, we throw $message = "Error while creating the rgb color from the hsl ($this)"; throw new ExceptionCompile($message, self::CANONICAL, 0, $e); } } /** * @throws ExceptionCompile */ public function setSaturation(int $saturation): ColorHsl { if ($saturation < 0 || $saturation > 100) { throw new ExceptionCompile("Saturation should be between 0 and 100"); } $this->saturation = $saturation; return $this; } public function toComplement(): ColorHsl { // Adjust Hue 180 degrees $this->hue += ($this->hue > 180) ? -180 : 180; return $this; } public function __toString() { return "hsl($this->hue deg, $this->saturation%, $this->lightness%)"; } public function darken(int $lightness = 5): ColorHsl { if ($this->lightness - $lightness < 0) { $this->lightness = 0; } $this->lightness -= $lightness; return $this; } /** * @throws ExceptionCompile */ public function diff($color): array { if ($color instanceof ColorRgb) { $color = $color->toHsl(); } return [ "h" => round($this->getHue() - $color->getHue(), 2), "s" => round($this->getSaturation() - $color->getSaturation(), 2), "l" => round($this->getLightness() - $color->getLightness(), 2), ]; } }