Commit Graph

291 Commits

Author SHA1 Message Date
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
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
d9bcb577d7 some minor test page code fixes 2024-11-07 12:05:23 +09:00
Clemens Schwaighofer
8613e8977b UrlRequests curl: move options set logic to main curl wrapper call
change the curlRequest call to options array and build the options array
there.
Remove any options check + pre build from the get/request calls

Update phpunit tests with string type body return
2024-11-07 11:22:36 +09:00
Clemens Schwaighofer
f9cf36524e UrlRequests auth set allowed in requests call
Removed the parseHeaders public call, headers must be set as array

Throw errors on invalid headers before sending them: Key/Value check
Add headers invalid check in phpunit

Auth headers can be set per call and will override global settings if matching
2024-11-06 18:42:35 +09:00
Clemens Schwaighofer
1653e6b684 Allow http_errors unset/set on each call
If set or not set, on each call this option can be set.
If set to null on call, the original value or default config value is used
2024-11-06 13:29:19 +09:00
Clemens Schwaighofer
c8bc0062ad URL Requests change error response
Instead of just throwing exception on 401 auth, throw exception for any
error code from 400 on
This can be turned off with the option "http_errors" set to false

Also updaed the exception content to match 400 or 500 error type with
more information attached

General Exception error codes:
Cnnn: Curl errors (FAILURE)
Rnnn: general class errors (ERROR)
Hnnn: http response errors (ERROR)
2024-11-06 12:48:01 +09:00
Clemens Schwaighofer
30e2f33620 Test calls update for admin area 2024-11-06 10:03:33 +09:00
Clemens Schwaighofer
4bc2ad8fa0 URL Requests basic tests file 2024-11-01 14:42:43 +09:00
Clemens Schwaighofer
d89c6d1bde UrlRequests target file renamed 2024-10-29 18:28:19 +09:00
Clemens Schwaighofer
1bff19f4b6 Update UrlRequests with patch, admin test page for it
Also update delete to have optional body (content)
2024-10-28 17:05:49 +09:00
Clemens Schwaighofer
934db50b3a Merge branch 'NewFeatures' into Feature-UrlRequestsCurl 2024-10-21 09:52:09 +09:00
Clemens Schwaighofer
a50a38fd40 DB IO Query Placeholder tests 2024-10-21 09:33:18 +09:00
Clemens Schwaighofer
3c5200cd99 Test run for Curl URL Requests 2024-10-21 09:32:20 +09:00
Clemens Schwaighofer
50a4b88f55 UrlRequests\Curl class
Basic interface class to CURL calls

Open:
clean up and check code is neutral
write tests, for this we need a running localhost server for tests to request to
2024-10-21 09:25:39 +09:00
Clemens Schwaighofer
7cced63c4b Update the Admin\Backend edit log call with query params and different data compressors
All queries uses now Params

On load checks for valid write types for edit log write, eg if bzip and lzip compression
are avaiable

adbEditLog:
Also add JSON type encoding for data outside STRING/SERIAL and BINARY/BZIP (bzip compressed)
Add ZLIB as altnerative to BZIP
Add alert if invalid type was set
Auto fallback to JSON if other write types are not available

adbLiveQueue:
Also convert the live queue query to a params style call
2024-10-16 16:21:51 +09:00
Clemens Schwaighofer
0c89840dba Admin\Backend move _POST action read to sub function and trigger not auto loading it
On default it still auto loads the _POST vars for backwards compatible, but add a load class
flag to ignore it "init_action_vars"

also add a get vor tha "acl" array adbGetAcl()
2024-10-16 12:15:19 +09:00
Clemens Schwaighofer
db144493f3 Message system: allow warning level to be logged
Like error messages, they are written to the log if debug is on or the
flag is explicit set
2024-09-24 15:10:53 +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
1b5437b675 Add testing for new added bom utf8 replace 2024-09-03 11:58:36 +09:00
Clemens Schwaighofer
a8d07634ff Add soba.egplusww.jp as local development host, jshint esversion update to 11 2024-08-07 13:41:09 +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
554dd5f73c Fix not closed <head> block in all admin test files 2024-08-05 12:53:48 +09:00
Clemens Schwaighofer
770d6f30a4 DB\IO Placeholder regex fix for json queries, test data updates
Some doc typo fixes
test updates with remove of not used sub calls

DB IO Placeholder regex now checks for any JSON/JSONB operators
2024-07-29 15:55:38 +09:00
Clemens Schwaighofer
9b69390fa2 Merge branch 'development' into NewFeatures 2024-05-22 10:47:12 +09:00
Clemens Schwaighofer
0524d8ac1b Update Symmetric Encryption
Can be used as a class with central key set.

for old static calls:
encrypt -> encryptKey
decrypt -> decryptKey
2024-05-22 10:43:54 +09:00
Clemens Schwaighofer
3fda1bef60 Move Symmetric Encryption Key generation into its own method
Test update for future class based encryption system without static
methods
2024-03-27 11:58:53 +09:00
Clemens Schwaighofer
13c76efcef Fix new layout path location
Was "www/layout/admin|frontend/..." now is
www/frontend/layout and www/admin/layout
2024-03-07 14:53:00 +09:00
Clemens Schwaighofer
b6ae3f99d9 Layout folder move to admin and frontend 2024-02-27 12:02:03 +09:00
Clemens Schwaighofer
ec9275d8d3 Add mime type get to the Check\File class 2024-01-11 16:15:08 +09:00
Clemens Schwaighofer
5f290419c6 Minor php cs fixes 2024-01-10 17:03:18 +09:00
Clemens Schwaighofer
4adb9fba30 DB IO Bug Fix for parameter detect after <>, DateTime code fix
Query Parameters where not detected after a <> unequal check

