scheme::rgb()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 6
ccs 4
cts 4
cp 1
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 4
nc 1
nop 4
crap 1
1
<?php
2
/**
3
 * Scheme Class
4
 * 
5
 * This class has all the predefined HSL scheme algorithms.
6
 */
7
8
namespace projectcleverweb\color;
9
10
/**
11
 * Scheme Class
12
 * 
13
 * This class has all the predefined HSL scheme algorithms.
14
 */
15
class scheme {
16
	
17
	/**
18
	 * A static reference to this class. (needed for child class late static binding)
19
	 * @var string
20
	 */
21
	protected static $this_class = __CLASS__;
22
	
23
	/**
24
	 * These colors are all close to each other on a color wheel.
25
	 * 
26
	 * @param  float     $h       The base color hue degree (0 - 359)
27
	 * @param  float     $s       The base color saturation percentage (0 - 100)
28
	 * @param  float     $l       The base color lighting percentage (0 - 100)
29
	 * @param  bool|null $is_dark Whether or not to treat the base color as a dark color. Leave as null to dynamically generate this.
30
	 * @return array              An array of 5 analogous colors where the first offset is the original input.
31
	 */
32 2
	public static function analogous_set (float $h = 0.0, float $s = 0.0, float $l = 0.0, $is_dark = NULL) :array {
33 2
		static::is_dark($is_dark, $h, $s, $l);
34 2
		$al = static::alt_light($l);
35 2
		$as = static::alt_saturation($s);
36
		// No inverting saturation
37 2
		$delta = FALSE;
38 2
		if ($s < 50) {
39 2
			$delta = TRUE;
40
		}
41 2
		return static::_assign_keys([
42 2
			[$h, $s, $l],
43 2
			[static::mod($h, -36, TRUE, 360), $as, $al],
44 2
			[static::mod($h, -18, TRUE, 360), static::mod($as, 6, $delta), static::mod($al, 6, $is_dark)],
45 2
			[static::mod($h, 18, TRUE, 360), static::mod($as, 6, $delta), static::mod($al, 6, $is_dark)],
46 2
			[static::mod($h, 36, TRUE, 360), $as, $al]
47
		]);
48
	}
49
	
50
	/**
51
	 * 2 of these colors are a different shade of the base color. The other 2 are
52
	 * a weighted opposite of the base color.
53
	 * 
54
	 * @param  float     $h       The base color hue degree (0 - 359)
55
	 * @param  float     $s       The base color saturation percentage (0 - 100)
56
	 * @param  float     $l       The base color lighting percentage (0 - 100)
57
	 * @param  bool|null $is_dark Whether or not to treat the base color as a dark color. Leave as null to dynamically generate this.
58
	 * @return array              An array of 5 complementary colors where the first offset is the original input.
59
	 */
60 2 View Code Duplication
	public static function complementary_set (float $h = 0.0, float $s = 0.0, float $l = 0.0, $is_dark = NULL) :array {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
61 2
		static::is_dark($is_dark, $h, $s, $l);
62 2
		$al = static::alt_light($l);
63 2
		$as = static::alt_saturation($s);
64 2
		return static::_assign_keys([
65 2
			[$h, $s, $l],
66 2
			[$h, $as, static::mod($al, 20, $is_dark)],
67 2
			[$h, $as, static::mod($al, 10, $is_dark)],
68 2
			[static::mod($h, 185, TRUE, 360), $as, $al],
69 2
			[static::mod($h, 185, TRUE, 360), $as, static::mod($al, 10, $is_dark)]
70
		]);
71
	}
72
	
73
	/**
74
	 * These colors use mathematical offsets that usually complement each other
75
	 * well, and can highlight the base color.
76
	 * 
77
	 * @param  float     $h       The base color hue degree (0 - 359)
78
	 * @param  float     $s       The base color saturation percentage (0 - 100)
79
	 * @param  float     $l       The base color lighting percentage (0 - 100)
80
	 * @param  bool|null $is_dark Whether or not to treat the base color as a dark color. Leave as null to dynamically generate this.
81
	 * @return array              An array of 5 compounding colors where the first offset is the original input.
82
	 */
83 2
	public static function compound_set (float $h = 0.0, float $s = 0.0, float $l = 0.0, $is_dark = NULL) :array {
84 2
		static::is_dark($is_dark, $h, $s, $l);
85 2
		$al = static::alt_light($l);
86 2
		$as = static::alt_saturation($s);
87
		// No inverting saturation
88 2
		$delta = FALSE;
89 2
		if ($s < 50) {
90 2
			$delta = TRUE;
91
		}
92 2
		return static::_assign_keys([
93 2
			[$h, $s, $l],
94 2
			[static::mod($h, 40, TRUE, 360), static::mod($as, 12, $delta), static::mod($al, 24, $is_dark)],
95 2
			[static::mod($h, 40, TRUE, 360), static::mod($as, 12, $delta), static::mod($al, 16, $is_dark)],
96 2
			[static::mod($h, 135, TRUE, 360), static::mod($as, 12, $delta), static::mod($al, 16, $is_dark)],
97 2
			[static::mod($h, 160, TRUE, 360), static::mod($as, 12, $delta), static::mod($al, 24, $is_dark)]
98
		]);
99
	}
100
	
101
	/**
102
	 * 5 complementary shades of one color.
103
	 * 
104
	 * @param  float     $h       The base color hue degree (0 - 359)
105
	 * @param  float     $s       The base color saturation percentage (0 - 100)
106
	 * @param  float     $l       The base color lighting percentage (0 - 100)
107
	 * @param  bool|null $is_dark Whether or not to treat the base color as a dark color. Leave as null to dynamically generate this.
108
	 * @return array              An array of 5 complementary shades of colors where the first offset is the original input.
109
	 */
110 2
	public static function monochromatic_set (float $h = 0.0, float $s = 0.0, float $l = 0.0, $is_dark = NULL) :array {
111 2
		static::is_dark($is_dark, $h, $s, $l);
112 2
		$al = static::alt_light($l);
113
		// Avoid black & white
114 2
		$delta = 0;
115 2
		if ($l > 40 && $l < 60) {
116 2
			$delta = 30;
117
		}
118 2
		return static::_assign_keys([
119 2
			[$h, $s, $l],
120 2
			[$h, $s, static::mod($al, -8, $is_dark)],
121 2
			[$h, $s, static::mod($al, 8, $is_dark)],
122 2
			[$h, $s, static::mod($al, 55 + $delta, $is_dark)],
123 2
			[$h, $s, static::mod($al, 45 + $delta, $is_dark)]
124
		]);
125
	}
126
	
127
	/**
128
	 * 5 different shades of one color.
129
	 * 
130
	 * @param  float     $h       The base color hue degree (0 - 359)
131
	 * @param  float     $s       The base color saturation percentage (0 - 100)
132
	 * @param  float     $l       The base color lighting percentage (0 - 100)
133
	 * @param  bool|null $is_dark Whether or not to treat the base color as a dark color. Leave as null to dynamically generate this.
134
	 * @return array              An array of 5 shades of a color where the first offset is the original input.
135
	 */
136 12
	public static function shades_set (float $h = 0.0, float $s = 0.0, float $l = 0.0, $is_dark = NULL) :array {
137 12
		static::is_dark($is_dark, $h, $s, $l);
138 12
		$al = static::alt_light($l);
139
		// Avoid black & white
140 12
		$delta = 0;
141 12
		if ($l >= 80) {
142 12
			$delta = -76;
143 12
		} elseif ($l <= 20) {
144 12
			$delta = 24;
145
		}
146 12
		return static::_assign_keys([
147 12
			[$h, $s, $l],
148 12
			[$h, $s, static::mod($al, $delta - 20, $is_dark)],
149 12
			[$h, $s, static::mod($al, $delta - 10, $is_dark)],
150 12
			[$h, $s, static::mod($al, $delta + 8, $is_dark)],
151 12
			[$h, $s, static::mod($al, $delta + 16, $is_dark)]
152
		]);
153
	}
154
	
155
	/**
156
	 * 3 of these colors are all equally distanced from each other on a color
157
	 * wheel, plus 1 alternated shade for the base color and the 1 color that is
158
	 * opposite of the base color.
159
	 * 
160
	 * @param  float     $h       The base color hue degree (0 - 359)
161
	 * @param  float     $s       The base color saturation percentage (0 - 100)
162
	 * @param  float     $l       The base color lighting percentage (0 - 100)
163
	 * @param  bool|null $is_dark Whether or not to treat the base color as a dark color. Leave as null to dynamically generate this.
164
	 * @return array              An array of 5 triangular colors where the first offset is the original input.
165
	 */
166 2 View Code Duplication
	public static function tetrad_set (float $h = 0.0, float $s = 0.0, float $l = 0.0, $is_dark = NULL) :array {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
167 2
		static::is_dark($is_dark, $h, $s, $l);
168 2
		$al = static::alt_light($l);
169 2
		$as = static::alt_saturation($s);
170 2
		return static::_assign_keys([
171 2
			[$h, $s, $l],
172 2
			[static::mod($h, 180, TRUE, 360), $as, $al],
173 2
			[static::mod($h, 120, TRUE, 360), $as, $al],
174 2
			[$h, $as, static::mod($al, 18, $is_dark)],
175 2
			[static::mod($h, -120, TRUE, 360), $as, $al]
176
		]);
177
	}
178
	
179
	/**
180
	 * 3 of these colors are all similarly distanced from each other on a color
181
	 * wheel, the base color has an alternate shade, and there is a weighted
182
	 * opposite color. These colors are all slightly closer to the base color
183
	 * than in a normal tetrad.
184
	 * 
185
	 * @param  float     $h       The base color hue degree (0 - 359)
186
	 * @param  float     $s       The base color saturation percentage (0 - 100)
187
	 * @param  float     $l       The base color lighting percentage (0 - 100)
188
	 * @param  bool|null $is_dark Whether or not to treat the base color as a dark color. Leave as null to dynamically generate this.
189
	 * @return array              An array of 5 triangular colors where the first offset is the original input.
190
	 */
191 2 View Code Duplication
	public static function weighted_tetrad_set (float $h = 0.0, float $s = 0.0, float $l = 0.0, $is_dark = NULL) :array {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
192 2
		static::is_dark($is_dark, $h, $s, $l);
193 2
		$al = static::alt_light($l);
194 2
		$as = static::alt_saturation($s);
195 2
		return static::_assign_keys([
196 2
			[$h, $s, $l],
197 2
			[static::mod($h, 160, TRUE, 360), $as, $al],
198 2
			[static::mod($h, 80, TRUE, 360), $as, $al],
199 2
			[$h, $as, static::mod($al, 18, $is_dark)],
200 2
			[static::mod($h, -80, TRUE, 360), $as, $al]
201
		]);
202
	}
203
	
204
	/**
205
	 * These colors are all equally distanced from each other on a color wheel,
206
	 * 2 of which have an alternate shade.
207
	 * 
208
	 * @param  float     $h       The base color hue degree (0 - 359)
209
	 * @param  float     $s       The base color saturation percentage (0 - 100)
210
	 * @param  float     $l       The base color lighting percentage (0 - 100)
211
	 * @param  bool|null $is_dark Whether or not to treat the base color as a dark color. Leave as null to dynamically generate this.
212
	 * @return array              An array of 5 triangular colors where the first offset is the original input.
213
	 */
214 2 View Code Duplication
	public static function triad_set (float $h = 0.0, float $s = 0.0, float $l = 0.0, $is_dark = NULL) :array {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
215 2
		static::is_dark($is_dark, $h, $s, $l);
216 2
		$al = static::alt_light($l);
217 2
		$as = static::alt_saturation($s);
218 2
		return static::_assign_keys([
219 2
			[$h, $s, $l],
220 2
			[static::mod($h, 120, TRUE, 360), $as, $al],
221 2
			[$h, $as, static::mod($al, 18, $is_dark)],
222 2
			[static::mod($h, -120, TRUE, 360), $as, $al],
223 2
			[static::mod($h, -120, TRUE, 360), $as, static::mod($al, 18, $is_dark)]
224
		]);
225
	}
226
	
227
	/**
228
	 * These colors are all similarly distanced from each other on a color wheel,
229
	 * 2 of which have an alternate shade. These colors are all slightly closer to
230
	 * the base color than in a normal triad.
231
	 * 
232
	 * @param  float     $h       The base color hue degree (0 - 359)
233
	 * @param  float     $s       The base color saturation percentage (0 - 100)
234
	 * @param  float     $l       The base color lighting percentage (0 - 100)
235
	 * @param  bool|null $is_dark Whether or not to treat the base color as a dark color. Leave as null to dynamically generate this.
236
	 * @return array              An array of 5 weighted triangular colors where the first offset is the original input.
237
	 */
238 2 View Code Duplication
	public static function weighted_triad_set (float $h = 0.0, float $s = 0.0, float $l = 0.0, $is_dark = NULL) :array {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
239 2
		static::is_dark($is_dark, $h, $s, $l);
240 2
		$al = static::alt_light($l);
241 2
		$as = static::alt_saturation($s);
242 2
		return static::_assign_keys([
243 2
			[$h, $s, $l],
244 2
			[static::mod($h, 80, TRUE, 360), $as, $al],
245 2
			[$h, $as, static::mod($al, 18, $is_dark)],
246 2
			[static::mod($h, -80, TRUE, 360), $as, $al],
247 2
			[static::mod($h, -80, TRUE, 360), $as, static::mod($al, 18, $is_dark)]
248
		]);
249
	}
250
	
251
	/**
252
	 * 4 of these colors form a rectangle on a color wheel, and 1 color is an
253
	 * alternate shade for the base color.
254
	 * 
255
	 * @param  float     $h       The base color hue degree (0 - 359)
256
	 * @param  float     $s       The base color saturation percentage (0 - 100)
257
	 * @param  float     $l       The base color lighting percentage (0 - 100)
258
	 * @param  bool|null $is_dark Whether or not to treat the base color as a dark color. Leave as null to dynamically generate this.
259
	 * @return array              An array of 5 rectangular colors where the first offset is the original input.
260
	 */
261 2 View Code Duplication
	public static function rectangular_set (float $h = 0.0, float $s = 0.0, float $l = 0.0, $is_dark = NULL) :array {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
262 2
		static::is_dark($is_dark, $h, $s, $l);
263 2
		$al = static::alt_light($l);
264 2
		$as = static::alt_saturation($s);
265 2
		return static::_assign_keys([
266 2
			[$h, $s, $l],
267 2
			[static::mod($h, 216, TRUE, 360), $as, $al],
268 2
			[static::mod($h, 180, TRUE, 360), $as, $al],
269 2
			[$h, $as, static::mod($al, 18, $is_dark)],
270 2
			[static::mod($h, 36, TRUE, 360), $as, $al]
271
		]);
272
	}
273
	
274
	/**
275
	 * Assigns keys to a hsl scheme array
276
	 * 
277
	 * @param  array $scheme The scheme to assign keys in
278
	 * @return array         The resulting scheme
279
	 */
280 30
	protected static function _assign_keys(array $scheme) :array {
281 30
		$keys = ['h', 's', 'l'];
282 30
		foreach ($scheme as &$color) {
283 30
			$color = array_combine($keys, $color);
284
		}
285 30
		return $scheme;
286
	}
287
	
288
	/**
289
	 * This prevents non-base colors from having either a too high or too low
290
	 * light value. If a value is too high or low, the resulting color sets will
291
	 * have many duplicate colors. This method doesn't prevent that, but it will
292
	 * reduce how often duplicate colors appear.
293
	 * 
294
	 * @param  float $light The light value to check
295
	 * @return float        The alternate light value to use
296
	 */
297 30
	protected static function alt_light(float $light = 0.0) {
298 30
		return (float) max(min($light, 93), 7);
299
	}
300
	
301
	/**
302
	 * This prevents non-base colors from having either a too low saturation value.
303
	 * If a value is too low, the resulting color sets will have many duplicate
304
	 * colors. This method doesn't prevent that, but it will reduce how often
305
	 * duplicate colors appear.
306
	 * 
307
	 * @param  float $saturation The saturation value to check
308
	 * @return float             The alternate saturation value to use
309
	 */
310 16
	protected static function alt_saturation(float $saturation = 0.0) {
311 16
		return (float) max($saturation, 7);
312
	}
313
	
314
	/**
315
	 * This allows easy modification of a number while forcing it to fall into a valid range.
316
	 * 
317
	 * @param  float   $number     The number to modify
318
	 * @param  float   $adjustment The amount of change to make to the $number
319
	 * @param  boolean $add        TRUE to add $adjustment to $number, FALSE to subtract $adjustment from $number
320
	 * @param  integer $max        The maximum value to allow. (Minimum is assumed to be 0)
321
	 * @return float               The resulting number.
322
	 */
323 30
	protected static function mod(float $number, float $adjustment, $add = TRUE, $max = 100) :float {
324 30
		if ($add) {
325 30
			return abs($number + $max + $adjustment) % $max;
326
		}
327 30
		return abs($number + $max - $adjustment) % $max;
328
	}
329
	
330
	/**
331
	 * Check if an HSL color is dark (YIQ)
332
	 * 
333
	 * @param  float $h The hue degree (0 - 359)
334
	 * @param  float $s The saturation percentage (0 - 100)
335
	 * @param  float $l The lighting percentage (0 - 100)
336
	 * @return void
337
	 */
338 30
	protected static function is_dark(&$is_dark, float $h = 0.0, float $s = 0.0, float $l = 0.0) {
339 30
		if (is_null($is_dark)) {
340 30
			$rgb     = convert\hsl::to_rgb($h, $s, $l);
0 ignored issues
show
Unused Code introduced by
The call to hsl::to_rgb() has too many arguments starting with $s.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
341 30
			$is_dark = check::is_dark($rgb['r'], $rgb['g'], $rgb['b']);
342
		}
343 30
		settype($is_dark, 'bool');
344 30
	}
345
	
346
	/**
347
	 * Generate a array of 5 rgb colors using the $scheme algorithm
348
	 * 
349
	 * @param  float  $h      The hue value
350
	 * @param  float  $s      The saturation value
351
	 * @param  float  $l      The light value
352
	 * @param  string $scheme The scheme algorithm to use
353
	 * @return array          Array of RGB colors
354
	 */
355 8
	public static function rgb(float $h = 0.0, float $s = 0.0, float $l = 0.0, string $scheme = '') :array {
356 8
		return static::_convert(
357 8
			static::hsl($h, $s, $l, $scheme),
358 8
			[__NAMESPACE__.'\\convert\\hsl', 'to_rgb']
359
		);
360
	}
361
	
362
	/**
363
	 * Generate a array of 5 hsl colors using the $scheme algorithm
364
	 * 
365
	 * @param  float  $h      The hue value
366
	 * @param  float  $s      The saturation value
367
	 * @param  float  $l      The light value
368
	 * @param  string $scheme The scheme algorithm to use
369
	 * @return array          Array of HSL colors
370
	 */
371 12
	public static function hsl(float $h = 0.0, float $s = 0.0, float $l = 0.0, string $scheme = '') :array {
372 12
		if (is_callable($callable = [static::$this_class, $scheme.'_set'])) {
373 10
			return call_user_func($callable, $h, $s, $l);
374
		}
375 2
		error::call(sprintf(
0 ignored issues
show
Bug introduced by
The method call() does not seem to exist on object<projectcleverweb\color\error>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
376 2
			'The $scheme "%s" is not a valid scheme name',
377
			$scheme,
378 2
			__CLASS__,
379 2
			__FUNCTION__
380
		));
381 2
		return [];
382
	}
383
	
384
	/**
385
	 * Generate a array of 5 hsb colors using the $scheme algorithm
386
	 * 
387
	 * @param  float  $h      The hue value
388
	 * @param  float  $s      The saturation value
389
	 * @param  float  $l      The light value
390
	 * @param  string $scheme The scheme algorithm to use
391
	 * @return array          Array of hex colors
392
	 */
393 2
	public static function hsb(float $h = 0.0, float $s = 0.0, float $l = 0.0, string $scheme = '') :array {
394 2
		return static::_convert(
395 2
			static::rgb($h, $s, $l, $scheme),
396 2
			[__NAMESPACE__.'\\convert\\rgb', 'to_hsb']
397
		);
398
	}
399
	
400
	/**
401
	 * Generate a array of 5 hex colors using the $scheme algorithm
402
	 * 
403
	 * @param  float  $h      The hue value
404
	 * @param  float  $s      The saturation value
405
	 * @param  float  $l      The light value
406
	 * @param  string $scheme The scheme algorithm to use
407
	 * @return array          Array of hex colors
408
	 */
409 2
	public static function hex(float $h = 0.0, float $s = 0.0, float $l = 0.0, string $scheme = '') :array {
410 2
		return static::_convert(
411 2
			static::rgb($h, $s, $l, $scheme),
412 2
			[__NAMESPACE__.'\\convert\\rgb', 'to_hex']
413
		);
414
	}
415
	
416
	/**
417
	 * Generate a array of 5 cmyk colors using the $scheme algorithm
418
	 * 
419
	 * @param  float  $h      The hue value
420
	 * @param  float  $s      The saturation value
421
	 * @param  float  $l      The light value
422
	 * @param  string $scheme The scheme algorithm to use
423
	 * @return array          Array of CMYK colors
424
	 */
425 2
	public static function cmyk(float $h = 0.0, float $s = 0.0, float $l = 0.0, string $scheme = '') :array {
426 2
		return static::_convert(
427 2
			static::rgb($h, $s, $l, $scheme),
428 2
			[__NAMESPACE__.'\\convert\\rgb', 'to_cmyk']
429
		);
430
	}
431
	
432
	/**
433
	 * Convert a color scheme to another color space
434
	 * 
435
	 * @param  array    $scheme   The current color scheme
436
	 * @param  callable $callback The conversion callback to use
437
	 * @return array              The converted color scheme
438
	 */
439 8
	protected static function _convert(array $scheme, callable $callback) {
440 8
		$scheme = array_values($scheme);
441 8
		foreach ($scheme as &$color) {
442 8
			$color = call_user_func_array($callback, $color);
443
		}
444 8
		return $scheme;
445
	}
446
}
447