Commit Graph

55 Commits

Author SHA1 Message Date
Clemens Schwaighofer
0a45300c21 fix the deprecation version for Colors class calls 2024-11-18 10:12:48 +09:00
Clemens Schwaighofer
54ce378ae2 Text fix for deprecation message 2024-11-18 10:10:39 +09:00
Clemens Schwaighofer
4ac659f7d9 Colors deprecation messages and remove Class Basic color convert calls 2024-11-18 09:50:24 +09:00
Clemens Schwaighofer
e5a9b149b1 phpstan fixes with move away from __get to dedicated get 2024-11-15 19:43:30 +09:00
Clemens Schwaighofer
5213805a58 phan updates 2024-11-15 18:18:45 +09:00
Clemens Schwaighofer
a9f1d878f7 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
2024-11-15 18:13:16 +09:00
Clemens Schwaighofer
3845bc7ff5 Color Coordinates class udpates
move creation into the main constructor and do not rely on "::create" or
any other pass through creation.

Make all constructors equal with options array so we can create an Interface

Remove all outsite setters. Once a color is set this color stays
2024-11-14 14:51:31 +09:00
Clemens Schwaighofer
32c192a362 Basic colors test add started
Also fixes for various things that come up during test writing

Test phpunit not yet finished (exceptions, etc)

Note: a lot of checks for extreme values are (int) so we do not fail
for small float values
2024-11-13 19:19:35 +09:00
Clemens Schwaighofer
2bd68f32ac Legacy color convert update to use new methods 2024-11-13 13:16:02 +09:00
Clemens Schwaighofer
625272198d Math matrix phpunit checks added 2024-11-13 11:42:24 +09:00
Clemens Schwaighofer
00821bd5ea Move all Cie XYZ to dedicated class as this is not used in direct frontend convert
Clean up old Colors class with calling new class calls

Test all and set phpstan deprecated messages

Add all missing convert functions for oklab/cielab/oklch/cielch calls

Prepare for test run creation
2024-11-12 18:53:18 +09:00
Clemens Schwaighofer
921b9cb3d9 Remove not used Color Coordinate classes and old oklab convert class 2024-11-12 18:53:02 +09:00
Clemens Schwaighofer
720b78b687 Add CIE XYZ classes for D50/D65 whitespace before clean up 2024-11-12 18:52:24 +09:00
Clemens Schwaighofer
565014e1e2 diff --git c/4dev/tests/Convert/CoreLibsConvertMathTest.php i/4dev/tests/Convert/CoreLibsConvertMathTest.php
index 9a97e37e..c98b4b2a 100644
--- c/4dev/tests/Convert/CoreLibsConvertMathTest.php
+++ i/4dev/tests/Convert/CoreLibsConvertMathTest.php
@@ -113,6 +113,8 @@ final class CoreLibsConvertMathTest extends TestCase
 			\CoreLibs\Convert\Math::initNumeric($input)
 		);
 	}
+
+	// TODO: cbrt tests
 }

 // __END__
diff --git c/www/admin/class_test.convert.colors.php i/www/admin/class_test.convert.colors.php
index 6f809691..a37cb2df 100644
--- c/www/admin/class_test.convert.colors.php
+++ i/www/admin/class_test.convert.colors.php
@@ -19,6 +19,8 @@ $LOG_FILE_ID = 'classTest-convert-colors';
 ob_end_flush();

 use CoreLibs\Convert\Colors;
+use CoreLibs\Convert\Color\Color;
+use CoreLibs\Convert\Color\Coordinates;
 use CoreLibs\Debug\Support as DgS;
 use CoreLibs\Convert\SetVarType;

@@ -52,16 +54,16 @@ try {
 	print "**Exception: " . $e->getMessage() . "<br><pre>" . print_r($e, true) . "</pre><br>";
 }
 // B(valid)
-$rgb = [10, 20, 30];
+$rgb = [50, 20, 30];
 $hex = '#0a141e';
 $hsb = [210, 67, 12];
 $hsb_f = [210.5, 67.5, 12.5];
-$hsl = [210, 50, 7.8];
+$hsb = [210, 50, 7.8];
 print "S::COLOR rgb->hex: $rgb[0], $rgb[1], $rgb[2]: " . Colors::rgb2hex($rgb[0], $rgb[1], $rgb[2]) . "<br>";
 print "S::COLOR hex->rgb: $hex: " . DgS::printAr(SetVarType::setArray(
 	Colors::hex2rgb($hex)
 )) . "<br>";
