Completed
Push — master ( 8a83ee...337dbc )
by Nicholas
03:33
created

scheme::hsb()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 6
ccs 0
cts 4
cp 0
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 4
nc 1
nop 4
crap 2
1
<?php
2
3
namespace projectcleverweb\color;
4
5
/**
6
 * Scheme Class
7
 * 
8
 * This class has all the predefined HSL scheme algorithms.
9
 */
10
class scheme {
11
	
12
	/**
13
	 * These colors are all close to each other on a color wheel.
14
	 * 
15
	 * @param  float     $h       The base color hue degree (0 - 359)
16
	 * @param  float     $s       The base color saturation percentage (0 - 100)
17
	 * @param  float     $l       The base color lighting percentage (0 - 100)
18
	 * @param  bool|null $is_dark Whether or not to treat the base color as a dark color. Leave as null to dynamically generate this.
19
	 * @return array              An array of 5 analogous colors where the first offset is the original input.
20
	 */
21
	public static function analogous_set (float $h = 0.0, float $s = 0.0, float $l = 0.0, $is_dark = NULL) :array {
22
		static::is_dark($is_dark, $h, $s, $l);
23
		$al = static::alt_light($l);
24
		$as = static::alt_saturation($s);
25
		// No inverting saturation
26
		$delta = FALSE;
27
		if ($s < 50) {
28
			$delta = TRUE;
29
		}
30
		return [
31
			[$h, $s, $l],
32
			[static::mod($h, -36, TRUE, 360), $as, $al],
33
			[static::mod($h, -18, TRUE, 360), static::mod($as, 6, $delta), static::mod($al, 6, $is_dark)],
34
			[static::mod($h, 18, TRUE, 360), static::mod($as, 6, $delta), static::mod($al, 6, $is_dark)],
35
			[static::mod($h, 36, TRUE, 360), $as, $al]
36
		];
37
	}
38
	
39
	/**
40
	 * 2 of these colors are a different shade of the base color. The other 2 are
41
	 * a weighted opposite of the base color.
42
	 * 
43
	 * @param  float     $h       The base color hue degree (0 - 359)
44
	 * @param  float     $s       The base color saturation percentage (0 - 100)
45
	 * @param  float     $l       The base color lighting percentage (0 - 100)
46
	 * @param  bool|null $is_dark Whether or not to treat the base color as a dark color. Leave as null to dynamically generate this.
47
	 * @return array              An array of 5 complementary colors where the first offset is the original input.
48
	 */
49 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...
50
		static::is_dark($is_dark, $h, $s, $l);
51
		$al = static::alt_light($l);
52
		$as = static::alt_saturation($s);
53
		return [
54
			[$h, $s, $l],
55
			[$h, $as, static::mod($al, 20, $is_dark)],
56
			[$h, $as, static::mod($al, 10, $is_dark)],
57
			[static::mod($h, 185, TRUE, 360), $as, $al],
58
			[static::mod($h, 185, TRUE, 360), $as, static::mod($al, 10, $is_dark)]
59
		];
60
	}
61
	
62
	/**
63
	 * These colors use mathematical offsets that usually complement each other
64
	 * well, and can highlight the base color.
65
	 * 
66
	 * @param  float     $h       The base color hue degree (0 - 359)
67
	 * @param  float     $s       The base color saturation percentage (0 - 100)
68
	 * @param  float     $l       The base color lighting percentage (0 - 100)
69
	 * @param  bool|null $is_dark Whether or not to treat the base color as a dark color. Leave as null to dynamically generate this.
70
	 * @return array              An array of 5 compounding colors where the first offset is the original input.
71
	 */
