Math: add epsilon compare for float, update Color Coordinate calls
Math has a compare with epsilon for float numbers. Use this for fixing sligth color conversion issues. NOTE: this might need some adjustment over time All phpunint tests written and checked
This commit is contained in:
@@ -16,6 +16,8 @@ final class CoreLibsConvertColorTest extends TestCase
|
|||||||
{
|
{
|
||||||
// 12 precision allowed, RGB and back has a lot of float imprecisions here
|
// 12 precision allowed, RGB and back has a lot of float imprecisions here
|
||||||
private const DELTA = 0.000000000001;
|
private const DELTA = 0.000000000001;
|
||||||
|
// rgb to oklab and back will have slight shift
|
||||||
|
private const DELTA_OKLAB = 1.05;
|
||||||
|
|
||||||
// sRGB base convert test, should round around and come out the same
|
// sRGB base convert test, should round around and come out the same
|
||||||
// use for RGB 0, 0, 0 in 60 steps and then max 255
|
// use for RGB 0, 0, 0 in 60 steps and then max 255
|
||||||
@@ -32,10 +34,12 @@ final class CoreLibsConvertColorTest extends TestCase
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: single test
|
||||||
|
|
||||||
public function testSingle()
|
public function testSingle()
|
||||||
{
|
{
|
||||||
$this->assertTrue(true, 'Single test');
|
$this->assertTrue(true, 'Single test');
|
||||||
// $rgb = Color\Coordinates\RGB::__constructFromArray([0, 0, 60]);
|
// $rgb = new Color\Coordinates\RGB([0, 0, 60]);
|
||||||
// print "IN: " . print_r($rgb, true) . "\n";
|
// print "IN: " . print_r($rgb, true) . "\n";
|
||||||
// $hsl = Color\Color::rgbToHsl($rgb);
|
// $hsl = Color\Color::rgbToHsl($rgb);
|
||||||
// print "to HSL: " . print_r($hsl, true) . "\n";
|
// print "to HSL: " . print_r($hsl, true) . "\n";
|
||||||
@@ -51,7 +55,7 @@ final class CoreLibsConvertColorTest extends TestCase
|
|||||||
// $rgb_r = Color\Color::hslToRgb($hsl_r);
|
// $rgb_r = Color\Color::hslToRgb($hsl_r);
|
||||||
// print "R to RGB: " . print_r($rgb_r, true) . "\n";
|
// print "R to RGB: " . print_r($rgb_r, true) . "\n";
|
||||||
|
|
||||||
// $hsl = Color\Coordinates\HSL::__constructFromArray([0, 0, 0]);
|
// $hsl = new Color\Coordinates\HSL([0, 0, 0]);
|
||||||
// print "IN HSL: " . print_r($hsl, true) . "\n";
|
// print "IN HSL: " . print_r($hsl, true) . "\n";
|
||||||
// $hsb = Color\Color::hslToHsb($hsl);
|
// $hsb = Color\Color::hslToHsb($hsl);
|
||||||
// print "to HSB: " . print_r($hsb, true) . "\n";
|
// print "to HSB: " . print_r($hsb, true) . "\n";
|
||||||
@@ -63,7 +67,7 @@ final class CoreLibsConvertColorTest extends TestCase
|
|||||||
// $hsl_r = Color\Color::hsbToHsl($hsb_r);
|
// $hsl_r = Color\Color::hsbToHsl($hsb_r);
|
||||||
// print "R to HSL: " . print_r($hsl_r, true) . "\n";
|
// print "R to HSL: " . print_r($hsl_r, true) . "\n";
|
||||||
// print "--------\n";
|
// print "--------\n";
|
||||||
// $hsb = Color\Coordinates\HSB::__constructFromArray([0, 20, 0]);
|
// $hsb = new Color\Coordinates\HSB([0, 20, 0]);
|
||||||
// print "IN HSB: " . print_r($hsb, true) . "\n";
|
// print "IN HSB: " . print_r($hsb, true) . "\n";
|
||||||
// $hsl = Color\Color::hsbToHsl($hsb);
|
// $hsl = Color\Color::hsbToHsl($hsb);
|
||||||
// print "to HSL: " . print_r($hsl, true) . "\n";
|
// print "to HSL: " . print_r($hsl, true) . "\n";
|
||||||
@@ -75,7 +79,7 @@ final class CoreLibsConvertColorTest extends TestCase
|
|||||||
// $hsb_r = Color\Color::hslToHsb($hsl_r);
|
// $hsb_r = Color\Color::hslToHsb($hsl_r);
|
||||||
// print "R to HSL: " . print_r($hsb_r, true) . "\n";
|
// print "R to HSL: " . print_r($hsb_r, true) . "\n";
|
||||||
// print "--------\n";
|
// print "--------\n";
|
||||||
// $hwb = Color\Coordinates\HWB::__constructFromArray([0, 20, 100]);
|
// $hwb = new Color\Coordinates\HWB([0, 20, 100]);
|
||||||
// print "IN: " . print_r($hwb, true) . "\n";
|
// print "IN: " . print_r($hwb, true) . "\n";
|
||||||
// $hsl = Color\Color::hwbToHsl($hwb);
|
// $hsl = Color\Color::hwbToHsl($hwb);
|
||||||
// print "to HSL: " . print_r($hsl, true) . "\n";
|
// print "to HSL: " . print_r($hsl, true) . "\n";
|
||||||
@@ -87,6 +91,8 @@ final class CoreLibsConvertColorTest extends TestCase
|
|||||||
// print "HSL to HWB: " . print_r($hwb_r, true) . "\n";
|
// print "HSL to HWB: " . print_r($hwb_r, true) . "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: RGB base
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* From/To RGB <-> ... conversion tests
|
* From/To RGB <-> ... conversion tests
|
||||||
*
|
*
|
||||||
@@ -116,7 +122,7 @@ final class CoreLibsConvertColorTest extends TestCase
|
|||||||
$b = 255;
|
$b = 255;
|
||||||
}
|
}
|
||||||
// base is always the same
|
// base is always the same
|
||||||
$color = Color\Coordinates\RGB::__constructFromArray([$r, $g, $b]);
|
$color = new Color\Coordinates\RGB([$r, $g, $b]);
|
||||||
$base = 'rgb';
|
$base = 'rgb';
|
||||||
foreach (['hsb', 'hsl', 'hwb'] as $coord) {
|
foreach (['hsb', 'hsl', 'hwb'] as $coord) {
|
||||||
// print "COORD: " . $coord . ", RGB: " . print_r($color->returnAsArray(), true) . "\n";
|
// print "COORD: " . $coord . ", RGB: " . print_r($color->returnAsArray(), true) . "\n";
|
||||||
@@ -145,6 +151,8 @@ final class CoreLibsConvertColorTest extends TestCase
|
|||||||
// HSB: saturation or brightness 0
|
// HSB: saturation or brightness 0
|
||||||
// HWB: blackness >= 80 and whitness >= 20 or B>=20 & W>=20 or B>=50 & W>=50
|
// HWB: blackness >= 80 and whitness >= 20 or B>=20 & W>=20 or B>=50 & W>=50
|
||||||
|
|
||||||
|
// MARK: HSL base
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Undocumented function
|
* Undocumented function
|
||||||
*
|
*
|
||||||
@@ -165,7 +173,7 @@ final class CoreLibsConvertColorTest extends TestCase
|
|||||||
if (($L == 0 or $L == 100)) {
|
if (($L == 0 or $L == 100)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$color = Color\Coordinates\HSL::__constructFromArray([$H, $S, $L]);
|
$color = new Color\Coordinates\HSL([$H, $S, $L]);
|
||||||
$base = 'hsl';
|
$base = 'hsl';
|
||||||
foreach (['hsb', 'hwb', 'rgb'] as $coord) {
|
foreach (['hsb', 'hwb', 'rgb'] as $coord) {
|
||||||
// for rgb hue on S = 0 is irrelevant (B/W)
|
// for rgb hue on S = 0 is irrelevant (B/W)
|
||||||
@@ -190,6 +198,8 @@ final class CoreLibsConvertColorTest extends TestCase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: HSB
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Undocumented function
|
* Undocumented function
|
||||||
*
|
*
|
||||||
@@ -210,7 +220,7 @@ final class CoreLibsConvertColorTest extends TestCase
|
|||||||
if ($S == 0 or $B == 0) {
|
if ($S == 0 or $B == 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$color = Color\Coordinates\HSB::__constructFromArray([$H, $S, $B]);
|
$color = new Color\Coordinates\HSB([$H, $S, $B]);
|
||||||
$base = 'hsb';
|
$base = 'hsb';
|
||||||
foreach (['hwb', 'hsl', 'rgb'] as $coord) {
|
foreach (['hwb', 'hsl', 'rgb'] as $coord) {
|
||||||
$target = $base . 'To' . ucfirst($coord);
|
$target = $base . 'To' . ucfirst($coord);
|
||||||
@@ -231,6 +241,7 @@ final class CoreLibsConvertColorTest extends TestCase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: HWB
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Undocumented function
|
* Undocumented function
|
||||||
@@ -258,7 +269,7 @@ final class CoreLibsConvertColorTest extends TestCase
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$base = 'hwb';
|
$base = 'hwb';
|
||||||
$color = Color\Coordinates\HWB::__constructFromArray([$H, $W, $B]);
|
$color = new Color\Coordinates\HWB([$H, $W, $B]);
|
||||||
foreach (['hsl', 'hsb', 'rgb'] as $coord) {
|
foreach (['hsl', 'hsb', 'rgb'] as $coord) {
|
||||||
// for rgb hue on S = 0 is irrelevant (B/W)
|
// for rgb hue on S = 0 is irrelevant (B/W)
|
||||||
if ($H > 0 && $coord == 'rgb') {
|
if ($H > 0 && $coord == 'rgb') {
|
||||||
@@ -282,12 +293,13 @@ final class CoreLibsConvertColorTest extends TestCase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: RGB to hex
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Undocumented function
|
* Undocumented function
|
||||||
*
|
*
|
||||||
* covers ::returnAsHex()
|
* @covers ::returnAsHex()
|
||||||
* covers ::__constructFromHexString()
|
* @testdox Convert from and to RGB via hex
|
||||||
* @testdox Convert from RGB to hex and back
|
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
@@ -308,10 +320,10 @@ final class CoreLibsConvertColorTest extends TestCase
|
|||||||
}
|
}
|
||||||
// with or without prefix
|
// with or without prefix
|
||||||
foreach ([true, false] as $hex_prefix) {
|
foreach ([true, false] as $hex_prefix) {
|
||||||
$hex_color = Color\Coordinates\RGB::__constructFromArray([$r, $g, $b])
|
$hex_color = (new Color\Coordinates\RGB([$r, $g, $b]))
|
||||||
->returnAsHex($hex_prefix);
|
->returnAsHex($hex_prefix);
|
||||||
// parse into hex to rgb and see if we get the same r/g/b
|
// parse into hex to rgb and see if we get the same r/g/b
|
||||||
$color = Color\Coordinates\RGB::__constructFromHexString($hex_color)->returnAsArray();
|
$color = (new Color\Coordinates\RGB($hex_color))->returnAsArray();
|
||||||
//
|
//
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
[$r, $g, $b],
|
[$r, $g, $b],
|
||||||
@@ -325,20 +337,849 @@ final class CoreLibsConvertColorTest extends TestCase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// oklab
|
// MARK: RGB Linear
|
||||||
|
|
||||||
// cie lab
|
/**
|
||||||
|
* linear RGB conversion tests
|
||||||
// create exceptions for all color spaces
|
*
|
||||||
|
* @covers ::fromLinear
|
||||||
public function testExceptionHSB(): void
|
* @covers ::toLinear
|
||||||
|
* @testdox Convert from and to RGB linear conversion check
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function testRgbFromToLinear()
|
||||||
{
|
{
|
||||||
|
$rgb = (new Color\Coordinates\RGB([10, 20, 30]))->toLinear();
|
||||||
|
$this->assertEquals(
|
||||||
|
true,
|
||||||
|
$rgb->linear,
|
||||||
|
'On create flagged linear missing'
|
||||||
|
);
|
||||||
|
$rgb_color = $rgb->returnAsArray();
|
||||||
|
$rgb->toLinear();
|
||||||
|
$this->assertEquals(
|
||||||
|
$rgb_color,
|
||||||
|
$rgb->returnAsArray(),
|
||||||
|
'Double linear call does double linear encoding'
|
||||||
|
);
|
||||||
|
$rgb->fromLinear();
|
||||||
|
$this->assertEquals(
|
||||||
|
false,
|
||||||
|
$rgb->linear,
|
||||||
|
'On reverse linear, flag is missing'
|
||||||
|
);
|
||||||
|
$rgb_color = $rgb->returnAsArray();
|
||||||
|
$this->assertEquals(
|
||||||
|
$rgb_color,
|
||||||
|
$rgb->returnAsArray(),
|
||||||
|
'Double linear inverse call does double linear decoding'
|
||||||
|
);
|
||||||
|
$rgb = new Color\Coordinates\RGB([20, 30, 40]);
|
||||||
|
$rgb_color = $rgb->returnAsArray();
|
||||||
|
$this->assertEquals(
|
||||||
|
false,
|
||||||
|
$rgb->linear,
|
||||||
|
'On create without linear flag is linear'
|
||||||
|
);
|
||||||
|
$rgb->toLinear();
|
||||||
|
$this->assertEquals(
|
||||||
|
true,
|
||||||
|
$rgb->linear,
|
||||||
|
'On linear call flag is not linear'
|
||||||
|
);
|
||||||
|
$rgb->fromLinear();
|
||||||
|
$this->assertEquals(
|
||||||
|
$rgb_color,
|
||||||
|
$rgb->returnAsArray(),
|
||||||
|
'conversion to and from linear not matching'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: okLab
|
||||||
|
|
||||||
|
/**
|
||||||
|
* From/To RGB <-> OkLab / OkLch
|
||||||
|
*
|
||||||
|
* @covers ::rgbToOkLab
|
||||||
|
* @covers ::rgbToOkLch
|
||||||
|
* @covers ::okLabToRgb
|
||||||
|
* @covers ::okLchToRgb
|
||||||
|
* @testdox Convert from and to RGB to OkLab / OkLch
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function testRgbColorCoordinateConvertToAndBackBackOkLab()
|
||||||
|
{
|
||||||
|
for ($r = 0; $r <= 300; $r += 60) {
|
||||||
|
for ($g = 0; $g <= 300; $g += 60) {
|
||||||
|
for ($b = 0; $b <= 300; $b += 60) {
|
||||||
|
// for this test we stay in the correct lane
|
||||||
|
if ($r > 255) {
|
||||||
|
$r = 255;
|
||||||
|
}
|
||||||
|
if ($g > 255) {
|
||||||
|
$g = 255;
|
||||||
|
}
|
||||||
|
if ($b > 255) {
|
||||||
|
$b = 255;
|
||||||
|
}
|
||||||
|
// base is always the same
|
||||||
|
$color = new Color\Coordinates\RGB([$r, $g, $b]);
|
||||||
|
$base = 'rgb';
|
||||||
|
foreach (['okLab', 'okLch'] as $coord) {
|
||||||
|
// print "COORD: " . $coord . ", RGB: " . print_r($color->returnAsArray(), true) . "\n";
|
||||||
|
// rgb to X and back must be same
|
||||||
|
$target = $base . 'To' . ucfirst($coord);
|
||||||
|
$source = $coord . 'To' . ucfirst($base);
|
||||||
|
$converted_color = Color\Color::$target($color);
|
||||||
|
$color_b = Color\Color::$source($converted_color);
|
||||||
|
// $converted_color = Color\Color::rgbToHsb($color);
|
||||||
|
// $rgb_b = Color\Color::hsbToRgb($converted_color);
|
||||||
|
$this->assertEqualsWithDelta(
|
||||||
|
$color->returnAsArray(),
|
||||||
|
$color_b->returnAsArray(),
|
||||||
|
self::DELTA_OKLAB,
|
||||||
|
'Convert ' . $base . ' to ' . $coord . ': ' . print_r($color->returnAsArray(), true) . '/'
|
||||||
|
. print_r($color_b->returnAsArray(), true)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* internal oklab/oklch conversion
|
||||||
|
*
|
||||||
|
* @covers ::okLchToOkLab
|
||||||
|
* @covers ::okLabToOkLch
|
||||||
|
* @testdox Convert from and to OkLab / OkLch
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function testOkLabOkLchColorCoordinateConvertToFrom()
|
||||||
|
{
|
||||||
|
for ($L = 0.0; $L <= 1.0; $L += 0.2) {
|
||||||
|
for ($C = 0.0; $C <= 0.5; $C += 0.1) {
|
||||||
|
for ($H = 0.0; $H <= 360.0; $H += 60.0) {
|
||||||
|
// chroma 0.0 is B/W skip it
|
||||||
|
if ($C == 0.0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$color = new Color\Coordinates\LCH([$L, $C, $H], 'OkLab');
|
||||||
|
$base = 'okLch';
|
||||||
|
foreach (['okLab'] as $coord) {
|
||||||
|
// rgb to X and back must be same
|
||||||
|
$target = $base . 'To' . ucfirst($coord);
|
||||||
|
$source = $coord . 'To' . ucfirst($base);
|
||||||
|
$converted_color = Color\Color::$target($color);
|
||||||
|
$color_b = Color\Color::$source($converted_color);
|
||||||
|
// $converted_color = Color\Color::rgbToHsb($color);
|
||||||
|
// $rgb_b = Color\Color::hsbToRgb($converted_color);
|
||||||
|
$this->assertEqualsWithDelta(
|
||||||
|
$color->returnAsArray(),
|
||||||
|
$color_b->returnAsArray(),
|
||||||
|
self::DELTA,
|
||||||
|
'Convert ' . $base . ' to ' . $coord . ': ' . print_r($color->returnAsArray(), true) . '/'
|
||||||
|
. print_r($color_b->returnAsArray(), true)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: CIELab
|
||||||
|
|
||||||
|
/**
|
||||||
|
* From/To RGB <-> Cie lab / Cie lch
|
||||||
|
*
|
||||||
|
* @covers ::rgbToLab
|
||||||
|
* @covers ::rgbToLch
|
||||||
|
* @covers ::labToRgb
|
||||||
|
* @covers ::lchToRgb
|
||||||
|
* @testdox Convert from and to RGB to Cie Lab / Cie Lch
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function testRgbColorCoordinateConvertToAndBackBackCieLab()
|
||||||
|
{
|
||||||
|
for ($r = 0; $r <= 300; $r += 60) {
|
||||||
|
for ($g = 0; $g <= 300; $g += 60) {
|
||||||
|
for ($b = 0; $b <= 300; $b += 60) {
|
||||||
|
// for this test we stay in the correct lane
|
||||||
|
if ($r > 255) {
|
||||||
|
$r = 255;
|
||||||
|
}
|
||||||
|
if ($g > 255) {
|
||||||
|
$g = 255;
|
||||||
|
}
|
||||||
|
if ($b > 255) {
|
||||||
|
$b = 255;
|
||||||
|
}
|
||||||
|
// base is always the same
|
||||||
|
$color = new Color\Coordinates\RGB([$r, $g, $b]);
|
||||||
|
$base = 'rgb';
|
||||||
|
foreach (['lab', 'lch'] as $coord) {
|
||||||
|
// print "COORD: " . $coord . ", RGB: " . print_r($color->returnAsArray(), true) . "\n";
|
||||||
|
// rgb to X and back must be same
|
||||||
|
$target = $base . 'To' . ucfirst($coord);
|
||||||
|
$source = $coord . 'To' . ucfirst($base);
|
||||||
|
$converted_color = Color\Color::$target($color);
|
||||||
|
$color_b = Color\Color::$source($converted_color);
|
||||||
|
// $converted_color = Color\Color::rgbToHsb($color);
|
||||||
|
// $rgb_b = Color\Color::hsbToRgb($converted_color);
|
||||||
|
$this->assertEqualsWithDelta(
|
||||||
|
$color->returnAsArray(),
|
||||||
|
$color_b->returnAsArray(),
|
||||||
|
self::DELTA_OKLAB,
|
||||||
|
'Convert ' . $base . ' to ' . $coord . ': ' . print_r($color->returnAsArray(), true) . '/'
|
||||||
|
. print_r($color_b->returnAsArray(), true)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* internal cie lab/cie lch conversion
|
||||||
|
*
|
||||||
|
* @covers ::lchToLab
|
||||||
|
* @covers ::labToLch
|
||||||
|
* @testdox Convert from and to Cie Lab / Cie Lch
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function testLabLchColorCoordinateConvertToFrom()
|
||||||
|
{
|
||||||
|
for ($L = 0.0; $L <= 1.0; $L += 0.2) {
|
||||||
|
for ($C = 0.0; $C <= 0.5; $C += 0.1) {
|
||||||
|
for ($H = 0.0; $H <= 360.0; $H += 60.0) {
|
||||||
|
// chroma 0.0 is B/W skip it
|
||||||
|
if ($C == 0.0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$color = new Color\Coordinates\LCH([$L, $C, $H], 'OkLab');
|
||||||
|
$base = 'lch';
|
||||||
|
foreach (['lab'] as $coord) {
|
||||||
|
// rgb to X and back must be same
|
||||||
|
$target = $base . 'To' . ucfirst($coord);
|
||||||
|
$source = $coord . 'To' . ucfirst($base);
|
||||||
|
$converted_color = Color\Color::$target($color);
|
||||||
|
$color_b = Color\Color::$source($converted_color);
|
||||||
|
// $converted_color = Color\Color::rgbToHsb($color);
|
||||||
|
// $rgb_b = Color\Color::hsbToRgb($converted_color);
|
||||||
|
$this->assertEqualsWithDelta(
|
||||||
|
$color->returnAsArray(),
|
||||||
|
$color_b->returnAsArray(),
|
||||||
|
self::DELTA,
|
||||||
|
'Convert ' . $base . ' to ' . $coord . ': ' . print_r($color->returnAsArray(), true) . '/'
|
||||||
|
. print_r($color_b->returnAsArray(), true)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Exceptions
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Undocumented function
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function providerHueBased(): array
|
||||||
|
{
|
||||||
|
// all HSB/V HSL HWB have the same value range, create test data for all of them
|
||||||
|
return [
|
||||||
|
'H' => [
|
||||||
|
'color' => [900, 10, 10],
|
||||||
|
'error_code' => 1,
|
||||||
|
'error_string' => '/ for hue is not in the range of 0 to 360$/'
|
||||||
|
],
|
||||||
|
'H' => [
|
||||||
|
'color' => [-1, 10, 10], 'error_code' => 1,
|
||||||
|
'error_string' => '/ for hue is not in the range of 0 to 360$/',
|
||||||
|
],
|
||||||
|
'H close' => [
|
||||||
|
'color' => [360.1, 10, 10],
|
||||||
|
'error_code' => 1,
|
||||||
|
'error_string' => '/ for hue is not in the range of 0 to 360$/'
|
||||||
|
],
|
||||||
|
'H close' => [
|
||||||
|
'color' => [-0.1, 10, 10], 'error_code' => 1,
|
||||||
|
'error_string' => '/ for hue is not in the range of 0 to 360$/',
|
||||||
|
],
|
||||||
|
'S/W' => [
|
||||||
|
'color' => [90, 900, 10], 'error_code' => 2,
|
||||||
|
'error_string' => 'is not in the range of 0 to 100',
|
||||||
|
],
|
||||||
|
'S/W' => [
|
||||||
|
'color' => [90, -1, 10], 'error_code' => 2,
|
||||||
|
'error_string' => 'is not in the range of 0 to 100',
|
||||||
|
],
|
||||||
|
'S/W close' => [
|
||||||
|
'color' => [90, 100.1, 10], 'error_code' => 2,
|
||||||
|
'error_string' => 'is not in the range of 0 to 100',
|
||||||
|
],
|
||||||
|
'S/W close' => [
|
||||||
|
'color' => [90, -0.1, 10], 'error_code' => 2,
|
||||||
|
'error_string' => 'is not in the range of 0 to 100',
|
||||||
|
],
|
||||||
|
'L/B' => [
|
||||||
|
'color' => [90, 10, 900], 'error_code' => 3,
|
||||||
|
'error_string' => 'is not in the range of 0 to 100',
|
||||||
|
],
|
||||||
|
'L/B' => [
|
||||||
|
'color' => [90, 10, -1], 'error_code' => 3,
|
||||||
|
'error_string' => 'is not in the range of 0 to 100',
|
||||||
|
],
|
||||||
|
'L/B close' => [
|
||||||
|
'color' => [90, 10, 100.1], 'error_code' => 3,
|
||||||
|
'error_string' => 'is not in the range of 0 to 100',
|
||||||
|
],
|
||||||
|
'L/B close' => [
|
||||||
|
'color' => [90, 10, -0.1], 'error_code' => 3,
|
||||||
|
'error_string' => 'is not in the range of 0 to 100',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: HSB Exceptions
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Undocumented function
|
||||||
|
*
|
||||||
|
* @dataProvider providerHueBased
|
||||||
|
* @testdox Exception handling for HSB for error $error_code [$_dataName]
|
||||||
|
*
|
||||||
|
* @param array $color
|
||||||
|
* @param int $error_code
|
||||||
|
* @param string $error_string
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function testExceptionHSB(array $color, int $error_code, string $error_string): void
|
||||||
|
{
|
||||||
|
// error string based on code
|
||||||
|
switch ($error_code) {
|
||||||
|
case 2:
|
||||||
|
$error_string = "/ for saturation $error_string$/";
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
$error_string = "/ for brightness $error_string$/";
|
||||||
|
break;
|
||||||
|
}
|
||||||
// for H/S/B exception the same
|
// for H/S/B exception the same
|
||||||
$this->expectException(\LengthException::class);
|
$this->expectException(\LengthException::class);
|
||||||
Color\Coordinates\HSB::__constructFromArray([900, 10, 10]);
|
$this->expectExceptionCode($error_code);
|
||||||
|
$this->expectExceptionMessageMatches($error_string);
|
||||||
|
new Color\Coordinates\HSB($color);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Undocumented function
|
||||||
|
*
|
||||||
|
* @testdox Exception handling for HSB general calls
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function testExceptionHSBGeneral()
|
||||||
|
{
|
||||||
|
// allow
|
||||||
|
$b = new Color\Coordinates\HSB([0, 0, 0], 'sRGB');
|
||||||
// invalid access to class
|
// invalid access to class
|
||||||
// $this->expectException(\ErrorException::class);
|
$b = new Color\Coordinates\HSB([0, 0, 0]);
|
||||||
|
$this->expectException(\ErrorException::class);
|
||||||
|
$this->expectExceptionCode(0);
|
||||||
|
$this->expectExceptionMessage("Creation of dynamic property is not allowed");
|
||||||
|
$b->g;
|
||||||
|
|
||||||
|
$this->expectException(\InvalidArgumentException::class);
|
||||||
|
$this->expectExceptionCode(0);
|
||||||
|
$this->expectExceptionMessage("Only array colors allowed");
|
||||||
|
new Color\Coordinates\HSB('string');
|
||||||
|
|
||||||
|
$this->expectException(\InvalidArgumentException::class);
|
||||||
|
$this->expectExceptionCode(0);
|
||||||
|
$this->expectExceptionMessage("Not allowed colorspace");
|
||||||
|
new Color\Coordinates\HSB([0, 0, 0], 'FOO_BAR');
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: HSL Exceptions
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Undocumented function
|
||||||
|
*
|
||||||
|
* @dataProvider providerHueBased
|
||||||
|
* @testdox Exception handling for HSL for error $error_code [$_dataName]
|
||||||
|
*
|
||||||
|
* @param array $color
|
||||||
|
* @param int $error_code
|
||||||
|
* @param string $error_string
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function testExceptionHSL(array $color, int $error_code, string $error_string): void
|
||||||
|
{
|
||||||
|
// error string based on code
|
||||||
|
switch ($error_code) {
|
||||||
|
case 2:
|
||||||
|
$error_string = "/ for saturation $error_string$/";
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
$error_string = "/ for lightness $error_string$/";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// for H/S/B exception the same
|
||||||
|
$this->expectException(\LengthException::class);
|
||||||
|
$this->expectExceptionCode($error_code);
|
||||||
|
$this->expectExceptionMessageMatches($error_string);
|
||||||
|
new Color\Coordinates\HSL($color);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Undocumented function
|
||||||
|
*
|
||||||
|
* @testdox Exception handling for HSL general calls
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function testExceptionHSLGeneral()
|
||||||
|
{
|
||||||
|
// allow
|
||||||
|
$b = new Color\Coordinates\HSL([0, 0, 0], 'sRGB');
|
||||||
|
// invalid access to class
|
||||||
|
$b = new Color\Coordinates\HSL([0, 0, 0]);
|
||||||
|
$this->expectException(\ErrorException::class);
|
||||||
|
$this->expectExceptionCode(0);
|
||||||
|
$this->expectExceptionMessage("Creation of dynamic property is not allowed");
|
||||||
|
$b->g;
|
||||||
|
|
||||||
|
$this->expectException(\InvalidArgumentException::class);
|
||||||
|
$this->expectExceptionCode(0);
|
||||||
|
$this->expectExceptionMessage("Only array colors allowed");
|
||||||
|
new Color\Coordinates\HSL('string');
|
||||||
|
|
||||||
|
$this->expectException(\InvalidArgumentException::class);
|
||||||
|
$this->expectExceptionCode(0);
|
||||||
|
$this->expectExceptionMessage("Not allowed colorspace");
|
||||||
|
new Color\Coordinates\HSL([0, 0, 0], 'FOO_BAR');
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: HWB Exceptions
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Undocumented function
|
||||||
|
*
|
||||||
|
* @dataProvider providerHueBased
|
||||||
|
* @testdox Exception handling for HWB for error $error_code [$_dataName]
|
||||||
|
*
|
||||||
|
* @param array $color
|
||||||
|
* @param int $error_code
|
||||||
|
* @param string $error_string
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function testExceptionHWB(array $color, int $error_code, string $error_string): void
|
||||||
|
{
|
||||||
|
// error string based on code
|
||||||
|
switch ($error_code) {
|
||||||
|
case 2:
|
||||||
|
$error_string = "/ for whiteness $error_string$/";
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
$error_string = "/ for blackness $error_string$/";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// for H/S/B exception the same
|
||||||
|
$this->expectException(\LengthException::class);
|
||||||
|
$this->expectExceptionCode($error_code);
|
||||||
|
$this->expectExceptionMessageMatches($error_string);
|
||||||
|
new Color\Coordinates\HWB($color);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Undocumented function
|
||||||
|
*
|
||||||
|
* @testdox Exception handling for HWB general calls
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function testExceptionHWBGeneral()
|
||||||
|
{
|
||||||
|
// allow
|
||||||
|
$b = new Color\Coordinates\HWB([0, 0, 0], 'sRGB');
|
||||||
|
// invalid access to class
|
||||||
|
$b = new Color\Coordinates\HWB([0, 0, 0]);
|
||||||
|
$this->expectException(\ErrorException::class);
|
||||||
|
$this->expectExceptionCode(0);
|
||||||
|
$this->expectExceptionMessage("Creation of dynamic property is not allowed");
|
||||||
|
$b->g;
|
||||||
|
|
||||||
|
$this->expectException(\InvalidArgumentException::class);
|
||||||
|
$this->expectExceptionCode(0);
|
||||||
|
$this->expectExceptionMessage("Only array colors allowed");
|
||||||
|
new Color\Coordinates\HWB('string');
|
||||||
|
|
||||||
|
$this->expectException(\InvalidArgumentException::class);
|
||||||
|
$this->expectExceptionCode(0);
|
||||||
|
$this->expectExceptionMessage("Not allowed colorspace");
|
||||||
|
new Color\Coordinates\HWB([0, 0, 0], 'FOO_BAR');
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: RGB Exceptions
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Undocumented function
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function providerRgbBased(): array
|
||||||
|
{
|
||||||
|
// all HSB/V HSL HWB have the same value range, create test data for all of them
|
||||||
|
return [
|
||||||
|
'R' => [
|
||||||
|
'color' => [900, 10, 10],
|
||||||
|
'error_code' => 1,
|
||||||
|
'error_string' => '/ is not in the range of 0 to 255$/',
|
||||||
|
'linear' => false,
|
||||||
|
],
|
||||||
|
'R' => [
|
||||||
|
'color' => [-1, 10, 10], 'error_code' => 1,
|
||||||
|
'error_string' => '/ is not in the range of 0 to 255$/',
|
||||||
|
'linear' => false,
|
||||||
|
],
|
||||||
|
'G' => [
|
||||||
|
'color' => [90, 900, 10], 'error_code' => 1,
|
||||||
|
'error_string' => '/ is not in the range of 0 to 255$/',
|
||||||
|
'linear' => false,
|
||||||
|
],
|
||||||
|
'G' => [
|
||||||
|
'color' => [90, -1, 10], 'error_code' => 1,
|
||||||
|
'error_string' => '/ is not in the range of 0 to 255$/',
|
||||||
|
'linear' => false,
|
||||||
|
],
|
||||||
|
'B' => [
|
||||||
|
'color' => [90, 10, 900], 'error_code' => 1,
|
||||||
|
'error_string' => '/ is not in the range of 0 to 255$/',
|
||||||
|
'linear' => false,
|
||||||
|
],
|
||||||
|
'B' => [
|
||||||
|
'color' => [90, 10, -1], 'error_code' => 1,
|
||||||
|
'error_string' => '/ is not in the range of 0 to 255$/',
|
||||||
|
'linear' => false,
|
||||||
|
],
|
||||||
|
'R linear' => [
|
||||||
|
'color' => [2, 0.5, 0.5],
|
||||||
|
'error_code' => 2,
|
||||||
|
'error_string' => '/ is not in the range of 0 to 1 for linear rgb$/',
|
||||||
|
'linear' => true,
|
||||||
|
],
|
||||||
|
'R linear' => [
|
||||||
|
'color' => [-1, 0.5, 0.5],
|
||||||
|
'error_code' => 2,
|
||||||
|
'error_string' => '/ is not in the range of 0 to 1 for linear rgb$/',
|
||||||
|
'linear' => true,
|
||||||
|
],
|
||||||
|
'G linear' => [
|
||||||
|
'color' => [0.5, 2, 0.5],
|
||||||
|
'error_code' => 2,
|
||||||
|
'error_string' => '/ is not in the range of 0 to 1 for linear rgb$/',
|
||||||
|
'linear' => true,
|
||||||
|
],
|
||||||
|
'G linear' => [
|
||||||
|
'color' => [0.5, -1, 0.5],
|
||||||
|
'error_code' => 2,
|
||||||
|
'error_string' => '/ is not in the range of 0 to 1 for linear rgb$/',
|
||||||
|
'linear' => true,
|
||||||
|
],
|
||||||
|
'B linear' => [
|
||||||
|
'color' => [0.5, 0.5, 2],
|
||||||
|
'error_code' => 2,
|
||||||
|
'error_string' => '/ is not in the range of 0 to 1 for linear rgb$/',
|
||||||
|
'linear' => true,
|
||||||
|
],
|
||||||
|
'B linear' => [
|
||||||
|
'color' => [0.5, 0.5, -1],
|
||||||
|
'error_code' => 2,
|
||||||
|
'error_string' => '/ is not in the range of 0 to 1 for linear rgb$/',
|
||||||
|
'linear' => true,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Undocumented function
|
||||||
|
*
|
||||||
|
* @dataProvider providerRgbBased
|
||||||
|
* @testdox Exception handling for RGB for error $error_code [$_dataName]
|
||||||
|
*
|
||||||
|
* @param string|array $color
|
||||||
|
* @param int $error_code
|
||||||
|
* @param string $error_string
|
||||||
|
* @param bool $linear
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function testExceptionRGB(string|array $color, int $error_code, string $error_string, bool $linear): void
|
||||||
|
{
|
||||||
|
// for RGB exception the same
|
||||||
|
$this->expectException(\LengthException::class);
|
||||||
|
$this->expectExceptionCode($error_code);
|
||||||
|
$this->expectExceptionMessageMatches($error_string);
|
||||||
|
new Color\Coordinates\RGB($color, options: ["linear" => $linear]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Undocumented function
|
||||||
|
*
|
||||||
|
* @covers ::__get
|
||||||
|
* @testdox Exception handling for RGB general calls
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function testExceptionRGBGeneral()
|
||||||
|
{
|
||||||
|
// allow
|
||||||
|
$b = new Color\Coordinates\RGB([0, 0, 0], 'sRGB');
|
||||||
|
// invalid access to class
|
||||||
|
$b = new Color\Coordinates\RGB([0, 0, 0]);
|
||||||
|
$this->expectException(\ErrorException::class);
|
||||||
|
$this->expectExceptionCode(0);
|
||||||
|
$this->expectExceptionMessage("Creation of dynamic property is not allowed");
|
||||||
|
$b->h;
|
||||||
|
|
||||||
|
$this->expectException(\InvalidArgumentException::class);
|
||||||
|
$this->expectExceptionCode(0);
|
||||||
|
$this->expectExceptionMessage("Not allowed colorspace");
|
||||||
|
new Color\Coordinates\RGB([0, 0, 0], 'FOO_BAR');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Undocumented function
|
||||||
|
*
|
||||||
|
* @covers ::setFromHex
|
||||||
|
* @testdox Exception handling for RGB setFromHex failues
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function testExceptionRGBFromHex()
|
||||||
|
{
|
||||||
|
$color = '';
|
||||||
|
$this->expectException(\InvalidArgumentException::class);
|
||||||
|
$this->expectExceptionCode(3);
|
||||||
|
$this->expectExceptionMessage('hex_string argument cannot be empty');
|
||||||
|
new Color\Coordinates\RGB($color);
|
||||||
|
|
||||||
|
$color = 'zshj';
|
||||||
|
$this->expectException(\InvalidArgumentException::class);
|
||||||
|
$this->expectExceptionCode(3);
|
||||||
|
$this->expectExceptionMessage('hex_string argument cannot be empty');
|
||||||
|
new Color\Coordinates\RGB($color);
|
||||||
|
|
||||||
|
$color = 'aabff';
|
||||||
|
$this->expectException(\UnexpectedValueException::class);
|
||||||
|
$this->expectExceptionCode(4);
|
||||||
|
$this->expectExceptionMessageMatches('/^Invalid hex_string: /');
|
||||||
|
new Color\Coordinates\RGB($color);
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Lab Exceptions
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Undocumented function
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function providerLabBased(): array
|
||||||
|
{
|
||||||
|
// all HSB/V HSL HWB have the same value range, create test data for all of them
|
||||||
|
return [
|
||||||
|
'L CieLab' => [
|
||||||
|
'color' => [900, 10, 10], 'error_code' => 1,
|
||||||
|
'error_string' => '/ for lightness is not in the range of 0 to 100 for CIE Lab$/',
|
||||||
|
'colorspace' => 'CIELab',
|
||||||
|
],
|
||||||
|
'L CieLab' => [
|
||||||
|
'color' => [-1, 10, 10], 'error_code' => 1,
|
||||||
|
'error_string' => '/ for lightness is not in the range of 0 to 100 for CIE Lab$/',
|
||||||
|
'colorspace' => 'CIELab',
|
||||||
|
],
|
||||||
|
'L OkLab' => [
|
||||||
|
'color' => [900, 0.2, 0.2], 'error_code' => 1,
|
||||||
|
'error_string' => '/ for lightness is not in the range of 0.0 to 1.0 for OkLab$/',
|
||||||
|
'colorspace' => 'OkLab',
|
||||||
|
],
|
||||||
|
'L OkLab' => [
|
||||||
|
'color' => [-1, 0.2, 0.2], 'error_code' => 1,
|
||||||
|
'error_string' => '/ for lightness is not in the range of 0.0 to 1.0 for OkLab$/',
|
||||||
|
'colorspace' => 'OkLab',
|
||||||
|
],
|
||||||
|
'a CieLab' => [
|
||||||
|
'color' => [90, 900, 10], 'error_code' => 2,
|
||||||
|
'error_string' => '/ for a is not in the range of -125 to 125 for CIE Lab$/',
|
||||||
|
'colorspace' => 'CIELab',
|
||||||
|
],
|
||||||
|
'a CieLab' => [
|
||||||
|
'color' => [90, -900, 10], 'error_code' => 2,
|
||||||
|
'error_string' => '/ for a is not in the range of -125 to 125 for CIE Lab$/',
|
||||||
|
'colorspace' => 'CIELab',
|
||||||
|
],
|
||||||
|
'a OkLab' => [
|
||||||
|
'color' => [0.5, 900, 0.2], 'error_code' => 2,
|
||||||
|
'error_string' => '/ for a is not in the range of -0.5 to 0.5 for OkLab$/',
|
||||||
|
'colorspace' => 'OkLab',
|
||||||
|
],
|
||||||
|
'a OkLab' => [
|
||||||
|
'color' => [0.6, -900, 0.2], 'error_code' => 2,
|
||||||
|
'error_string' => '/ for a is not in the range of -0.5 to 0.5 for OkLab$/',
|
||||||
|
'colorspace' => 'OkLab',
|
||||||
|
],
|
||||||
|
'b CieLab' => [
|
||||||
|
'color' => [90, 10, 900], 'error_code' => 3,
|
||||||
|
'error_string' => '/ for b is not in the range of -125 to 125 for CIE Lab$/',
|
||||||
|
'colorspace' => 'CIELab',
|
||||||
|
],
|
||||||
|
'b CieLab' => [
|
||||||
|
'color' => [90, 10, -999], 'error_code' => 3,
|
||||||
|
'error_string' => '/ for b is not in the range of -125 to 125 for CIE Lab$/',
|
||||||
|
'colorspace' => 'CIELab',
|
||||||
|
],
|
||||||
|
'b OkLab' => [
|
||||||
|
'color' => [0.6, 0.2, 900], 'error_code' => 3,
|
||||||
|
'error_string' => '/ for b is not in the range of -0.5 to 0.5 for OkLab$/',
|
||||||
|
'colorspace' => 'OkLab',
|
||||||
|
],
|
||||||
|
'b OkLab' => [
|
||||||
|
'color' => [0.6, 0.2, -999], 'error_code' => 3,
|
||||||
|
'error_string' => '/ for b is not in the range of -0.5 to 0.5 for OkLab$/',
|
||||||
|
'colorspace' => 'OkLab',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Undocumented function
|
||||||
|
*
|
||||||
|
* @dataProvider providerLabBased
|
||||||
|
* @testdox Exception handling for Lab for error $error_code [$_dataName]
|
||||||
|
*
|
||||||
|
* @param string|array $color
|
||||||
|
* @param int $error_code
|
||||||
|
* @param string $error_string
|
||||||
|
* @param string $colorspace
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function testExceptionLab(
|
||||||
|
string|array $color,
|
||||||
|
int $error_code,
|
||||||
|
string $error_string,
|
||||||
|
string $colorspace
|
||||||
|
): void {
|
||||||
|
// for RGB exception the same
|
||||||
|
$this->expectException(\LengthException::class);
|
||||||
|
$this->expectExceptionCode($error_code);
|
||||||
|
$this->expectExceptionMessageMatches($error_string);
|
||||||
|
new Color\Coordinates\Lab($color, colorspace: $colorspace);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Undocumented function
|
||||||
|
*
|
||||||
|
* @covers ::__get
|
||||||
|
* @testdox Exception handling for Lab general calls
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function testExceptionLabGeneral()
|
||||||
|
{
|
||||||
|
// allow
|
||||||
|
$b = new Color\Coordinates\Lab([0, 0, 0], 'OkLab');
|
||||||
|
// invalid access to class
|
||||||
|
$b = new Color\Coordinates\Lab([0, 0, 0], 'CIELab');
|
||||||
|
$this->expectException(\ErrorException::class);
|
||||||
|
$this->expectExceptionCode(0);
|
||||||
|
$this->expectExceptionMessage("Creation of dynamic property is not allowed");
|
||||||
|
$b->x;
|
||||||
|
|
||||||
|
$this->expectException(\InvalidArgumentException::class);
|
||||||
|
$this->expectExceptionCode(0);
|
||||||
|
$this->expectExceptionMessage("Only array colors allowed");
|
||||||
|
new Color\Coordinates\Lab('string', 'CIELab');
|
||||||
|
|
||||||
|
$this->expectException(\InvalidArgumentException::class);
|
||||||
|
$this->expectExceptionCode(0);
|
||||||
|
$this->expectExceptionMessage("Not allowed colorspace");
|
||||||
|
new Color\Coordinates\Lab([0, 0, 0], 'FOO_BAR');
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: LCH Exceptions
|
||||||
|
|
||||||
|
// public function testExceptionLch(string|array $color, int $error_code, string $error_string): void
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Undocumented function
|
||||||
|
*
|
||||||
|
* @covers ::__get
|
||||||
|
* @testdox Exception handling for LCH general calls
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function testExceptionLchGeneral()
|
||||||
|
{
|
||||||
|
// allow
|
||||||
|
$b = new Color\Coordinates\LCH([0, 0, 0], 'OkLab');
|
||||||
|
// invalid access to class
|
||||||
|
$b = new Color\Coordinates\LCH([0, 0, 0], 'CIELab');
|
||||||
|
$this->expectException(\ErrorException::class);
|
||||||
|
$this->expectExceptionCode(0);
|
||||||
|
$this->expectExceptionMessage("Creation of dynamic property is not allowed");
|
||||||
|
$b->x;
|
||||||
|
|
||||||
|
$this->expectException(\InvalidArgumentException::class);
|
||||||
|
$this->expectExceptionCode(0);
|
||||||
|
$this->expectExceptionMessage("Only array colors allowed");
|
||||||
|
new Color\Coordinates\LCH('string', 'CIELab');
|
||||||
|
|
||||||
|
$this->expectException(\InvalidArgumentException::class);
|
||||||
|
$this->expectExceptionCode(0);
|
||||||
|
$this->expectExceptionMessage("Not allowed colorspace");
|
||||||
|
new Color\Coordinates\LCH([0, 0, 0], 'FOO_BAR');
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: XYZ Exceptions
|
||||||
|
|
||||||
|
// Note, we do not check for value exceptions here
|
||||||
|
// public function testExceptionXyz(string|array $color, int $error_code, string $error_string): void
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Undocumented function
|
||||||
|
*
|
||||||
|
* @covers ::__get
|
||||||
|
* @testdox Exception handling for XYZ general calls
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function testExceptionXyzGeneral()
|
||||||
|
{
|
||||||
|
// allow
|
||||||
|
$b = new Color\Coordinates\XYZ([0, 0, 0], 'CIEXYZ');
|
||||||
|
// invalid access to class
|
||||||
|
$b = new Color\Coordinates\XYZ([0, 0, 0]);
|
||||||
|
$this->expectException(\ErrorException::class);
|
||||||
|
$this->expectExceptionCode(0);
|
||||||
|
$this->expectExceptionMessage("Creation of dynamic property is not allowed");
|
||||||
|
$b->x;
|
||||||
|
|
||||||
|
$this->expectException(\InvalidArgumentException::class);
|
||||||
|
$this->expectExceptionCode(0);
|
||||||
|
$this->expectExceptionMessage("Only array colors allowed");
|
||||||
|
new Color\Coordinates\XYZ('string');
|
||||||
|
|
||||||
|
$this->expectException(\InvalidArgumentException::class);
|
||||||
|
$this->expectExceptionCode(0);
|
||||||
|
$this->expectExceptionMessage("Not allowed colorspace");
|
||||||
|
new Color\Coordinates\XYZ([0, 0, 0], 'FOO_BAR');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -142,7 +142,6 @@ final class CoreLibsConvertMathTest extends TestCase
|
|||||||
*/
|
*/
|
||||||
public function testCbrt(float|int $number, float|string $expected, int $round_to): void
|
public function testCbrt(float|int $number, float|string $expected, int $round_to): void
|
||||||
{
|
{
|
||||||
print "OUT: " . \CoreLibs\Convert\Math::cbrt($number) . "\n";
|
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
$expected,
|
$expected,
|
||||||
round(\CoreLibs\Convert\Math::cbrt($number), $round_to)
|
round(\CoreLibs\Convert\Math::cbrt($number), $round_to)
|
||||||
@@ -264,6 +263,130 @@ final class CoreLibsConvertMathTest extends TestCase
|
|||||||
\CoreLibs\Convert\Math::multiplyMatrices($input_a, $input_b)
|
\CoreLibs\Convert\Math::multiplyMatrices($input_a, $input_b)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Undocumented function
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function providerEqualWithEpsilon(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'equal' => [
|
||||||
|
'a' => 0.000000000000000222,
|
||||||
|
'b' => 0.000000000000000222,
|
||||||
|
'epsilon' => PHP_FLOAT_EPSILON,
|
||||||
|
'equal' => true,
|
||||||
|
],
|
||||||
|
'almost equal' => [
|
||||||
|
'a' => 0.000000000000000222,
|
||||||
|
'b' => 0.000000000000000232,
|
||||||
|
'epsilon' => PHP_FLOAT_EPSILON,
|
||||||
|
'equal' => true,
|
||||||
|
],
|
||||||
|
'not equal' => [
|
||||||
|
'a' => 0.000000000000000222,
|
||||||
|
'b' => 0.000000000000004222,
|
||||||
|
'epsilon' => PHP_FLOAT_EPSILON,
|
||||||
|
'equal' => false,
|
||||||
|
],
|
||||||
|
'equal, different epsilon' => [
|
||||||
|
'a' => 0.000000000000000222,
|
||||||
|
'b' => 0.000000000000004222,
|
||||||
|
'epsilon' => 0.0001,
|
||||||
|
'equal' => true,
|
||||||
|
],
|
||||||
|
'not equal, different epsilon' => [
|
||||||
|
'a' => 0.0001,
|
||||||
|
'b' => 0.0002,
|
||||||
|
'epsilon' => 0.0001,
|
||||||
|
'equal' => false,
|
||||||
|
]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Undocumented function
|
||||||
|
*
|
||||||
|
* @covers ::equalWithEpsilon
|
||||||
|
* @dataProvider providerEqualWithEpsilon
|
||||||
|
* @testdox equalWithEpsilon with $a and $b and Epsilon: $epsilon must be equal: $equal [$_dataName]
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function testEqualWithEpsilon(float $a, float $b, float $epsilon, bool $equal): void
|
||||||
|
{
|
||||||
|
$this->assertEquals(
|
||||||
|
$equal,
|
||||||
|
\CoreLibs\Convert\Math::equalWithEpsilon($a, $b, $epsilon)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Undocumented function
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function providerCompareWithEpsilon(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'smaller, true' => [
|
||||||
|
'value' => 0.0001,
|
||||||
|
'compare' => '<',
|
||||||
|
'limit' => 0.0002,
|
||||||
|
'epsilon' => 0.00001,
|
||||||
|
'match' => true,
|
||||||
|
],
|
||||||
|
'smaller, false' => [
|
||||||
|
'value' => 0.0001,
|
||||||
|
'compare' => '<',
|
||||||
|
'limit' => 0.0001,
|
||||||
|
'epsilon' => 0.00001,
|
||||||
|
'match' => false,
|
||||||
|
],
|
||||||
|
'bigger, true' => [
|
||||||
|
'value' => 0.0002,
|
||||||
|
'compare' => '>',
|
||||||
|
'limit' => 0.0001,
|
||||||
|
'epsilon' => 0.00001,
|
||||||
|
'match' => true,
|
||||||
|
],
|
||||||
|
'bigger, false' => [
|
||||||
|
'value' => 0.0001,
|
||||||
|
'compare' => '>',
|
||||||
|
'limit' => 0.0001,
|
||||||
|
'epsilon' => 0.00001,
|
||||||
|
'match' => false,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Undocumented function
|
||||||
|
*
|
||||||
|
* @covers ::compareWithEpsilon
|
||||||
|
* @dataProvider providerCompareWithEpsilon
|
||||||
|
* @testdox compareWithEpsilon $value $compare $limit with $epsilon must match: $match [$_dataName]
|
||||||
|
*
|
||||||
|
* @param float $value
|
||||||
|
* @param string $compare
|
||||||
|
* @param float $limit
|
||||||
|
* @param float $epslion
|
||||||
|
* @param bool $match
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function testCompareWithEpsilon(
|
||||||
|
float $value,
|
||||||
|
string $compare,
|
||||||
|
float $limit,
|
||||||
|
float $epsilon,
|
||||||
|
bool $match
|
||||||
|
): void {
|
||||||
|
$this->assertEquals(
|
||||||
|
$match,
|
||||||
|
\CoreLibs\Convert\Math::compareWithEpsilon($value, $compare, $limit, $epsilon)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// __END__
|
// __END__
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ print '<div><h1>' . $PAGE_NAME . '</h1></div>';
|
|||||||
|
|
||||||
// define a list of from to color sets for conversion test
|
// define a list of from to color sets for conversion test
|
||||||
|
|
||||||
$hwb = Color::hsbToHwb(Coordinates\HSB::__constructFromArray([
|
$hwb = Color::hsbToHwb(new Coordinates\HSB([
|
||||||
160,
|
160,
|
||||||
0,
|
0,
|
||||||
50,
|
50,
|
||||||
@@ -81,7 +81,7 @@ print "HWB: " . DgS::printAr($hwb) . "<br>";
|
|||||||
$hsb = Color::hwbToHsb($hwb);
|
$hsb = Color::hwbToHsb($hwb);
|
||||||
print "HSB: " . DgS::printAr($hsb) . "<br>";
|
print "HSB: " . DgS::printAr($hsb) . "<br>";
|
||||||
|
|
||||||
$oklch = Color::rgbToOkLch(Coordinates\RGB::__constructFromArray([
|
$oklch = Color::rgbToOkLch(Coordinates\RGB::create([
|
||||||
250,
|
250,
|
||||||
0,
|
0,
|
||||||
0
|
0
|
||||||
@@ -90,7 +90,7 @@ print "OkLch: " . DgS::printAr($oklch) . "<br>";
|
|||||||
$rgb = Color::okLchToRgb($oklch);
|
$rgb = Color::okLchToRgb($oklch);
|
||||||
print "OkLch -> RGB: " . DgS::printAr($rgb) . "<br>";
|
print "OkLch -> RGB: " . DgS::printAr($rgb) . "<br>";
|
||||||
|
|
||||||
$oklab = Color::rgbToOkLab(Coordinates\RGB::__constructFromArray([
|
$oklab = Color::rgbToOkLab(Coordinates\RGB::create([
|
||||||
250,
|
250,
|
||||||
0,
|
0,
|
||||||
0
|
0
|
||||||
@@ -101,24 +101,24 @@ $rgb = Color::okLabToRgb($oklab);
|
|||||||
print "OkLab -> RGB: " . DgS::printAr($rgb) . "<br>";
|
print "OkLab -> RGB: " . DgS::printAr($rgb) . "<br>";
|
||||||
print display($rgb->toCssString(), $rgb->toCssString(), 'OkLab to RGB');
|
print display($rgb->toCssString(), $rgb->toCssString(), 'OkLab to RGB');
|
||||||
|
|
||||||
$rgb = Coordinates\RGB::__constructFromArray([250, 100, 10])->toLinear();
|
$rgb = Coordinates\RGB::create([250, 100, 10])->toLinear();
|
||||||
print "RGBlinear: " . DgS::printAr($rgb) . "<br>";
|
print "RGBlinear: " . DgS::printAr($rgb) . "<br>";
|
||||||
$rgb = Coordinates\RGB::__constructFromArray([0, 0, 0])->toLinear();
|
$rgb = Coordinates\RGB::create([0, 0, 0])->toLinear();
|
||||||
print "RGBlinear: " . DgS::printAr($rgb) . "<br>";
|
print "RGBlinear: " . DgS::printAr($rgb) . "<br>";
|
||||||
|
|
||||||
$cie_lab = Color::okLabToLab($oklab);
|
$cie_lab = Color::okLabToLab($oklab);
|
||||||
print "CieLab: " . DgS::printAr($cie_lab) . "<br>";
|
print "CieLab: " . DgS::printAr($cie_lab) . "<br>";
|
||||||
print display($cie_lab->toCssString(), $cie_lab->toCssString(), 'OkLab to Cie Lab');
|
print display($cie_lab->toCssString(), $cie_lab->toCssString(), 'OkLab to Cie Lab');
|
||||||
|
|
||||||
$rgb = Coordinates\RGB::__constructFromArray([0, 0, 60]);
|
$rgb = Coordinates\RGB::create([0, 0, 60]);
|
||||||
$hsb = Color::rgbToHsb($rgb);
|
$hsb = Color::rgbToHsb($rgb);
|
||||||
$rgb_b = Color::hsbToRgb($hsb);
|
$rgb_b = Color::hsbToRgb($hsb);
|
||||||
print "RGB: " . DgS::printAr($rgb) . "<br>";
|
print "RGB: " . DgS::printAr($rgb) . "<br>";
|
||||||
print "RGB->HSB: " . DgS::printAr($hsb) . "<br>";
|
print "RGB->HSB: " . DgS::printAr($hsb) . "<br>";
|
||||||
print "HSB->RGB: " . DgS::printAr($rgb_b) . "<br>";
|
print "HSB->RGB: " . DgS::printAr($rgb_b) . "<br>";
|
||||||
|
|
||||||
$hsl = Coordinates\HSL::__constructFromArray([0, 20, 0]);
|
$hsl = Coordinates\HSL::create([0, 20, 0]);
|
||||||
$hsb = Coordinates\HSB::__constructFromArray([0, 20, 0]);
|
$hsb = Coordinates\HSB::create([0, 20, 0]);
|
||||||
$hsl_from_hsb = Color::hsbToHsl($hsb);
|
$hsl_from_hsb = Color::hsbToHsl($hsb);
|
||||||
print "HSL from HSB: " . DgS::printAr($hsl_from_hsb) . "<br>";
|
print "HSL from HSB: " . DgS::printAr($hsl_from_hsb) . "<br>";
|
||||||
|
|
||||||
|
|||||||
@@ -250,7 +250,7 @@ class CieXyz
|
|||||||
{
|
{
|
||||||
// if not linear, convert to linear
|
// if not linear, convert to linear
|
||||||
if (!$rgb->linear) {
|
if (!$rgb->linear) {
|
||||||
$rgb->toLinear();
|
$rgb = (new RGB($rgb->returnAsArray()))->toLinear();
|
||||||
}
|
}
|
||||||
return new XYZ(Math::multiplyMatrices(
|
return new XYZ(Math::multiplyMatrices(
|
||||||
[
|
[
|
||||||
|
|||||||
@@ -766,11 +766,6 @@ class Color
|
|||||||
public static function rgbToLab(RGB $rgb): Lab
|
public static function rgbToLab(RGB $rgb): Lab
|
||||||
{
|
{
|
||||||
return CieXyz::rgbViaXyzD65ViaXyzD50ToLab($rgb);
|
return CieXyz::rgbViaXyzD65ViaXyzD50ToLab($rgb);
|
||||||
/* return CieXyz::xyzD50ToLab(
|
|
||||||
CieXyz::xyzD65ToXyzD50(
|
|
||||||
CieXyz::linRgbToXyzD65($rgb)
|
|
||||||
)
|
|
||||||
); */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -783,11 +778,6 @@ class Color
|
|||||||
public static function labToRgb(Lab $lab): RGB
|
public static function labToRgb(Lab $lab): RGB
|
||||||
{
|
{
|
||||||
return CieXyz::labViaXyzD50ViaXyzD65ToRgb($lab);
|
return CieXyz::labViaXyzD50ViaXyzD65ToRgb($lab);
|
||||||
/* return CieXyz::xyzD65ToLinRgb(
|
|
||||||
CieXyz::xyzD50ToXyxD65(
|
|
||||||
CieXyz::labToXyzD50($lab)
|
|
||||||
)
|
|
||||||
)->fromLinear(); */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: RGB <-> Lch (Cie)
|
// MARK: RGB <-> Lch (Cie)
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace CoreLibs\Convert\Color\Coordinates;
|
namespace CoreLibs\Convert\Color\Coordinates;
|
||||||
|
|
||||||
|
use CoreLibs\Convert\Color\Utils;
|
||||||
|
|
||||||
class HSB implements Interface\CoordinatesInterface
|
class HSB implements Interface\CoordinatesInterface
|
||||||
{
|
{
|
||||||
/** @var array<string> allowed colorspaces */
|
/** @var array<string> allowed colorspaces */
|
||||||
@@ -83,10 +85,11 @@ class HSB implements Interface\CoordinatesInterface
|
|||||||
}
|
}
|
||||||
switch ($name) {
|
switch ($name) {
|
||||||
case 'H':
|
case 'H':
|
||||||
if ((int)$value == 360) {
|
if ($value == 360.0) {
|
||||||
$value = 0;
|
$value = 0;
|
||||||
}
|
}
|
||||||
if ((int)$value < 0 || (int)$value > 360) {
|
// if ($value < 0 || $value > 360) {
|
||||||
|
if (Utils::compare(0.0, $value, 360.0, Utils::EPSILON_SMALL)) {
|
||||||
throw new \LengthException(
|
throw new \LengthException(
|
||||||
'Argument value ' . $value . ' for hue is not in the range of 0 to 360',
|
'Argument value ' . $value . ' for hue is not in the range of 0 to 360',
|
||||||
1
|
1
|
||||||
@@ -94,7 +97,8 @@ class HSB implements Interface\CoordinatesInterface
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'S':
|
case 'S':
|
||||||
if ((int)$value < 0 || (int)$value > 100) {
|
// if ($value < 0 || $value > 100) {
|
||||||
|
if (Utils::compare(0.0, $value, 100.0, Utils::EPSILON_SMALL)) {
|
||||||
throw new \LengthException(
|
throw new \LengthException(
|
||||||
'Argument value ' . $value . ' for saturation is not in the range of 0 to 100',
|
'Argument value ' . $value . ' for saturation is not in the range of 0 to 100',
|
||||||
2
|
2
|
||||||
@@ -102,7 +106,8 @@ class HSB implements Interface\CoordinatesInterface
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'B':
|
case 'B':
|
||||||
if ((int)$value < 0 || (int)$value > 100) {
|
// if ($value < 0 || $value > 100) {
|
||||||
|
if (Utils::compare(0.0, $value, 100.0, Utils::EPSILON_SMALL)) {
|
||||||
throw new \LengthException(
|
throw new \LengthException(
|
||||||
'Argument value ' . $value . ' for brightness is not in the range of 0 to 100',
|
'Argument value ' . $value . ' for brightness is not in the range of 0 to 100',
|
||||||
3
|
3
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace CoreLibs\Convert\Color\Coordinates;
|
namespace CoreLibs\Convert\Color\Coordinates;
|
||||||
|
|
||||||
use CoreLibs\Convert\Color\Stringify;
|
use CoreLibs\Convert\Color\Utils;
|
||||||
|
|
||||||
class HSL implements Interface\CoordinatesInterface
|
class HSL implements Interface\CoordinatesInterface
|
||||||
{
|
{
|
||||||
@@ -84,10 +84,11 @@ class HSL implements Interface\CoordinatesInterface
|
|||||||
}
|
}
|
||||||
switch ($name) {
|
switch ($name) {
|
||||||
case 'H':
|
case 'H':
|
||||||
if ((int)$value == 360) {
|
if ($value == 360.0) {
|
||||||
$value = 0;
|
$value = 0;
|
||||||
}
|
}
|
||||||
if ((int)$value < 0 || (int)$value > 360) {
|
// if ($value < 0 || $value > 360) {
|
||||||
|
if (Utils::compare(0.0, $value, 360.0, Utils::EPSILON_SMALL)) {
|
||||||
throw new \LengthException(
|
throw new \LengthException(
|
||||||
'Argument value ' . $value . ' for hue is not in the range of 0 to 360',
|
'Argument value ' . $value . ' for hue is not in the range of 0 to 360',
|
||||||
1
|
1
|
||||||
@@ -95,7 +96,8 @@ class HSL implements Interface\CoordinatesInterface
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'S':
|
case 'S':
|
||||||
if ((int)$value < 0 || (int)$value > 100) {
|
// if ($value < 0 || $value > 100) {
|
||||||
|
if (Utils::compare(0.0, $value, 100.0, Utils::EPSILON_SMALL)) {
|
||||||
throw new \LengthException(
|
throw new \LengthException(
|
||||||
'Argument value ' . $value . ' for saturation is not in the range of 0 to 100',
|
'Argument value ' . $value . ' for saturation is not in the range of 0 to 100',
|
||||||
2
|
2
|
||||||
@@ -103,7 +105,8 @@ class HSL implements Interface\CoordinatesInterface
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'L':
|
case 'L':
|
||||||
if ((int)$value < 0 || (int)$value > 100) {
|
// if ($value < 0 || $value > 100) {
|
||||||
|
if (Utils::compare(0.0, $value, 100.0, Utils::EPSILON_SMALL)) {
|
||||||
throw new \LengthException(
|
throw new \LengthException(
|
||||||
'Argument value ' . $value . ' for lightness is not in the range of 0 to 100',
|
'Argument value ' . $value . ' for lightness is not in the range of 0 to 100',
|
||||||
3
|
3
|
||||||
@@ -183,7 +186,7 @@ class HSL implements Interface\CoordinatesInterface
|
|||||||
. $this->S
|
. $this->S
|
||||||
. ' '
|
. ' '
|
||||||
. $this->L
|
. $this->L
|
||||||
. Stringify::setOpacity($opacity)
|
. Utils::setOpacity($opacity)
|
||||||
. ')';
|
. ')';
|
||||||
return $string;
|
return $string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace CoreLibs\Convert\Color\Coordinates;
|
namespace CoreLibs\Convert\Color\Coordinates;
|
||||||
|
|
||||||
use CoreLibs\Convert\Color\Stringify;
|
use CoreLibs\Convert\Color\Utils;
|
||||||
|
|
||||||
class HWB implements Interface\CoordinatesInterface
|
class HWB implements Interface\CoordinatesInterface
|
||||||
{
|
{
|
||||||
@@ -84,10 +84,11 @@ class HWB implements Interface\CoordinatesInterface
|
|||||||
}
|
}
|
||||||
switch ($name) {
|
switch ($name) {
|
||||||
case 'H':
|
case 'H':
|
||||||
if ((int)$value == 360) {
|
if ($value == 360.0) {
|
||||||
$value = 0;
|
$value = 0;
|
||||||
}
|
}
|
||||||
if ((int)$value < 0 || (int)$value > 360) {
|
// if ($value < 0 || $value > 360) {
|
||||||
|
if (Utils::compare(0.0, $value, 360.0, Utils::EPSILON_SMALL)) {
|
||||||
throw new \LengthException(
|
throw new \LengthException(
|
||||||
'Argument value ' . $value . ' for hue is not in the range of 0 to 360',
|
'Argument value ' . $value . ' for hue is not in the range of 0 to 360',
|
||||||
1
|
1
|
||||||
@@ -95,7 +96,8 @@ class HWB implements Interface\CoordinatesInterface
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'W':
|
case 'W':
|
||||||
if ((int)$value < 0 || (int)$value > 100) {
|
// if ($value < 0 || $value > 100) {
|
||||||
|
if (Utils::compare(0.0, $value, 100.0, Utils::EPSILON_SMALL)) {
|
||||||
throw new \LengthException(
|
throw new \LengthException(
|
||||||
'Argument value ' . $value . ' for whiteness is not in the range of 0 to 100',
|
'Argument value ' . $value . ' for whiteness is not in the range of 0 to 100',
|
||||||
2
|
2
|
||||||
@@ -103,7 +105,8 @@ class HWB implements Interface\CoordinatesInterface
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'B':
|
case 'B':
|
||||||
if ((int)$value < 0 || (int)$value > 100) {
|
// if ($value < 0 || $value > 100) {
|
||||||
|
if (Utils::compare(0.0, $value, 100.0, Utils::EPSILON_SMALL)) {
|
||||||
throw new \LengthException(
|
throw new \LengthException(
|
||||||
'Argument value ' . $value . ' for blackness is not in the range of 0 to 100',
|
'Argument value ' . $value . ' for blackness is not in the range of 0 to 100',
|
||||||
3
|
3
|
||||||
@@ -183,7 +186,7 @@ class HWB implements Interface\CoordinatesInterface
|
|||||||
. $this->W
|
. $this->W
|
||||||
. ' '
|
. ' '
|
||||||
. $this->B
|
. $this->B
|
||||||
. Stringify::setOpacity($opacity)
|
. Utils::setOpacity($opacity)
|
||||||
. ')';
|
. ')';
|
||||||
return $string;
|
return $string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace CoreLibs\Convert\Color\Coordinates;
|
namespace CoreLibs\Convert\Color\Coordinates;
|
||||||
|
|
||||||
use CoreLibs\Convert\Color\Stringify;
|
use CoreLibs\Convert\Color\Utils;
|
||||||
|
|
||||||
class LCH implements Interface\CoordinatesInterface
|
class LCH implements Interface\CoordinatesInterface
|
||||||
{
|
{
|
||||||
@@ -94,43 +94,43 @@ class LCH implements Interface\CoordinatesInterface
|
|||||||
throw new \ErrorException('Creation of dynamic property is not allowed', 0);
|
throw new \ErrorException('Creation of dynamic property is not allowed', 0);
|
||||||
}
|
}
|
||||||
switch ($name) {
|
switch ($name) {
|
||||||
// case 'L':
|
case 'L':
|
||||||
// if ($this->colorspace == 'cie' && ($value < 0 || $value > 100)) {
|
// if ($this->colorspace == 'CIELab' && ($value < 0 || $value > 100)) {
|
||||||
// throw new \LengthException(
|
if ($this->colorspace == 'CIELab' && Utils::compare(0.0, $value, 100.0, Utils::ESPILON_BIG)) {
|
||||||
// 'Argument value ' . $value . ' for lightness is not in the range of '
|
|
||||||
// . '0 to 100',
|
|
||||||
// 3
|
|
||||||
// );
|
|
||||||
// } elseif ($this->colorspace == 'ok' && ($value < 0 || $value > 1)) {
|
|
||||||
// throw new \LengthException(
|
|
||||||
// 'Argument value ' . $value . ' for lightness is not in the range of '
|
|
||||||
// . '0 to 1',
|
|
||||||
// 3
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
// break;
|
|
||||||
// case 'c':
|
|
||||||
// if ($this->colorspace == 'cie' && ($value < 0 || $value > 230)) {
|
|
||||||
// throw new \LengthException(
|
|
||||||
// 'Argument value ' . $value . ' for chroma is not in the range of '
|
|
||||||
// . '0 to 230 with normal upper limit of 150',
|
|
||||||
// 3
|
|
||||||
// );
|
|
||||||
// } elseif ($this->colorspace == 'ok' && ($value < 0 || $value > 0.5)) {
|
|
||||||
// throw new \LengthException(
|
|
||||||
// 'Argument value ' . $value . ' for chroma is not in the range of '
|
|
||||||
// . '0 to 0.5 with normal upper limit of 0.5',
|
|
||||||
// 3
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
// break;
|
|
||||||
case 'h':
|
|
||||||
if ($value == 360) {
|
|
||||||
$value = 0;
|
|
||||||
}
|
|
||||||
if ($value < 0 || $value > 360) {
|
|
||||||
throw new \LengthException(
|
throw new \LengthException(
|
||||||
'Argument value ' . $value . ' for lightness is not in the range of 0 to 360',
|
'Argument value ' . $value . ' for lightness is not in the range of 0 to 100 for CIE Lab',
|
||||||
|
1
|
||||||
|
);
|
||||||
|
// } elseif ($this->colorspace == 'OkLab' && ($value < 0 || $value > 1)) {
|
||||||
|
} elseif ($this->colorspace == 'OkLab' && Utils::compare(0.0, $value, 1.0, Utils::EPSILON_SMALL)) {
|
||||||
|
throw new \LengthException(
|
||||||
|
'Argument value ' . $value . ' for lightness is not in the range of 0.0 to 1.0 for OkLab',
|
||||||
|
1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'C':
|
||||||
|
// if ($this->colorspace == 'CIELab' && ($value < 0 || $value > 230)) {
|
||||||
|
if ($this->colorspace == 'CIELab' && Utils::compare(0.0, $value, 230.0, Utils::EPSILON_SMALL)) {
|
||||||
|
throw new \LengthException(
|
||||||
|
'Argument value ' . $value . ' for chroma is not in the range of '
|
||||||
|
. '0 to 150 and a maximum of 230 for CIE Lab',
|
||||||
|
1
|
||||||
|
);
|
||||||
|
// } elseif ($this->colorspace == 'OkLab' && ($value < 0 || $value > 0.55)) {
|
||||||
|
} elseif ($this->colorspace == 'OkLab' && Utils::compare(0.0, $value, 0.55, Utils::EPSILON_SMALL)) {
|
||||||
|
throw new \LengthException(
|
||||||
|
'Argument value ' . $value . ' for lightness is not in the range of '
|
||||||
|
. '0.0 to 0.4 and a maximum of 0.5 for OkLab',
|
||||||
|
1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'H':
|
||||||
|
// if ($value < 0 || $value > 360) {
|
||||||
|
if (Utils::compare(0.0, $value, 360.0, Utils::EPSILON_SMALL)) {
|
||||||
|
throw new \LengthException(
|
||||||
|
'Argument value ' . $value . ' for hue is not in the range of 0.0 to 360.0',
|
||||||
1
|
1
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -217,7 +217,7 @@ class LCH implements Interface\CoordinatesInterface
|
|||||||
. $this->c
|
. $this->c
|
||||||
. ' '
|
. ' '
|
||||||
. $this->h
|
. $this->h
|
||||||
. Stringify::setOpacity($opacity)
|
. Utils::setOpacity($opacity)
|
||||||
. ');';
|
. ');';
|
||||||
|
|
||||||
return $string;
|
return $string;
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace CoreLibs\Convert\Color\Coordinates;
|
namespace CoreLibs\Convert\Color\Coordinates;
|
||||||
|
|
||||||
use CoreLibs\Convert\Color\Stringify;
|
use CoreLibs\Convert\Color\Utils;
|
||||||
|
|
||||||
class Lab implements Interface\CoordinatesInterface
|
class Lab implements Interface\CoordinatesInterface
|
||||||
{
|
{
|
||||||
@@ -95,35 +95,53 @@ class Lab implements Interface\CoordinatesInterface
|
|||||||
if (!property_exists($this, $name)) {
|
if (!property_exists($this, $name)) {
|
||||||
throw new \ErrorException('Creation of dynamic property is not allowed', 0);
|
throw new \ErrorException('Creation of dynamic property is not allowed', 0);
|
||||||
}
|
}
|
||||||
// switch ($name) {
|
switch ($name) {
|
||||||
// case 'L':
|
case 'L':
|
||||||
// if ($value == 360) {
|
// if ($this->colorspace == 'CIELab' && ($value < 0 || $value > 100)) {
|
||||||
// $value = 0;
|
if ($this->colorspace == 'CIELab' && Utils::compare(0.0, $value, 100.0, Utils::ESPILON_BIG)) {
|
||||||
// }
|
throw new \LengthException(
|
||||||
// if ($value < 0 || $value > 360) {
|
'Argument value ' . $value . ' for lightness is not in the range of 0 to 100 for CIE Lab',
|
||||||
// throw new \LengthException(
|
1
|
||||||
// 'Argument value ' . $value . ' for lightness is not in the range of 0 to 360',
|
);
|
||||||
// 1
|
// } elseif ($this->colorspace == 'OkLab' && ($value < 0 || $value > 1)) {
|
||||||
// );
|
} elseif ($this->colorspace == 'OkLab' && Utils::compare(0.0, $value, 1.0, Utils::EPSILON_SMALL)) {
|
||||||
// }
|
throw new \LengthException(
|
||||||
// break;
|
'Argument value ' . $value . ' for lightness is not in the range of 0.0 to 1.0 for OkLab',
|
||||||
// case 'a':
|
1
|
||||||
// if ($value < 0 || $value > 100) {
|
);
|
||||||
// throw new \LengthException(
|
}
|
||||||
// 'Argument value ' . $value . ' for a is not in the range of 0 to 100',
|
break;
|
||||||
// 2
|
case 'a':
|
||||||
// );
|
// if ($this->colorspace == 'CIELab' && ($value < -125 || $value > 125)) {
|
||||||
// }
|
if ($this->colorspace == 'CIELab' && Utils::compare(-125.0, $value, 125.0, Utils::EPSILON_SMALL)) {
|
||||||
// break;
|
throw new \LengthException(
|
||||||
// case 'b':
|
'Argument value ' . $value . ' for a is not in the range of -125 to 125 for CIE Lab',
|
||||||
// if ($value < 0 || $value > 100) {
|
2
|
||||||
// throw new \LengthException(
|
);
|
||||||
// 'Argument value ' . $value . ' for b is not in the range of 0 to 100',
|
// } elseif ($this->colorspace == 'OkLab' && ($value < -0.55 || $value > 0.55)) {
|
||||||
// 3
|
} elseif ($this->colorspace == 'OkLab' && Utils::compare(-0.55, $value, 0.55, Utils::EPSILON_SMALL)) {
|
||||||
// );
|
throw new \LengthException(
|
||||||
// }
|
'Argument value ' . $value . ' for a is not in the range of -0.5 to 0.5 for OkLab',
|
||||||
// break;
|
2
|
||||||
// }
|
);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'b':
|
||||||
|
// if ($this->colorspace == 'CIELab' && ($value < -125 || $value > 125)) {
|
||||||
|
if ($this->colorspace == 'CIELab' && Utils::compare(-125.0, $value, 125.0, Utils::EPSILON_SMALL)) {
|
||||||
|
throw new \LengthException(
|
||||||
|
'Argument value ' . $value . ' for b is not in the range of -125 to 125 for CIE Lab',
|
||||||
|
3
|
||||||
|
);
|
||||||
|
// } elseif ($this->colorspace == 'OkLab' && ($value < -0.55 || $value > 0.55)) {
|
||||||
|
} elseif ($this->colorspace == 'OkLab' && Utils::compare(-0.55, $value, 0.55, Utils::EPSILON_SMALL)) {
|
||||||
|
throw new \LengthException(
|
||||||
|
'Argument value ' . $value . ' for b is not in the range of -0.5 to 0.5 for OkLab',
|
||||||
|
3
|
||||||
|
);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
$this->$name = $value;
|
$this->$name = $value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -205,7 +223,7 @@ class Lab implements Interface\CoordinatesInterface
|
|||||||
. $this->a
|
. $this->a
|
||||||
. ' '
|
. ' '
|
||||||
. $this->b
|
. $this->b
|
||||||
. Stringify::setOpacity($opacity)
|
. Utils::setOpacity($opacity)
|
||||||
. ');';
|
. ');';
|
||||||
|
|
||||||
return $string;
|
return $string;
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace CoreLibs\Convert\Color\Coordinates;
|
namespace CoreLibs\Convert\Color\Coordinates;
|
||||||
|
|
||||||
use CoreLibs\Convert\Color\Stringify;
|
use CoreLibs\Convert\Color\Utils;
|
||||||
|
|
||||||
class RGB implements Interface\CoordinatesInterface
|
class RGB implements Interface\CoordinatesInterface
|
||||||
{
|
{
|
||||||
@@ -94,8 +94,11 @@ class RGB implements Interface\CoordinatesInterface
|
|||||||
// if not linear
|
// if not linear
|
||||||
if (!$this->linear && ((int)$value < 0 || (int)$value > 255)) {
|
if (!$this->linear && ((int)$value < 0 || (int)$value > 255)) {
|
||||||
throw new \LengthException('Argument value ' . $value . ' for color ' . $name
|
throw new \LengthException('Argument value ' . $value . ' for color ' . $name
|
||||||
. ' is not in the range of 0 to 255', 1);
|
. ' is not in the range of 0 to 255', 1);
|
||||||
} elseif ($this->linear && ((int)$value < 0 || (int)$value > 1)) {
|
} elseif (
|
||||||
|
// $this->linear && ($value < 0.0 || $value > 1.0)
|
||||||
|
$this->linear && Utils::compare(0.0, $value, 1.0, 0.000001)
|
||||||
|
) {
|
||||||
throw new \LengthException('Argument value ' . $value . ' for color ' . $name
|
throw new \LengthException('Argument value ' . $value . ' for color ' . $name
|
||||||
. ' is not in the range of 0 to 1 for linear rgb', 2);
|
. ' is not in the range of 0 to 1 for linear rgb', 2);
|
||||||
}
|
}
|
||||||
@@ -244,6 +247,10 @@ class RGB implements Interface\CoordinatesInterface
|
|||||||
*/
|
*/
|
||||||
public function toLinear(): self
|
public function toLinear(): self
|
||||||
{
|
{
|
||||||
|
// if linear, as is
|
||||||
|
if ($this->linear) {
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
$this->flagLinear(true)->setFromArray(array_map(
|
$this->flagLinear(true)->setFromArray(array_map(
|
||||||
callback: function (int|float $v) {
|
callback: function (int|float $v) {
|
||||||
$v = (float)($v / 255);
|
$v = (float)($v / 255);
|
||||||
@@ -268,6 +275,10 @@ class RGB implements Interface\CoordinatesInterface
|
|||||||
*/
|
*/
|
||||||
public function fromLinear(): self
|
public function fromLinear(): self
|
||||||
{
|
{
|
||||||
|
// if not linear, as is
|
||||||
|
if (!$this->linear) {
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
$this->flagLinear(false)->setFromArray(array_map(
|
$this->flagLinear(false)->setFromArray(array_map(
|
||||||
callback: function (int|float $v) {
|
callback: function (int|float $v) {
|
||||||
$abs = abs($v);
|
$abs = abs($v);
|
||||||
@@ -282,7 +293,6 @@ class RGB implements Interface\CoordinatesInterface
|
|||||||
},
|
},
|
||||||
array: $this->returnAsArray(),
|
array: $this->returnAsArray(),
|
||||||
));
|
));
|
||||||
// $this->linear = false;
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -307,7 +317,7 @@ class RGB implements Interface\CoordinatesInterface
|
|||||||
. (int)round($this->G, 0)
|
. (int)round($this->G, 0)
|
||||||
. ' '
|
. ' '
|
||||||
. (int)round($this->B, 0)
|
. (int)round($this->B, 0)
|
||||||
. Stringify::setOpacity($opacity)
|
. Utils::setOpacity($opacity)
|
||||||
. ')';
|
. ')';
|
||||||
if ($was_linear) {
|
if ($was_linear) {
|
||||||
$this->toLinear();
|
$this->toLinear();
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace CoreLibs\Convert\Color\Coordinates;
|
namespace CoreLibs\Convert\Color\Coordinates;
|
||||||
|
|
||||||
|
// use CoreLibs\Convert\Color\Utils;
|
||||||
|
|
||||||
class XYZ implements Interface\CoordinatesInterface
|
class XYZ implements Interface\CoordinatesInterface
|
||||||
{
|
{
|
||||||
/** @var array<string> allowed colorspaces */
|
/** @var array<string> allowed colorspaces */
|
||||||
@@ -101,9 +103,11 @@ class XYZ implements Interface\CoordinatesInterface
|
|||||||
if (!property_exists($this, $name)) {
|
if (!property_exists($this, $name)) {
|
||||||
throw new \ErrorException('Creation of dynamic property is not allowed', 0);
|
throw new \ErrorException('Creation of dynamic property is not allowed', 0);
|
||||||
}
|
}
|
||||||
// if ($value < 0 || $value > 255) {
|
// TODO: setup XYZ value limits
|
||||||
|
// X: 0 to 95.047, Y: 0 to 100, Z: 0 to 108.88
|
||||||
|
// if (Utils::compare(0.0, $value, 100.0, Utils::EPSILON_SMALL))) {
|
||||||
// throw new \LengthException('Argument value ' . $value . ' for color ' . $name
|
// throw new \LengthException('Argument value ' . $value . ' for color ' . $name
|
||||||
// . ' is not in the range of 0 to 255', 1);
|
// . ' is not in the range of 0 to 100.0', 1);
|
||||||
// }
|
// }
|
||||||
$this->$name = $value;
|
$this->$name = $value;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,25 +19,6 @@ use CoreLibs\Convert\Color\Coordinates\LCH;
|
|||||||
|
|
||||||
class Stringify
|
class Stringify
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* Build the opactiy sub string part and return it
|
|
||||||
*
|
|
||||||
* @param null|float|string|null $opacity
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public static function setOpacity(null|float|string $opacity = null): string
|
|
||||||
{
|
|
||||||
// set opacity, either a string or float
|
|
||||||
if (is_string($opacity)) {
|
|
||||||
$opacity = ' / ' . $opacity;
|
|
||||||
} elseif ($opacity !== null) {
|
|
||||||
$opacity = ' / ' . $opacity;
|
|
||||||
} else {
|
|
||||||
$opacity = '';
|
|
||||||
}
|
|
||||||
return $opacity;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* return the CSS string including optional opacity
|
* return the CSS string including optional opacity
|
||||||
*
|
*
|
||||||
|
|||||||
56
www/lib/CoreLibs/Convert/Color/Utils.php
Normal file
56
www/lib/CoreLibs/Convert/Color/Utils.php
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AUTHOR: Clemens Schwaighofer
|
||||||
|
* CREATED: 2024/11/14
|
||||||
|
* DESCRIPTION:
|
||||||
|
* Utils for color
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace CoreLibs\Convert\Color;
|
||||||
|
|
||||||
|
use CoreLibs\Convert\Math;
|
||||||
|
|
||||||
|
class Utils
|
||||||
|
{
|
||||||
|
/** @var int deviation allowed for valid data checks, small */
|
||||||
|
public const EPSILON_SMALL = 0.000000000001;
|
||||||
|
/** @var int deviation allowed for valid data checks, medium */
|
||||||
|
public const EPSILON_MEDIUM = 0.0000001;
|
||||||
|
/** @var int deviation allowed for valid data checks, big */
|
||||||
|
public const ESPILON_BIG = 0.0001;
|
||||||
|
|
||||||
|
public static function compare(float $lower, float $value, float $upper, float $epslion): bool
|
||||||
|
{
|
||||||
|
if (
|
||||||
|
Math::compareWithEpsilon($value, '<', $lower, $epslion) ||
|
||||||
|
Math::compareWithEpsilon($value, '>', $upper, $epslion)
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build the opactiy sub string part and return it
|
||||||
|
*
|
||||||
|
* @param null|float|string|null $opacity
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function setOpacity(null|float|string $opacity = null): string
|
||||||
|
{
|
||||||
|
// set opacity, either a string or float
|
||||||
|
if (is_string($opacity)) {
|
||||||
|
$opacity = ' / ' . $opacity;
|
||||||
|
} elseif ($opacity !== null) {
|
||||||
|
$opacity = ' / ' . $opacity;
|
||||||
|
} else {
|
||||||
|
$opacity = '';
|
||||||
|
}
|
||||||
|
return $opacity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// __END__
|
||||||
@@ -68,6 +68,66 @@ class Math
|
|||||||
return pow((float)$number, 1.0 / 3);
|
return pow((float)$number, 1.0 / 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* use PHP_FLOAT_EPSILON to compare if two float numbers are matching
|
||||||
|
*
|
||||||
|
* @param float $x
|
||||||
|
* @param float $y
|
||||||
|
* @param float $epsilon [default=PHP_FLOAT_EPSILON]
|
||||||
|
* @return bool True equal
|
||||||
|
*/
|
||||||
|
public static function equalWithEpsilon(float $x, float $y, float $epsilon = PHP_FLOAT_EPSILON): bool
|
||||||
|
{
|
||||||
|
if (abs($x - $y) < $epsilon) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compare two value base on direction given
|
||||||
|
* The default delta is PHP_FLOAT_EPSILON
|
||||||
|
*
|
||||||
|
* @param float $value
|
||||||
|
* @param string $compare
|
||||||
|
* @param float $limit
|
||||||
|
* @param float $epsilon [default=PHP_FLOAT_EPSILON]
|
||||||
|
* @return bool True on smaller/large or equal
|
||||||
|
*/
|
||||||
|
public static function compareWithEpsilon(
|
||||||
|
float $value,
|
||||||
|
string $compare,
|
||||||
|
float $limit,
|
||||||
|
float $epsilon = PHP_FLOAT_EPSILON
|
||||||
|
): bool {
|
||||||
|
switch ($compare) {
|
||||||
|
case '<':
|
||||||
|
if ($value < ($limit - $epsilon)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '<=':
|
||||||
|
if ($value <= ($limit - $epsilon)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '==':
|
||||||
|
return self::equalWithEpsilon($value, $limit, $epsilon);
|
||||||
|
break;
|
||||||
|
case '>':
|
||||||
|
if ($value > ($limit + $epsilon)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '>=':
|
||||||
|
if ($value >= ($limit + $epsilon)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function is directly inspired by the multiplyMatrices() function in color.js
|
* This function is directly inspired by the multiplyMatrices() function in color.js
|
||||||
* form Lea Verou and Chris Lilley.
|
* form Lea Verou and Chris Lilley.
|
||||||
|
|||||||
Reference in New Issue
Block a user