-print "C::S/COLOR rgb->hext: $hex: " . DgS::printAr(SetVarType::setArray(
+print "C::S/COLOR rgb->hex: $hex: " . DgS::printAr(SetVarType::setArray(
 	CoreLibs\Convert\Colors::hex2rgb($hex)
 )) . "<br>";
 // C(to hsb/hsl)
@@ -82,9 +84,9 @@ print "S::COLOR hsb_f->rgb: $hsb_f[0], $hsb_f[1], $hsb_f[2]: "
 	. DgS::printAr(SetVarType::setArray(
 		Colors::hsb2rgb($hsb_f[0], $hsb_f[1], $hsb_f[2])
 	)) . "<br>";
-print "S::COLOR hsl->rgb: $hsl[0], $hsl[1], $hsl[2]: "
+print "S::COLOR hsl->rgb: $hsb[0], $hsb[1], $hsb[2]: "
 	. DgS::printAr(SetVarType::setArray(
-		Colors::hsl2rgb($hsl[0], $hsl[1], $hsl[2])
+		Colors::hsl2rgb($hsb[0], $hsb[1], $hsb[2])
 	)) . "<br>";

 $hsb = [0, 0, 5];
@@ -102,8 +104,44 @@ print "RANDOM IN: H: " . $h . ", S: " . $s . ", B/L: " . $b . "/" . $l . "<br>";
 print "RANDOM hsb->rgb: <pre>" . DgS::printAr(SetVarType::setArray(Colors::hsb2rgb($h, $s, $b))) . "</pre><br>";
 print "RANDOM hsl->rgb: <pre>" . DgS::printAr(SetVarType::setArray(Colors::hsl2rgb($h, $s, $l))) . "</pre><br>";

+$rgb = [0, 0, 0];
+print "rgb 0,0,0: " . Dgs::printAr($rgb) . " => " . Dgs::printAr(Colors::rgb2hsb($rgb[0], $rgb[1], $rgb[2])) . "<br>";
+
 // TODO: run compare check input must match output

+$hwb = Color::hsbToHwb(Coordinates\HSB::__constructFromArray([
+	160,
+	0,
+	50,
+]));
+print "HWB: " . DgS::printAr($hwb) . "<br>";
+$hsb = Color::hwbToHsb($hwb);
+print "HSB: " . DgS::printAr($hsb) . "<br>";
+
+$oklch = Color::rgbToOkLch(Coordinates\RGB::__constructFromArray([
+	250,
+	0,
+	0
+]));
+print "OkLch: " . DgS::printAr($oklch) . "<br>";
+$rgb = Color::okLchToRgb($oklch);
+print "OkLch -> RGB: " . DgS::printAr($rgb) . "<br>";
+
+$oklab = Color::rgbToOkLab(Coordinates\RGB::__constructFromArray([
+	250,
+	0,
+	0
+]));
+print "OkLab: " . DgS::printAr($oklab) . "<br>";
+$rgb = Color::okLabToRgb($oklab);
+print "OkLab -> RGB: " . DgS::printAr($rgb) . "<br>";
+
+$rgb = Coordinates\RGB::__constructFromArray([250, 100, 10])->toLinear();
+print "RGBlinear: " . DgS::printAr($rgb) . "<br>";
+$rgb = Coordinates\RGB::__constructFromArray([0, 0, 0])->toLinear();
+print "RGBlinear: " . DgS::printAr($rgb) . "<br>";
+
+
 print "</body></html>";

 // __END__
diff --git c/www/lib/CoreLibs/Convert/Color/Color.php i/www/lib/CoreLibs/Convert/Color/Color.php
new file mode 100644
index 00000000..c56f8c11
--- /dev/null
+++ i/www/lib/CoreLibs/Convert/Color/Color.php
@@ -0,0 +1,803 @@
+<?php
+
+/**
+ * AUTHOR: Clemens Schwaighofer
+ * CREATED: 2024/11/11
+ * DESCRIPTION:
+ * Color Coordinate and Color Space conversions
+ *
+ * We convert between color cooradinates and color spaces
+ * as seen in the list below
+ *
+ * |      |         RGB           |     Oklab     |       Cie
+ * |      |     | HSB |     |     |       |       |        |        |
+ * |      | RGB | HSV | HSL | HWB | OkLab | OkLch | CieLab | CieLch |
+ * -------+-----+-----+-----+-----+-------+-------+--------+--------+
+ * RGB    |  -  |  o  |  o  |  o  |   o   |   o   |        |        |
+ * HSB/HB |  o  |  -  |  o  |  o  |       |       |        |        |
+ * HSL    |  o  |  o  |  -  |  o  |   o   |   o   |        |        |
+ * HWB    |  o  |  o  |  o  |  -  |       |       |        |        |
+ * OkLab  |  o  |     |  o  |     |   -   |       |        |        |
+ * OkLch  |  o  |     |  o  |     |       |   -   |        |        |
+ * CieLab |     |     |     |     |       |       |   -    |        |
+ * CieLch |     |     |     |     |       |       |        |   -    |
+ *
+ * All color coordinates are classes
+ * The data can then be converted to a CSS string
+*/
+
+declare(strict_types=1);
+
+namespace CoreLibs\Convert\Color;
+
+use CoreLibs\Convert\Math;
+use CoreLibs\Convert\Color\Coordinates\RGB;
+use CoreLibs\Convert\Color\Coordinates\HSL;
+use CoreLibs\Convert\Color\Coordinates\HSB;
+use CoreLibs\Convert\Color\Coordinates\HWB;
+use CoreLibs\Convert\Color\Coordinates\LCH;
+use CoreLibs\Convert\Color\Coordinates\Lab;
+use CoreLibs\Convert\Color\Coordinates\XYZD65;
+
+class Color
+{
+	// MARK: RGB <-> HSL
+
+	/**
+	 * converts a RGB (0-255) to HSL
+	 * return:
+	 * class with hue (0-360), saturation (0-100%) and luminance (0-100%)
+	 *
+	 * @param  RGB $rgb Class for rgb
+	 * @return HSL      Class hue/sat/luminance
+	 */
+	public static function rgbToHsl(RGB $rgb): HSL
+	{
+		$red = $rgb->R / 255;
+		$green = $rgb->G / 255;
+		$blue = $rgb->B / 255;
+
+		$min = min($red, $green, $blue);
+		$max = max($red, $green, $blue);
+		$chroma = $max - $min;
+		$sat = 0;
+		$hue = 0;
+		// luminance
+		$lum = ($max + $min) / 2;
+
+		// achromatic
+		if ($chroma == 0) {
+			// H, S, L
+			return HSL::__constructFromArray([
+				0.0,
+				0.0,
+				$lum * 100,
+			]);
+		} else {
+			$sat = $chroma / (1 - abs(2 * $lum - 1));
+			if ($max == $red) {
+				$hue = fmod((($green - $blue) / $chroma), 6);
+				if ($hue < 0) {
+					$hue = (6 - fmod(abs($hue), 6));
+				}
+			} elseif ($max == $green) {
+				$hue = ($blue - $red) / $chroma + 2;
+			} elseif ($max == $blue) {
+				$hue = ($red - $green) / $chroma + 4;
+			}
+			$hue = $hue * 60;
+			// $sat = 1 - abs(2 * $lum - 1);
+			return HSL::__constructFromArray([
+				$hue,
+				$sat * 100,
+				$lum * 100,
+			]);
+		}
+	}
+
+	/**
+	 * converts an HSL to RGB
+	 * if HSL value is invalid, set this value to 0
+	 *
+	 * @param  HSL $hsl Class with hue: 0-360 (degrees),
+	 *                             saturation: 0-100,
+	 *                             luminance: 0-100
+	 * @return RGB      Class for rgb
+	 */
+	public static function hslToRgb(HSL $hsl): RGB
+	{
+		$hue = $hsl->H;
+		$sat = $hsl->S;
+		$lum = $hsl->L;
+		// calc to internal convert value for hue
+		$hue = (1 / 360) * $hue;
+		// convert to internal 0-1 format
+		$sat /= 100;
+		$lum /= 100;
+		// if saturation is 0
+		if ($sat == 0) {
+			$lum = round($lum * 255);
+			return RGB::__constructFromArray([$lum, $lum, $lum]);
+		} else {
+			$m2 = $lum < 0.5 ? $lum * ($sat + 1) : ($lum + $sat) - ($lum * $sat);
+			$m1 = $lum * 2 - $m2;
+			$hueue = function ($base) use ($m1, $m2) {
+				// base = hue, hue > 360 (1) - 360 (1), else < 0 + 360 (1)
+				$base = $base < 0 ? $base + 1 : ($base > 1 ? $base - 1 : $base);
+				// 6: 60, 2: 180, 3: 240
+				// 2/3 = 240
+				// 1/3 = 120 (all from 360)
+				if ($base * 6 < 1) {
+					return $m1 + ($m2 - $m1) * $base * 6;
+				}
+				if ($base * 2 < 1) {
+					return $m2;
+				}
+				if ($base * 3 < 2) {
+					return $m1 + ($m2 - $m1) * ((2 / 3) - $base) * 6;
+				}
+				return $m1;
+			};
+
+			return RGB::__constructFromArray([
+				255 * $hueue($hue + (1 / 3)),
+				255 * $hueue($hue),
+				255 * $hueue($hue - (1 / 3)),
+			]);
+		}
+	}
+
+		// MARK: RGB <-> HSB
+
+	/**
+	 * rgb2hsb does not clean convert back to rgb in a round trip
+	 * converts RGB to HSB/V values
+	 * returns:
+	 * Class with hue (0-360), sat (0-100%), brightness/value (0-100%)
+	 *
+	 * @param  RGB $rgb Class for rgb
+	 * @return HSB      Class Hue, Sat, Brightness/Value
+	 */
+	public static function rgbToHsb(RGB $rgb): HSB
+	{
+		$red = $rgb->R / 255;
+		$green = $rgb->G / 255;
+		$blue = $rgb->B / 255;
+
+		$MAX = max($red, $green, $blue);
+		$MIN = min($red, $green, $blue);
+		$HUE = 0;
+		$DELTA = $MAX - $MIN;
+
+		// achromatic
+		if ($MAX == $MIN) {
+			return HSB::__constructFromArray([0, 0, $MAX * 100]);
+		}
+		if ($red == $MAX) {
+			$HUE = fmod(($green - $blue) / $DELTA, 6);
+		} elseif ($green == $MAX) {
+			$HUE = (($blue - $red) / $DELTA) + 2;
+		} elseif ($blue == $MAX) {
+			$HUE = (($red - $green) / $DELTA) + 4;
+		}
+		$HUE *= 60;
+		// avoid negative
+		if ($HUE < 0) {
+			$HUE += 360;
+		}
+
+		return HSB::__constructFromArray([
+			$HUE, // Hue
+			($DELTA / $MAX) * 100, // Saturation
+			$MAX * 100, // Brightness
+		]);
+	}
+
+	/**
+	 * hsb2rgb does not clean convert back to hsb in a round trip
+	 * converts HSB/V to RGB values RGB is full INT
+	 * if HSB/V value is invalid, sets this value to 0
+	 *
+	 * @param  HSB $hsb hue 0-360 (int),
+	 *                  saturation 0-100 (int),
+	 *                  brightness/value 0-100 (int)
+	 * @return RGB      Class for RGB
+	 */
+	public static function hsbToRgb(HSB $hsb): RGB
+	{
+		$H = $hsb->H;
+		$S = $hsb->S;
+		$V = $hsb->B;
+		// convert to internal 0-1 format
+		$S /= 100;
+		$V /= 100;
+
+		if ($S == 0) {
+			$V = $V * 255;
+			return RGB::__constructFromArray([$V, $V, $V]);
+		}
+
+		$Hi = floor($H / 60);
+		$f = ($H / 60) - $Hi;
+		$p = $V * (1 - $S);
+		$q = $V * (1 - ($S * $f));
+		$t = $V * (1 - ($S * (1 - $f)));
+
+		switch ($Hi) {
+			case 0:
+				$red = $V;
+				$green = $t;
+				$blue = $p;
+				break;
+			case 1:
+				$red = $q;
+				$green = $V;
+				$blue = $p;
+				break;
+			case 2:
+				$red = $p;
+				$green = $V;
+				$blue = $t;
+				break;
+			case 3:
+				$red = $p;
+				$green = $q;
+				$blue = $V;
+				break;
+			case 4:
+				$red = $t;
+				$green = $p;
+				$blue = $V;
+				break;
+			case 5:
+				$red = $V;
+				$green = $p;
+				$blue = $q;
+				break;
+			default:
+				$red = 0;
+				$green = 0;
+				$blue = 0;
+		}
+
+		return RGB::__constructFromArray([
+			$red * 255,
+			$green * 255,
+			$blue * 255,
+		]);
+	}
+
+	// MARK: HSL <-> HSB
+
+	/**
+	 * Convert HSL to HSB
+	 *
+	 * @param  HSL $hsl
+	 * @return HSB
+	 */
+	public static function hslToHsb(HSL $hsl): HSB
+	{
+		$saturation = $hsl->S / 100;
+		$lightness = $hsl->L / 100;
+		$value  = $lightness + $saturation * min($lightness, 1 - $lightness);
+		// check for black and white
+		$saturation = ($value === 0) ?
+			0 :
+			200 * (1 - $lightness / $value);
+		return HSB::__constructFromArray([
+			$hsl->H,
+			$saturation,
+			$value * 100,
+		]);
+	}
+
+	/**
+	 * Convert HSB to HSL
+	 *
+	 * @param  HSB $hsb
+	 * @return HSL
+	 */
+	public static function hsbToHsl(HSB $hsb): HSL
+	{
+		// hsv/toHsl
+		$hue = $hsb->H;
+		$saturation = $hsb->S / 100;
+		$value = $hsb->V / 100;
+
+		$lightness = $value * (1 - $saturation / 2);
+		// check for B/W
+		$saturation = in_array($lightness, [0, 1], true) ?
+			0 :
+			100 * ($value - $lightness) / min($lightness, 1 - $lightness)
+		;
+
+		return HSL::__constructFromArray([
+			$hue,
+			$saturation,
+			$lightness * 100,
+		]);
+	}
+
+	// MARK: HSB <-> HWB
+
+	/**
+	 * convert HSB to HWB
+	 *
+	 * @param  HSB $hsb
+	 * @return HWB
+	 */
+	public static function hsbToHwb(HSB $hsb): HWB
+	{
+		// hsv\Hwb
+		return HWB::__constructFromArray([
+			$hsb->H, // hue,
+			$hsb->B * (100 - $hsb->S) / 100, // 2: brightness, 1: saturation
+			100 - $hsb->B,
+		]);
+	}
+
+	/**
+	 * convert HWB to HSB
+	 *
+	 * @param  HWB $hwb
+	 * @return HSB
+	 */
+	public static function hwbToHsb(HWB $hwb): HSB
+	{
+		$hue = $hwb->H;
+		$whiteness = $hwb->W / 100;
+		$blackness = $hwb->B / 100;
+
+		$sum = $whiteness + $blackness;
+		// for black and white
+		if ($sum >= 1) {
+			$saturation = 0;
+			$value = $whiteness / $sum * 100;
+		} else {
+			$value = 1 - $blackness;
+			$saturation = $value === 0 ? 0 : (1 - $whiteness / $value) * 100;
+			$value *= 100;
+		}
+
+		return HSB::__constructFromArray([
+			$hue,
+			$saturation,
+			$value,
+		]);
+	}
+
+	// MARK: RGB <-> HWB
+
+	/**
+	 * Convert RGB to HWB
+	 * via rgb -> hsl -> hsb -> hwb
+	 *
+	 * @param  RGB $rgb
+	 * @return HWB
+	 */
+	public static function rgbToHwb(RGB $rgb): HWB
+	{
+		return self::hsbToHwb(
+			self::hslToHsb(
+				self::rgbToHsl($rgb)
+			)
+		);
+	}
+
+	/**
+	 * Convert HWB to RGB
+	 * via hwb -> hsb -> hsl -> rgb
+	 *
+	 * @param  HWB $hwb
+	 * @return RGB
+	 */
+	public static function hwbToRgb(HWB $hwb): RGB
+	{
+		return self::hslToRgb(
+			self::hsbToHsl(
+				self::hwbToHsb($hwb)
+			)
+		);
+	}
+
+	// MARK: HSL <-> HWB
+
+	/**
+	 * Convert HSL to HWB
+	 * via hsl -> hsb -> hwb
+	 *
+	 * @param  HSL $hsl
+	 * @return HWB
+	 */
+	public static function hslToHwb(HSL $hsl): HWB
+	{
+		return self::hsbToHwb(
+			self::hslToHsb(
+				$hsl
+			)
+		);
+	}
+
+	/**
+	 * Convert HWB to HSL
+	 * via hwb -> hsb -> hsl
+	 *
+	 * @param  HWB $hwb
+	 * @return HSL
+	 */
+	public static function hwbToHsl(HWB $hwb): HSL
+	{
+		return self::hsbToHsl(
+			self::hwbToHsb($hwb)
+		);
+	}
+
+	// MARK: OkLch <-> OkLab
+
+	/**
+	 * okLAab to okLCH
+	 *
+	 * @param  Lab $lab
+	 * @return LCH
+	 */
+	public static function okLabToOkLch(Lab $lab): LCH
+	{
+		// okLab\toOkLch
+		$a = $lab->a;
+		$b = $lab->b;
+
+		$hue = atan2($b, $a) * 180 / pi();
+
+		return LCH::__constructFromArray([
+			$lab->L,
+			sqrt($a ** 2 + $b ** 2),
+			$hue >= 0 ? $hue : $hue + 360,
+		]);
+	}
+
+	/**
+	 * okLCH to okLab
+	 *
+	 * @param  LCH $lch
+	 * @return Lab
+	 */
+	public static function okLchToOkLab(LCH $lch): Lab
+	{
+		// oklch/toOkLab
+		// oklch to oklab
+		return Lab::__constructFromArray([
+			$lch->L,
+			$lch->C * cos($lch->H * pi() / 180), // a
+			$lch->C * sin($lch->H * pi() / 180), // b
+		], 'Oklab');
+	}
+
+	// MARK: xyzD65 <-> linearRGB
+
+	/**
+	 * convert linear RGB to xyz D65
+	 * if rgb is not flagged linear, it will be auto converted
+	 *
+	 * @param  RGB  $rgb
+	 * @return XYZD65
+	 */
+	public static function linRgbToXyzD65(RGB $rgb): XYZD65
+	{
+		// if not linear, convert to linear
+		if (!$rgb->linear) {
+			$rgb->toLinear();
+		}
+		return XYZD65::__constructFromArray(Math::multiplyMatrices(
+			[
+				[0.41239079926595934, 0.357584339383878, 0.1804807884018343],
+				[0.21263900587151027, 0.715168678767756, 0.07219231536073371],
+				[0.01933081871559182, 0.11919477979462598, 0.9505321522496607],
+			],
+			$rgb->returnAsArray()
+		));
+	}
+
+	/**
+	 * Convert xyz D65 to linear RGB
+	 *
+	 * @param  XYZD65 $xyzD65
+	 * @return RGB
+	 */
+	public static function xyzD65ToLinRgb(XYZD65 $xyzD65): RGB
+	{
+		// xyz D65 to linrgb
+		return RGB::__constructFromArray(Math::multiplyMatrices(
+			a : [
+				[  3.2409699419045226,  -1.537383177570094,   -0.4986107602930034  ],
+				[ -0.9692436362808796,   1.8759675015077202,   0.04155505740717559 ],
+				[  0.05563007969699366, -0.20397695888897652,  1.0569715142428786  ],
+			],
+			b : $xyzD65->returnAsArray()
+		), linear: true);
+	}
+
+	// MARK: xyzD65 <-> OkLab
+
+	/**
+	 * xyz D65 to OkLab
+	 *
+	 * @param  XYZD65 $xyzD65
+	 * @return Lab
+	 */
+	public static function xyzD65ToOkLab(XYZD65 $xyzD65): Lab
+	{
+		return Lab::__constructFromArray(Math::multiplyMatrices(
+			[
+				[0.2104542553, 0.7936177850, -0.0040720468],
+				[1.9779984951, -2.4285922050, 0.4505937099],
+				[0.0259040371, 0.7827717662, -0.8086757660],
+			],
+			array_map(
+				callback: fn ($v) => pow($v, 1 / 3),
+				array: Math::multiplyMatrices(
+					a: [
+						[0.8190224432164319, 0.3619062562801221, -0.12887378261216414],
+						[0.0329836671980271, 0.9292868468965546, 0.03614466816999844],
+						[0.048177199566046255, 0.26423952494422764, 0.6335478258136937],
+					],
+					b: $xyzD65->returnAsArray(),
+				),
+			)
+		), 'Oklab');
+	}
+
+	/**
+	 * xyz D65 to OkLab
+	 *
+	 * @param  Lab    $lab
+	 * @return XYZD65
+	 */
+	public static function okLabToXyzD65(Lab $lab): XYZD65
+	{
+		return XYZD65::__constructFromArray(Math::multiplyMatrices(
+			a: [
+					[1.2268798733741557, -0.5578149965554813, 0.28139105017721583],
+					[-0.04057576262431372, 1.1122868293970594, -0.07171106666151701],
+					[-0.07637294974672142, -0.4214933239627914, 1.5869240244272418],
+			],
+			b: array_map(
+				callback: fn ($v) => is_numeric($v) ? $v ** 3 : 0,
+				array: Math::multiplyMatrices(
+					a: [
+							[0.99999999845051981432, 0.39633779217376785678, 0.21580375806075880339],
+							[1.0000000088817607767, -0.1055613423236563494, -0.063854174771705903402],
+							[1.0000000546724109177, -0.089484182094965759684, -1.2914855378640917399],
+					],
+					// Divide $lightness by 100 to convert from CSS OkLab
+					b: $lab->returnAsArray(),
+				),
+			),
+		));
+	}
+
+	// MARK: rgb <-> oklab
+
+	/**
+	 * Undocumented function
+	 *
+	 * @param  RGB $rgb
+	 * @return Lab
+	 */
+	public static function rgbToOkLab(RGB $rgb): Lab
+	{
+		return self::xyzD65ToOkLab(
+			self::linRgbToXyzD65($rgb)
+		);
+	}
+
+	/**
+	 * Undocumented function
+	 *
+	 * @param  Lab $lab
+	 * @return RGB
+	 */
+	public static function okLabToRgb(Lab $lab): RGB
+	{
+		return self::xyzD65ToLinRgb(
+			self::okLabToXyzD65($lab)
+		)->fromLinear();
+	}
+
+	// MARK: rgb <-> oklch
+
+	/**
+	 * convert rgb to OkLch
+	 * via rgb -> linear rgb -> xyz D65 -> OkLab -> OkLch
+	 *
+	 * @param  RGB $rbh
+	 * @return LCH
+	 */
+	public static function rgbToOkLch(RGB $rgb): LCH
+	{
+		return self::okLabToOkLch(
+			self::rgbToOkLab($rgb)
+		);
+	}
+
+	/**
+	 * Convert OkLch to rgb
+	 * via OkLab -> OkLch -> xyz D65 -> linear rgb -> rgb
+	 *
+	 * @param  LCH $lch
+	 * @return RGB
+	 */
+	public static function okLchToRgb(LCH $lch): RGB
+	{
+		return self::okLabToRgb(
+			self::okLchToOkLab($lch)
+		);
+	}
+
+	// MARK: HSL <-> OKLab
+
+	/**
+	 * Undocumented function
+	 *
+	 * @param  HSL $hsl
+	 * @return Lab
+	 */
+	public static function hslToOkLab(HSL $hsl): Lab
+	{
+		return self::rgbToOkLab(
+			self::hslToRgb($hsl)
+		);
+	}
+
+	/**
+	 * Undocumented function
+	 *
+	 * @param  Lab $lab
+	 * @return HSL
+	 */
+	public static function okLabToHsl(Lab $lab): HSL
+	{
+		return self::rgbToHsl(
+			self::okLabToRgb($lab)
+		);
+	}
+
+	// MARK: HSL <-> OKLCH
+
+	/**
+	 * Undocumented function
+	 *
+	 * @param  HSL $hsl
+	 * @return LCH
+	 */
+	public static function hslToOkLch(HSL $hsl): LCH
+	{
+		return self::rgbToOkLch(
+			self::hslToRgb($hsl)
+		);
+	}
+
+	/**
+	 * Undocumented function
+	 *
+	 * @param  LCH $lch
+	 * @return HSL
+	 */
+	public static function okLchToHsl(LCH $lch): HSL
+	{
+		return self::rgbToHsl(
+			self::okLchToRgb($lch)
+		);
+	}
+
+	// MARK: HSB <-> OKLab
+
+	/**
+	 * Undocumented function
+	 *
+	 * @param  HSB $hsb
+	 * @return Lab
+	 */
+	public static function hsbToOkLab(HSB $hsb): Lab
+	{
+		return self::rgbToOkLab(
+			self::hsbToRgb($hsb)
+		);
+	}
+
+	/**
+	 * Undocumented function
+	 *
+	 * @param  Lab $lab
+	 * @return HSB
+	 */
+	public static function okLabToHsb(Lab $lab): HSB
+	{
+		return self::rgbToHsb(
+			self::okLabToRgb($lab)
+		);
+	}
+
+	// MARK: HSB <-> OKLCH
+
+	/**
+	 * Undocumented function
+	 *
+	 * @param  HSB $hsb
+	 * @return LCH
+	 */
+	public static function hsbToOkLch(HSB $hsb): LCH
+	{
+		return self::rgbToOkLch(
+			self::hsbToRgb($hsb)
+		);
+	}
+
+	/**
+	 * Undocumented function
+	 *
+	 * @param  LCH $lch
+	 * @return HSB
+	 */
+	public static function okLchToHsb(LCH $lch): HSB
+	{
+		return self::rgbToHsb(
+			self::okLchToRgb($lch)
+		);
+	}
+
+	// MARK: HWB <-> OKLab
+
+	/**
+	 * Undocumented function
+	 *
+	 * @param  HWB $hwb
+	 * @return Lab
+	 */
+	public function hwbToOkLab(HWB $hwb): Lab
+	{
+		return self::rgbToOkLab(
+			self::hwbToRgb($hwb)
+		);
+	}
+
+	/**
+	 * Undocumented function
+	 *
+	 * @param  Lab $lab
+	 * @return HWB
+	 */
+	public function okLabToHwb(Lab $lab): HWB
+	{
+		return self::rgbToHwb(
+			self::okLabToRgb($lab)
+		);
+	}
+
+	// MARK: HWB <-> OKLCH
+
+	/**
+	 * Undocumented function
+	 *
+	 * @param  HWB $hwb
+	 * @return LCH
+	 */
+	public function hwbToOkLch(HWB $hwb): LCH
+	{
+		return self::rgbToOkLch(
+			self::hwbToRgb($hwb)
+		);
+	}
+
+	/**
+	 * Undocumented function
+	 *
+	 * @param  LCH $lch
+	 * @return HWB
+	 */
+	public function okLchToHwb(LCH $lch): HWB
+	{
+		return self::rgbToHwb(
+			self::okLchToRgb($lch)
+		);
+	}
+}
diff --git c/www/lib/CoreLibs/Convert/Color/Coordinates/HSB.php i/www/lib/CoreLibs/Convert/Color/Coordinates/HSB.php
new file mode 100644
index 00000000..b435a9ef
--- /dev/null
+++ i/www/lib/CoreLibs/Convert/Color/Coordinates/HSB.php
@@ -0,0 +1,155 @@
+<?php
+
+/**
+ * AUTHOR: Clemens Schwaighofer
+ * CREATED: 2024/11/11
+ * DESCRIPTION:
+ * Color Coordinate: HSB/HSV
+*/
+
+declare(strict_types=1);
+
+namespace CoreLibs\Convert\Color\Coordinates;
+
+class HSB
+{
+	/** @var float hue */
+	private float $H = 0.0;
+	/** @var float saturation */
+	private float $S = 0.0;
+	/** @var float brightness / value */
+	private float $B = 0.0;
+
+	/**
+	 * HSB (HSV) color coordinates
+	 * Hue/Saturation/Brightness or Value
+	 */
+	public function __construct()
+	{
+	}
+
+	/**
+	 * set with each value as parameters
+	 *
+	 * @param  float $H Hue
+	 * @param  float $S Saturation
+	 * @param  float $B Brightness
+	 * @return self
+	 */
+	public static function __constructFromSet(float $H, float $S, float $B): self
+	{
+		return (new HSB())->setAsArray([$H, $S, $B]);
+	}
+
+	/**
+	 * set from array
+	 * where 0: Hue, 1: Saturation, 2: Brightness
+	 *
+	 * @param  array{0:float,1:float,2:float} $hsb
+	 * @return self
+	 */
+	public static function __constructFromArray(array $hsb): self
+	{
+		return (new HSB())->setAsArray($hsb);
+	}
+
+	/**
+	 * set color
+	 *
+	 * @param  string $name
+	 * @param  float  $value
+	 * @return void
+	 */
+	public function __set(string $name, float $value): void
+	{
+		$name = strtoupper($name);
+		if (!property_exists($this, $name)) {
+			throw new \ErrorException('Creation of dynamic property is not allowed', 0);
+		}
+		switch ($name) {
+			case 'H':
+				if ($value == 360) {
+					$value = 0;
+				}
+				if ($value < 0 || $value > 359) {
+					throw new \LengthException(
+						'Argument value ' . $value . ' for hue is not in the range of 0 to 359',
+						1
+					);
+				}
+				break;
+			case 'S':
+				if ($value < 0 || $value > 100) {
+					throw new \LengthException(
+						'Argument value ' . $value . ' for saturation is not in the range of 0 to 100',
+						2
+					);
+				}
+				break;
+			case 'B':
+				if ($value < 0 || $value > 100) {
+					throw new \LengthException(
+						'Argument value ' . $value . ' for brightness is not in the range of 0 to 100',
+						3
+					);
+				}
+				break;
+		}
+		$this->$name = $value;
+	}
+
+	/**
+	 * get color
+	 *
+	 * @param string $name
+	 * @return float
+	 */
+	public function __get(string $name): float
+	{
+		$name = strtoupper($name);
+		if (!property_exists($this, $name)) {
+			throw new \ErrorException('Creation of dynamic property is not allowed', 0);
+		}
+		return $this->$name;
+	}
+
+	/**
+	 * Returns the color as array
+	 * where 0: Hue, 1: Saturation, 2: Brightness
+	 *
+	 * @return array{0:float,1:float,2:float}
+	 */
+	public function returnAsArray(): array
+	{
+		return [$this->H, $this->S, $this->B];
+	}
+
+	/**
+	 * set color as array
+	 * where 0: Hue, 1: Saturation, 2: Brightness
+	 *
+	 * @param  array{0:float,1:float,2:float} $hsb
+	 * @return self
+	 */
+	public function setAsArray(array $hsb): self
+	{
+		$this->__set('H', $hsb[0]);
+		$this->__set('S', $hsb[1]);
+		$this->__set('B', $hsb[2]);
+		return $this;
+	}
+
+	/**
+	 * no hsb in css
+	 *
+	 * @param  float|string|null $opacity
+	 * @return string
+	 * @throws \ErrorException
+	 */
+	public function toCssString(null|float|string $opacity = null): string
+	{
+		throw new \ErrorException('HSB is not available as CSS color string', 0);
+	}
+}
+
+// __END__
diff --git c/www/lib/CoreLibs/Convert/Color/Coordinates/HSL.php i/www/lib/CoreLibs/Convert/Color/Coordinates/HSL.php
new file mode 100644
index 00000000..21be7fe5
--- /dev/null
+++ i/www/lib/CoreLibs/Convert/Color/Coordinates/HSL.php
@@ -0,0 +1,140 @@
+<?php
+
+/**
+ * AUTHOR: Clemens Schwaighofer
+ * CREATED: 2024/11/11
+ * DESCRIPTION:
+ * Color Coordinate: HSL
+*/
+
+declare(strict_types=1);
+
+namespace CoreLibs\Convert\Color\Coordinates;
+
+class HSL
+{
+	/** @var float hue */
+	private float $H = 0.0;
+	/** @var float saturation */
+	private float $S = 0.0;
+	/** @var float lightness (luminance) */
+	private float $L = 0.0;
+	/**
+	 * Color Coordinate HSL
+	 * Hue/Saturation/Lightness
+	 */
+	public function __construct()
+	{
+	}
+
+	/**
+	 * set with each value as parameters
+	 *
+	 * @param  float $H Hue
+	 * @param  float $S Saturation
+	 * @param  float $L Lightness
+	 * @return self
+	 */
+	public static function __constructFromSet(float $H, float $S, float $L): self
+	{
+		return (new HSL())->setAsArray([$H, $S, $L]);
+	}
+
+	/**
+	 * set from array
+	 * where 0: Hue, 1: Saturation, 2: Lightness
+	 *
+	 * @param  array{0:float,1:float,2:float} $hsl
+	 * @return self
+	 */
+	public static function __constructFromArray(array $hsl): self
+	{
+		return (new HSL())->setAsArray($hsl);
+	}
+
+	/**
+	 * set color
+	 *
+	 * @param  string $name
+	 * @param  float  $value
+	 * @return void
+	 */
+	public function __set(string $name, float $value): void
+	{
+		if (!property_exists($this, $name)) {
+			throw new \ErrorException('Creation of dynamic property is not allowed', 0);
+		}
+		switch ($name) {
+			case 'H':
+				if ($value == 360) {
+					$value = 0;
+				}
+				if ($value < 0 || $value > 359) {
+					throw new \LengthException(
+						'Argument value ' . $value . ' for hue is not in the range of 0 to 359',
+						1
+					);
+				}
+				break;
+			case 'S':
+				if ($value < 0 || $value > 100) {
+					throw new \LengthException(
+						'Argument value ' . $value . ' for saturation is not in the range of 0 to 100',
+						2
+					);
+				}
+				break;
+			case 'L':
+				if ($value < 0 || $value > 100) {
+					throw new \LengthException(
+						'Argument value ' . $value . ' for luminance is not in the range of 0 to 100',
+						3
+					);
+				}
+				break;
+		}
+		$this->$name = $value;
+	}
+
+	/**
+	 * get color
+	 *
+	 * @param string $name
+	 * @return float
+	 */
+	public function __get(string $name): float
+	{
+		if (!property_exists($this, $name)) {
+			throw new \ErrorException('Creation of dynamic property is not allowed', 0);
+		}
+		return $this->$name;
+	}
+
+	/**
+	 * Returns the color as array
+	 * where 0: Hue, 1: Saturation, 2: Lightness
+	 *
+	 * @return array{0:float,1:float,2:float}
+	 */
+	public function returnAsArray(): array
+	{
+		return [$this->H, $this->S, $this->L];
+	}
+
+	/**
+	 * set color as array
+	 * where 0: Hue, 1: Saturation, 2: Lightness
+	 *
+	 * @param  array{0:float,1:float,2:float} $hsl
+	 * @return self
+	 */
+	public function setAsArray(array $hsl): self
+	{
+		$this->__set('H', $hsl[0]);
+		$this->__set('S', $hsl[1]);
+		$this->__set('L', $hsl[2]);
+		return $this;
+	}
+}
+
+// __END__
diff --git c/www/lib/CoreLibs/Convert/Color/Coordinates/HWB.php i/www/lib/CoreLibs/Convert/Color/Coordinates/HWB.php
new file mode 100644
index 00000000..ee6b7f63
--- /dev/null
+++ i/www/lib/CoreLibs/Convert/Color/Coordinates/HWB.php
@@ -0,0 +1,140 @@
+<?php
+
+/**
+ * AUTHOR: Clemens Schwaighofer
+ * CREATED: 2024/11/11
+ * DESCRIPTION:
+ * Color Coordinate: HWB
+*/
+
+declare(strict_types=1);
+
+namespace CoreLibs\Convert\Color\Coordinates;
+
+class HWB
+{
+	/** @var float Hue */
+	private float $H = 0.0;
+	/** @var float Whiteness */
+	private float $W = 0.0;
+	/** @var float Blackness */
+	private float $B = 0.0;
+	/**
+	 * Color Coordinate: HWB
+	 * Hue/Whiteness/Blackness
+	 */
+	public function __construct()
+	{
+	}
+
+	/**
+	 * set with each value as parameters
+	 *
+	 * @param  float $H Hue
+	 * @param  float $W Whiteness
+	 * @param  float $B Blackness
+	 * @return self
+	 */
+	public static function __constructFromSet(float $H, float $W, float $B): self
+	{
+		return (new HWB())->setAsArray([$H, $W, $B]);
+	}
+
+	/**
+	 * set from array
+	 * where 0: Hue, 1: Whiteness, 2: Blackness
+	 *
+	 * @param  array{0:float,1:float,2:float} $hwb
+	 * @return self
+	 */
+	public static function __constructFromArray(array $hwb): self
+	{
+		return (new HWB())->setAsArray($hwb);
+	}
+
+	/**
+	 * set color
+	 *
+	 * @param  string $name
+	 * @param  float  $value
+	 * @return void
+	 */
+	public function __set(string $name, float $value): void
+	{
+		if (!property_exists($this, $name)) {
+			throw new \ErrorException('Creation of dynamic property is not allowed', 0);
+		}
+		switch ($name) {
+			case 'H':
+				if ($value == 360) {
+					$value = 0;
+				}
+				if ($value < 0 || $value > 360) {
+					throw new \LengthException(
+						'Argument value ' . $value . ' for hue is not in the range of 0 to 360',
+						1
+					);
+				}
+				break;
+			case 'W':
+				if ($value < 0 || $value > 100) {
+					throw new \LengthException(
+						'Argument value ' . $value . ' for saturation is not in the range of 0 to 100',
+						2
+					);
+				}
+				break;
+			case 'B':
+				if ($value < 0 || $value > 100) {
+					throw new \LengthException(
+						'Argument value ' . $value . ' for luminance is not in the range of 0 to 100',
+						3
+					);
+				}
+				break;
+		}
+		$this->$name = $value;
+	}
+
+	/**
+	 * get color
+	 *
+	 * @param string $name
+	 * @return float
+	 */
+	public function __get(string $name): float
+	{
+		if (!property_exists($this, $name)) {
+			throw new \ErrorException('Creation of dynamic property is not allowed', 0);
+		}
+		return $this->$name;
+	}
+
+	/**
+	 * Returns the color as array
+	 * where 0: Hue, 1: Whiteness, 2: Blackness
+	 *
+	 * @return array{0:float,1:float,2:float}
+	 */
+	public function returnAsArray(): array
+	{
+		return [$this->H, $this->W, $this->B];
+	}
+
+	/**
+	 * set color as array
+	 * where 0: Hue, 1: Whiteness, 2: Blackness
+	 *
+	 * @param  array{0:float,1:float,2:float} $hwb
+	 * @return self
+	 */
+	public function setAsArray(array $hwb): self
+	{
+		$this->__set('H', $hwb[0]);
+		$this->__set('W', $hwb[1]);
+		$this->__set('B', $hwb[2]);
+		return $this;
+	}
+}
+
+// __END__
diff --git c/www/lib/CoreLibs/Convert/Color/Coordinates/LCH.php i/www/lib/CoreLibs/Convert/Color/Coordinates/LCH.php
new file mode 100644
index 00000000..648fb466
--- /dev/null
+++ i/www/lib/CoreLibs/Convert/Color/Coordinates/LCH.php
@@ -0,0 +1,169 @@
+<?php
+
+/**
+ * AUTHOR: Clemens Schwaighofer
+ * CREATED: 2024/11/11
+ * DESCRIPTION:
+ * Color Coordinate: Lch
+ * for oklch or cie
+*/
+
+declare(strict_types=1);
+
+namespace CoreLibs\Convert\Color\Coordinates;
+
+class LCH
+{
+	/** @var float Lightness/Luminance
+	 * CIE: 0 to 100
+	 * OKlch: 0.0 to 1.0
+	 * BOTH: 0% to 100%
+	 */
+	private float $L = 0.0;
+	/** @var float Chroma
+	 * CIE: 0 to 150, cannot be more than 230
+	 * OkLch: 0 to 0.4, does not exceed 0.5
+	 * BOTH: 0% to 100% (0 to 150, 0 to 0.4)
+	 */
+	private float $C = 0.0;
+	/** @var float Hue
+	 * 0 to 360 deg
+	 */
+	private float $H = 0.0;
+
+	/** @var string color space: either ok or cie */
+	private string $colorspace = '';
+
+	/**
+	 * Color Coordinate Lch
+	 * for oklch
+	 */
+	public function __construct()
+	{
+	}
+
+	/**
+	 * set with each value as parameters
+	 *
+	 * @param  float $L
+	 * @param  float $c
+	 * @param  float $h
+	 * @return self
+	 */
+	public static function __constructFromSet(float $L, float $c, float $h): self
+	{
+		return (new LCH())->setAsArray([$L, $c, $h]);
+	}
+
+	/**
+	 * set from array
+	 * where 0: Lightness, 1: Chroma, 2: Hue
+	 *
+	 * @param  array{0:float,1:float,2:float} $lch
+	 * @return self
+	 */
+	public static function __constructFromArray(array $lch): self
+	{
+		return (new LCH())->setAsArray($lch);
+	}
+
+	/**
+	 * set color
+	 *
+	 * @param  string $name
+	 * @param  float  $value
+	 * @return void
+	 */
+	public function __set(string $name, float $value): void
+	{
+		if (!property_exists($this, $name)) {
+			throw new \ErrorException('Creation of dynamic property is not allowed', 0);
+		}
+		switch ($name) {
+			// case 'L':
+			// 	if ($this->colorspace == 'cie' && ($value < 0 || $value > 100)) {
+			// 		throw new \LengthException(
+			// 			'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(
+						'Argument value ' . $value . ' for lightness is not in the range of 0 to 360',
+						1
+					);
+				}
+				break;
+		}
+		$this->$name = $value;
+	}
+
+	/**
+	 * get color
+	 *
+	 * @param string $name
+	 * @return float
+	 */
+	public function __get(string $name): float
+	{
+		if (!property_exists($this, $name)) {
+			throw new \ErrorException('Creation of dynamic property is not allowed', 0);
+		}
+		return $this->$name;
+	}
+
+	/**
+	 * Returns the color as array
+	 * where 0: Lightness, 1: Chroma, 2: Hue
+	 *
+	 * @return array{0:float,1:float,2:float}
+	 */
+	public function returnAsArray(): array
+	{
+		return [$this->L, $this->C, $this->H];
+	}
+
+	/**
+	 * set color as array
+	 * where 0: Lightness, 1: Chroma, 2: Hue
+	 *
+	 * @param  array{0:float,1:float,2:float} $lch
+	 * @return self
+	 */
+	public function setAsArray(array $lch): self
+	{
+		$this->__set('L', $lch[0]);
+		$this->__set('C', $lch[1]);
+		$this->__set('H', $lch[2]);
+		return $this;
+	}
+}
+
+// __END__
diff --git c/www/lib/CoreLibs/Convert/Color/Coordinates/Lab.php i/www/lib/CoreLibs/Convert/Color/Coordinates/Lab.php
new file mode 100644
index 00000000..e2eb11a4
--- /dev/null
+++ i/www/lib/CoreLibs/Convert/Color/Coordinates/Lab.php
@@ -0,0 +1,177 @@
+<?php
+
+/**
+ * AUTHOR: Clemens Schwaighofer
+ * CREATED: 2024/11/11
+ * DESCRIPTION:
+ * Color Coordinate: Lab
+ * for oklab or cie
+*/
+
+declare(strict_types=1);
+
+namespace CoreLibs\Convert\Color\Coordinates;
+
+class Lab
+{
+	/** @var array<string> allowed colorspaces */
+	private const COLORSPACES = ['Oklab', 'cie'];
+
+	/** @var float lightness/luminance
+	 * CIE: 0f to 100f
+	 * OKlab: 0.0 to 1.0
+	 * BOTH: 0% to 100%
+	 */
+	private float $L = 0.0;
+	/** @var float a axis distance
+	 * CIE: -125 to 125, cannot be more than +/- 160
+	 * OKlab: -0.4 to 0.4, cannot exceed +/- 0.5
+	 * BOTH: -100% to 100% (+/-125 or 0.4)
+	 */
+	private float $a = 0.0;
+	/** @var float b axis distance
+	 * CIE: -125 to 125, cannot be more than +/- 160
+	 * OKlab: -0.4 to 0.4, cannot exceed +/- 0.5
+	 * BOTH: -100% to 100% (+/-125 or 0.4)
+	 */
+	private float $b = 0.0;
+
+	/** @var string color space: either ok or cie */
+	private string $colorspace = '';
+
+	/**
+	 * Color Coordinate: Lab
+	 * for oklab or cie
+	 */
+	public function __construct()
+	{
+	}
+
+	/**
+	 * set with each value as parameters
+	 *
+	 * @param  float $L
+	 * @param  float $a
+	 * @param  float $b
+	 * @param  string $colorspace
+	 * @return self
+	 */
+	public static function __constructFromSet(float $L, float $a, float $b, string $colorspace): self
+	{
+		return (new Lab())->setColorspace($colorspace)->setAsArray([$L, $a, $b]);
+	}
+
+	/**
+	 * set from array
+	 * where 0: Lightness, 1: a, 2: b
+	 *
+	 * @param  array{0:float,1:float,2:float} $rgb
+	 * @param  string $colorspace
+	 * @return self
+	 */
+	public static function __constructFromArray(array $lab, string $colorspace): self
+	{
+		return (new Lab())->setColorspace($colorspace)->setAsArray($lab);
+	}
+
+	/**
+	 * set color
+	 *
+	 * @param  string $name
+	 * @param  float  $value
+	 * @return void
+	 */
+	public function __set(string $name, float $value): void
+	{
+		if (!property_exists($this, $name)) {
+			throw new \ErrorException('Creation of dynamic property is not allowed', 0);
+		}
+		// switch ($name) {
+		// 	case 'L':
+		// 		if ($value == 360) {
+		// 			$value = 0;
+		// 		}
+		// 		if ($value < 0 || $value > 360) {
+		// 			throw new \LengthException(
+		// 				'Argument value ' . $value . ' for lightness is not in the range of 0 to 360',
+		// 				1
+		// 			);
+		// 		}
+		// 		break;
+		// 	case 'a':
+		// 		if ($value < 0 || $value > 100) {
+		// 			throw new \LengthException(
+		// 				'Argument value ' . $value . ' for a is not in the range of 0 to 100',
+		// 				2
+		// 			);
+		// 		}
+		// 		break;
+		// 	case 'b':
+		// 		if ($value < 0 || $value > 100) {
+		// 			throw new \LengthException(
+		// 				'Argument value ' . $value . ' for b is not in the range of 0 to 100',
+		// 				3
+		// 			);
+		// 		}
+		// 		break;
+		// }
+		$this->$name = $value;
+	}
+
+	/**
+	 * get color
+	 *
+	 * @param string $name
+	 * @return float
+	 */
+	public function __get(string $name): float
+	{
+		if (!property_exists($this, $name)) {
+			throw new \ErrorException('Creation of dynamic property is not allowed', 0);
+		}
+		return $this->$name;
+	}
+
+	/**
+	 * set the colorspace
+	 *
+	 * @param  string $colorspace
+	 * @return self
+	 */
+	private function setColorspace(string $colorspace): self
+	{
+		if (!in_array($colorspace, $this::COLORSPACES)) {
+			throw new \InvalidArgumentException('Not allowed colorspace', 0);
+		}
+		$this->colorspace = $colorspace;
+		return $this;
+	}
+
+	/**
+	 * Returns the color as array
+	 * where 0: Lightness, 1: a, 2: b
+	 *
+	 * @return array{0:float,1:float,2:float}
+	 */
+	public function returnAsArray(): array
+	{
+		return [$this->L, $this->a, $this->b];
+	}
+
+	/**
+	 * set color as array
+	 * where 0: Lightness, 1: a, 2: b
+	 *
+	 * @param  array{0:float,1:float,2:float} $lab
+	 * @return self
+	 */
+	public function setAsArray(array $lab): self
+	{
+		$this->__set('L', $lab[0]);
+		$this->__set('a', $lab[1]);
+		$this->__set('b', $lab[2]);
+		return $this;
+	}
+}
+
+// __END__
diff --git c/www/lib/CoreLibs/Convert/Color/Coordinates/RGB.php i/www/lib/CoreLibs/Convert/Color/Coordinates/RGB.php
new file mode 100644
index 00000000..acc81952
--- /dev/null
+++ i/www/lib/CoreLibs/Convert/Color/Coordinates/RGB.php
@@ -0,0 +1,226 @@
+<?php
+
+/**
+ * AUTHOR: Clemens Schwaighofer
+ * CREATED: 2024/11/11
+ * DESCRIPTION:
+ * Color Coordinate: RGB
+*/
+
+declare(strict_types=1);
+
+namespace CoreLibs\Convert\Color\Coordinates;
+
+class RGB
+{
+	/** @var float red 0 to 255 or 0.0f to 1.0f for linear RGB */
+	private float $R = 0.0;
+	/** @var float green 0 to 255 or 0.0f to 1.0f for linear RGB */
+	private float $G = 0.0;
+	/** @var float blue 0 to 255 or 0.0f to 1.0f for linear RGB */
+	private float $B = 0.0;
+
+	/** @var bool set if this is linear */
+	private bool $linear = false;
+
+	/**
+	 * Color Coordinate RGB
+	 */
+	public function __construct()
+	{
+	}
+
+	/**
+	 * set with each value as parameters
+	 *
+	 * @param  float $R Red
+	 * @param  float $G Green
+	 * @param  float $B Blue
+	 * @param  bool $linear [default=false]
+	 * @return self
+	 */
+	public static function __constructFromSet(float $R, float $G, float $B, bool $linear = false): self
+	{
+		return (new RGB())->flagLinear($linear)->setAsArray([$R, $G, $B]);
+	}
+
+	/**
+	 * set from array
+	 * where 0: Red, 1: Green, 2: Blue
+	 *
+	 * @param  array{0:float,1:float,2:float} $rgb
+	 * @param  bool $linear [default=false]
+	 * @return self
+	 */
+	public static function __constructFromArray(array $rgb, bool $linear = false): self
+	{
+		return (new RGB())->flagLinear($linear)->setAsArray($rgb);
+	}
+
+	/**
+	 * set color
+	 *
+	 * @param  string $name
+	 * @param  float  $value
+	 * @return void
+	 */
+	public function __set(string $name, float $value): void
+	{
+		// do not allow setting linear from outside
+		if ($name == 'linear') {
+			return;
+		}
+		if (!property_exists($this, $name)) {
+			throw new \ErrorException('Creation of dynamic property is not allowed', 0);
+		}
+		// if not linear
+		if (!$this->linear && ($value < 0 || $value > 255)) {
+			throw new \LengthException('Argument value ' . $value . ' for color ' . $name
+				. ' is not in the range of 0 to 255', 1);
+		} elseif ($this->linear && ($value < -10E10 || $value > 1)) {
+			// not allow very very small negative numbers
+			throw new \LengthException('Argument value ' . $value . ' for color ' . $name
+				. ' is not in the range of 0 to 1 for linear rgb', 1);
+		}
+		$this->$name = $value;
+	}
+
+	/**
+	 * get color
+	 *
+	 * @param string $name
+	 * @return float|bool
+	 */
+	public function __get(string $name): float|bool
+	{
+		if (!property_exists($this, $name)) {
+			throw new \ErrorException('Creation of dynamic property is not allowed', 0);
+		}
+		return $this->$name;
+	}
+
+	/**
+	 * Returns the color as array
+	 * where 0: Red, 1: Green, 2: Blue
+	 *
+	 * @return array{0:float,1:float,2:float}
+	 */
+	public function returnAsArray(): array
+	{
+		return [$this->R, $this->G, $this->B];
+	}
+
+	/**
+	 * set color as array
+	 * where 0: Red, 1: Green, 2: Blue
+	 *
+	 * @param  array{0:float,1:float,2:float} $rgb
+	 * @return self
+	 */
+	public function setAsArray(array $rgb): self
+	{
+		$this->__set('R', $rgb[0]);
+		$this->__set('G', $rgb[1]);
+		$this->__set('B', $rgb[2]);
+		return $this;
+	}
+
+	/**
+	 * set as linear
+	 * can be used as chain call on create if input is linear RGB
+	 * RGB::__construct**(...)->flagLinear();
+	 * as it returns self
+	 *
+	 * @return self
+	 */
+	private function flagLinear(bool $linear): self
+	{
+		$this->linear = $linear;
+		return $this;
+	}
+
+	/**
+	 * Both function source:
+	 * https://bottosson.github.io/posts/colorwrong/#what-can-we-do%3F
+	 * but reverse f: fromLinear and f_inv for toLinear
+	 * Code copied from here:
+	 * https://stackoverflow.com/a/12894053
+	 *
+	 * converts RGB to linear
+	 * We come from 0-255 so we need to divide by 255
+	 *
+	 * @return self
+	 */
+	public function toLinear(): self
+	{
+		$this->flagLinear(true)->setAsArray(array_map(
+			callback: function (int|float $v) {
+				$v = (float)($v / 255);
+				$abs = abs($v);
+				$sign = ($v < 0) ? -1 : 1;
+				return (float)(
+					$abs <= 0.04045 ?
+						$v / 12.92 :
+						$sign * pow(($abs + 0.055) / 1.055, 2.4)
+				);
+			},
+			array: $this->returnAsArray(),
+		));
+		return $this;
+	}
+
+	/**
+	 * convert back to normal sRGB from linear RGB
+	 * we go to 0-255 rgb so we multiply by 255
+	 *
+	 * @return self
+	 */
+	public function fromLinear(): self
+	{
+		$this->flagLinear(false)->setAsArray(array_map(
+			callback: function (int|float $v) {
+				$abs  = abs($v);
+				$sign = ($v < 0) ? -1 : 1;
+				// during reverse in some situations the values can become negative in very small ways
+				// like -...E16 and ...E17
+				return ($ret = (float)(255 * (
+					$abs <= 0.0031308 ?
+						$v * 12.92 :
+						$sign * (1.055 * pow($abs, 1.0 / 2.4) - 0.055)
+				))) < 0 ? 0 : $ret;
+			},
+			array: $this->returnAsArray(),
+		));
+		// $this->linear = false;
+		return $this;
+	}
+
+	/**
+	 * convert to css string with optional opacity
+	 * Note: if this is a linea RGB, this data will not be correct
+	 *
+	 * @param  float|string|null $opacity
+	 * @return string
+	 */
+	public function toCssString(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 'rgb('
+			. (int)round($this->R, 0)
+			. ' '
+			. (int)round($this->G, 0)
+			. ' '
+			. (int)round($this->B, 0)
+			. $opacity
+			. ')';
+	}
+}
+
+// __END__
diff --git c/www/lib/CoreLibs/Convert/Color/Coordinates/XYZD65.php i/www/lib/CoreLibs/Convert/Color/Coordinates/XYZD65.php
new file mode 100644
index 00000000..ebdf633d
--- /dev/null
+++ i/www/lib/CoreLibs/Convert/Color/Coordinates/XYZD65.php
@@ -0,0 +1,116 @@
+<?php
+
+/**
+ * AUTHOR: Clemens Schwaighofer
+ * CREATED: 2024/11/11
+ * DESCRIPTION:
+ * Color Coordinate: XYZ (Cie)
+ * Note, this is only for the D65 whitepoint
+ * https://en.wikipedia.org/wiki/CIE_1931_color_space#Construction_of_the_CIE_XYZ_color_space_from_the_Wright%E2%80%93Guild_data
+ * https://en.wikipedia.org/wiki/Standard_illuminant#D65_values
+*/
+
+declare(strict_types=1);
+
+namespace CoreLibs\Convert\Color\Coordinates;
+
+class XYZD65
+{
+	private float $X = 0.0;
+	private float $Y = 0.0;
+	private float $Z = 0.0;
+
+	/**
+	 * Color Coordinate Lch
+	 * for oklch
+	 */
+	public function __construct()
+	{
+	}
+
+	/**
+	 * set with each value as parameters
+	 *
+	 * @param  float $X
+	 * @param  float $Y
+	 * @param  float $Z
+	 * @return self
+	 */
+	public static function __constructFromSet(float $X, float $Y, float $Z): self
+	{
+		return (new XYZD65())->setAsArray([$X, $Y, $Z]);
+	}
+
+	/**
+	 * set from array
+	 * where 0: X, 1: Y, 2: Z
+	 *
+	 * @param  array{0:float,1:float,2:float} $xyzD65
+	 * @return self
+	 */
+	public static function __constructFromArray(array $xyzD65): self
+	{
+		return (new XYZD65())->setAsArray($xyzD65);
+	}
+
+	/**
+	 * set color
+	 *
+	 * @param  string $name
+	 * @param  float  $value
+	 * @return void
+	 */
+	public function __set(string $name, float $value): void
+	{
+		if (!property_exists($this, $name)) {
+			throw new \ErrorException('Creation of dynamic property is not allowed', 0);
+		}
+		// if ($value < 0 || $value > 255) {
+		// 	throw new \LengthException('Argument value ' . $value . ' for color ' . $name
+		// 		. ' is not in the range of 0 to 255', 1);
+		// }
+		$this->$name = $value;
+	}
+
+	/**
+	 * get color
+	 *
+	 * @param string $name
+	 * @return float
+	 */
+	public function __get(string $name): float
+	{
+		if (!property_exists($this, $name)) {
+			throw new \ErrorException('Creation of dynamic property is not allowed', 0);
+		}
+		return $this->$name;
+	}
+
+	/**
+	 * Returns the color as array
+	 * where 0: X, 1: Y, 2: Z
+	 *
+	 * @return array{0:float,1:float,2:float}
+	 */
+	public function returnAsArray(): array
+	{
+		return [$this->X, $this->Y, $this->Z];
+	}
+
+	/**
+	 * set color as array
+	 * where 0: X, 1: Y, 2: Z
+	 *
+	 * @param  array{0:float,1:float,2:float} $xyzD65
+	 * @return self
+	 */
+	public function setAsArray(array $xyzD65): self
+	{
+		$this->__set('X', $xyzD65[0]);
+		$this->__set('Y', $xyzD65[1]);
+		$this->__set('Z', $xyzD65[2]);
+		return $this;
+	}
+}
+
+// __END__
diff --git c/www/lib/CoreLibs/Convert/Color/OkLab.php i/www/lib/CoreLibs/Convert/Color/OkLab.php
new file mode 100644
index 00000000..2bbdbbc2
--- /dev/null
+++ i/www/lib/CoreLibs/Convert/Color/OkLab.php
@@ -0,0 +1,80 @@
+<?php
+
+/**
+ * AUTHOR: Clemens Schwaighofer
+ * CREATED: 2024/11/7
+ * DESCRIPTION:
+ * oklab conversions
+ * rgb -> oklab
+ * oklab -> rgb
+ * rgb -> okhsl
+ * okshl -> rgb
+ * rgb -> okhsv
+ * okhsv -> rgb
+*/
+
+declare(strict_types=1);
+
+namespace CoreLibs\Convert\Color;
+
+class OkLab
+{
+	/**
+	 * lines sRGB to oklab
+	 *
+	 * @param  int   $red
+	 * @param  int   $green
+	 * @param  int   $blue
+	 * @return array<float>
+	 */
+	public static function srgb2okLab(int $red, int $green, int $blue): array
+	{
+		$l = (float)0.4122214708 * (float)$red +
+			(float)0.5363325363 * (float)$green +
+			(float)0.0514459929 * (float)$blue;
+		$m = (float)0.2119034982 * (float)$red +
+			(float)0.6806995451 * (float)$green +
+			(float)0.1073969566 * (float)$blue;
+		$s = (float)0.0883024619 * (float)$red +
+			(float)0.2817188376 * (float)$green +
+			(float)0.6299787005 * (float)$blue;
+
+		// cbrtf = 3 root (val)
+		$l_ = pow($l, 1.0 / 3);
+		$m_ = pow($m, 1.0 / 3);
+		$s_ = pow($s, 1.0 / 3);
+
+		return [
+			(float)0.2104542553 * $l_ + (float)0.7936177850 * $m_ - (float)0.0040720468 * $s_,
+			(float)1.9779984951 * $l_ - (float)2.4285922050 * $m_ + (float)0.4505937099 * $s_,
+			(float)0.0259040371 * $l_ + (float)0.7827717662 * $m_ - (float)0.8086757660 * $s_,
+		];
+	}
+
+	/**
+	 * convert okLab to linear sRGB
+	 *
+	 * @param  float $L
+	 * @param  float $a
+	 * @param  float $b
+	 * @return array<int>
+	 */
+	public static function okLab2srgb(float $L, float $a, float $b): array
+	{
+		$l_ = $L + (float)0.3963377774 * $a + (float)0.2158037573 * $b;
+		$m_ = $L - (float)0.1055613458 * $a - (float)0.0638541728 * $b;
+		$s_ = $L - (float)0.0894841775 * $a - (float)1.2914855480 * $b;
+
+		$l = $l_ * $l_ * $l_;
+		$m = $m_ * $m_ * $m_;
+		$s = $s_ * $s_ * $s_;
+
+		return [
+			(int)round(+(float)4.0767416621 * $l - (float)3.3077115913 * $m + (float)0.2309699292 * $s),
+			(int)round(-(float)1.2684380046 * $l + (float)2.6097574011 * $m - (float)0.3413193965 * $s),
+			(int)round(-(float)0.0041960863 * $l - (float)0.7034186147 * $m + (float)1.7076147010 * $s),
+		];
+	}
+}
+
+// __END__
diff --git c/www/lib/CoreLibs/Convert/Color/Stringify.php i/www/lib/CoreLibs/Convert/Color/Stringify.php
new file mode 100644
index 00000000..6ca68431
--- /dev/null
+++ i/www/lib/CoreLibs/Convert/Color/Stringify.php
@@ -0,0 +1,35 @@
+<?php
+
+/**
+ * AUTHOR: Clemens Schwaighofer
+ * CREATED: 2024/11/11
+ * DESCRIPTION:
+ * Convert color coordinate to CSS string
+*/
+
+declare(strict_types=1);
+
+namespace CoreLibs\Convert\Color;
+
+use CoreLibs\Convert\Color\Coordinates\RGB;
+use CoreLibs\Convert\Color\Coordinates\HSL;
+use CoreLibs\Convert\Color\Coordinates\HWB;
+use CoreLibs\Convert\Color\Coordinates\Lab;
+use CoreLibs\Convert\Color\Coordinates\LCH;
+
+class Stringify
+{
+	/**
+	 * Undocumented function
+	 *
+	 * @param  RGB|Lab|LCH|HSL|HWB $data
+	 * @param  null|float|string   $opacity
+	 * @return string
+	 */
+	public static function toCssString(RGB|Lab|LCH|HSL|HWB $data, null|float|string $opacity): string
+	{
+		return $data->toCssString($opacity);
+	}
+}
+
+// __END__
diff --git c/www/lib/CoreLibs/Convert/Colors.php i/www/lib/CoreLibs/Convert/Colors.php
index f9f56171..8ff32608 100644
--- c/www/lib/CoreLibs/Convert/Colors.php
+++ i/www/lib/CoreLibs/Convert/Colors.php
@@ -120,26 +120,29 @@ class Colors
 		$MAX = max($red, $green, $blue);
 		$MIN = min($red, $green, $blue);
 		$HUE = 0;