72
	public static function compound_set (float $h = 0.0, float $s = 0.0, float $l = 0.0, $is_dark = NULL) :array {
73
		static::is_dark($is_dark, $h, $s, $l);
74
		$al = static::alt_light($l);
75
		$as = static::alt_saturation($s);
76
		// No inverting saturation
77
		$delta = FALSE;
78
		if ($s < 50) {
79
			$delta = TRUE;
80
		}
81
		return [
82
			[$h, $s, $l],
83
			[static::mod($h, 40, TRUE, 360), static::mod($as, 12, $delta), static::mod($al, 24, $is_dark)],
84
			[static::mod($h, 40, TRUE, 360), static::mod($as, 12, $delta), static::mod($al, 16, $is_dark)],
85
			[static::mod($h, 135, TRUE, 360), static::mod($as, 12, $delta), static::mod($al, 16, $is_dark)],
86
			[static::mod($h, 160, TRUE, 360), static::mod($as, 12, $delta), static::mod($al, 24, $is_dark)]
87
		];
88
	}
89
	
90
	/**
91
	 * 5 complementary shades of one color.
92
	 * 
93
	 * @param  float     $h       The base color hue degree (0 - 359)
94
	 * @param  float     $s       The base color saturation percentage (0 - 100)
95
	 * @param  float     $l       The base color lighting percentage (0 - 100)
96
	 * @param  bool|null $is_dark Whether or not to treat the base color as a dark color. Leave as null to dynamically generate this.
97
	 * @return array              An array of 5 complementary shades of colors where the first offset is the original input.
98
	 */
99
	public static function monochromatic_set (float $h = 0.0, float $s = 0.0, float $l = 0.0, $is_dark = NULL) :array {
100
		static::is_dark($is_dark, $h, $s, $l);
101
		$al = static::alt_light($l);
102
		// Avoid black & white
103
		$delta = 0;
104
		if ($l > 40 && $l < 60) {
105
			$delta = 30;
106
		}
107
		return [
108
			[$h, $s, $l],
109
			[$h, $s, static::mod($al, -8, $is_dark)],
110
			[$h, $s, static::mod($al, 8, $is_dark)],
111
			[$h, $s, static::mod($al, 55 + $delta, $is_dark)],
112
			[$h, $s, static::mod($al, 45 + $delta, $is_dark)]
113
		];
114
	}
115
	
116
	/**
117
	 * 5 different shades of one color.
118
	 * 
119
	 * @param  float     $h       The base color hue degree (0 - 359)
120
	 * @param  float     $s       The base color saturation percentage (0 - 100)
121
	 * @param  float     $l       The base color lighting percentage (0 - 100)
122
	 * @param  bool|null $is_dark Whether or not to treat the base color as a dark color. Leave as null to dynamically generate this.
123
	 * @return array              An array of 5 shades of a color where the first offset is the original input.
124
	 */
125
	public static function shades_set (float $h = 0.0, float $s = 0.0, float $l = 0.0, $is_dark = NULL) :array {
126
		static::is_dark($is_dark, $h, $s, $l);
127
		$al = static::alt_light($l);
128
		// Avoid black & white
129
		$delta = 0;
130
		if ($l <= 10 && $l >= 90) {
131
			$delta = 50;
132
		}
133
		return [
134
			[$h, $s, $l],
135
			[$h, $s, max(min(static::mod($al, -20 - $delta, $is_dark), 97), 5)],
136
			[$h, $s, max(min(static::mod($al, -10 - $delta, $is_dark), 97), 5)],
137
			[$h, $s, max(min(static::mod($al, 8 + $delta, $is_dark), 97), 5)],
138
			[$h, $s, max(min(static::mod($al, 16 + $delta, $is_dark), 97), 5)]
139
		];
140
	}
141
	
142
	/**
143
	 * 3 of these colors are all equally distanced from each other on a color
144
	 * wheel, plus 1 alternated shade for the base color and the 1 color that is
145
	 * opposite of the base color.
146
	 * 
147
	 * @param  float     $h       The base color hue degree (0 - 359)
148
	 * @param  float     $s       The base color saturation percentage (0 - 100)
149
	 * @param  float     $l       The base color lighting percentage (0 - 100)
150
	 * @param  bool|null $is_dark Whether or not to treat the base color as a dark color. Leave as null to dynamically generate this.
151
	 * @return array              An array of 5 triangular colors where the first offset is the original input.
152
	 */
