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