Completed
Push — master ( f86bb9...a6139a )
by Nicholas
02:54
created

scheme::triad()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 12
Code Lines 10

Duplication

Lines 12
Ratio 100 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 12
loc 12
ccs 0
cts 9
cp 0
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 10
nc 1
nop 4
crap 2
1
<?php
2
3
4
namespace projectcleverweb\color;
5
6
/**
7
 * Scheme Class
8
 * 
9
 * This class has all the predefined HSL scheme algorithms.
10
 */
11
class scheme {
12
	
13
	/**
14
	 * These colors are all close to each other on a color wheel.
15
	 * 
16
	 * @param  float|integer $h       The base color hue degree (0 - 359)
17
	 * @param  float|integer $s       The base color saturation percentage (0 - 100)
18
	 * @param  float|integer $l       The base color lighting percentage (0 - 100)
19
	 * @param  bool|null     $is_dark Whether or not to treat the base color as a dark color. Leave as null to dynamically generate this.
20
	 * @return array                  An array of 5 analogous colors where the first offset is the original input.
21
	 */
22
	public static function analogous_set (float $h = 0, float $s = 0, float $l = 0, $is_dark = NULL) :array {
23
		static::is_dark($is_dark, $h, $s, $l);
24
		$al = static::alt_light($l);
25
		$as = static::alt_saturation($s);
26
		// No inverting saturation
27
		$delta = FALSE;
28
		if ($s < 50) {
29
			$delta = TRUE;
30
		}
31
		return [
32
			[$h, $s, $l],
33
			[static::mod($h, -36, TRUE, 360), $as, $al],
34
			[static::mod($h, -18, TRUE, 360), static::mod($as, 6, $delta), static::mod($al, 6, $is_dark)],
35
			[static::mod($h, 18, TRUE, 360), static::mod($as, 6, $delta), static::mod($al, 6, $is_dark)],
36
			[static::mod($h, 36, TRUE, 360), $as, $al]
37
		];
38
	}
39
	
40
	/**
41
	 * 2 of these colors are a different shade of the base color. The other 2 are
42
	 * a weighted opposite of the base color.
43
	 * 
44
	 * @param  float|integer $h       The base color hue degree (0 - 359)
45
	 * @param  float|integer $s       The base color saturation percentage (0 - 100)
46
	 * @param  float|integer $l       The base color lighting percentage (0 - 100)
47
	 * @param  bool|null     $is_dark Whether or not to treat the base color as a dark color. Leave as null to dynamically generate this.
48
	 * @return array                  An array of 5 complementary colors where the first offset is the original input.
49
	 */
50 View Code Duplication
	public static function complementary_set (float $h = 0, float $s = 0, float $l = 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...
51
		static::is_dark($is_dark, $h, $s, $l);
52
		$al = static::alt_light($l);
53
		$as = static::alt_saturation($s);
54
		return [
55
			[$h, $s, $l],
56
			[$h, $as, static::mod($al, 20, $is_dark)],
57
			[$h, $as, static::mod($al, 10, $is_dark)],
58
			[static::mod($h, 185, TRUE, 360), $as, $al],
59
			[static::mod($h, 185, TRUE, 360), $as, static::mod($al, 10, $is_dark)]
60
		];
61
	}
62
	
63
	/**
64
	 * These colors use mathematical offsets that usually complement each other
65
	 * well, and can highlight the base color.
66
	 * 
67
	 * @param  float|integer $h       The base color hue degree (0 - 359)
68
	 * @param  float|integer $s       The base color saturation percentage (0 - 100)
69
	 * @param  float|integer $l       The base color lighting percentage (0 - 100)
70
	 * @param  bool|null     $is_dark Whether or not to treat the base color as a dark color. Leave as null to dynamically generate this.
71
	 * @return array                  An array of 5 compounding colors where the first offset is the original input.
72
	 */
73
	public static function compound_set (float $h = 0, float $s = 0, float $l = 0, $is_dark = NULL) :array {
74
		static::is_dark($is_dark, $h, $s, $l);
75
		$al = static::alt_light($l);
76
		$as = static::alt_saturation($s);
77
		// No inverting saturation
78
		$delta = FALSE;
79
		if ($s < 50) {
80
			$delta = TRUE;
81
		}
82
		return [
83
			[$h, $s, $l],
84
			[static::mod($h, 40, TRUE, 360), static::mod($as, 12, $delta), static::mod($al, 24, $is_dark)],
85
			[static::mod($h, 40, TRUE, 360), static::mod($as, 12, $delta), static::mod($al, 16, $is_dark)],
86
			[static::mod($h, 135, TRUE, 360), static::mod($as, 12, $delta), static::mod($al, 16, $is_dark)],
87
			[static::mod($h, 160, TRUE, 360), static::mod($as, 12, $delta), static::mod($al, 24, $is_dark)]
88
		];
89
	}
90
	
91
	/**
92
	 * 5 complementary shades of one color.
93
	 * 
94
	 * @param  float|integer $h       The base color hue degree (0 - 359)
95
	 * @param  float|integer $s       The base color saturation percentage (0 - 100)
96
	 * @param  float|integer $l       The base color lighting percentage (0 - 100)
97
	 * @param  bool|null     $is_dark Whether or not to treat the base color as a dark color. Leave as null to dynamically generate this.
98
	 * @return array                  An array of 5 complementary shades of colors where the first offset is the original input.
99
	 */
100
	public static function monochromatic_set (float $h = 0, float $s = 0, float $l = 0, $is_dark = NULL) :array {
101
		static::is_dark($is_dark, $h, $s, $l);
102
		$al = static::alt_light($l);
103
		// Avoid black & white
104
		$delta = 0;
105
		if ($l > 40 && $l < 60) {
106
			$delta = 30;
107
		}
108
		return [
109
			[$h, $s, $l],
110
			[$h, $s, static::mod($al, -8, $is_dark)],
111
			[$h, $s, static::mod($al, 8, $is_dark)],
112
			[$h, $s, static::mod($al, 55 + $delta, $is_dark)],
113
			[$h, $s, static::mod($al, 45 + $delta, $is_dark)]
114
		];
115
	}
116
	
117
	/**
118
	 * 5 different shades of one color.
119
	 * 
120
	 * @param  float|integer $h       The base color hue degree (0 - 359)
121
	 * @param  float|integer $s       The base color saturation percentage (0 - 100)
122
	 * @param  float|integer $l       The base color lighting percentage (0 - 100)
123
	 * @param  bool|null     $is_dark Whether or not to treat the base color as a dark color. Leave as null to dynamically generate this.
124
	 * @return array                  An array of 5 shades of a color where the first offset is the original input.
125
	 */
126
	public static function shades_set (float $h = 0, float $s = 0, float $l = 0, $is_dark = NULL) :array {
127
		static::is_dark($is_dark, $h, $s, $l);
128
		$al = static::alt_light($l);
129
		// Avoid black & white
130
		$delta = 0;
131
		if ($l <= 10 && $l >= 90) {
132
			$delta = 50;
133
		}
134
		return [
135
			[$h, $s, $l],
136
			[$h, $s, max(min(static::mod($al, -20 - $delta, $is_dark), 97), 5)],
137
			[$h, $s, max(min(static::mod($al, -10 - $delta, $is_dark), 97), 5)],
138
			[$h, $s, max(min(static::mod($al, 8 + $delta, $is_dark), 97), 5)],
139
			[$h, $s, max(min(static::mod($al, 16 + $delta, $is_dark), 97), 5)]
140
		];
141
	}
142
	
143
	/**
144
	 * 3 of these colors are all equally distanced from each other on a color
145
	 * wheel, plus 1 alternated shade for the base color and the 1 color that is
146
	 * opposite of the base color.
147
	 * 
148
	 * @param  float|integer $h       The base color hue degree (0 - 359)
149
	 * @param  float|integer $s       The base color saturation percentage (0 - 100)
150
	 * @param  float|integer $l       The base color lighting percentage (0 - 100)
151
	 * @param  bool|null     $is_dark Whether or not to treat the base color as a dark color. Leave as null to dynamically generate this.
152
	 * @return array                  An array of 5 triangular colors where the first offset is the original input.
153
	 */
154 View Code Duplication
	public static function tetrad_set (float $h = 0, float $s = 0, float $l = 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...
155
		static::is_dark($is_dark, $h, $s, $l);
156
		$al = static::alt_light($l);
157
		$as = static::alt_saturation($s);
158
		return [
159
			[$h, $s, $l],
160
			[static::mod($h, 180, TRUE, 360), $as, $al],
161
			[static::mod($h, 120, TRUE, 360), $as, $al],
162
			[$h, $as, static::mod($al, 18, $is_dark)],
163
			[static::mod($h, -120, TRUE, 360), $as, $al]
164
		];
165
	}
166
	
167
	/**
168
	 * 3 of these colors are all similarly distanced from each other on a color
169
	 * wheel, the base color has an alternate shade, and there is a weighted
170
	 * opposite color. These colors are all slightly closer to the base color
171
	 * than in a normal tetrad.
172
	 * 
173
	 * @param  float|integer $h       The base color hue degree (0 - 359)
174
	 * @param  float|integer $s       The base color saturation percentage (0 - 100)
175
	 * @param  float|integer $l       The base color lighting percentage (0 - 100)
176
	 * @param  bool|null     $is_dark Whether or not to treat the base color as a dark color. Leave as null to dynamically generate this.
177
	 * @return array                  An array of 5 triangular colors where the first offset is the original input.
178
	 */
179 View Code Duplication
	public static function weighted_tetrad_set (float $h = 0, float $s = 0, float $l = 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...
180
		static::is_dark($is_dark, $h, $s, $l);
181
		$al = static::alt_light($l);
182
		$as = static::alt_saturation($s);
183
		return [
184
			[$h, $s, $l],
185
			[static::mod($h, 160, TRUE, 360), $as, $al],
186
			[static::mod($h, 80, TRUE, 360), $as, $al],
187
			[$h, $as, static::mod($al, 18, $is_dark)],
188
			[static::mod($h, -80, TRUE, 360), $as, $al]
189
		];
190
	}
191
	
192
	/**
193
	 * These colors are all equally distanced from each other on a color wheel,
194
	 * 2 of which have an alternate shade.
195
	 * 
196
	 * @param  float|integer $h       The base color hue degree (0 - 359)
197
	 * @param  float|integer $s       The base color saturation percentage (0 - 100)
198
	 * @param  float|integer $l       The base color lighting percentage (0 - 100)
199
	 * @param  bool|null     $is_dark Whether or not to treat the base color as a dark color. Leave as null to dynamically generate this.
200
	 * @return array                  An array of 5 triangular colors where the first offset is the original input.
201
	 */
202 View Code Duplication
	public static function triad_set (float $h = 0, float $s = 0, float $l = 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...
203
		static::is_dark($is_dark, $h, $s, $l);
204
		$al = static::alt_light($l);
205
		$as = static::alt_saturation($s);
206
		return [
207
			[$h, $s, $l],
208
			[static::mod($h, 120, TRUE, 360), $as, $al],
209
			[$h, $as, static::mod($al, 18, $is_dark)],
210
			[static::mod($h, -120, TRUE, 360), $as, $al],
211
			[static::mod($h, -120, TRUE, 360), $as, static::mod($al, 18, $is_dark)]
212
		];
213
	}
214
	
215
	/**
216
	 * These colors are all similarly distanced from each other on a color wheel,
217
	 * 2 of which have an alternate shade. These colors are all slightly closer to
218
	 * the base color than in a normal triad.
219
	 * 
220
	 * @param  float|integer $h       The base color hue degree (0 - 359)
221
	 * @param  float|integer $s       The base color saturation percentage (0 - 100)
222
	 * @param  float|integer $l       The base color lighting percentage (0 - 100)
223
	 * @param  bool|null     $is_dark Whether or not to treat the base color as a dark color. Leave as null to dynamically generate this.
224
	 * @return array                  An array of 5 weighted triangular colors where the first offset is the original input.
225
	 */
226 View Code Duplication
	public static function weighted_triad_set (float $h = 0, float $s = 0, float $l = 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...
227
		static::is_dark($is_dark, $h, $s, $l);
228
		$al = static::alt_light($l);
229
		$as = static::alt_saturation($s);
230
		return [
231
			[$h, $s, $l],
232
			[static::mod($h, 80, TRUE, 360), $as, $al],
233
			[$h, $as, static::mod($al, 18, $is_dark)],
234
			[static::mod($h, -80, TRUE, 360), $as, $al],
235
			[static::mod($h, -80, TRUE, 360), $as, static::mod($al, 18, $is_dark)]
236
		];
237
	}
238
	
239
	/**
240
	 * 4 of these colors form a rectangle on a color wheel, and 1 color is an
241
	 * alternate shade for the base color.
242
	 * 
243
	 * @param  float|integer $h       The base color hue degree (0 - 359)
244
	 * @param  float|integer $s       The base color saturation percentage (0 - 100)
245
	 * @param  float|integer $l       The base color lighting percentage (0 - 100)
246
	 * @param  bool|null     $is_dark Whether or not to treat the base color as a dark color. Leave as null to dynamically generate this.
247
	 * @return array                  An array of 5 rectangular colors where the first offset is the original input.
248
	 */
249 View Code Duplication
	public static function rectangular_set (float $h = 0, float $s = 0, float $l = 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...
250
		static::is_dark($is_dark, $h, $s, $l);
251
		$al = static::alt_light($l);
252
		$as = static::alt_saturation($s);
253
		return [
254
			[$h, $s, $l],
255
			[static::mod($h, 216, TRUE, 360), $as, $al],
256
			[static::mod($h, 180, TRUE, 360), $as, $al],
257
			[$h, $as, static::mod($al, 18, $is_dark)],
258
			[static::mod($h, 36, TRUE, 360), $as, $al]
259
		];
260
	}
261
	
262
	/**
263
	 * This prevents non-base colors from having either a too high or too low
264
	 * light value. If a value is too high or low, the resulting color sets will
265
	 * have many duplicate colors. This method doesn't prevent that, but it will
266
	 * reduce how often duplicate colors appear.
267
	 * 
268
	 * @param  float  $light The light value to check
269
	 * @return float         The alternate light value to use
270
	 */
271
	protected static function alt_light(float $light = 0.0) {
272
		return (float) max(min($light, 93), 7);
273
	}
274
	
275
	/**
276
	 * This prevents non-base colors from having either a too low saturation value.
277
	 * If a value is too low, the resulting color sets will have many duplicate
278
	 * colors. This method doesn't prevent that, but it will reduce how often
279
	 * duplicate colors appear.
280
	 * 
281
	 * @param  float  $light The light value to check
0 ignored issues
show
Bug introduced by
There is no parameter named $light. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
282
	 * @return float         The alternate light value to use
283
	 */
284
	protected static function alt_saturation(float $saturation = 0.0) {
285
		return (float) max($saturation, 7);
286
	}
287
	
288
	/**
289
	 * This allows easy modification of a number while forcing it to fall into a valid range.
290
	 * 
291
	 * @param  float   $number     The number to modify
292
	 * @param  float   $adjustment The amount of change to make to the $number
293
	 * @param  boolean $add        TRUE to add $adjustment to $number, FALSE to subtract $adjustment from $number
294
	 * @param  integer $max        The maximum value to allow. (Minimum is assumed to be 0)
295
	 * @return float               The resulting number.
296
	 */
297
	protected static function mod(float $number, float $adjustment, $add = TRUE, $max = 100) :float {
298
		if ($add) {
299
			return abs($number + $max + $adjustment) % $max;
300
		}
301
		return abs($number + $max - $adjustment) % $max;
302
	}
303
	
304
	/**
305
	 * Check if an HSL color is dark (YIQ)
306
	 * 
307
	 * @param  float|integer $h The hue degree (0 - 359)
308
	 * @param  float|integer $s The saturation percentage (0 - 100)
309
	 * @param  float|integer $l The lighting percentage (0 - 100)
310
	 * @return boolean          TRUE if the color is dark, FALSE otherwise.
0 ignored issues
show
Documentation introduced by
Should the return type not be boolean|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
311
	 */
312
	protected static function is_dark(&$is_dark, float $h = 0, float $s = 0, float $l = 0) {
313
		if (is_null($is_dark)) {
314
			$rgb = generate::hsl_to_rgb($h, $s, $l);
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 5 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
315
			$is_dark = check::is_dark($rgb['r'], $rgb['g'], $rgb['b']);
316
		}
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 hex 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 hex(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_hex']
370
		);
371
	}
372
	
373
	/**
374
	 * Generate a array of 5 cmyk 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 CMYK colors
381
	 */
382
	public static function cmyk(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_cmyk']
386
		);
387
	}
388
	
389
	/**
390
	 * Convert a color scheme to another color space
391
	 * 
392
	 * @param  array    $scheme   The current color scheme
393
	 * @param  callable $callback The conversion callback to use
394
	 * @return array              The converted color scheme
395
	 */
396
	protected static function _convert(array $scheme, callable $callback) {
397
		$scheme = array_values($scheme);
398
		foreach ($scheme as &$color) {
399
			$color = call_user_func_array($callback, $color);
400
		}
401
		return $scheme;
402
	}
403
}
404