153 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...
154
		static::is_dark($is_dark, $h, $s, $l);
155
		$al = static::alt_light($l);
156
		$as = static::alt_saturation($s);
157
		return [
158
			[$h, $s, $l],
159
			[static::mod($h, 180, TRUE, 360), $as, $al],
160
			[static::mod($h, 120, TRUE, 360), $as, $al],
161
			[$h, $as, static::mod($al, 18, $is_dark)],
162
			[static::mod($h, -120, TRUE, 360), $as, $al]
163
		];
164
	}
165
	
166
	/**
167
	 * 3 of these colors are all similarly distanced from each other on a color
168
	 * wheel, the base color has an alternate shade, and there is a weighted
169
	 * opposite color. These colors are all slightly closer to the base color
170
	 * than in a normal tetrad.
171
	 * 
172
	 * @param  float     $h       The base color hue degree (0 - 359)
173
	 * @param  float     $s       The base color saturation percentage (0 - 100)
174
	 * @param  float     $l       The base color lighting percentage (0 - 100)
175
	 * @param  bool|null $is_dark Whether or not to treat the base color as a dark color. Leave as null to dynamically generate this.
176
	 * @return array              An array of 5 triangular colors where the first offset is the original input.
177
	 */
178 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...
179
		static::is_dark($is_dark, $h, $s, $l);
180
		$al = static::alt_light($l);
181
		$as = static::alt_saturation($s);
182
		return [
183
			[$h, $s, $l],
184
			[static::mod($h, 160, TRUE, 360), $as, $al],
185
			[static::mod($h, 80, TRUE, 360), $as, $al],
186
			[$h, $as, static::mod($al, 18, $is_dark)],
187
			[static::mod($h, -80, TRUE, 360), $as, $al]
188
		];
189
	}
190
	
191
	/**
192
	 * These colors are all equally distanced from each other on a color wheel,
193
	 * 2 of which have an alternate shade.
194
	 * 
195
	 * @param  float     $h       The base color hue degree (0 - 359)
196
	 * @param  float     $s       The base color saturation percentage (0 - 100)
197
	 * @param  float     $l       The base color lighting percentage (0 - 100)
198
	 * @param  bool|null $is_dark Whether or not to treat the base color as a dark color. Leave as null to dynamically generate this.
199
	 * @return array              An array of 5 triangular colors where the first offset is the original input.
200
	 */
201 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...
202
		static::is_dark($is_dark, $h, $s, $l);
203
		$al = static::alt_light($l);
204
		$as = static::alt_saturation($s);
205
		return [
206
			[$h, $s, $l],
207
			[static::mod($h, 120, TRUE, 360), $as, $al],
208
			[$h, $as, static::mod($al, 18, $is_dark)],
209
			[static::mod($h, -120, TRUE, 360), $as, $al],
210
			[static::mod($h, -120, TRUE, 360), $as, static::mod($al, 18, $is_dark)]
211
		];
212
	}
213
	
214
	/**
215
	 * These colors are all similarly distanced from each other on a color wheel,
216
	 * 2 of which have an alternate shade. These colors are all slightly closer to
217
	 * the base color than in a normal triad.
218
	 * 
219
	 * @param  float     $h       The base color hue degree (0 - 359)
220
	 * @param  float     $s       The base color saturation percentage (0 - 100)
221
	 * @param  float     $l       The base color lighting percentage (0 - 100)
222
	 * @param  bool|null $is_dark Whether or not to treat the base color as a dark color. Leave as null to dynamically generate this.
223
	 * @return array              An array of 5 weighted triangular colors where the first offset is the original input.
224
	 */
225 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...
226
		static::is_dark($is_dark, $h, $s, $l);
227
		$al = static::alt_light($l);
228
		$as = static::alt_saturation($s);