+		$DELTA = $MAX - $MIN;

+		// achromatic
 		if ($MAX == $MIN) {
 			return [0, 0, round($MAX * 100)];
 		}
 		if ($red == $MAX) {
-			$HUE = ($green - $blue) / ($MAX - $MIN);
+			$HUE = fmod(($green - $blue) / $DELTA, 6);
 		} elseif ($green == $MAX) {
-			$HUE = 2 + (($blue - $red) / ($MAX - $MIN));
+			$HUE = (($blue - $red) / $DELTA) + 2;
 		} elseif ($blue == $MAX) {
-			$HUE = 4 + (($red - $green) / ($MAX - $MIN));
+			$HUE = (($red - $green) / $DELTA) + 4;
 		}
 		$HUE *= 60;
+		// avoid negative
 		if ($HUE < 0) {
 			$HUE += 360;
 		}

 		return [
-			(int)round($HUE),
-			(int)round((($MAX - $MIN) / $MAX) * 100),
-			(int)round($MAX * 100)
+			(int)round($HUE), // Hue
+			(int)round(($DELTA / $MAX) * 100), // Saturation
+			(int)round($MAX * 100) // Value/Brightness
 		];
 	}

diff --git c/www/lib/CoreLibs/Convert/Math.php i/www/lib/CoreLibs/Convert/Math.php
index 205abbf1..26739c5f 100644
--- c/www/lib/CoreLibs/Convert/Math.php
+++ i/www/lib/CoreLibs/Convert/Math.php
@@ -56,6 +56,95 @@ class Math
 			return (float)$number;
 		}
 	}