DateTime fix for last element, use array_pop instead of $array[count - 1]
2023-11-29 10:44:21 +09:00
Clemens Schwaighofer
8bf694b2b2 ArrayHandler: find next or previous key in array and return it
get next or previous key in array
2023-11-02 14:01:47 +09:00
Clemens Schwaighofer
caf03421a7 Bug fix for DB\IO param prepare call with wrong param array check
The global param array in the class instead of the param array passed
to the method was checked
2023-10-31 10:18:44 +09:00
Clemens Schwaighofer
7b9a0043d3 Add Support::getCallStack
returns full call stack for call (excluding self)
2023-10-23 16:59:14 +09:00
Clemens Schwaighofer
6517747fef Move old intervalStringFormat into the class_test.datetime.php file
For current reference testing, will be removed later.

Update phpunit tests (ongoing)
2023-10-18 13:12:48 +09:00
Clemens Schwaighofer
80d2215f2b Combined\DateTime new intervalStringFormat method
new method to replace old timeStringFormat method:
- has year/month data too
- can format with natural names (minutes, seconds, etc)
- can have normal naming (5 hours, 1 minute and 10 seconds)
- skip or not skip zero values in between (6h 0m 1s -> 6h 1s)
- skip or add trailing zero values (6m 0s -> 6m)
- add or not add milliseconds with decimal nano seconds
- drop nano seconds (115.55ms -> 115ms)
- truncate value after a certain part (eg only show up to days)
- add leading 0s to only milli seconds values (115ms -> 0s 115ms)
- namespace separator (6h -> 6 h)

Bug fix for timeStringFormat
- 1.5s and 1.05s and 1.005s all where 5ms -> fixed to 500ms, 50ms 5ms
- bug fix for 0ms drop even thought show ms is requested

Start unit testing part
2023-10-17 19:14:12 +09:00
Clemens Schwaighofer
89e3888bf8 Update DB\IO with auto query placeholder rewrite and better error logging
All errors have context that is used to add query, params, etc info
for logging into the DB.
Avoid double logging for PostGreSQL direct errors as those will be
logged now in context to the actual error log
Remove error: 16 missing/empty dbh has this is handled with error 14
in the connect method.

Auto convert ?, :named to $numbered, default off. Activate with
'db_convert_placeholder' flag or method dbSetConvertPlaceholder.
Converted result data for single queries in dbGetPlaceholderConverted
or in the cursor_ext array in placeholer_converted key

Do not auto translate debug queries with placeholder values in query
but keep them in the array in the context array. If needed
'db_debug_replace_placeholder' can be set to show prepared query
with placeholder replaced in the context

New methods:
public function dbSetConvertPlaceholder(bool $flag): void
public function dbGetConvertPlaceholder(): bool
public function dbSetConvertPlaceholderTarget(string $target): bool
public function dbGetConvertPlaceholderTarget(): string
public function dbSetDebugReplacePlaceholder(bool $flag): void
public function dbGetDebugReplacePlaceholder(): bool
public function dbGetPlaceholderConverted(): array

Chagned to public:
public function dbCheckQueryForSelect(string $query): bool
public function dbCheckQueryForInsert(string $query, bool $pure = false): bool
public function dbCheckQueryForUpdate(string $query): bool
2023-10-16 14:43:55 +09:00
Clemens Schwaighofer
a8ca5d7a2b Add missing exceptions to Output\Image image rotate
Also create a basic phpunit class to test exceptions only
2023-10-12 17:08:44 +09:00
Clemens Schwaighofer
2f8f98642b Update Logging\ErrorMsg to add a Jump Target list for direct jumps
So we can return a list for css element ids we want to jump directly to
2023-10-02 14:02:00 +09:00
Clemens Schwaighofer
a84a745be2 Logging\ErrorMsg class update to log error level automatically for debug
if log level is debug, automatically log the error messages.
We still skip warn and info levels from logging.

The rest is based on the logging level (notice eg only gets logged if
log level is at least notice)
2023-10-02 12:23:44 +09:00
Clemens Schwaighofer
d06769c48b empty admin folder page for edit base page creation tests 2023-09-27 11:38:32 +09:00
Clemens Schwaighofer
fe459aec80 Update ErrorMsg with optional target_style string for one style
Applied to master or to target style
2023-09-15 18:22:38 +09:00
Clemens Schwaighofer
de0ed058ca ErrorMessage: new flag for logging 'error' level to log 2023-09-11 13:35:35 +09:00
Clemens Schwaighofer
68c9164eaa New ErrorMessage class for frontend return error messages 2023-09-08 18:30:05 +09:00
Clemens Schwaighofer
09839f3451 phpstan fixes 2023-08-31 19:04:45 +09:00
Clemens Schwaighofer
545de5c4a1 Fixed more Exceptions to be not Errors but Exceptions
DateTime, Session, FileWrite, Image, SymmetricEncryption

phpunit tests updated, run checks added
2023-08-31 18:06:02 +09:00
Clemens Schwaighofer
2fe37bf92a Exceptions change in Check\Colors, add in Cmbined\ArrayHandler
Chech\Colors now throws correct exceptions for wrong values
Combined\ArrayHandler will throw errors and not return false
2023-08-31 12:07:28 +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
46e1419ef5 phan checks and updates 2023-08-30 19:26:13 +09:00