229
		return [
230
			[$h, $s, $l],
231
			[static::mod($h, 80, TRUE, 360), $as, $al],
232
			[$h, $as, static::mod($al, 18, $is_dark)],
233
			[static::mod($h, -80, TRUE, 360), $as, $al],
234
			[static::mod($h, -80, TRUE, 360), $as, static::mod($al, 18, $is_dark)]
235
		];
236
	}
237
	
238
	/**
239
	 * 4 of these colors form a rectangle on a color wheel, and 1 color is an
240
	 * alternate shade for the base color.
241
	 * 
242
	 * @param  float     $h       The base color hue degree (0 - 359)
243
	 * @param  float     $s       The base color saturation percentage (0 - 100)
244
	 * @param  float     $l       The base color lighting percentage (0 - 100)
245
	 * @param  bool|null $is_dark Whether or not to treat the base color as a dark color. Leave as null to dynamically generate this.
246
	 * @return array              An array of 5 rectangular colors where the first offset is the original input.
247
	 */
248 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...
249
		static::is_dark($is_dark, $h, $s, $l);
250
		$al = static::alt_light($l);
251
		$as = static::alt_saturation($s);
252
		return [
253
			[$h, $s, $l],
254
			[static::mod($h, 216, TRUE, 360), $as, $al],
255
			[static::mod($h, 180, TRUE, 360), $as, $al],
256
			[$h, $as, static::mod($al, 18, $is_dark)],
257
			[static::mod($h, 36, TRUE, 360), $as, $al]
258
		];
259
	}
260
	
261
	/**
262
	 * This prevents non-base colors from having either a too high or too low
263
	 * light value. If a value is too high or low, the resulting color sets will
264
	 * have many duplicate colors. This method doesn't prevent that, but it will
265
	 * reduce how often duplicate colors appear.
266
	 * 
267
	 * @param  float $light The light value to check
268
	 * @return float        The alternate light value to use
269
	 */
270
	protected static function alt_light(float $light = 0.0) {
271
		return (float) max(min($light, 93), 7);
272
	}
273
	
274
	/**
275
	 * This prevents non-base colors from having either a too low saturation value.
276
	 * If a value is too low, the resulting color sets will have many duplicate
277
	 * colors. This method doesn't prevent that, but it will reduce how often
278
	 * duplicate colors appear.
279
	 * 
280
	 * @param  float $saturation The saturation value to check
281
	 * @return float             The alternate saturation value to use
282
	 */
283
	protected static function alt_saturation(float $saturation = 0.0) {
284
		return (float) max($saturation, 7);
285
	}
286
	
287
	/**
288
	 * This allows easy modification of a number while forcing it to fall into a valid range.
289
	 * 
290
	 * @param  float   $number     The number to modify
291
	 * @param  float   $adjustment The amount of change to make to the $number
292
	 * @param  boolean $add        TRUE to add $adjustment to $number, FALSE to subtract $adjustment from $number
293
	 * @param  integer $max        The maximum value to allow. (Minimum is assumed to be 0)
294
	 * @return float               The resulting number.
295
	 */
296
	protected static function mod(float $number, float $adjustment, $add = TRUE, $max = 100) :float {
297
		if ($add) {
298
			return abs($number + $max + $adjustment) % $max;
299
		}
300
		return abs($number + $max - $adjustment) % $max;
301
	}
302
	
303
	/**
304
	 * Check if an HSL color is dark (YIQ)
305
	 * 
306
	 * @param  float $h The hue degree (0 - 359)
307
	 * @param  float $s The saturation percentage (0 - 100)
308
	 * @param  float $l The lighting percentage (0 - 100)
309
	 * @return void
310
	 */
311
	protected static function is_dark(&$is_dark, float $h = 0.0, float $s = 0.0, float $l = 0.0) {
312
		if (is_null($is_dark)) {
313
			$rgb     = generate::hsl_to_rgb($h, $s, $l);
314
			$is_dark = check::is_dark($rgb['r'], $rgb['g'], $rgb['b']);
315
		}
316
		settype($is_dark, 'bool');
317
	}
318
	