+
+	/**
+	 * calc cube root
+	 *
+	 * @param  float $number Number to cubic root
+	 * @return float         Calculated value
+	 */
+	public static function cbrt(float $number): float
+	{
+		return pow($number, 1.0 / 3);
+	}
+
+	/**
+	 * This function is directly inspired by the multiplyMatrices() function in color.js
+	 * form Lea Verou and Chris Lilley.
+	 * (see https://github.com/LeaVerou/color.js/blob/main/src/multiply-matrices.js)
+	 * From:
+	 * 3842cf51c9/src/utils/utils.php (L507)
+	 *
+	 * It returns an array which is the product of the two number matrices passed as parameters.
+	 *
+	 * @param  array<array<int|float>> $a m x n matrice
+	 * @param  array<array<int|float>> $b n x p matrice
+	 *
+	 * @return array<array<int|float>>    m x p product
+	 */
+	public static function multiplyMatrices(array $a, array $b): array
+	{
+		$m = count($a);
+
+		if (!is_array($a[0] ?? null)) {
+			// $a is vector, convert to [[a, b, c, ...]]
+			$a = [ $a ];
+		}
+
+		if (!is_array($b[0])) {
+			// $b is vector, convert to [[a], [b], [c], ...]]
+			$b = array_map(
+				callback: fn ($v) => [ $v ],
+				array: $b,
+			);
+		}
+
+		$p = count($b[0]);
+
+		// transpose $b:
+		$bCols = array_map(
+			callback: fn ($k) => \array_map(
+				(fn ($i) => $i[$k]),
+				$b,
+			),
+			array: array_keys($b[0]),
+		);
+
+		$product = array_map(
+			callback: fn ($row) => array_map(
+				callback: fn ($col) => is_array($row) ?
+					array_reduce(
+						array: $row,
+						callback: fn ($a, $v, $i = null) => $a + $v * (
+							$col[$i ?? array_search($v, $row)] ?? 0
+						),
+						initial: 0,
+					) :
+					array_reduce(
+						array: $col,
+						callback: fn ($a, $v) => $a + $v * $row,
+						initial: 0,
+					),
+				array: $bCols,
+			),
+			array: $a,
+		);
+
+		if ($m === 1) {
+			// Avoid [[a, b, c, ...]]:
+			$product = $product[0];
+		}
+
+		if ($p === 1) {
+			// Avoid [[a], [b], [c], ...]]:
+			return array_map(
+				callback: fn ($v) => $v[0],
+				array: $product,
+			);
+		}
+
+		return $product;
+	}
 }

 // __END__
