xref: /template/strap/ComboStrap/ColorHsl.php (revision 4cadd4f8c541149bdda95f080e38a6d4e3a640ca)
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