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
|
||||
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
|
||||
// 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()
|
||||
{
|
||||
$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";
|
||||
// $hsl = Color\Color::rgbToHsl($rgb);
|
||||
// print "to HSL: " . print_r($hsl, true) . "\n";
|
||||
@@ -51,7 +55,7 @@ final class CoreLibsConvertColorTest extends TestCase
|
||||
// $rgb_r = Color\Color::hslToRgb($hsl_r);
|
||||
// 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";
|
||||
// $hsb = Color\Color::hslToHsb($hsl);
|
||||
// print "to HSB: " . print_r($hsb, true) . "\n";
|
||||
@@ -63,7 +67,7 @@ final class CoreLibsConvertColorTest extends TestCase
|
||||
// $hsl_r = Color\Color::hsbToHsl($hsb_r);
|
||||
// print "R to HSL: " . print_r($hsl_r, true) . "\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";
|
||||
// $hsl = Color\Color::hsbToHsl($hsb);
|
||||
// print "to HSL: " . print_r($hsl, true) . "\n";
|
||||
@@ -75,7 +79,7 @@ final class CoreLibsConvertColorTest extends TestCase
|
||||
// $hsb_r = Color\Color::hslToHsb($hsl_r);
|
||||
// print "R to HSL: " . print_r($hsb_r, true) . "\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";
|
||||
// $hsl = Color\Color::hwbToHsl($hwb);
|
||||
// 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";
|
||||
}
|
||||
|
||||
// MARK: RGB base
|
||||
|
||||
/**
|
||||
* From/To RGB <-> ... conversion tests
|
||||
*
|
||||
@@ -116,7 +122,7 @@ final class CoreLibsConvertColorTest extends TestCase
|
||||
$b = 255;
|
||||
}
|
||||
// base is always the same
|
||||
$color = Color\Coordinates\RGB::__constructFromArray([$r, $g, $b]);
|
||||
$color = new Color\Coordinates\RGB([$r, $g, $b]);
|
||||
$base = 'rgb';
|
||||
foreach (['hsb', 'hsl', 'hwb'] as $coord) {
|
||||
// print "COORD: " . $coord . ", RGB: " . print_r($color->returnAsArray(), true) . "\n";
|
||||
@@ -145,6 +151,8 @@ final class CoreLibsConvertColorTest extends TestCase
|
||||
// HSB: saturation or brightness 0
|
||||
// HWB: blackness >= 80 and whitness >= 20 or B>=20 & W>=20 or B>=50 & W>=50
|
||||
|
||||
// MARK: HSL base
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
@@ -165,7 +173,7 @@ final class CoreLibsConvertColorTest extends TestCase
|
||||
if (($L == 0 or $L == 100)) {
|
||||
continue;
|
||||
}
|
||||
$color = Color\Coordinates\HSL::__constructFromArray([$H, $S, $L]);
|
||||
$color = new Color\Coordinates\HSL([$H, $S, $L]);
|
||||
$base = 'hsl';
|
||||
foreach (['hsb', 'hwb', 'rgb'] as $coord) {
|
||||
// for rgb hue on S = 0 is irrelevant (B/W)
|
||||
@@ -190,6 +198,8 @@ final class CoreLibsConvertColorTest extends TestCase
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: HSB
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
@@ -210,7 +220,7 @@ final class CoreLibsConvertColorTest extends TestCase
|
||||
if ($S == 0 or $B == 0) {
|
||||
continue;
|
||||
}
|
||||
$color = Color\Coordinates\HSB::__constructFromArray([$H, $S, $B]);
|
||||
$color = new Color\Coordinates\HSB([$H, $S, $B]);
|
||||
$base = 'hsb';
|
||||
foreach (['hwb', 'hsl', 'rgb'] as $coord) {
|
||||
$target = $base . 'To' . ucfirst($coord);
|
||||
@@ -231,6 +241,7 @@ final class CoreLibsConvertColorTest extends TestCase
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: HWB
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
@@ -258,7 +269,7 @@ final class CoreLibsConvertColorTest extends TestCase
|
||||
continue;
|
||||
}
|
||||
$base = 'hwb';
|
||||
$color = Color\Coordinates\HWB::__constructFromArray([$H, $W, $B]);
|
||||
$color = new Color\Coordinates\HWB([$H, $W, $B]);
|
||||
foreach (['hsl', 'hsb', 'rgb'] as $coord) {
|
||||
// for rgb hue on S = 0 is irrelevant (B/W)
|
||||
if ($H > 0 && $coord == 'rgb') {
|
||||
@@ -282,12 +293,13 @@ final class CoreLibsConvertColorTest extends TestCase
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: RGB to hex
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* covers ::returnAsHex()
|
||||
* covers ::__constructFromHexString()
|
||||
* @testdox Convert from RGB to hex and back
|
||||
* @covers ::returnAsHex()
|
||||
* @testdox Convert from and to RGB via hex
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
@@ -308,10 +320,10 @@ final class CoreLibsConvertColorTest extends TestCase
|
||||
}
|
||||
// with or without 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);
|
||||
// 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(
|
||||
[$r, $g, $b],
|
||||
@@ -325,20 +337,849 @@ final class CoreLibsConvertColorTest extends TestCase
|
||||
}
|
||||
}
|
||||
|
||||
// oklab
|
||||
// MARK: RGB Linear
|
||||
|
||||
// cie lab
|
||||
|
||||
// create exceptions for all color spaces
|
||||
|
||||
public function testExceptionHSB(): void
|
||||
/**
|
||||
* linear RGB conversion tests
|
||||
*
|
||||
* @covers ::fromLinear
|
||||
* @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
|
||||
$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
|
||||
// $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
|
||||
{
|
||||
print "OUT: " . \CoreLibs\Convert\Math::cbrt($number) . "\n";
|
||||
$this->assertEquals(
|
||||
$expected,
|
||||
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)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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__
|
||||
|
||||
Reference in New Issue
Block a user