2024-11-11 18:48:56 +09:00
Clemens Schwaighofer
5cec54d508 Add "Success" to message logging levels, fixes for PHP 8.4, other preg_match fixes
The Logger/MessageLevel gets "success" as level 110 to something a bit
heigher than "ok" which is the general "OK" for anything ending without
an error. The "success" is currently only used in file uploads with the
java script ajax file uploader

Fix any "type $var = null" with correctly "?type $var = null" for PHP 8.4 (phphan)

Fix preg match no return catches for DB IO compare version and for language
look up.
2024-09-20 13:33:19 +09:00
Clemens Schwaighofer
ef80cba561 Add new functions: get IPs, strip UTF8 BOM from text, text updates
Add the following new static methods

Convert\Strings::stripUTF8BomBytes: removes the UTF8 BOM bytes from the beginning of a line
Used for CSV files created in Excel for the first header entry (line 0/row 0)

Get\Systen::getIpAddresses: gets all IP addresses for the the current access user
and returns an array

Moved the frontend folder detection from the first load config to the config.path.php

Cleaned up the translations JS scripts
2024-09-03 09:49:01 +09:00
Clemens Schwaighofer
aa2b60973e HTML::htmlent and HTML::checked updates
Changed Params form ENT_COMPAT | ENT_HTML401 to ENT_QUOTES | ENT_HTML5
Flags can be overwritten on call