319
	/**
320
	 * Generate a array of 5 rgb colors using the $scheme algorithm
321
	 * 
322
	 * @param  float  $h      The hue value
323
	 * @param  float  $s      The saturation value
324
	 * @param  float  $l      The light value
325
	 * @param  string $scheme The scheme algorithm to use
326
	 * @return array          Array of RGB colors
327
	 */
328
	public static function rgb(float $h = 0.0, float $s = 0.0, float $l = 0.0, string $scheme = '') :array {
329
		return static::_convert(
330
			static::hsl($h, $s, $l, $scheme),
331
			[new generate, 'hsl_to_rgb']
332
		);
333
	}
334
	
335
	/**
336
	 * Generate a array of 5 hsl colors using the $scheme algorithm
337
	 * 
338
	 * @param  float  $h      The hue value
339
	 * @param  float  $s      The saturation value
340
	 * @param  float  $l      The light value
341
	 * @param  string $scheme The scheme algorithm to use
342
	 * @return array          Array of HSL colors
343
	 */
344
	public static function hsl(float $h = 0.0, float $s = 0.0, float $l = 0.0, string $scheme = '') :array {
345
		if (is_callable($callable = [new scheme, $scheme.'_set'])) {
346
			return call_user_func($callable, $h, $s, $l);
347
		}
348
		error::call(sprintf(
349
			'The $scheme "%s" is not a valid scheme name',
350
			$scheme,
351
			__CLASS__,
352
			__FUNCTION__
353
		));
354
		return [];
355
	}
356
	
357
	/**
358
	 * Generate a array of 5 hsb colors using the $scheme algorithm
359
	 * 
360
	 * @param  float  $h      The hue value
361
	 * @param  float  $s      The saturation value
362
	 * @param  float  $l      The light value
363
	 * @param  string $scheme The scheme algorithm to use
364
	 * @return array          Array of hex colors
365
	 */
366
	public static function hsb(float $h = 0.0, float $s = 0.0, float $l = 0.0, string $scheme = '') :array {
367
		return static::_convert(
368
			static::rgb($h, $s, $l, $scheme),
369
			[new generate, 'rgb_to_hsb']
370
		);
371
	}
372
	
373
	/**
374
	 * Generate a array of 5 hex colors using the $scheme algorithm
375
	 * 
376
	 * @param  float  $h      The hue value
377
	 * @param  float  $s      The saturation value
378
	 * @param  float  $l      The light value
379
	 * @param  string $scheme The scheme algorithm to use
380
	 * @return array          Array of hex colors
381
	 */
382
	public static function hex(float $h = 0.0, float $s = 0.0, float $l = 0.0, string $scheme = '') :array {
383
		return static::_convert(
384
			static::rgb($h, $s, $l, $scheme),
385
			[new generate, 'rgb_to_hex']
386
		);
387
	}
388
	
389
	/**
390
	 * Generate a array of 5 cmyk colors using the $scheme algorithm
391
	 * 
392
	 * @param  float  $h      The hue value
393
	 * @param  float  $s      The saturation value
394
	 * @param  float  $l      The light value
395
	 * @param  string $scheme The scheme algorithm to use
396
	 * @return array          Array of CMYK colors
397
	 */
398
	public static function cmyk(float $h = 0.0, float $s = 0.0, float $l = 0.0, string $scheme = '') :array {
399
		return static::_convert(
400
			static::rgb($h, $s, $l, $scheme),
401
			[new generate, 'rgb_to_cmyk']
402
		);
403
	}
404
	
405
	/**
406
	 * Convert a color scheme to another color space
407
	 * 
408
	 * @param  array    $scheme   The current color scheme
409
	 * @param  callable $callback The conversion callback to use
410
	 * @return array              The converted color scheme
411
	 */
412
	protected static function _convert(array $scheme, callable $callback) {
413
		$scheme = array_values($scheme);
414
		foreach ($scheme as &$color) {
415
			$color = call_user_func_array($callback, $color);
416
		}
417
		return $scheme;
418
	}
419
}
420