1<?php 2/** 3 * Copyright (c) 2020. ComboStrap, Inc. and its affiliates. All Rights Reserved. 4 * 5 * This source code is licensed under the GPL license found in the 6 * COPYING file in the root directory of this source tree. 7 * 8 * @license GPL 3 (https://www.gnu.org/licenses/gpl-3.0.en.html) 9 * @author ComboStrap <support@combostrap.com> 10 * 11 */ 12 13namespace ComboStrap; 14class ColorHsl 15{ 16 17 const CANONICAL = "color"; 18 private $hue; 19 20 private $saturation; 21 /** 22 * @var float|int 23 */ 24 private $lightness; 25 26 /** 27 * ColorHsl constructor. 28 * @param $hue 29 * @param $saturation 30 * @param float|int $lightness 31 */ 32 public function __construct($hue, $saturation, $lightness) 33 { 34 $this->hue = $hue; 35 $this->saturation = $saturation; 36 $this->lightness = $lightness; 37 } 38 39 40 public static function createFromChannels(float $hue, float $saturation, float $lightness): ColorHsl 41 { 42 return new ColorHsl($hue, $saturation, $lightness); 43 } 44 45 public function getLightness() 46 { 47 return $this->lightness; 48 } 49 50 public function getSaturation() 51 { 52 return $this->saturation; 53 } 54 55 public function getHue() 56 { 57 return $this->hue; 58 } 59 60 /** 61 * @throws ExceptionCompile 62 */ 63 public function setLightness(int $int): ColorHsl 64 { 65 if ($int < 0 || $int > 100) { 66 throw new ExceptionCompile("Lightness should be between 0 and 100"); 67 } 68 $this->lightness = $int; 69 return $this; 70 } 71 72 /** 73 * @return ColorRgb Reference: 74 * 75 * Reference: 76 * https://en.wikipedia.org/wiki/HSL_and_HSV#HSL_to_RGB 77 * https://gist.github.com/brandonheyer/5254516 78 * @throws ExceptionCompile 79 */ 80 function toRgb(): ColorRgb 81 { 82 83 $lightness = $this->lightness / 100; 84 $saturation = $this->saturation / 100; 85 $hue = $this->hue; 86 87 $chroma = (1 - abs(2 * $lightness - 1)) * $saturation; 88 $x = $chroma * (1 - abs(fmod(($hue / 60), 2) - 1)); 89 $m = $lightness - ($chroma / 2); 90 91 if ($hue < 60) { 92 $red = $chroma; 93 $green = $x; 94 $blue = 0; 95 } else if ($hue < 120) { 96 $red = $x; 97 $green = $chroma; 98 $blue = 0; 99 } else if ($hue < 180) { 100 $red = 0; 101 $green = $chroma; 102 $blue = $x; 103 } else if ($hue < 240) { 104 $red = 0; 105 $green = $x; 106 $blue = $chroma; 107 } else if ($hue < 300) { 108 $red = $x; 109 $green = 0; 110 $blue = $chroma; 111 } else { 112 $red = $chroma; 113 $green = 0; 114 $blue = $x; 115 } 116 117 $red = ($red + $m) * 255; 118 $green = ($green + $m) * 255; 119 $blue = ($blue + $m) * 255; 120 121 /** 122 * To the closest integer 123 */ 124 try { 125 return ColorRgb::createFromRgbChannels( 126 intval(round($red)), 127 intval(round($green)), 128 intval(round($blue)) 129 ); 130 } catch (ExceptionCompile $e) { 131 // should not happen but yeah, who knows 132 // and because there is no safe constructor, no safe default, we throw 133 $message = "Error while creating the rgb color from the hsl ($this)"; 134 throw new ExceptionCompile($message, self::CANONICAL, 0, $e); 135 } 136 137 } 138 139 /** 140 * @throws ExceptionCompile 141 */ 142 public function setSaturation(int $saturation): ColorHsl 143 { 144 if ($saturation < 0 || $saturation > 100) { 145 throw new ExceptionCompile("Saturation should be between 0 and 100"); 146 } 147 $this->saturation = $saturation; 148 return $this; 149 } 150 151 public function toComplement(): ColorHsl 152 { 153 // Adjust Hue 180 degrees 154 $this->hue += ($this->hue > 180) ? -180 : 180; 155 return $this; 156 } 157 158 public function __toString() 159 { 160 return "hsl($this->hue deg, $this->saturation%, $this->lightness%)"; 161 } 162 163 public function darken(int $lightness = 5): ColorHsl 164 { 165 if ($this->lightness - $lightness < 0) { 166 $this->lightness = 0; 167 } 168 $this->lightness -= $lightness; 169 return $this; 170 } 171 172 /** 173 * @throws ExceptionCompile 174 */ 175 public function diff($color): array 176 { 177 if ($color instanceof ColorRgb) { 178 $color = $color->toHsl(); 179 } 180 181 return [ 182 "h" => round($this->getHue() - $color->getHue(), 2), 183 "s" => round($this->getSaturation() - $color->getSaturation(), 2), 184 "l" => round($this->getLightness() - $color->getLightness(), 2), 185 ]; 186 } 187 188 189} 190