Logic clean up for return flow

HTML::checked gets logic updated with less nested ifs
2024-08-05 13:24:37 +09:00
Clemens Schwaighofer
f4ff31721b phpunit test add php 8.3, remove special code for mb_encode_mimeheader
In the past we had a special function to do mb_encode_mimeheader correctly.

Since PHP 8.2 this works perfectly fine, so all the code was removed and
replaced with just the normal "mb_encode_mimeheader" call with the same
settings as before:
- set global encoding to parameter
- run encoding with charset, 'B' for transfer and use the line break given in parameter
- reset the global encoding to previous set
2024-04-17 10:08:12 +09:00
Clemens Schwaighofer
f59c325b05 Bug fix in string path slash fix
if null error return original path
2024-03-07 14:37:05 +09:00
Clemens Schwaighofer
a377ab4b61 Strip multiple slashes in CoreLibs Convert Strings 2024-02-14 12:26:17 +09:00
Clemens Schwaighofer
d7c0054ea0 Change default Exception to proper named Exceptions
Byte: InvalidArgumentException for parameters

Image: RunetimeException for cannot write file to system
2023-10-18 09:30:08 +09:00
Clemens Schwaighofer
f9558cd3aa Fix for the SetVarType / SetVarTypeNull
string: if it is stringable in anyway, set string (it converts)
this is not check IF it is a string value as it was before
int/float: same, if it is numerc it will be convert to int or float
All other stay the same

Note "set ..." imply to set, and not to convert to 0 if it is int
string that can be covnerted to int
2023-09-05 14:26:57 +09:00
Clemens Schwaighofer
33cb05a002 Update to Exceptions: add codes, update phpunit tests
DB Class throws Exception if on init it fails to connect to the DB,
will not throw Exception if failed connection during execution but
will do the normal retry and soft failure run
DB\ArrayIO will throw Exception on missing table array and table name

All Exceptions have a code set
2023-09-01 08:37:15 +09:00
Clemens Schwaighofer
cd81d15d9a Convert\Color methods will throw Exception instead of false on error
All Color methods will throw Exceptions:
LengthException,
InvalidArgumentException,
UnexpectedValueException

instead of returning bool: false

All methods will return valid color data as expected only
2023-08-31 10:45:33 +09:00
Clemens Schwaighofer
d70182a84e Add JSON convert array to json with always return string
Allows the same post run error check like the other way around
2023-08-02 16:12:01 +09:00
Clemens Schwaighofer
c383a7b7b7 Update convert colors to return false and not bool
All convert color either return the color value or false.
To make sure any checker knows that we only return "value" or "false"
change all return bool to false
2023-08-02 07:29:49 +09:00
Clemens Schwaighofer
75c4c98de8 Convert all classes to strict variable types
All variable declarations in all classes have a strict type set

Exception: constants (will be setable from PHP 8.3 on), resources (no type)

Debug\LoggingLegacy is kept as is, will be deprecated
2023-05-31 15:58:06 +09:00
Clemens Schwaighofer
1596654149 Moved minimum php version to 8.1
All PostgreSQL calls are now Connection/Resource object types and not
resource
All methods have parameter type set
2023-02-28 17:36:19 +09:00
Clemens Schwaighofer
7b9dc9c8b2 Rename the VarSetType class to SetVarType
The old name made no sense and it was only used in two projects, so the
deprecation step has been skipped
2023-02-22 06:32:58 +09:00
Clemens Schwaighofer
f508b607a6 Add new class for auto set vars to certain types or null
Convert\VarSetType for always return type expected (eg string will be string)
or Convert\VarSetTypeNull to return string or null on failure

The return value for failure if not matching type can be set for both, but
only for Convert\VarSetTypeNull the return value can be set to null.
2023-02-08 12:12:46 +09:00
Clemens Schwaighofer
737f70fac5 Fix phpdoc Exception name with missing \ 2023-01-10 14:40:16 +09:00
Clemens Schwaighofer
0328ccd2fe Convert\Colors fixes for from HSB/HSL Hue 360
If hue 360 is given, it is no longer an error but internally converted to 0
2023-01-10 14:07:01 +09:00
Clemens Schwaighofer
eba1e2885f Convert\Byte add exception
And exception is thrown for invalid flags
2023-01-10 14:06:09 +09:00
Clemens Schwaighofer
9936fc04da Convert\Byte class fix string in abs call 2022-12-09 16:33:47 +09:00
Clemens Schwaighofer
1e0dfa2106 Update documentation for Convert\Byte Class
add more info in parameters flag
2022-12-09 13:43:15 +09:00
Clemens Schwaighofer
7773b78e17 Fix in CoreLib Bytes for null to trim 2022-11-09 18:11:14 +09:00
Clemens Schwaighofer
0fd89727e9 Update Output\Form to allow ACL controlled edit/view entries
Fixed phpunit test runs with encoding test run and not resetting the
subsitute character back to default

Note: There are mime encoding failures for php 7.4 and 8.0 and one ACL
login failure test for php 7.4
2022-09-06 11:17:03 +09:00
Clemens Schwaighofer
24f553a17e Update comments for split element counter 2022-07-29 13:20:54 +09:00
Clemens Schwaighofer
9a3ea2f7db update strings class with split counter method 2022-07-29 13:19:45 +09:00
Clemens Schwaighofer
bcdb877d90 Phan/phpstan fixes 2022-07-29 11:06:53 +09:00
Clemens Schwaighofer
7e6474195b String split format fix for non ascii characters
Currently just abort and return string as is
2022-07-29 10:38:32 +09:00
Clemens Schwaighofer
1795d3ba6c String convert class added
Currently with one method: splitFormatString

Converts a string to a string with separater characters based on a
format string
2022-07-29 07:00:02 +09:00
Clemens Schwaighofer
e1340acf55 Class header info string fix 2022-07-29 06:59:33 +09:00
Clemens Schwaighofer
3085b52714 Update PHPdoc parts
Add empty line between description and first @param entry.
Some other minor comment layout and text fixes
2022-06-02 16:07:38 +09:00
Clemens Schwaighofer
6f4c5e36e6 Move Encoding class away from the Language namespace
Language\Encoding::__mbMimeEncode -> Convert\MimeEncode::__mbMimeEncode
Langauge\Encoding::checkConvertEncoding -> Check\Encoding::checkConvertEncoding
Langauge\Encoding::setErrorChar -> Check\Encoding::setErrorChar
Langauge\Encoding::getErrorChar -> Encoding::getErrorChar
Langauge\Encoding::convertEncoding -> Convert\Encoding::convertEncoding

Also fixed encoding check that not only a code point but a string can
also be used as a parameter.

Update phpunit tests and split them out for each class

Normal test page is still combined for all classes but updated to
correctly use each class
2022-04-13 09:25:42 +09:00
Clemens Schwaighofer
60613bf311 Comment fixes in Convert\Color 2022-03-23 11:38:05 +09:00
Clemens Schwaighofer
2a583e525c Update config.master, color convert hsb/hsl is full float
All Convert\Color calls for hsb or hsl are full float compatible
2022-03-23 10:38:23 +09:00
Clemens Schwaighofer
0109a67b20 Change Random key base to be string not array
Because we do not have double byte characters in there we use a string
so we don't have to do any array work. Return is also a string and not
an array that is then converted to string.

Add info to Colors Class for oklab code that we should use as basic for
all conversions
2022-02-21 06:50:45 +09:00
Clemens Schwaighofer
13c0fcd869 Fixes for CoreLibs based on phpunit tests
Add note that on change in lib/ folder (add/name/delete) 'composer
dump-autoload' must be run to update the composer auto loader as this is
currently on testing to not use internal autoloader

update all composer/vender autoload configs

Check\Basic: just apply updates in deprecated method calls
Check\Jason: has been deprecaged and moved to Convert\Json. Primary issue
was wrong name "Jason" instead of "Json"
Check\Password: only
Check\PhpVersion: fix version check for >9 numbers
Combined\Array: variable name change to be more clear, all searches are
strict for recoursive search, new option for recoursive search many to
return only found array data and no control array info. for backwards
compatible this is default set to ($old = true) and needs to be set to
false to get the new format,
array search normal has a new strict flag for forcing strict compare on
search.
remove some unneeded is_array checks,
fixed the flatten array to key to not only use leave elements, but all
array keys, if only leaves are wanted the new method
flattenArrayKeyLeavesOnly only returns key from leaves
Combined\DateTime: checkDateTime got more correct error checks on
invalid data
compareDate uses strtotimestamp for more easier compare like
compareDateTime does, both to a check on inalid timestamp now
calcDaysInterval also aborts on invalid data now
Convert\Byte: str to bytes does not drop the minus sign anymore
Convert\Colors: any error will now return false and not set to some
neutral gray. also fix missing round on hsb/hsl special return groups
Convert\Html: add constants for CHECKED/SELECTED options, fix remove
linebreak to not add two spaces if \r\n was found
Convert\Json: moved from Check\Jason and add two new error types
Convert\MimeAppName: do not set if mime type or app name is empty
Create\Hash: add crc32b to hash allows types so we can create a normal
not reversed crc32b
Create\Uids: move default hash type to var in class, fix defined
constant check
Debug\FileWriter: add log folder setting to override config constant
settings and also check if we can actually write to the folder and if
BASE and LOG constants are not empty
Get\System: add constant for getPageName and fix getHostName to be more
shorter and faster
Language\L10n: remove \Basic class extends because we don't need it
there at all
Template\SmartyExtend: fix constant check
2022-01-13 13:20:28 +09:00
Clemens Schwaighofer
4c859ada01 Add phpunit tests folder, fix in Math method floorp when precision was larger then number length 2021-10-29 11:12:23 +09:00