From df8bfd50836d81005c9d966c546fcae7ccb3b2b1 Mon Sep 17 00:00:00 2001 From: Wacton Date: Sat, 16 Apr 2022 17:38:46 +0000 Subject: [PATCH] Add LUV support --- README.md | 6 + Unicolour.Example/Program.cs | 15 +- Unicolour.Example/gradients.png | Bin 22546 -> 37940 bytes Unicolour.Tests/ConfigurationTests.cs | 13 +- Unicolour.Tests/ConversionTests.cs | 26 +- Unicolour.Tests/CoordinateSpaceTests.cs | 131 ++++++++ Unicolour.Tests/EqualityTests.cs | 19 ++ Unicolour.Tests/Factories/ColorMineFactory.cs | 34 +- Unicolour.Tests/Factories/ColourfulFactory.cs | 62 +++- .../Factories/ITestColourFactory.cs | 17 +- Unicolour.Tests/Factories/OpenCvCsvFactory.cs | 19 +- Unicolour.Tests/Factories/OpenCvFactory.cs | 183 ++++++----- Unicolour.Tests/Factories/SixLaborsFactory.cs | 73 ++++- Unicolour.Tests/InterpolateLuvTests.cs | 93 ++++++ Unicolour.Tests/OtherLibraryTests.cs | 118 +++---- Unicolour.Tests/RangeClampTests.cs | 14 +- Unicolour.Tests/SmokeTests.cs | 11 + Unicolour.Tests/Utils/AssertUtils.cs | 8 +- Unicolour.Tests/Utils/OpenCvColours.csv | 291 +++++++++--------- Unicolour.Tests/Utils/TestColour.cs | 5 +- Unicolour.Tests/Utils/TestColours.cs | 6 +- Unicolour/ColourTriplet.cs | 1 + Unicolour/Conversion.cs | 109 ++++--- Unicolour/Hsb.cs | 10 +- Unicolour/Hsl.cs | 8 +- Unicolour/Interpolation.cs | 9 + Unicolour/Luv.cs | 16 + Unicolour/Rgb.cs | 19 +- Unicolour/Unicolour.cs | 4 + Unicolour/Unicolour.csproj | 6 +- Unicolour/UnicolourConstructors.cs | 6 + Unicolour/UnicolourSpaces.cs | 20 +- 32 files changed, 941 insertions(+), 411 deletions(-) create mode 100644 Unicolour.Tests/CoordinateSpaceTests.cs create mode 100644 Unicolour.Tests/InterpolateLuvTests.cs create mode 100644 Unicolour/Luv.cs diff --git a/README.md b/README.md index db277933..662515af 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,9 @@ A `Unicolour` encapsulates a single colour and its representation across differe - HSL - CIE XYZ - CIE LAB +- CIE LUV +- ~~CIE LCHab~~ _(coming soon)_ +- ~~CIE LCHuv~~ _(coming soon)_ - Oklab Unicolour uses sRGB as the default RGB model and standard illuminant D65 (2° observer) as the default white point of the XYZ colour space. @@ -42,6 +45,7 @@ var unicolour = Unicolour.FromHsb(327.6, 0.922, 1.0); var unicolour = Unicolour.FromHsl(327.6, 1.0, 0.539); var unicolour = Unicolour.FromXyz(0.47, 0.24, 0.3); var unicolour = Unicolour.FromLab(55.96, +84.54, -5.7); +var unicolour = Unicolour.FromLuv(55.96, +131.47, -24.35); var unicolour = Unicolour.FromOklab(0.65, 0.26, -0.01); ``` @@ -52,6 +56,7 @@ var hsb = unicolour.Hsb; var hsl = unicolour.Hsl; var xyz = unicolour.Xyz; var lab = unicolour.Lab; +var luv = unicolour.Luv; var oklab = unicolour.Oklab; ``` @@ -62,6 +67,7 @@ var interpolated = unicolour1.InterpolateHsb(unicolour2, 0.5); var interpolated = unicolour1.InterpolateHsl(unicolour2, 0.5); var interpolated = unicolour1.InterpolateXyz(unicolour2, 0.5); var interpolated = unicolour1.InterpolateLab(unicolour2, 0.5); +var interpolated = unicolour1.InterpolateLuv(unicolour2, 0.5); var interpolated = unicolour1.InterpolateOklab(unicolour2, 0.5); ``` diff --git a/Unicolour.Example/Program.cs b/Unicolour.Example/Program.cs index 6a27fe60..a39e571a 100644 --- a/Unicolour.Example/Program.cs +++ b/Unicolour.Example/Program.cs @@ -13,15 +13,19 @@ var font = fontFamily.CreateFont(24); var textRgba32 = AsRgba32(Unicolour.FromHex("#E8E8FF")); -var labels = new List {"RGB", "HSB", "HSL", "XYZ", "LAB", "OKLAB"}; +var labels = new List {"RGB", "HSB", "HSL", "XYZ", "LAB", "LUV", "OKLAB"}; var purple = Unicolour.FromHsb(260, 1.0, 0.33); var orange = Unicolour.FromHsb(30, 0.66, 1.0); -var black = Unicolour.FromRgb(0, 0, 0); +var pink = Unicolour.FromHex("#FF1493"); var cyan = Unicolour.FromRgb255(0, 255, 255); +var black = Unicolour.FromRgb(0, 0, 0); +var green = Unicolour.FromRgb(0, 1, 0); -var image = new Image(gradientWidth * 2, gradientHeight * labels.Count); +var image = new Image(gradientWidth * 3, gradientHeight * labels.Count); Draw(purple, orange, 0); -Draw(black, cyan, 1); +Draw(pink, cyan, 1); +Draw(black, green, 2); +image.Save("gradients.png"); void Draw(Unicolour start, Unicolour end, int column) { @@ -35,6 +39,7 @@ void Draw(Unicolour start, Unicolour end, int column) start.InterpolateHsl(end, distance), start.InterpolateXyz(end, distance), start.InterpolateLab(end, distance), + start.InterpolateLuv(end, distance), start.InterpolateOklab(end, distance) }; @@ -49,8 +54,6 @@ void Draw(Unicolour start, Unicolour end, int column) } } -image.Save("gradients.png"); - void SetPixels(int column, int pixelIndex, List unicolours) { for (var y = 0; y < gradientHeight; y++) diff --git a/Unicolour.Example/gradients.png b/Unicolour.Example/gradients.png index 92cdc7e29faea664ad65eedd4184f9622a25267a..4327ef826ca19a51ac7c212ab582d83cc394648a 100644 GIT binary patch literal 37940 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

$W^X0qcTtGM+rvY}Y#FkpIV*7GQDRe*?gE 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 Y>d#MoaQ)eJcfnt=cDPjY?;H334^jW5F8}}l literal 22546 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

9R^8l`Jza{cx@j-@nAN(T}W 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 diff --git a/Unicolour.Tests/ConfigurationTests.cs b/Unicolour.Tests/ConfigurationTests.cs index 63c73453..5fc59135 100644 --- a/Unicolour.Tests/ConfigurationTests.cs +++ b/Unicolour.Tests/ConfigurationTests.cs @@ -44,7 +44,7 @@ public static void StandardRgbD65ToXyzD65() { Xyz = new(0.200757, 0.119618, 0.506757), Lab = new(41.1553, 51.4108, -56.4485), - // Luv = new(41.1553, 16.3709, -86.7190) + Luv = new(41.1553, 16.3709, -86.7190) }; Assert.That(rgbToXyzMatrix.Data, Is.EqualTo(expectedMatrixA).Within(0.0005)); @@ -113,7 +113,7 @@ public static void StandardRgbD65ToXyzD50() { Xyz = new(0.187691, 0.115771, 0.381093), Lab = new(40.5359, 46.0847, -57.1158), - // Luv = new(40.5359, 18.7523, -78.2057) + Luv = new(40.5359, 18.7523, -78.2057) }; Assert.That(rgbToXyzMatrix.Data, Is.EqualTo(expectedMatrix).Within(0.0000001)); @@ -167,7 +167,7 @@ public static void AdobeRgbD65ToXyzD65() { Xyz = new(0.234243, 0.134410, 0.535559), Lab = new(43.4203, 57.3600, -55.4259), - // Luv = new(43.4203, 25.4480, -87.3268) + Luv = new(43.4203, 25.4480, -87.3268) }; AssertColour(unicolour, expectedColour); @@ -210,7 +210,7 @@ public static void AdobeRgbD65ToXyzD50() { Xyz = new(0.221673, 0.130920, 0.402670), Lab = new(42.9015, 52.4152, -55.9013), - // Luv = new(42.9015, 29.0751, -78.5576) + Luv = new(42.9015, 29.0751, -78.5576) }; AssertColour(unicolour, expectedColour); @@ -253,7 +253,7 @@ public static void WideGamutRgbD50ToXyzD65() { Xyz = new(0.251993, 0.102404, 0.550393), Lab = new(38.2704, 87.2838, -65.7493), - // Luv = new(38.2704, 47.3837, -99.6819) + Luv = new(38.2704, 47.3837, -99.6819) }; AssertColour(unicolour, expectedColour); @@ -296,7 +296,7 @@ public static void WideGamutRgbD50ToXyzD50() { Xyz = new(0.238795, 0.099490, 0.413181), Lab = new(37.7508, 82.3084, -66.1402), - // Luv = new(37.7508, 55.1488, -91.6044) + Luv = new(37.7508, 55.1488, -91.6044) }; AssertColour(unicolour, expectedColour); @@ -327,5 +327,6 @@ private static void AssertColour(Unicolour unicolour, TestColour expected) if (expected.Rgb != null) AssertUtils.AssertColourTriplet(unicolour.Rgb.Triplet, expected.Rgb!, 0.01); if (expected.Xyz != null) AssertUtils.AssertColourTriplet(unicolour.Xyz.Triplet, expected.Xyz!, 0.001); if (expected.Lab != null) AssertUtils.AssertColourTriplet(unicolour.Lab.Triplet, expected.Lab!, 0.05); + if (expected.Luv != null) AssertUtils.AssertColourTriplet(unicolour.Luv.Triplet, expected.Luv!, 0.1); } } \ No newline at end of file diff --git a/Unicolour.Tests/ConversionTests.cs b/Unicolour.Tests/ConversionTests.cs index b6c9bb3b..4035bf7a 100644 --- a/Unicolour.Tests/ConversionTests.cs +++ b/Unicolour.Tests/ConversionTests.cs @@ -12,6 +12,7 @@ public class ConversionTests private const double HslTolerance = 0.00000001; private const double XyzTolerance = 0.00000001; private const double LabTolerance = 0.00000001; + private const double LuvTolerance = 0.00000001; private const double OklabTolerance = 0.000001; [Test] @@ -46,6 +47,9 @@ public void HslSameAfterDeconversion() [Test] public void LabSameAfterDeconversion() => AssertUtils.AssertRandomLabColours(AssertLabDeconversion); + [Test] + public void LuvSameAfterDeconversion() => AssertUtils.AssertRandomLuvColours(AssertLuvDeconversion); + [Test] public void OklabSameAfterDeconversion() => AssertUtils.AssertRandomOklabColours(AssertOklabDeconversion); @@ -54,7 +58,7 @@ private static void AssertRgbConversion(TestColour namedColour) var systemColour = ColorTranslator.FromHtml(namedColour.Hex!); var rgb = new Rgb(systemColour.R / 255.0, systemColour.G / 255.0, systemColour.B / 255.0, Configuration.Default); var hsb = Conversion.RgbToHsb(rgb); - var hsl = Conversion.RgbToHsl(rgb); + var hsl = Conversion.HsbToHsl(hsb); var expectedRoundedHsb = namedColour.Hsb; var expectedRoundedHsl = namedColour.Hsl; @@ -100,7 +104,7 @@ private static void AssertHsbDeconversion(Hsb original) private static void AssertHslDeconversion(ColourTriplet triplet) => AssertHslDeconversion(new Hsl(triplet.First, triplet.Second, triplet.Third)); private static void AssertHslDeconversion(Hsl original) { - var deconverted = Conversion.RgbToHsl(Conversion.HsbToRgb(Conversion.HslToHsb(original), Configuration.Default)); + var deconverted = Conversion.HsbToHsl(Conversion.HslToHsb(original)); AssertUtils.AssertColourTriplet(deconverted.Triplet, original.Triplet, HslTolerance, true); } @@ -108,8 +112,14 @@ private static void AssertHslDeconversion(Hsl original) private static void AssertXyzDeconversion(Xyz original) { // note: cannot test deconversion via RGB space as XYZ <-> RGB is not 1:1 - var deconverted = Conversion.LabToXyz(Conversion.XyzToLab(original, Configuration.Default), Configuration.Default); - AssertUtils.AssertColourTriplet(deconverted.Triplet, original.Triplet, XyzTolerance); + var deconvertedViaLab = Conversion.LabToXyz(Conversion.XyzToLab(original, Configuration.Default), Configuration.Default); + AssertUtils.AssertColourTriplet(deconvertedViaLab.Triplet, original.Triplet, XyzTolerance); + + var deconvertedViaLuv = Conversion.LuvToXyz(Conversion.XyzToLuv(original, Configuration.Default), Configuration.Default); + AssertUtils.AssertColourTriplet(deconvertedViaLuv.Triplet, original.Triplet, XyzTolerance); + + var deconvertedViaOklab = Conversion.OklabToXyz(Conversion.XyzToOklab(original, Configuration.Default), Configuration.Default); + AssertUtils.AssertColourTriplet(deconvertedViaOklab.Triplet, original.Triplet, XyzTolerance); } private static void AssertLabDeconversion(ColourTriplet triplet) => AssertLabDeconversion(new Lab(triplet.First, triplet.Second, triplet.Third)); @@ -120,6 +130,14 @@ private static void AssertLabDeconversion(Lab original) AssertUtils.AssertColourTriplet(deconverted.Triplet, original.Triplet, LabTolerance); } + private static void AssertLuvDeconversion(ColourTriplet triplet) => AssertLuvDeconversion(new Luv(triplet.First, triplet.Second, triplet.Third)); + private static void AssertLuvDeconversion(Luv original) + { + // note: cannot test deconversion via RGB space as XYZ <-> RGB is not 1:1 + var deconverted = Conversion.XyzToLuv(Conversion.LuvToXyz(original, Configuration.Default), Configuration.Default); + AssertUtils.AssertColourTriplet(deconverted.Triplet, original.Triplet, LuvTolerance); + } + private static void AssertOklabDeconversion(ColourTriplet triplet) => AssertOklabDeconversion(new Oklab(triplet.First, triplet.Second, triplet.Third)); private static void AssertOklabDeconversion(Oklab original) { diff --git a/Unicolour.Tests/CoordinateSpaceTests.cs b/Unicolour.Tests/CoordinateSpaceTests.cs new file mode 100644 index 00000000..d45fd61c --- /dev/null +++ b/Unicolour.Tests/CoordinateSpaceTests.cs @@ -0,0 +1,131 @@ +namespace Wacton.Unicolour.Tests; + +using System; +using NUnit.Framework; +using Wacton.Unicolour.Tests.Utils; + +/* + * Conversions that are just translations to/from cylindrical coordinate spaces + * should have input values clamped to the range of the source coordinate space + * otherwise they will be mapped to incorrect values + * e.g. RGB with negative -> HSB should produce the same value as RGB with zero -> HSB + */ +public class CoordinateSpaceTests +{ + [Test] + public void CartesianRgbToCylindricalHsb() + { + ColourTriplet upperOutRange = new(1.00001, 100, double.PositiveInfinity); + ColourTriplet upperInRange = new(1, 1, 1); + ColourTriplet lowerOutRange = new(-0.00001, -100, double.NegativeInfinity); + ColourTriplet lowerInRange = new(0, 0, 0); + AssertRgbToHsb(upperInRange, upperOutRange); + AssertRgbToHsb(lowerInRange, lowerOutRange); + } + + [Test] + public void CylindricalHsbToCartesianRgb() + { + ColourTriplet upperOutRange = new(360.00001, 100, double.PositiveInfinity); + ColourTriplet upperInRange = new(0.00001, 1, 1); + ColourTriplet lowerOutRange = new(-0.00001, -100, double.NegativeInfinity); + ColourTriplet lowerInRange = new(359.99999, 0, 0); + AssertHsbToRgb(upperInRange, upperOutRange); + AssertHsbToRgb(lowerInRange, lowerOutRange); + } + + [Test] + public void CylindricalHsbToCylindricalHsl() + { + ColourTriplet upperOutRange = new(360.00001, 100, double.PositiveInfinity); + ColourTriplet upperInRange = new(0.00001, 1, 1); + ColourTriplet lowerOutRange = new(-0.00001, -100, double.NegativeInfinity); + ColourTriplet lowerInRange = new(359.99999, 0, 0); + AssertHsbToHsl(upperInRange, upperOutRange); + AssertHsbToHsl(lowerInRange, lowerOutRange); + } + + [Test] + public void CylindricalHslToCylindricalHsb() + { + ColourTriplet upperOutRange = new(360.00001, 100, double.PositiveInfinity); + ColourTriplet upperInRange = new(0.00001, 1, 1); + ColourTriplet lowerOutRange = new(-0.00001, -100, double.NegativeInfinity); + ColourTriplet lowerInRange = new(359.99999, 0, 0); + AssertHslToHsb(upperInRange, upperOutRange); + AssertHslToHsb(lowerInRange, lowerOutRange); + } + + private static void AssertRgbToHsb(ColourTriplet inRange, ColourTriplet outRange) + { + Rgb GetInput(ColourTriplet triplet) => new(triplet.First, triplet.Second, triplet.Third, Configuration.Default); + var inRangeInput = GetInput(inRange); + var outRangeInput = GetInput(outRange); + + Hsb GetOutput(Rgb rgb) => Conversion.RgbToHsb(rgb); + var inRangeOutput = GetOutput(inRangeInput); + var outRangeOutput = GetOutput(outRangeInput); + + AssertTriplets( + AsTriplets(inRangeInput), AsTriplets(outRangeInput), + AsTriplets(inRangeOutput), AsTriplets(outRangeOutput)); + } + + private static void AssertHsbToRgb(ColourTriplet inRange, ColourTriplet outRange) + { + Hsb GetInput(ColourTriplet triplet) => new(triplet.First, triplet.Second, triplet.Third); + var inRangeInput = GetInput(inRange); + var outRangeInput = GetInput(outRange); + + Rgb GetOutput(Hsb hsb) => Conversion.HsbToRgb(hsb, Configuration.Default); + var inRangeOutput = GetOutput(inRangeInput); + var outRangeOutput = GetOutput(outRangeInput); + + AssertTriplets( + AsTriplets(inRangeInput), AsTriplets(outRangeInput), + AsTriplets(inRangeOutput), AsTriplets(outRangeOutput)); + } + + private static void AssertHsbToHsl(ColourTriplet inRange, ColourTriplet outRange) + { + Hsb GetInput(ColourTriplet triplet) => new(triplet.First, triplet.Second, triplet.Third); + var inRangeInput = GetInput(inRange); + var outRangeInput = GetInput(outRange); + + Hsl GetOutput(Hsb hsb) => Conversion.HsbToHsl(hsb); + var inRangeOutput = GetOutput(inRangeInput); + var outRangeOutput = GetOutput(outRangeInput); + + AssertTriplets( + AsTriplets(inRangeInput), AsTriplets(outRangeInput), + AsTriplets(inRangeOutput), AsTriplets(outRangeOutput)); + } + + private static void AssertHslToHsb(ColourTriplet inRange, ColourTriplet outRange) + { + Hsl GetInput(ColourTriplet triplet) => new(triplet.First, triplet.Second, triplet.Third); + var inRangeInput = GetInput(inRange); + var outRangeInput = GetInput(outRange); + + Hsb GetOutput(Hsl hsl) => Conversion.HslToHsb(hsl); + var inRangeOutput = GetOutput(inRangeInput); + var outRangeOutput = GetOutput(outRangeInput); + + AssertTriplets( + AsTriplets(inRangeInput), AsTriplets(outRangeInput), + AsTriplets(inRangeOutput), AsTriplets(outRangeOutput)); + } + + private static void AssertTriplets(Triplets inRangeInput, Triplets outRangeInput, Triplets inRangeOutput, Triplets outRangeOutput) + { + AssertUtils.AssertColourTriplet(outRangeInput.Constrained, inRangeInput.Unconstrained, 0.00001); + AssertUtils.AssertColourTriplet(outRangeOutput.Unconstrained, inRangeOutput.Unconstrained, 0.00001); + AssertUtils.AssertColourTriplet(outRangeOutput.Constrained, inRangeOutput.Unconstrained, 0.00001); + } + + private static Triplets AsTriplets(Rgb rgb) => new(rgb.Triplet, rgb.ConstrainedTriplet); + private static Triplets AsTriplets(Hsb hsb) => new(hsb.Triplet, hsb.ConstrainedTriplet); + private static Triplets AsTriplets(Hsl hsl) => new(hsl.Triplet, hsl.ConstrainedTriplet); + + private record Triplets(ColourTriplet Unconstrained, ColourTriplet Constrained); +} \ No newline at end of file diff --git a/Unicolour.Tests/EqualityTests.cs b/Unicolour.Tests/EqualityTests.cs index a3bbc33f..0489fe8e 100644 --- a/Unicolour.Tests/EqualityTests.cs +++ b/Unicolour.Tests/EqualityTests.cs @@ -46,6 +46,14 @@ public void EqualLabGivesEqualObjects() AssertUnicoloursEqual(unicolour1, unicolour2); } + [Test] + public void EqualLuvGivesEqualObjects() + { + var unicolour1 = GetRandomLuvUnicolour(); + var unicolour2 = Unicolour.FromLuv(unicolour1.Luv.L, unicolour1.Luv.U, unicolour1.Luv.V, unicolour1.Alpha.A); + AssertUnicoloursEqual(unicolour1, unicolour2); + } + [Test] public void EqualOklabGivesEqualObjects() { @@ -99,6 +107,15 @@ public void NotEqualLabGivesNotEqualObjects() AssertUnicoloursNotEqual(unicolour1, unicolour2, unicolour => unicolour.Lab.Triplet); } + [Test] + public void NotEqualLuvGivesNotEqualObjects() + { + var unicolour1 = GetRandomLuvUnicolour(); + var differentTuple = GetDifferent(unicolour1.Luv.Triplet, 1.0).Tuple; + var unicolour2 = Unicolour.FromLuv(differentTuple, unicolour1.Alpha.A + 0.1); + AssertUnicoloursNotEqual(unicolour1, unicolour2, unicolour => unicolour.Luv.Triplet); + } + [Test] public void NotEqualOklabGivesNotEqualObjects() { @@ -144,6 +161,7 @@ public void DifferentConfigurationObjects() private static Unicolour GetRandomHslUnicolour() => Unicolour.FromHsl(TestColours.GetRandomHsl().Tuple, TestColours.GetRandomAlpha()); private static Unicolour GetRandomXyzUnicolour() => Unicolour.FromXyz(TestColours.GetRandomXyz().Tuple, TestColours.GetRandomAlpha()); private static Unicolour GetRandomLabUnicolour() => Unicolour.FromLab(TestColours.GetRandomLab().Tuple, TestColours.GetRandomAlpha()); + private static Unicolour GetRandomLuvUnicolour() => Unicolour.FromLuv(TestColours.GetRandomLuv().Tuple, TestColours.GetRandomAlpha()); private static Unicolour GetRandomOklabUnicolour() => Unicolour.FromOklab(TestColours.GetRandomOklab().Tuple, TestColours.GetRandomAlpha()); private static ColourTriplet GetDifferent(ColourTriplet triplet, double diff = 0.1) => new(triplet.First + diff, triplet.Second + diff, triplet.Third + diff); @@ -154,6 +172,7 @@ private static void AssertUnicoloursEqual(Unicolour unicolour1, Unicolour unicol AssertEqual(unicolour1.Hsl, unicolour2.Hsl); AssertEqual(unicolour1.Xyz, unicolour2.Xyz); AssertEqual(unicolour1.Lab, unicolour2.Lab); + AssertEqual(unicolour1.Luv, unicolour2.Luv); AssertEqual(unicolour1.Oklab, unicolour2.Oklab); AssertEqual(unicolour1.Alpha, unicolour2.Alpha); AssertEqual(unicolour1.Luminance, unicolour2.Luminance); diff --git a/Unicolour.Tests/Factories/ColorMineFactory.cs b/Unicolour.Tests/Factories/ColorMineFactory.cs index de4ceb8e..b66fdc9d 100644 --- a/Unicolour.Tests/Factories/ColorMineFactory.cs +++ b/Unicolour.Tests/Factories/ColorMineFactory.cs @@ -8,14 +8,16 @@ namespace Wacton.Unicolour.Tests.Factories; using ColorMineHsl = ColorMine.ColorSpaces.Hsl; using ColorMineXyz = ColorMine.ColorSpaces.Xyz; using ColorMineLab = ColorMine.ColorSpaces.Lab; +using ColorMineLuv = ColorMine.ColorSpaces.Luv; +/* + * ColorMine doesn't expose linear RGB + * ColorMine does a bad job of converting to HSL + * ColorMine does a terrible job of converting from XYZ / LAB / LUV + */ internal class ColorMineFactory : ITestColourFactory { - /* - * ColorMine doesn't expose linear RGB and does a bad job of converting to HSL - */ - private static readonly Tolerances BaseTolerances = new() { Rgb = 0.00000000001, Hsb = 0.0000005, Hsl = 0.0125, Xyz = 0.0005, Lab = 0.05 }; - private static readonly Tolerances FromHslTolerances = BaseTolerances with { Rgb = 0.0005, Hsb = 0.00005 }; + private static readonly Tolerances Tolerances = new() { Rgb = 0.0005, Hsb = 0.00005, Hsl = 0.0125, Xyz = 0.0005, Lab = 0.05, Luv = 0.05 }; public TestColour FromRgb(double r, double g, double b, string name) { @@ -32,7 +34,8 @@ public TestColour FromRgb255(int r255, int g255, int b255, string name) var hsl = rgb.To(); var xyz = rgb.To(); var lab = rgb.To(); - return Create(name, rgb, hsb, hsl, xyz, lab, BaseTolerances); + var luv = rgb.To(); + return Create(name, rgb, hsb, hsl, xyz, lab, luv, Tolerances); } public TestColour FromHsb(double h, double s, double b, string name) @@ -42,10 +45,11 @@ public TestColour FromHsb(double h, double s, double b, string name) var hsl = hsb.To(); var xyz = hsb.To(); var lab = hsb.To(); - return Create(name, rgb, hsb, hsl, xyz, lab, BaseTolerances); + var luv = hsb.To(); + return Create(name, rgb, hsb, hsl, xyz, lab, luv, Tolerances); } - // for some reason HSL uses 0-100 despite HSB using 0-1 + // ColorMine HSL for some reason uses 0-100 despite HSB using 0-1 public TestColour FromHsl(double h, double s, double l, string name) { var hsl = new ColorMineHsl {H = h, S = s * 100, L = l * 100}; @@ -53,10 +57,19 @@ public TestColour FromHsl(double h, double s, double l, string name) var hsb = hsl.To(); var xyz = hsl.To(); var lab = hsl.To(); - return Create(name, rgb, hsb, hsl, xyz, lab, FromHslTolerances); + var luv = hsl.To(); + return Create(name, rgb, hsb, hsl, xyz, lab, luv, Tolerances); } - private static TestColour Create(string name, ColorMineRgb rgb, ColorMineHsb hsb, ColorMineHsl hsl, ColorMineXyz xyz, ColorMineLab lab, Tolerances tolerances) + // ColorMine from XYZ, LAB & LUV is so bad for most conversions, it's not worth testing + public TestColour FromXyz(double x, double y, double z, string name) => throw new NotImplementedException(); + public TestColour FromLab(double l, double a, double b, string name) => throw new NotImplementedException(); + public TestColour FromLuv(double l, double u, double v, string name) => throw new NotImplementedException(); + + private static TestColour Create(string name, + ColorMineRgb rgb, ColorMineHsb hsb, ColorMineHsl hsl, + ColorMineXyz xyz, ColorMineLab lab, ColorMineLuv luv, + Tolerances tolerances) { var hueExclusions = new List(); if (HasInconsistentHue(hsb, hsl)) hueExclusions.Add("ColorMine converts via RGB and loses hue when greyscale"); @@ -71,6 +84,7 @@ private static TestColour Create(string name, ColorMineRgb rgb, ColorMineHsb hsb Hsl = new(hsl.H, hsl.S / 100.0, hsl.L / 100.0), Xyz = new(xyz.X / 100.0, xyz.Y / 100.0, xyz.Z / 100.0), Lab = new(lab.L, lab.A, lab.B), + Luv = new(luv.L, luv.U, luv.V), Tolerances = tolerances, ExcludeFromHueBasedTestReasons = hueExclusions }; diff --git a/Unicolour.Tests/Factories/ColourfulFactory.cs b/Unicolour.Tests/Factories/ColourfulFactory.cs index dc15f1c2..b17a5d69 100644 --- a/Unicolour.Tests/Factories/ColourfulFactory.cs +++ b/Unicolour.Tests/Factories/ColourfulFactory.cs @@ -1,46 +1,77 @@ namespace Wacton.Unicolour.Tests.Factories; +using System; using Colourful; using Wacton.Unicolour.Tests.Utils; using ColourfulRgb = Colourful.RGBColor; using ColourfulRgbLinear = Colourful.LinearRGBColor; using ColourfulXyz = Colourful.XYZColor; using ColourfulLab = Colourful.LabColor; +using ColourfulLuv = Colourful.LuvColor; using ColourfulIlluminants = Colourful.Illuminants; +/* + * Colourful doesn't support HSB / HSL + */ internal class ColourfulFactory : ITestColourFactory { - /* - * Colourful doesn't support HSB / HSL - */ - private static readonly Tolerances Tolerances = new() { Rgb = 0.00000000001, RgbLinear = 0.00000000001, Xyz = 0.00000000001, Lab = 0.0000005 }; + private static readonly Tolerances Tolerances = new() { Rgb = 0.00000000001, RgbLinear = 0.00000000001, Xyz = 0.00000000001, Lab = 0.0000005, Luv = 0.0000005 }; public TestColour FromRgb(double r, double g, double b, string name) { - var rgbLinearConverter = new ConverterBuilder().FromRGB(RGBWorkingSpaces.sRGB).ToLinearRGB().Build(); + var rgbLinearConverter = new ConverterBuilder().FromRGB(RGBWorkingSpaces.sRGB).ToLinearRGB(RGBWorkingSpaces.sRGB).Build(); var xyzConverter = new ConverterBuilder().FromRGB(RGBWorkingSpaces.sRGB).ToXYZ(ColourfulIlluminants.D65).Build(); var labConverter = new ConverterBuilder().FromRGB(RGBWorkingSpaces.sRGB).ToLab(ColourfulIlluminants.D65).Build(); + var luvConverter = new ConverterBuilder().FromRGB(RGBWorkingSpaces.sRGB).ToLuv(ColourfulIlluminants.D65).Build(); var rgb = new ColourfulRgb(r, g, b); var rgbLinear = rgbLinearConverter.Convert(rgb); var xyz = xyzConverter.Convert(rgb); var lab = labConverter.Convert(rgb); - return Create(name, rgb, rgbLinear, xyz, lab); + var luv = luvConverter.Convert(rgb); + return Create(name, rgb, rgbLinear, xyz, lab, luv); } - // Colourful does not support HSB - public TestColour FromHsb(double h, double s, double b, string name) + // Colourful does not support HSB or HSL + public TestColour FromHsb(double h, double s, double b, string name) => throw new NotImplementedException(); + public TestColour FromHsl(double h, double s, double l, string name) => throw new NotImplementedException(); + + public TestColour FromXyz(double x, double y, double z, string name) { - return new TestColour(); + var rgbConverter = new ConverterBuilder().FromXYZ(ColourfulIlluminants.D65).ToRGB(RGBWorkingSpaces.sRGB).Build(); + var rgbLinearConverter = new ConverterBuilder().FromXYZ(ColourfulIlluminants.D65).ToLinearRGB(RGBWorkingSpaces.sRGB).Build(); + var labConverter = new ConverterBuilder().FromXYZ(ColourfulIlluminants.D65).ToLab(ColourfulIlluminants.D65).Build(); + var luvConverter = new ConverterBuilder().FromXYZ(ColourfulIlluminants.D65).ToLuv(ColourfulIlluminants.D65).Build(); + + var xyz = new ColourfulXyz(x, y, z); + var rgb = rgbConverter.Convert(xyz); + var rgbLinear = rgbLinearConverter.Convert(xyz); + var lab = labConverter.Convert(xyz); + var luv = luvConverter.Convert(xyz); + return Create(name, rgb, rgbLinear, xyz, lab, luv); } - // Colourful does not support HSL - public TestColour FromHsl(double h, double s, double l, string name) + public TestColour FromLab(double l, double a, double b, string name) { - return new TestColour(); + var rgbConverter = new ConverterBuilder().FromLab(ColourfulIlluminants.D65).ToRGB(RGBWorkingSpaces.sRGB).Build(); + var rgbLinearConverter = new ConverterBuilder().FromLab(ColourfulIlluminants.D65).ToLinearRGB(RGBWorkingSpaces.sRGB).Build(); + var xyzConverter = new ConverterBuilder().FromLab(ColourfulIlluminants.D65).ToXYZ(ColourfulIlluminants.D65).Build(); + var luvConverter = new ConverterBuilder().FromLab(ColourfulIlluminants.D65).ToLuv(ColourfulIlluminants.D65).Build(); + + var lab = new ColourfulLab(l, a, b); + var rgb = rgbConverter.Convert(lab); + var rgbLinear = rgbLinearConverter.Convert(lab); + var xyz = xyzConverter.Convert(lab); + var luv = luvConverter.Convert(lab); + return Create(name, rgb, rgbLinear, xyz, lab, luv); } - private static TestColour Create(string name, ColourfulRgb rgb, ColourfulRgbLinear rgbLinear, ColourfulXyz xyz, ColourfulLab lab) + // Colourful LUV appears to not convert correctly + public TestColour FromLuv(double l, double u, double v, string name) => throw new NotImplementedException(); + + private static TestColour Create(string name, + ColourfulRgb rgb, ColourfulRgbLinear rgbLinear, + ColourfulXyz xyz, ColourfulLab lab, ColourfulLuv luv) { return new TestColour { @@ -49,7 +80,10 @@ private static TestColour Create(string name, ColourfulRgb rgb, ColourfulRgbLine RgbLinear = new(rgbLinear.R, rgbLinear.G, rgbLinear.B), Xyz = new(xyz.X, xyz.Y, xyz.Z), Lab = new(lab.L, lab.a, lab.b), - Tolerances = Tolerances + Luv = new(luv.L, luv.u, luv.v), + Tolerances = Tolerances, + IsRgbConstrained = false, + IsRgbLinearConstrained = false }; } } \ No newline at end of file diff --git a/Unicolour.Tests/Factories/ITestColourFactory.cs b/Unicolour.Tests/Factories/ITestColourFactory.cs index b77e5adc..cb54d1f2 100644 --- a/Unicolour.Tests/Factories/ITestColourFactory.cs +++ b/Unicolour.Tests/Factories/ITestColourFactory.cs @@ -4,7 +4,7 @@ internal interface ITestColourFactory { - TestColour FromRgb255(int r255, int g255, int b255) => FromRgb255(r255, g255, b255, $"RGB [{r255:000} {g255:000} {b255:000}]"); + TestColour FromRgb255(int r255, int g255, int b255) => FromRgb255(r255, g255, b255, $"RGB ({r255:000} {g255:000} {b255:000})"); TestColour FromRgb255(int r255, int g255, int b255, string name) { var r = r255 / 255.0; @@ -13,12 +13,21 @@ TestColour FromRgb255(int r255, int g255, int b255, string name) return FromRgb(r, g, b, name); } - TestColour FromRgb(double r, double g, double b) => FromRgb(r, g, b, $"RGB [{r} {g} {b}]"); + TestColour FromRgb(ColourTriplet triplet) => FromRgb(triplet.First, triplet.Second, triplet.Third, $"RGB {triplet}"); TestColour FromRgb(double r, double g, double b, string name); - TestColour FromHsb(double h, double s, double b) => FromHsb(h, s, b, $"HSB [{h} {s} {b}]"); + TestColour FromHsb(ColourTriplet triplet) => FromHsb(triplet.First, triplet.Second, triplet.Third, $"HSB {triplet}"); TestColour FromHsb(double h, double s, double b, string name); - TestColour FromHsl(double h, double s, double l) => FromHsl(h, s, l, $"HSL [{h} {s} {l}]"); + TestColour FromHsl(ColourTriplet triplet) => FromHsl(triplet.First, triplet.Second, triplet.Third, $"HSL {triplet}"); TestColour FromHsl(double h, double s, double l, string name); + + TestColour FromXyz(ColourTriplet triplet) => FromXyz(triplet.First, triplet.Second, triplet.Third, $"XYZ {triplet}"); + TestColour FromXyz(double x, double y, double z, string name); + + TestColour FromLab(ColourTriplet triplet) => FromLab(triplet.First, triplet.Second, triplet.Third, $"LAB {triplet}"); + TestColour FromLab(double l, double a, double b, string name); + + TestColour FromLuv(ColourTriplet triplet) => FromLuv(triplet.First, triplet.Second, triplet.Third, $"LUV {triplet}"); + TestColour FromLuv(double l, double u, double v, string name); } \ No newline at end of file diff --git a/Unicolour.Tests/Factories/OpenCvCsvFactory.cs b/Unicolour.Tests/Factories/OpenCvCsvFactory.cs index 568456a1..abe1bd04 100644 --- a/Unicolour.Tests/Factories/OpenCvCsvFactory.cs +++ b/Unicolour.Tests/Factories/OpenCvCsvFactory.cs @@ -14,7 +14,7 @@ internal static class OpenCvCsvFactory static OpenCvCsvFactory() { - ColoursFromCsv = File.ReadAllLines(Path.Combine("Utils", "OpenCvColours.csv")).Select(FromCsvRow).ToList(); + ColoursFromCsv = File.ReadAllLines(Path.Combine("Utils", "OpenCvColours.csv")).Select(FromCsvRow).Skip(1).ToList(); } public static TestColour FromName(string name) => ColoursFromCsv.Single(x => x.Name == name); @@ -29,6 +29,7 @@ private static TestColour FromCsvRow(string csvRow) Hsl = new(FromText(items[7]), FromText(items[8]), FromText(items[9])), Xyz = new(FromText(items[10]), FromText(items[11]), FromText(items[12])), Lab = new(FromText(items[13]), FromText(items[14]), FromText(items[15])), + Luv = new(FromText(items[16]), FromText(items[17]), FromText(items[18])), Tolerances = OpenCvFactory.Tolerances }; } @@ -38,6 +39,18 @@ private static TestColour FromCsvRow(string csvRow) private static void GenerateCsvFile() { var rows = new List(); + var headerRow = new List + { + "Name", + "RGB - R", "RGB - G", "RGB - B", + "HSB - H", "HSB - S", "HSB - B", + "HSL - H", "HSL - S", "HSL - L", + "XYZ - X", "XYZ - Y", "XYZ - Z", + "LAB - L", "LAB - A", "LAB - B", + "LUV - L", "LUV - U", "LUV - V", + }; + rows.Add(string.Join(", ", headerRow)); + foreach (var namedColour in TestColours.NamedColours) { var systemColour = ColorTranslator.FromHtml(namedColour.Hex!); @@ -52,8 +65,10 @@ private static void GenerateCsvFile() testColour.Name!, Stringify(testColour.Rgb!.First), Stringify(testColour.Rgb.Second), Stringify(testColour.Rgb.Third), Stringify(testColour.Hsb!.First), Stringify(testColour.Hsb.Second), Stringify(testColour.Hsb.Third), + Stringify(testColour.Hsl!.First), Stringify(testColour.Hsl.Second), Stringify(testColour.Hsl.Third), Stringify(testColour.Xyz!.First), Stringify(testColour.Xyz.Second), Stringify(testColour.Xyz.Third), - Stringify(testColour.Lab!.First), Stringify(testColour.Lab.Second), Stringify(testColour.Lab.Third) + Stringify(testColour.Lab!.First), Stringify(testColour.Lab.Second), Stringify(testColour.Lab.Third), + Stringify(testColour.Luv!.First), Stringify(testColour.Luv.Second), Stringify(testColour.Luv.Third) }; rows.Add(string.Join(", ", row)); diff --git a/Unicolour.Tests/Factories/OpenCvFactory.cs b/Unicolour.Tests/Factories/OpenCvFactory.cs index 203a418d..277ff44d 100644 --- a/Unicolour.Tests/Factories/OpenCvFactory.cs +++ b/Unicolour.Tests/Factories/OpenCvFactory.cs @@ -1,87 +1,135 @@ namespace Wacton.Unicolour.Tests.Factories; +using System; using System.Collections.Generic; -using System.Drawing; -using System.Globalization; -using System.IO; +using System.Linq; using OpenCvSharp; using Wacton.Unicolour.Tests.Utils; +/* + * OpenCV doesn't expose linear RGB + * OpenCV RGB -> XYZ expects to linear RGB values (that have not been companded) + * OpenCV XYZ -> RGB actually converts to linear RGB (not companded) + * OpenCV LAB / LUV -> RGB clamps values, causing errors in subsequent conversions (e.g. LAB -> RGB -> XYZ / LUV) + * OpenCV RGB -> LUV doesn't seem to work when LUV contains negative values + */ internal class OpenCvFactory : ITestColourFactory { - /* - * OpenCV doesn't expose linear RGB - * ----- - * at this point I'm pretty sure OpenCV doesn't calculate RGB -> LAB correctly - * since all other libraries and online tools calculate the same LAB as Unicolour - * the LAB test tolerances are so large it's only worth testing against for regression purposes - */ - public static readonly Tolerances Tolerances = new() {Rgb = 0.005, Hsb = 0.00005, Hsl = 0.00005, Xyz = 0.0005, Lab = 50.0}; + public static readonly Tolerances Tolerances = new() {Rgb = 0.05, Hsb = 0.0125, Hsl = 0.01, Xyz = 0.0005, Lab = 1.0, Luv = 1.0}; public TestColour FromRgb(double r, double g, double b, string name) { - var rLinear = Companding.InverseStandardRgb(r); - var gLinear = Companding.InverseStandardRgb(g); - var bLinear = Companding.InverseStandardRgb(b); + var rgbVec = AsVec(r, g, b); + var rgb = GetInputVec(rgbVec); + var hsb = GetConvertedVec(rgbVec, ColorConversionCodes.RGB2HSV_FULL); + var hls = GetConvertedVec(rgbVec, ColorConversionCodes.RGB2HLS_FULL); + var xyz = GetConvertedVec(ToLinear(rgbVec), ColorConversionCodes.RGB2XYZ); + var lab = GetConvertedVec(rgbVec, ColorConversionCodes.RGB2Lab); + var luv = GetConvertedVec(rgbVec, ColorConversionCodes.RGB2Luv); + return Create(name, rgb, hsb, hls, xyz, lab, luv, Tolerances); + } + + public TestColour FromHsb(double h, double s, double b, string name) + { + var hsbVec = AsVec(h, s, b); + var hsb = GetInputVec(hsbVec); + var rgb = GetConvertedVec(hsbVec, ColorConversionCodes.HSV2RGB); - // it appears that OpenCV's RGB -> XYZ and RGB -> LAB conversions - // expect to receive RGB values that have already undergone linear correction... - var rgb = GetInputVec((r, g, b)); - var hsb = GetConvertedVec((r, g, b), ColorConversionCodes.RGB2HSV_FULL); - var hls = GetConvertedVec((r, g, b), ColorConversionCodes.RGB2HLS_FULL); - var xyz = GetConvertedVec((rLinear, gLinear, bLinear), ColorConversionCodes.RGB2XYZ); - var lab = GetConvertedVec((rLinear, gLinear, bLinear), ColorConversionCodes.RGB2Lab); + var hls = GetConvertedVec(rgb, ColorConversionCodes.RGB2HLS_FULL); + var xyz = GetConvertedVec(ToLinear(rgb), ColorConversionCodes.RGB2XYZ); + var lab = GetConvertedVec(rgb, ColorConversionCodes.RGB2Lab); + var luv = GetConvertedVec(rgb, ColorConversionCodes.RGB2Luv); + return Create(name, rgb, hsb, hls, xyz, lab, luv, Tolerances); + } + + public TestColour FromHsl(double h, double s, double l, string name) + { + var hlsVec = AsVec(h, l, s); + var hls = GetInputVec(hlsVec); + var rgb = GetConvertedVec(hlsVec, ColorConversionCodes.HLS2RGB); + + var hsb = GetConvertedVec(rgb, ColorConversionCodes.RGB2HSV_FULL); + var xyz = GetConvertedVec(ToLinear(rgb), ColorConversionCodes.RGB2XYZ); + var lab = GetConvertedVec(rgb, ColorConversionCodes.RGB2Lab); + var luv = GetConvertedVec(rgb, ColorConversionCodes.RGB2Luv); + return Create(name, rgb, hsb, hls, xyz, lab, luv, Tolerances); + } + + // OpenCV XYZ -> RGB converts to linear RGB, which does not easily feed into any more conversions + public TestColour FromXyz(double x, double y, double z, string name) + { + var xyzVec = AsVec(x, y, z); + var xyz = GetInputVec(xyzVec); + var rgbLinear = GetConvertedVec(xyzVec, ColorConversionCodes.XYZ2RGB); return new TestColour { Name = name, - Rgb = new(rgb.Item0, rgb.Item1, rgb.Item2), - Hsb = new(hsb.Item0, hsb.Item1, hsb.Item2), - Hsl = new(hls.Item0, hls.Item2, hls.Item1), + RgbLinear = new(rgbLinear.Item0, rgbLinear.Item1, rgbLinear.Item2), Xyz = new(xyz.Item0, xyz.Item1, xyz.Item2), - Lab = new(lab.Item0, lab.Item1, lab.Item2), - Tolerances = Tolerances + Tolerances = Tolerances with {RgbLinear = 0.005}, + IsRgbLinearConstrained = false }; } - - // OpenCV can only convert to RGB from HSB - public TestColour FromHsb(double h, double s, double b, string name) + + // OpenCV LAB -> RGB clamps values, causing errors in subsequent conversions + public TestColour FromLab(double l, double a, double b, string name) { - var rgb = GetConvertedVec((h, s, b), ColorConversionCodes.HSV2RGB); - var hsb = GetInputVec((h, s, b)); + var labVec = AsVec(l, a, b); + var lab = GetInputVec(labVec); + var rgb = GetConvertedVec(labVec, ColorConversionCodes.Lab2RGB); - return new TestColour - { - Name = name, - Rgb = new(rgb.Item0, rgb.Item1, rgb.Item2), - Hsb = new(hsb.Item0, hsb.Item1, hsb.Item2), - Tolerances = Tolerances - }; + var hsb = GetConvertedVec(rgb, ColorConversionCodes.RGB2HSV_FULL); + var hls = GetConvertedVec(rgb, ColorConversionCodes.RGB2HLS_FULL); + return Create(name, rgb, hsb, hls, null, lab, null, Tolerances); } - - // OpenCV can only convert to RGB from HSL - public TestColour FromHsl(double h, double s, double l, string name) + + // OpenCV LUV -> RGB clamps values, causing errors in subsequent conversions + // OpenCV LUV -> RGB doesn't seem to work when LUV contains negative values + public TestColour FromLuv(double l, double u, double v, string name) + { + var canHandleLuv = l >= 0 && u >= 0 && v >= 0; + if (!canHandleLuv) return new TestColour { Name = name, Tolerances = Tolerances }; + + var luvVec = AsVec(l, u, v); + var luv = GetInputVec(luvVec); + var rgb = GetConvertedVec(luvVec, ColorConversionCodes.Luv2RGB); + + var hsb = GetConvertedVec(rgb, ColorConversionCodes.RGB2HSV_FULL); + var hls = GetConvertedVec(rgb, ColorConversionCodes.RGB2HLS_FULL); + return Create(name, rgb, hsb, hls, null, null, luv, Tolerances); + } + + private static TestColour Create(string name, + Vec3f rgb, Vec3f hsb, Vec3f hls, + Vec3f? xyz, Vec3f? lab, Vec3f? luv, + Tolerances tolerances) { - var rgb = GetConvertedVec((h, l, s), ColorConversionCodes.HLS2RGB); - var hsl = GetInputVec((h, s, l)); + var hueExclusions = new List(); + if (HasLowChroma(rgb)) hueExclusions.Add("OpenCV converts via RGB and does not handle low RGB chroma"); return new TestColour { Name = name, Rgb = new(rgb.Item0, rgb.Item1, rgb.Item2), - Hsl = new(hsl.Item0, hsl.Item1, hsl.Item2), - Tolerances = Tolerances + Hsb = new(hsb.Item0, hsb.Item1, hsb.Item2), + Hsl = new(hls.Item0, hls.Item2, hls.Item1), + Xyz = xyz != null ? new(xyz.Value.Item0, xyz.Value.Item1, xyz.Value.Item2) : null, + Lab = lab != null ? new(lab.Value.Item0, lab.Value.Item1, lab.Value.Item2) : null, + Luv = luv != null ? new(luv.Value.Item0, luv.Value.Item1, luv.Value.Item2) : null, + Tolerances = tolerances, + ExcludeFromHueBasedTestReasons = hueExclusions }; } - private static Vec3f GetInputVec((double, double, double) input) + private static Vec3f GetInputVec(Vec3f input) { var (i1, i2, i3) = input; var mat = new Mat(1, 1, MatType.CV_32FC3, new Scalar(i1, i2, i3)); return mat.Get(0, 0); } - - private static Vec3f GetConvertedVec((double, double, double) input, ColorConversionCodes conversionCode) + + private static Vec3f GetConvertedVec(Vec3f input, ColorConversionCodes conversionCode) { var (i1, i2, i3) = input; var matIn = new Mat(1, 1, MatType.CV_32FC3, new Scalar(i1, i2, i3)); @@ -89,33 +137,18 @@ private static Vec3f GetConvertedVec((double, double, double) input, ColorConver Cv2.CvtColor(matIn, matOut, conversionCode); return matOut.Get(0, 0); } - - public void GenerateCsvFile() + + private static Vec3f AsVec(double first, double second, double third) => new((float)first, (float)second, (float)third); + private static Vec3f ToLinear(Vec3f input) => DoOperation(input, value => (float) Companding.InverseStandardRgb(value)); + private static Vec3f DoOperation(Vec3f input, Func function) => new(function(input.Item0), function(input.Item1), function(input.Item2)); + + private static bool HasLowChroma(Vec3f rgb) { - var rows = new List(); - foreach (var namedColour in TestColours.NamedColours) - { - var systemColour = ColorTranslator.FromHtml(namedColour.Hex!); - var (r255, g255, b255) = (systemColour.R, systemColour.G, systemColour.B); - var r = r255 / 255.0; - var g = g255 / 255.0; - var b = b255 / 255.0; - var testColour = FromRgb(r, g, b, namedColour.Name!); - - string Stringify(double value) => value.ToString(CultureInfo.InvariantCulture); - var row = new List - { - testColour.Name!, - Stringify(testColour.Rgb!.First), Stringify(testColour.Rgb.Second), Stringify(testColour.Rgb.Third), - Stringify(testColour.Hsb!.First), Stringify(testColour.Hsb.Second), Stringify(testColour.Hsb.Third), - Stringify(testColour.Hsl!.First), Stringify(testColour.Hsl.Second), Stringify(testColour.Hsl.Third), - Stringify(testColour.Xyz!.First), Stringify(testColour.Xyz.Second), Stringify(testColour.Xyz.Third), - Stringify(testColour.Lab!.First), Stringify(testColour.Lab.Second), Stringify(testColour.Lab.Third) - }; - - rows.Add(string.Join(", ", row)); - } - - File.WriteAllLines("OpenCvColours.csv", rows); + // OpenCV can end up with extreme values (e.g. 0 or multiple of 60 hue) if the chroma is small + // (potentially due to using floats instead of doubles) + // which causes significant deviation from Unicolour calculations with very small values + var components = new[] {rgb.Item0, rgb.Item1, rgb.Item2}; + var chroma = components.Max() - components.Min(); + return chroma < 0.000001; } } \ No newline at end of file diff --git a/Unicolour.Tests/Factories/SixLaborsFactory.cs b/Unicolour.Tests/Factories/SixLaborsFactory.cs index 60baad95..1f9fffcc 100644 --- a/Unicolour.Tests/Factories/SixLaborsFactory.cs +++ b/Unicolour.Tests/Factories/SixLaborsFactory.cs @@ -12,18 +12,17 @@ namespace Wacton.Unicolour.Tests.Factories; using SixLaborsHsl = SixLabors.ImageSharp.ColorSpaces.Hsl; using SixLaborsXyz = SixLabors.ImageSharp.ColorSpaces.CieXyz; using SixLaborsLab = SixLabors.ImageSharp.ColorSpaces.CieLab; +using SixLaborsLuv = SixLabors.ImageSharp.ColorSpaces.CieLuv; using SixLaborsIlluminants = SixLabors.ImageSharp.ColorSpaces.Illuminants; +/* + * SixLabors doesn't do a great job of converting to or from LAB / LUV + * SixLabors doesn't handle very small RGB -> HSB / HSL + * SixLabors produces unexpected results for XYZ -> HSB / HSL due to clamping RGB during conversion + */ internal class SixLaborsFactory : ITestColourFactory { - /* - * SixLabors doesn't seem to do a great job of converting to LAB - * and does a poor job of converting from decimal RGB -> HSB / HSL - */ - private static readonly Tolerances BaseTolerances = new() { Rgb = 0.001, RgbLinear = 0.005, Hsb = 0.000005, Hsl = 0.000005, Xyz = 0.005, Lab = 0.1 }; - private static readonly Tolerances FromRgbTolerances = BaseTolerances with { Hsb = 0.05, Hsl = 0.05 }; - private static readonly Tolerances FromHsbTolerances = BaseTolerances with { Hsl = 0.00005, Lab = 0.125 }; - private static readonly Tolerances FromHslTolerances = BaseTolerances with { Rgb = 0.05, Hsb = 0.00005, Lab = 0.175 }; + private static readonly Tolerances Tolerances = new() { Rgb = 0.001, RgbLinear = 0.005, Hsb = 0.000005, Hsl = 0.000005, Xyz = 0.005, Lab = 0.1, Luv = 0.2 }; private static readonly ColorSpaceConverter Converter = new(new ColorSpaceConverterOptions { @@ -36,13 +35,13 @@ public TestColour FromRgb255(int r255, int g255, int b255, string name) var g = g255 / 255.0; var b = b255 / 255.0; var rgb = new SixLaborsRgb((float) r, (float) g, (float) b, RgbWorkingSpaces.SRgb); - return FromRgb(rgb, name, BaseTolerances); + return FromRgb(rgb, name, Tolerances); } public TestColour FromRgb(double r, double g, double b, string name) { var rgb = new SixLaborsRgb((float) r, (float) g, (float) b, RgbWorkingSpaces.SRgb); - return FromRgb(rgb, name, FromRgbTolerances); + return FromRgb(rgb, name, Tolerances with {Hsb = 0.05, Hsl = 0.05}); } private static TestColour FromRgb(SixLaborsRgb rgb, string name, Tolerances tolerances) @@ -52,7 +51,8 @@ private static TestColour FromRgb(SixLaborsRgb rgb, string name, Tolerances tole var hsl = Converter.ToHsl(rgb); var xyz = Converter.ToCieXyz(rgb); var lab = Converter.ToCieLab(rgb); - return Create(name, rgb, rgbLinear, hsb, hsl, xyz, lab, tolerances); + var luv = Converter.ToCieLuv(rgb); + return Create(name, rgb, rgbLinear, hsb, hsl, xyz, lab, luv, tolerances); } public TestColour FromHsb(double h, double s, double b, string name) @@ -63,7 +63,8 @@ public TestColour FromHsb(double h, double s, double b, string name) var hsl = Converter.ToHsl(hsb); var xyz = Converter.ToCieXyz(hsb); var lab = Converter.ToCieLab(hsb); - return Create(name, rgb, rgbLinear, hsb, hsl, xyz, lab, FromHsbTolerances); + var luv = Converter.ToCieLuv(hsb); + return Create(name, rgb, rgbLinear, hsb, hsl, xyz, lab, luv, Tolerances with {Hsl = 0.00005, Lab = 0.125}); } public TestColour FromHsl(double h, double s, double l, string name) @@ -74,10 +75,32 @@ public TestColour FromHsl(double h, double s, double l, string name) var hsb = Converter.ToHsv(hsl); var xyz = Converter.ToCieXyz(hsl); var lab = Converter.ToCieLab(hsl); - return Create(name, rgb, rgbLinear, hsb, hsl, xyz, lab, FromHslTolerances); + var luv = Converter.ToCieLuv(hsl); + return Create(name, rgb, rgbLinear, hsb, hsl, xyz, lab, luv, Tolerances with {Rgb = 0.05, Hsb = 0.00005, Lab = 0.175}); } - private static TestColour Create(string name, SixLaborsRgb rgb, SixLaborsRgbLinear rgbLinear, SixLaborsHsb hsb, SixLaborsHsl hsl, SixLaborsXyz xyz, SixLaborsLab lab, Tolerances tolerances) + // SixLabors XYZ -> HSB / HSL uses a clamped version of RGB during conversion + // which significantly affects the result when transforming to the hue-based cylindrical model + // for this reason, not comparing hue-based values here + public TestColour FromXyz(double x, double y, double z, string name) + { + var xyz = new SixLaborsXyz((float) x, (float) y, (float) z); + var rgb = Converter.ToRgb(xyz); + var rgbLinear = Converter.ToLinearRgb(xyz); + var lab = Converter.ToCieLab(xyz); + var luv = Converter.ToCieLuv(xyz); + return CreateWithoutHue(name, rgb, rgbLinear, xyz, lab, luv, Tolerances); + } + + // SixLabors doesn't do a good job of converting from LAB / LUV even when specifying D65 illuminant + // potentially due to clamping XYZ values during conversion (e.g. LAB -> XYZ -> RGB) + public TestColour FromLab(double l, double a, double b, string name) => throw new NotImplementedException(); + public TestColour FromLuv(double l, double u, double v, string name) => throw new NotImplementedException(); + + private static TestColour Create(string name, + SixLaborsRgb rgb, SixLaborsRgbLinear rgbLinear, SixLaborsHsb hsb, SixLaborsHsl hsl, + SixLaborsXyz xyz, SixLaborsLab lab, SixLaborsLuv luv, + Tolerances tolerances) { var hueExclusions = new List(); if (HasInconsistentHue(hsb, hsl)) hueExclusions.Add("SixLabors converts via RGB and loses hue when greyscale"); @@ -92,6 +115,28 @@ private static TestColour Create(string name, SixLaborsRgb rgb, SixLaborsRgbLine Hsl = new(hsl.H, hsl.S, hsl.L), Xyz = new(xyz.X, xyz.Y, xyz.Z), Lab = new(lab.L, lab.A, lab.B), + Luv = new(luv.L, luv.U, luv.V), + Tolerances = tolerances, + ExcludeFromHueBasedTestReasons = hueExclusions + }; + } + + private static TestColour CreateWithoutHue(string name, + SixLaborsRgb rgb, SixLaborsRgbLinear rgbLinear, + SixLaborsXyz xyz, SixLaborsLab lab, SixLaborsLuv luv, + Tolerances tolerances) + { + var hueExclusions = new List(); + if (HasLowChroma(rgb)) hueExclusions.Add("SixLabors converts via RGB and does not handle low RGB chroma"); + + return new TestColour + { + Name = name, + Rgb = new(rgb.R, rgb.G, rgb.B), + RgbLinear = new(rgbLinear.R, rgbLinear.G, rgbLinear.B), + Xyz = new(xyz.X, xyz.Y, xyz.Z), + Lab = new(lab.L, lab.A, lab.B), + Luv = new(luv.L, luv.U, luv.V), Tolerances = tolerances, ExcludeFromHueBasedTestReasons = hueExclusions }; diff --git a/Unicolour.Tests/InterpolateLuvTests.cs b/Unicolour.Tests/InterpolateLuvTests.cs new file mode 100644 index 00000000..59d5c0b1 --- /dev/null +++ b/Unicolour.Tests/InterpolateLuvTests.cs @@ -0,0 +1,93 @@ +namespace Wacton.Unicolour.Tests; + +using NUnit.Framework; + +public class InterpolateLuvTests +{ + [Test] + public void SameColour() + { + var unicolour1 = Unicolour.FromLuv(50, -50, 50, 0.5); + var unicolour2 = Unicolour.FromLuv(50, -50, 50, 0.5); + var interpolated1 = unicolour1.InterpolateLuv(unicolour2, 0.25); + var interpolated2 = unicolour2.InterpolateLuv(unicolour1, 0.75); + var interpolated3 = unicolour1.InterpolateLuv(unicolour2, 0.75); + var interpolated4 = unicolour2.InterpolateLuv(unicolour1, 0.25); + + AssertInterpolated(interpolated1, (50, -50, 50, 0.5)); + AssertInterpolated(interpolated2, (50, -50, 50, 0.5)); + AssertInterpolated(interpolated3, (50, -50, 50, 0.5)); + AssertInterpolated(interpolated4, (50, -50, 50, 0.5)); + } + + [Test] + public void Equidistant() + { + var unicolour1 = Unicolour.FromLuv(0, -100, -100, 0.0); + var unicolour2 = Unicolour.FromLuv(50, 100, 100); + var interpolated1 = unicolour1.InterpolateLuv(unicolour2, 0.5); + var interpolated2 = unicolour2.InterpolateLuv(unicolour1, 0.5); + + AssertInterpolated(interpolated1, (25, 0, 0, 0.5)); + AssertInterpolated(interpolated2, (25, 0, 0, 0.5)); + } + + [Test] + public void CloserToEndColour() + { + var unicolour1 = Unicolour.FromLuv(0, 100, -100); + var unicolour2 = Unicolour.FromLuv(80, -100, 100, 0.5); + var interpolated1 = unicolour1.InterpolateLuv(unicolour2, 0.75); + var interpolated2 = unicolour2.InterpolateLuv(unicolour1, 0.75); + + AssertInterpolated(interpolated1, (60, -50, 50, 0.625)); + AssertInterpolated(interpolated2, (20, 50, -50, 0.875)); + } + + [Test] + public void CloserToStartColour() + { + var unicolour1 = Unicolour.FromLuv(0, 100, -100); + var unicolour2 = Unicolour.FromLuv(80, -100, 100, 0.5); + var interpolated1 = unicolour1.InterpolateLuv(unicolour2, 0.25); + var interpolated2 = unicolour2.InterpolateLuv(unicolour1, 0.25); + + AssertInterpolated(interpolated1, (20, 50, -50, 0.875)); + AssertInterpolated(interpolated2, (60, -50, 50, 0.625)); + } + + [Test] + public void BeyondEndColour() + { + var unicolour1 = Unicolour.FromLuv(20, -25.6, 25.6, 0.8); + var unicolour2 = Unicolour.FromLuv(30, 25.6, -25.6, 0.9); + var interpolated1 = unicolour1.InterpolateLuv(unicolour2, 1.5); + var interpolated2 = unicolour2.InterpolateLuv(unicolour1, 1.5); + + AssertInterpolated(interpolated1, (35, 51.2, -51.2, 0.95)); + AssertInterpolated(interpolated2, (15, -51.2, 51.2, 0.75)); + } + + [Test] + public void BeyondStartColour() + { + var unicolour1 = Unicolour.FromLuv(20, -25.6, 25.6, 0.8); + var unicolour2 = Unicolour.FromLuv(30, 25.6, -25.6, 0.9); + var interpolated1 = unicolour1.InterpolateLuv(unicolour2, -0.5); + var interpolated2 = unicolour2.InterpolateLuv(unicolour1, -0.5); + + AssertInterpolated(interpolated1, (15, -51.2, 51.2, 0.75)); + AssertInterpolated(interpolated2, (35, 51.2, -51.2, 0.95)); + } + + private static void AssertInterpolated(Unicolour unicolour, (double l, double u, double v, double alpha) expected) + { + var actualLuv = unicolour.Luv; + var actualAlpha = unicolour.Alpha; + + Assert.That(actualLuv.L, Is.EqualTo(expected.l).Within(0.00000000005)); + Assert.That(actualLuv.U, Is.EqualTo(expected.u).Within(0.00000000005)); + Assert.That(actualLuv.V, Is.EqualTo(expected.v).Within(0.00000000005)); + Assert.That(actualAlpha.A, Is.EqualTo(expected.alpha).Within(0.00000000005)); + } +} \ No newline at end of file diff --git a/Unicolour.Tests/OtherLibraryTests.cs b/Unicolour.Tests/OtherLibraryTests.cs index d09c23ed..e2e16624 100644 --- a/Unicolour.Tests/OtherLibraryTests.cs +++ b/Unicolour.Tests/OtherLibraryTests.cs @@ -7,24 +7,31 @@ namespace Wacton.Unicolour.Tests; public class OtherLibraryTests { - private static readonly OpenCvFactory OpenCvFactory = new(); - private static readonly ColourfulFactory ColourfulFactory = new(); - private static readonly ColorMineFactory ColorMineFactory = new(); - private static readonly SixLaborsFactory SixLaborsFactory = new(); + private static readonly ITestColourFactory OpenCvFactory = new OpenCvFactory(); + private static readonly ITestColourFactory ColourfulFactory = new ColourfulFactory(); + private static readonly ITestColourFactory ColorMineFactory = new ColorMineFactory(); + private static readonly ITestColourFactory SixLaborsFactory = new SixLaborsFactory(); private static bool IsWindows() => Environment.OSVersion.Platform == PlatformID.Win32NT; + + private delegate Unicolour UnicolourFromTuple((double first, double second, double third) tuple, double alpha = 1.0); + private delegate TestColour TestColourFromTuple(ColourTriplet triplet); [Test] public void OpenCvWindows() { // I've given up trying to make OpenCvSharp work in a dockerised unix environment... Assume.That(IsWindows()); + AssertUtils.AssertNamedColours(namedColour => AssertFromHex(namedColour.Hex!, OpenCvFactory)); AssertUtils.AssertRandomHexColours(hex => AssertFromHex(hex, OpenCvFactory)); AssertUtils.AssertRandomRgb255Colours(triplet => AssertFromRgb255(triplet, OpenCvFactory)); - AssertUtils.AssertRandomRgbColours(triplet => AssertFromRgb(triplet, OpenCvFactory)); - AssertUtils.AssertRandomHsbColours(triplet => AssertFromHsb(triplet, OpenCvFactory)); - AssertUtils.AssertRandomHslColours(triplet => AssertFromHsl(triplet, OpenCvFactory)); + AssertUtils.AssertRandomRgbColours(triplet => AssertTriplet(triplet, Unicolour.FromRgb, OpenCvFactory.FromRgb)); + AssertUtils.AssertRandomHsbColours(triplet => AssertTriplet(triplet, Unicolour.FromHsb, OpenCvFactory.FromHsb)); + AssertUtils.AssertRandomHslColours(triplet => AssertTriplet(triplet, Unicolour.FromHsl, OpenCvFactory.FromHsl)); + AssertUtils.AssertRandomXyzColours(triplet => AssertTriplet(triplet, Unicolour.FromXyz, OpenCvFactory.FromXyz)); + AssertUtils.AssertRandomLabColours(triplet => AssertTriplet(triplet, Unicolour.FromLab, OpenCvFactory.FromLab)); + AssertUtils.AssertRandomLuvColours(triplet => AssertTriplet(triplet, Unicolour.FromLuv, OpenCvFactory.FromLuv)); } [Test] @@ -37,85 +44,75 @@ public void OpenCvCrossPlatform() [Test] public void Colourful() { - // no asserting random HSB colours because Colourful doesn't support HSB/HSL + // not asserting random HSB colours because Colourful doesn't support HSB/HSL + // and not testing from LUV because it appears to give wrong values AssertUtils.AssertNamedColours(namedColour => AssertFromHex(namedColour.Hex!, ColourfulFactory)); AssertUtils.AssertRandomHexColours(hex => AssertFromHex(hex, ColourfulFactory)); AssertUtils.AssertRandomRgb255Colours(triplet => AssertFromRgb255(triplet, ColourfulFactory)); - AssertUtils.AssertRandomRgbColours(triplet => AssertFromRgb(triplet, ColourfulFactory)); + AssertUtils.AssertRandomRgbColours(triplet => AssertTriplet(triplet, Unicolour.FromRgb, ColourfulFactory.FromRgb)); + AssertUtils.AssertRandomXyzColours(triplet => AssertTriplet(triplet, Unicolour.FromXyz, ColourfulFactory.FromXyz)); + AssertUtils.AssertRandomLabColours(triplet => AssertTriplet(triplet, Unicolour.FromLab, ColourfulFactory.FromLab)); } [Test] public void ColorMine() { - // no asserting random RGB 0-1 colours because ColorMine only accepts RGB 255 + // not asserting random RGB 0-1 colours because ColorMine only accepts RGB 255 + // and not testing from XYZ / LAB / LUV because it does a terrible job AssertUtils.AssertNamedColours(namedColour => AssertFromHex(namedColour.Hex!, ColorMineFactory)); AssertUtils.AssertRandomHexColours(hex => AssertFromHex(hex, ColorMineFactory)); - AssertUtils.AssertRandomRgb255Colours(triplet => AssertFromRgb255(triplet, ColorMineFactory)); - AssertUtils.AssertRandomHsbColours(triplet => AssertFromHsb(triplet, ColorMineFactory)); - AssertUtils.AssertRandomHslColours(triplet => AssertFromHsl(triplet, ColorMineFactory)); + AssertUtils.AssertRandomRgb255Colours(triplet => AssertFromRgb255(triplet, ColorMineFactory)); + AssertUtils.AssertRandomHsbColours(triplet => AssertTriplet(triplet, Unicolour.FromHsb, ColorMineFactory.FromHsb)); + AssertUtils.AssertRandomHslColours(triplet => AssertTriplet(triplet, Unicolour.FromHsl, ColorMineFactory.FromHsl)); } [Test] public void SixLabors() { + // not testing from LAB / LUV as SixLabors does not handle it well AssertUtils.AssertNamedColours(namedColour => AssertFromHex(namedColour.Hex!, SixLaborsFactory)); AssertUtils.AssertRandomHexColours(hex => AssertFromHex(hex, SixLaborsFactory)); - AssertUtils.AssertRandomRgb255Colours(triplet => AssertFromRgb255(triplet, SixLaborsFactory)); - AssertUtils.AssertRandomRgbColours(triplet => AssertFromRgb(triplet, SixLaborsFactory)); - AssertUtils.AssertRandomHsbColours(triplet => AssertFromHsb(triplet, SixLaborsFactory)); - AssertUtils.AssertRandomHslColours(triplet => AssertFromHsl(triplet, SixLaborsFactory)); + AssertUtils.AssertRandomRgb255Colours(triplet => AssertFromRgb255(triplet, SixLaborsFactory)); + AssertUtils.AssertRandomRgbColours(triplet => AssertTriplet(triplet, Unicolour.FromRgb, SixLaborsFactory.FromRgb)); + AssertUtils.AssertRandomHsbColours(triplet => AssertTriplet(triplet, Unicolour.FromHsb, SixLaborsFactory.FromHsb)); + AssertUtils.AssertRandomHslColours(triplet => AssertTriplet(triplet, Unicolour.FromHsl, SixLaborsFactory.FromHsl)); + AssertUtils.AssertRandomXyzColours(triplet => AssertTriplet(triplet, Unicolour.FromXyz, SixLaborsFactory.FromXyz)); } + private static void AssertTriplet(ColourTriplet triplet, UnicolourFromTuple getUnicolour, TestColourFromTuple getTestColour) + { + var unicolour = getUnicolour(triplet.Tuple); + var testColour = getTestColour(triplet); + AssertTestColour(unicolour, testColour); + } + private static void AssertFromHex(string hex, ITestColourFactory testColourFactory) { + hex = "#FF7F50"; var unicolour = Unicolour.FromHex(hex); var (r255, g255, b255, _) = SystemColorUtils.HexToRgb255(hex); - var testColour = testColourFactory.FromRgb255(r255, g255, b255); + var testColour = testColourFactory.FromRgb255(r255, g255, b255, $"HEX [{hex}]"); AssertHex(unicolour, hex); - AssertTestColour(unicolour, testColour, $"HEX [{hex}]"); + AssertTestColour(unicolour, testColour); } private static void AssertFromRgb255(ColourTriplet triplet, ITestColourFactory testColourFactory) { var (first, second, third) = triplet; - var r255 = (int)(first / 255.0); - var g255 = (int)(second / 255.0); - var b255 = (int)(third / 255.0); + var r255 = (int)first; + var g255 = (int)second; + var b255 = (int)third; var unicolour = Unicolour.FromRgb255(r255, g255, b255); var testColour = testColourFactory.FromRgb255(r255, g255, b255); - AssertTestColour(unicolour, testColour, $"RGB [{unicolour.Rgb.Triplet255}]"); - } - - private static void AssertFromRgb(ColourTriplet triplet, ITestColourFactory testColourFactory) - { - var (r, g, b) = triplet; - var unicolour = Unicolour.FromRgb(r, g, b); - var testColour = testColourFactory.FromRgb(r, g, b); - AssertTestColour(unicolour, testColour, $"RGB [{unicolour.Rgb}]"); + AssertTestColour(unicolour, testColour); } - - private static void AssertFromHsb(ColourTriplet triplet, ITestColourFactory testColourFactory) - { - var (h, s, b) = triplet; - var unicolour = Unicolour.FromHsb(h, s, b); - var testColour = testColourFactory.FromHsb(h, s, b); - AssertTestColour(unicolour, testColour, $"HSB [{unicolour.Hsb}]"); - } - - private static void AssertFromHsl(ColourTriplet triplet, ITestColourFactory testColourFactory) - { - var (h, s, l) = triplet; - var unicolour = Unicolour.FromHsl(h, s, l); - var testColour = testColourFactory.FromHsl(h, s, l); - AssertTestColour(unicolour, testColour, $"HSL [{unicolour.Hsl}]"); - } - + private static void AssertFromCsvData(string hex, string name) { var unicolour = Unicolour.FromHex(hex); var otherLibColour = OpenCvCsvFactory.FromName(name); AssertHex(unicolour, hex); - AssertTestColour(unicolour, otherLibColour, $"NAME [{name}]"); + AssertTestColour(unicolour, otherLibColour); } private static void AssertHex(Unicolour unicolour, string hex) @@ -127,32 +124,35 @@ private static void AssertHex(Unicolour unicolour, string hex) Assert.That(unicolour.Alpha.Hex, Is.EqualTo(expectedA.ToUpper())); } - private static void AssertTestColour(Unicolour unicolour, TestColour testColour, string source) + private static void AssertTestColour(Unicolour unicolour, TestColour testColour) { var colourName = testColour.Name; var tolerances = testColour.Tolerances; if (colourName == null) throw new ArgumentException("Malformed test colour: no name"); if (tolerances == null) throw new ArgumentException("Malformed test colour: no tolerances"); - AssertColourTriplet(unicolour.Rgb.Triplet, testColour.Rgb, tolerances.Rgb, $"{source} -> RGB"); - AssertColourTriplet(unicolour.Rgb.TripletLinear, testColour.RgbLinear, tolerances.RgbLinear, $"{source} -> RGB Linear"); - AssertColourTriplet(unicolour.Xyz.Triplet, testColour.Xyz, tolerances.Xyz, $"{source} -> XYZ"); - AssertColourTriplet(unicolour.Lab.Triplet, testColour.Lab, tolerances.Lab, $"{source} -> LAB"); + var unicolourRgb = testColour.IsRgbConstrained ? unicolour.Rgb.ConstrainedTriplet : unicolour.Rgb.Triplet; + var unicolourRgbLinear = testColour.IsRgbLinearConstrained ? unicolour.Rgb.ConstrainedTripletLinear : unicolour.Rgb.TripletLinear; + AssertColourTriplet(unicolourRgb, testColour.Rgb, tolerances.Rgb, $"{colourName} -> RGB"); + AssertColourTriplet(unicolourRgbLinear, testColour.RgbLinear, tolerances.RgbLinear, $"{colourName} -> RGB Linear"); + AssertColourTriplet(unicolour.Xyz.Triplet, testColour.Xyz, tolerances.Xyz, $"{colourName} -> XYZ"); + AssertColourTriplet(unicolour.Lab.Triplet, testColour.Lab, tolerances.Lab, $"{colourName} -> LAB"); + AssertColourTriplet(unicolour.Luv.Triplet, testColour.Luv, tolerances.Luv, $"{colourName} -> LUV"); if (testColour.ExcludeFromHueBasedTest) { var reasons = string.Join(", ", testColour.ExcludeFromHueBasedTestReasons); - Console.WriteLine($"Excluded test colour {source} -> HSB [{unicolour.Hsb}] / HSL [{unicolour.Hsl}] because: {reasons}"); + Console.WriteLine($"Excluded test colour {colourName} -> HSB [{unicolour.Hsb}] / HSL [{unicolour.Hsl}] because: {reasons}"); return; } - AssertColourTriplet(unicolour.Hsb.Triplet, testColour.Hsb, tolerances.Hsb, $"{source} -> HSB", true); - AssertColourTriplet(unicolour.Hsl.Triplet, testColour.Hsl, tolerances.Hsl, $"{source} -> HSL", true); + AssertColourTriplet(unicolour.Hsb.ConstrainedTriplet, testColour.Hsb, tolerances.Hsb, $"{colourName} -> HSB", true); + AssertColourTriplet(unicolour.Hsl.ConstrainedTriplet, testColour.Hsl, tolerances.Hsl, $"{colourName} -> HSL", true); } - private static void AssertColourTriplet(ColourTriplet actual, ColourTriplet? expected, double tolerance, string details, bool hasHue = false) + private static void AssertColourTriplet(ColourTriplet actual, ColourTriplet? expected, double tolerance, string info, bool hasHue = false) { if (expected == null) return; - AssertUtils.AssertColourTriplet(actual, expected, tolerance, hasHue, details); + AssertUtils.AssertColourTriplet(actual, expected, tolerance, hasHue, info); } } \ No newline at end of file diff --git a/Unicolour.Tests/RangeClampTests.cs b/Unicolour.Tests/RangeClampTests.cs index 3a27ca61..3284b2be 100644 --- a/Unicolour.Tests/RangeClampTests.cs +++ b/Unicolour.Tests/RangeClampTests.cs @@ -12,8 +12,8 @@ public static void RgbRange() Range bRange = new(0.0, 1.0); var beyondMax = new Rgb(rRange.BeyondMax, gRange.BeyondMax, bRange.BeyondMax, Configuration.Default); var beyondMin = new Rgb(rRange.BeyondMin, gRange.BeyondMin, bRange.BeyondMin, Configuration.Default); - AssertClamped(beyondMax.ClampedTriplet, beyondMax.Triplet); - AssertClamped(beyondMin.Triplet, beyondMin.ClampedTriplet); + AssertConstrained(beyondMax.ConstrainedTriplet, beyondMax.Triplet); + AssertConstrained(beyondMin.Triplet, beyondMin.ConstrainedTriplet); } [Test] @@ -24,8 +24,8 @@ public static void HsbRange() Range bRange = new(0.0, 1.0); var beyondMax = new Hsb(hRange.BeyondMax, sRange.BeyondMax, bRange.BeyondMax); var beyondMin = new Hsb(hRange.BeyondMin, sRange.BeyondMin, bRange.BeyondMin); - AssertClamped(beyondMax.ClampedTriplet, beyondMax.Triplet); - AssertClamped(beyondMin.Triplet, beyondMin.ClampedTriplet); + AssertConstrained(beyondMax.ConstrainedTriplet, beyondMax.Triplet); + AssertConstrained(beyondMin.Triplet, beyondMin.ConstrainedTriplet); } [Test] @@ -36,11 +36,11 @@ public static void HslRange() Range lRange = new(0.0, 1.0); var beyondMax = new Hsl(hRange.BeyondMax, sRange.BeyondMax, lRange.BeyondMax); var beyondMin = new Hsl(hRange.BeyondMin, sRange.BeyondMin, lRange.BeyondMin); - AssertClamped(beyondMax.ClampedTriplet, beyondMax.Triplet); - AssertClamped(beyondMin.Triplet, beyondMin.ClampedTriplet); + AssertConstrained(beyondMax.ConstrainedTriplet, beyondMax.Triplet); + AssertConstrained(beyondMin.Triplet, beyondMin.ConstrainedTriplet); } - private static void AssertClamped(ColourTriplet lesser, ColourTriplet greater) + private static void AssertConstrained(ColourTriplet lesser, ColourTriplet greater) { Assert.That(lesser.First, Is.LessThan(greater.First)); Assert.That(lesser.Second, Is.LessThan(greater.Second)); diff --git a/Unicolour.Tests/SmokeTests.cs b/Unicolour.Tests/SmokeTests.cs index 50e3dd14..58e09046 100644 --- a/Unicolour.Tests/SmokeTests.cs +++ b/Unicolour.Tests/SmokeTests.cs @@ -59,6 +59,14 @@ public static void UnicolourLab() AssertLab((100, 128, 128)); AssertLab((50, -1, 1)); } + + [Test] + public static void UnicolourLuv() + { + AssertLuv((0, -100, -100)); + AssertLuv((100, 100, 100)); + AssertLuv((50, -1, 1)); + } [Test] public static void UnicolourOklab() @@ -73,6 +81,7 @@ public static void UnicolourOklab() private static void AssertHsl((double, double, double) tuple) => AssertInit(tuple, Unicolour.FromHsl, Unicolour.FromHsl, Unicolour.FromHsl, Unicolour.FromHsl); private static void AssertXyz((double, double, double) tuple) => AssertInit(tuple, Unicolour.FromXyz, Unicolour.FromXyz, Unicolour.FromXyz, Unicolour.FromXyz); private static void AssertLab((double, double, double) tuple) => AssertInit(tuple, Unicolour.FromLab, Unicolour.FromLab, Unicolour.FromLab, Unicolour.FromLab); + private static void AssertLuv((double, double, double) tuple) => AssertInit(tuple, Unicolour.FromLuv, Unicolour.FromLuv, Unicolour.FromLuv, Unicolour.FromLuv); private static void AssertOklab((double, double, double) tuple) => AssertInit(tuple, Unicolour.FromOklab, Unicolour.FromOklab, Unicolour.FromOklab, Unicolour.FromOklab); private delegate Unicolour FromValues(double first, double second, double third, double alpha = 1.0); @@ -140,6 +149,8 @@ private static void AssertNoError(Unicolour unicolour) Assert.DoesNotThrow(() => _ = unicolour.Hsl); Assert.DoesNotThrow(() => _ = unicolour.Xyz); Assert.DoesNotThrow(() => _ = unicolour.Lab); + Assert.DoesNotThrow(() => _ = unicolour.Luv); + Assert.DoesNotThrow(() => _ = unicolour.Oklab); Assert.DoesNotThrow(() => _ = unicolour.Alpha); Assert.DoesNotThrow(() => _ = unicolour.Config); Assert.DoesNotThrow(() => _ = unicolour.Luminance); diff --git a/Unicolour.Tests/Utils/AssertUtils.cs b/Unicolour.Tests/Utils/AssertUtils.cs index cebc9d76..c2033c74 100644 --- a/Unicolour.Tests/Utils/AssertUtils.cs +++ b/Unicolour.Tests/Utils/AssertUtils.cs @@ -14,6 +14,7 @@ internal static class AssertUtils public static void AssertRandomHslColours(Action action) => AssertItems(TestColours.RandomHslColours, action); public static void AssertRandomXyzColours(Action action) => AssertItems(TestColours.RandomXyzColours, action); public static void AssertRandomLabColours(Action action) => AssertItems(TestColours.RandomLabColours, action); + public static void AssertRandomLuvColours(Action action) => AssertItems(TestColours.RandomLuvColours, action); public static void AssertRandomOklabColours(Action action) => AssertItems(TestColours.RandomOklabColours, action); private static void AssertItems(List itemsToAssert, Action assertAction) @@ -24,11 +25,12 @@ private static void AssertItems(List itemsToAssert, Action assertAction } } - public static void AssertColourTriplet(ColourTriplet actual, ColourTriplet expected, double tolerance, bool hasHue = false, string? details = null) + public static void AssertColourTriplet(ColourTriplet actual, ColourTriplet expected, double tolerance, bool hasHue = false, string? info = null) { double NormalisedFirst(double value) => hasHue ? value / 360.0 : value; - - string FailMessage(string channel) => $"{channel}{(details == null ? string.Empty : $" ({details})")}"; + + var details = $"Expected --- {expected}\nActual ----- {actual}"; + string FailMessage(string channel) => $"{(info == null ? string.Empty : $"{info} · ")}{channel}\n{details}"; Assert.That(NormalisedFirst(actual.First), Is.EqualTo(NormalisedFirst(expected.First)).Within(tolerance), FailMessage("Channel 1")); Assert.That(actual.Second, Is.EqualTo(expected.Second).Within(tolerance), FailMessage("Channel 2")); Assert.That(actual.Third, Is.EqualTo(expected.Third).Within(tolerance), FailMessage("Channel 3")); diff --git a/Unicolour.Tests/Utils/OpenCvColours.csv b/Unicolour.Tests/Utils/OpenCvColours.csv index b640249c..42026b85 100644 --- a/Unicolour.Tests/Utils/OpenCvColours.csv +++ b/Unicolour.Tests/Utils/OpenCvColours.csv @@ -1,145 +1,146 @@ -Alice Blue, 0.9411764740943909, 0.9725490212440491, 1, 208.00006103515625, 0.058823518455028534, 1, 208, 1, 0.970588207244873, 0.8754762411117554, 0.9287939667701721, 1.0789587497711182, 93.682861328125, -2.484375, -9.5625 -Antique White, 0.9803921580314636, 0.9215686321258545, 0.843137264251709, 34.28568649291992, 0.13999997079372406, 0.9803921580314636, 34.28571319580078, 0.7777780294418335, 0.9117647409439087, 0.8139658570289612, 0.8464831709861755, 0.7632243633270264, 86.553955078125, 5.5, 23.359375 -Aqua, 0, 1, 1, 180, 0.9999998807907104, 1, 180, 1, 0.5, 0.5380030274391174, 0.7873290181159973, 1.0694199800491333, 91.11328125, -48.09375, -14.125 -Aquamarine, 0.49803921580314636, 1, 0.8313725590705872, 159.84375, 0.5019606947898865, 1, 159.84375, 1, 0.7490196228027344, 0.5639011859893799, 0.807809591293335, 0.7489018440246582, 89.447021484375, -66.78125, 28.40625 -Azure, 0.9411764740943909, 1, 1, 179.9998779296875, 0.058823518455028534, 1, 180, 1, 0.970588207244873, 0.897400975227356, 0.9726435542106628, 1.0862669944763184, 97.747802734375, -10.53125, -3.5625 -Beige, 0.9607843160629272, 0.9607843160629272, 0.8627451062202454, 59.99992370605469, 0.10204079747200012, 0.9607843160629272, 60.000003814697266, 0.5555555820465088, 0.9117647409439087, 0.8322436213493347, 0.8988521099090576, 0.8065600991249084, 91.094970703125, -8, 24.546875 -Bisque, 1, 0.8941176533699036, 0.7686274647712708, 32.5423583984375, 0.23137250542640686, 1, 32.54237365722656, 1, 0.884313702583313, 0.7894670963287354, 0.8073461651802063, 0.6363427639007568, 83.673095703125, 13.46875, 36.5 -Black, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -Blanched Almond, 1, 0.9215686321258545, 0.8039215803146362, 35.99998092651367, 0.19607838988304138, 1, 36, 1, 0.9019607901573181, 0.8196672201156616, 0.8508632779121399, 0.6984653472900391, 87.2802734375, 7.796875, 33.546875 -Blue, 0, 0, 1, 240, 0.9999998807907104, 1, 240, 1, 0.5, 0.18042300641536713, 0.0721689984202385, 0.9502270221710205, 32.293701171875, 79.1875, -107.859375 -Blue Violet, 0.5411764979362488, 0.16862745583057404, 0.886274516582489, 271.1475524902344, 0.8097344040870667, 0.886274516582489, 271.1475524902344, 0.7593361735343933, 0.5274509787559509, 0.25068020820617676, 0.12621363997459412, 0.7304641008377075, 27.099609375, 64.625, -81.25 -Brown, 0.6470588445663452, 0.16470588743686676, 0.16470588743686676, 0, 0.7454544305801392, 0.6470588445663452, 0, 0.5942029356956482, 0.4058823585510254, 0.16764701902866364, 0.09824936091899872, 0.032035328447818756, 18.353271484375, 37.6875, 25.890625 -Burlywood, 0.8705882430076599, 0.7215686440467834, 0.529411792755127, 33.7930908203125, 0.39189180731773376, 0.8705882430076599, 33.7931022644043, 0.5686275362968445, 0.7000000476837158, 0.5163891315460205, 0.5156236290931702, 0.3014764189720154, 56.8359375, 19.359375, 42.4375 -Cadet Blue, 0.37254902720451355, 0.6196078658103943, 0.6274510025978088, 181.84617614746094, 0.4062499403953552, 0.6274510025978088, 181.84616088867188, 0.2549019753932953, 0.5, 0.23288553953170776, 0.2942303717136383, 0.377002090215683, 33.642578125, -17.546875, -7 -Chartreuse, 0.49803921580314636, 1, 0, 90.1176528930664, 0.9999998807907104, 1, 90.11764526367188, 1, 0.5, 0.44511520862579346, 0.7602953314781189, 0.12329627573490143, 88.1103515625, -82.765625, 83.640625 -Chocolate, 0.8235294222831726, 0.4117647111415863, 0.11764705926179886, 24.999996185302734, 0.8571427464485168, 0.8235294222831726, 25.000001907348633, 0.75, 0.47058823704719543, 0.31867295503616333, 0.23902495205402374, 0.04163479432463646, 36.273193359375, 50.359375, 48.09375 -Coral, 1, 0.49803921580314636, 0.3137255012989044, 16.114280700683594, 0.6862744688987732, 1, 16.11428451538086, 1, 0.656862735748291, 0.5028159618377686, 0.3702393174171448, 0.12085746228694916, 56.036376953125, 72.421875, 63.34375 -Cornflower Blue, 0.3921568691730499, 0.5843137502670288, 0.929411768913269, 218.54014587402344, 0.5780590176582336, 0.929411768913269, 218.54014587402344, 0.791907548904419, 0.6607843041419983, 0.31282591819763184, 0.3031572103500366, 0.8430083990097046, 38.653564453125, 37.96875, -74.96875 -Cornsilk, 1, 0.9725490212440491, 0.8627451062202454, 47.99995803833008, 0.13725487887859344, 1, 48, 1, 0.9313725233078003, 0.8772358298301697, 0.9356323480606079, 0.8112900257110596, 94.488525390625, -3, 29.46875 -Crimson, 0.8627451062202454, 0.0784313753247261, 0.23529411852359772, 348, 0.9090908169746399, 0.8627451062202454, 348, 0.8333333730697632, 0.47058823704719543, 0.3058439791202545, 0.1604711264371872, 0.05760817229747772, 37.9150390625, 62.203125, 47.8125 -Cyan, 0, 1, 1, 180, 0.9999998807907104, 1, 180, 1, 0.5, 0.5380030274391174, 0.7873290181159973, 1.0694199800491333, 91.11328125, -48.09375, -14.125 -Dark Blue, 0, 0, 0.545098066329956, 240, 0.9999997615814209, 0.545098066329956, 240, 1, 0.272549033164978, 0.04658212512731552, 0.018632797524333, 0.24533233046531677, 3.533935546875, 24.4375, -38.578125 -Dark Cyan, 0, 0.545098066329956, 0.545098066329956, 179.99998474121094, 0.9999997615814209, 0.545098066329956, 180, 1, 0.272549033164978, 0.13890314102172852, 0.20327484607696533, 0.2761059105396271, 24.49951171875, -18.125, -5.34375 -Dark Goldenrod, 0.7215686440467834, 0.5254902243614197, 0.04313725605607033, 42.65895080566406, 0.9402172565460205, 0.7215686440467834, 42.658958435058594, 0.8871794939041138, 0.38235294818878174, 0.283547043800354, 0.2726714313030243, 0.04086246341466904, 32.8369140625, 23.109375, 43.09375 -Dark Gray, 0.6627451181411743, 0.6627451181411743, 0.6627451181411743, 0, 0, 0.6627451181411743, 0, 0, 0.6627451181411743, 0.37709841132164, 0.3967552185058594, 0.4319688677787781, 42.840576171875, 0.03125, 0.015625 -Dark Green, 0, 0.3921568691730499, 0, 120, 0.9999997019767761, 0.3921568691730499, 120, 1, 0.19607843458652496, 0.04556916654109955, 0.0911383330821991, 0.015189680270850658, 9.423828125, -18.984375, 13.71875 -Dark Khaki, 0.7411764860153198, 0.7176470756530762, 0.41960784792900085, 55.609737396240234, 0.4338623583316803, 0.7411764860153198, 55.60975646972656, 0.38317760825157166, 0.5803921818733215, 0.4057421386241913, 0.4574859142303467, 0.20598962903022766, 49.98779296875, -6.875, 44.703125 -Dark Magenta, 0.545098066329956, 0, 0.545098066329956, 300, 0.9999997615814209, 0.545098066329956, 300, 1, 0.272549033164978, 0.15307042002677917, 0.07354079931974411, 0.2503240406513214, 12.8662109375, 37.171875, -22.96875 -Dark Olive Green, 0.3333333432674408, 0.41960784792900085, 0.18431372940540314, 82.00001525878906, 0.560747504234314, 0.41960784792900085, 82, 0.3896103799343109, 0.3019607961177826, 0.09517066180706024, 0.12651889026165009, 0.046292148530483246, 12.87841796875, -12.5625, 15.859375 -Dark Orange, 1, 0.5490196347236633, 0, 32.9411735534668, 0.9999998807907104, 1, 32.94117736816406, 1, 0.5, 0.5062285661697388, 0.40022218227386475, 0.05059244483709335, 57.330322265625, 68.484375, 68.84375 -Dark Orchid, 0.6000000238418579, 0.19607843458652496, 0.800000011920929, 280.1298522949219, 0.7549018859863281, 0.800000011920929, 280.1298828125, 0.6062992215156555, 0.49803921580314636, 0.25173529982566833, 0.13413403928279877, 0.5837336778640747, 24.517822265625, 55.921875, -61.5 -Dark Red, 0.545098066329956, 0, 0, 0, 0.9999997615814209, 0.545098066329956, 0, 1, 0.272549033164978, 0.10648829489946365, 0.05490800365805626, 0.004991707392036915, 10.174560546875, 30.28125, 16.0625 -Dark Salmon, 0.9137254953384399, 0.5882353186607361, 0.47843137383461, 15.135133743286133, 0.47639480233192444, 0.9137254953384399, 15.135137557983398, 0.7161290049552917, 0.6960784196853638, 0.4802568256855011, 0.4054543673992157, 0.23703771829605103, 50.69580078125, 49.890625, 43.015625 -Dark Sea Green, 0.5607843399047852, 0.7372549176216125, 0.5607843399047852, 120, 0.23936164379119873, 0.7372549176216125, 120, 0.25139662623405457, 0.6490195989608765, 0.3426717221736908, 0.4378833770751953, 0.32625696063041687, 48.47412109375, -31.84375, 25.796875 -Dark Slate Blue, 0.2823529541492462, 0.239215686917305, 0.545098066329956, 248.46153259277344, 0.5611510276794434, 0.545098066329956, 248.4615478515625, 0.39000004529953003, 0.3921568691730499, 0.08999692648649216, 0.06578757613897324, 0.252147376537323, 6.9091796875, 21.65625, -33.015625 -Dark Slate Gray, 0.18431372940540314, 0.30980393290519714, 0.30980393290519714, 179.99993896484375, 0.4050631523132324, 0.30980393290519714, 180, 0.2539682388305664, 0.24705883860588074, 0.053789474070072174, 0.06760461628437042, 0.08416478335857391, 5.46875, -4.3125, -1.515625 -Dark Turquoise, 0, 0.8078431487083435, 0.8196078538894653, 180.8612518310547, 0.9999998807907104, 0.8196078538894653, 180.86123657226562, 1, 0.40980392694473267, 0.3357378840446472, 0.48741617798805237, 0.6794284582138062, 58.85009765625, -31.9375, -12.4375 -Dark Violet, 0.5803921818733215, 0, 0.8274509906768799, 282.0852966308594, 0.9999998807907104, 0.8274509906768799, 282.0852966308594, 1, 0.41372549533843994, 0.23967167735099792, 0.10999131202697754, 0.6247087717056274, 24.49951171875, 59.84375, -68.859375 -Deep Pink, 1, 0.0784313753247261, 0.5764706134796143, 327.574462890625, 0.9215685129165649, 1, 327.574462890625, 1, 0.5392156839370728, 0.46759656071662903, 0.23873063921928406, 0.29741615056991577, 53.814697265625, 81.453125, 34.9375 -Deep Sky Blue, 0, 0.7490196228027344, 1, 195.05882263183594, 0.9999998807907104, 1, 195.05882263183594, 1, 0.5, 0.36672061681747437, 0.4447641968727112, 1.0123260021209717, 55.938720703125, 16, -68.9375 -Dim Gray, 0.4117647111415863, 0.4117647111415863, 0.4117647111415863, 0, 0, 0.4117647111415863, 0, 0, 0.4117647111415863, 0.13426454365253448, 0.14126330614089966, 0.15380096435546875, 14.166259765625, 0.109375, 0.046875 -Dodger Blue, 0.11764705926179886, 0.5647059082984924, 1, 209.60000610351562, 0.8823528289794922, 1, 209.60000610351562, 1, 0.5588235259056091, 0.28550490736961365, 0.27438414096832275, 0.9837203025817871, 40.753173828125, 54.34375, -93.78125 -Firebrick, 0.6980392336845398, 0.13333334028720856, 0.13333334028720856, 0, 0.808988630771637, 0.6980392336845398, 0, 0.6792452931404114, 0.4156862795352936, 0.19223062694072723, 0.10727573186159134, 0.02571427822113037, 22.332763671875, 43.15625, 32.078125 -Floral White, 1, 0.9803921580314636, 0.9411764740943909, 39.99991989135742, 0.058823518455028534, 1, 40, 1, 0.970588207244873, 0.9115046262741089, 0.9592306017875671, 0.9612758755683899, 96.39892578125, 0.359375, 11.703125 -Forest Green, 0.13333334028720856, 0.545098066329956, 0.13333334028720856, 120, 0.7553955316543579, 0.545098066329956, 120, 0.6069364547729492, 0.3392156958580017, 0.10180484503507614, 0.18919843435287476, 0.046282973140478134, 23.333740234375, -31.421875, 29.3125 -Fuchsia, 1, 0, 1, 300, 0.9999998807907104, 1, 300, 1, 0.5, 0.5928760170936584, 0.2848399877548218, 0.9695610404014587, 60.321044921875, 98.234375, -60.828125 -Gainsboro, 0.8627451062202454, 0.8627451062202454, 0.8627451062202454, 0, 0, 0.8627451062202454, 0, 0, 0.8627451062202454, 0.6802351474761963, 0.715693473815918, 0.7792141437530518, 74.15771484375, 0.015625, 0 -Ghost White, 0.9725490212440491, 0.9725490212440491, 1, 240, 0.02745097503066063, 1, 240, 1, 0.9862744808197021, 0.9032419919967651, 0.9431107044219971, 1.0802602767944336, 94.88525390625, 2.90625, -7.65625 -Gold, 1, 0.843137264251709, 0, 50.58823013305664, 0.9999998807907104, 1, 50.588233947753906, 1, 0.5, 0.6554437875747681, 0.6986526250839233, 0.10033071041107178, 76.7333984375, 19.84375, 80.140625 -Goldenrod, 0.8549019694328308, 0.6470588445663452, 0.125490203499794, 42.903221130371094, 0.8532109260559082, 0.8549019694328308, 42.903228759765625, 0.7440000176429749, 0.4901960790157318, 0.426321417093277, 0.4192340672016144, 0.07212784886360168, 49.267578125, 28.4375, 56.875 -Gray, 0.7450980544090271, 0.7450980544090271, 0.7450980544090271, 0, 0, 0.7450980544090271, 0, 0, 0.7450980544090271, 0.4894065856933594, 0.5149176716804504, 0.5606186985969543, 54.7607421875, 0.03125, 0.015625 -Web Gray, 0.501960813999176, 0.501960813999176, 0.501960813999176, 0, 0, 0.501960813999176, 0, 0, 0.501960813999176, 0.20516590774059296, 0.2158605009317398, 0.23501898348331451, 22.979736328125, 0.03125, 0.015625 -Green, 0, 1, 0, 120, 0.9999998807907104, 1, 120, 1, 0.5, 0.3575800061225891, 0.7151600122451782, 0.11919300258159637, 87.738037109375, -86.1875, 83.171875 -Web Green, 0, 0.501960813999176, 0, 120, 0.9999997615814209, 0.501960813999176, 120, 1, 0.250980406999588, 0.07718739658594131, 0.15437479317188263, 0.025729062035679817, 18.84765625, -28.9375, 26.015625 -Green Yellow, 0.6784313917160034, 1, 0.18431372940540314, 83.65385437011719, 0.8156861662864685, 1, 83.65383911132812, 1, 0.5921568870544434, 0.5350667238235474, 0.8060835003852844, 0.1542835831642151, 89.208984375, -73.375, 84.453125 -Honeydew, 0.9411764740943909, 1, 0.9411764740943909, 120, 0.058823518455028534, 1, 120, 1, 0.970588207244873, 0.8741925954818726, 0.9633602499961853, 0.9640365839004517, 96.966552734375, -16.5625, 12.171875 -Hot Pink, 1, 0.4117647111415863, 0.7058823704719543, 330, 0.5882351994514465, 1, 330, 1, 0.7058823704719543, 0.5453130006790161, 0.3466355800628662, 0.46986567974090576, 55.8837890625, 79.96875, 12.65625 -Indian Red, 0.8039215803146362, 0.3607843220233917, 0.3607843220233917, 0, 0.5512194037437439, 0.8039215803146362, 0, 0.5305164456367493, 0.5823529362678528, 0.3093794584274292, 0.21409708261489868, 0.12625597417354584, 33.72802734375, 50.890625, 34.703125 -Indigo, 0.29411765933036804, 0, 0.5098039507865906, 274.6153869628906, 0.9999997615814209, 0.5098039507865906, 274.6153869628906, 1, 0.2549019753932953, 0.06929568946361542, 0.031073689460754395, 0.21347758173942566, 3.8330078125, 23.5, -31.5625 -Ivory, 1, 1, 0.9411764740943909, 59.9998779296875, 0.058823518455028534, 1, 60, 1, 0.970588207244873, 0.9272476434707642, 0.9907166957855225, 0.9665235280990601, 99.249267578125, -5.390625, 15.75 -Khaki, 0.9411764740943909, 0.9019607901573181, 0.5490196347236633, 53.99998474121094, 0.41666659712791443, 0.9411764740943909, 54.000003814697266, 0.7692306637763977, 0.7450980544090271, 0.6896663904190063, 0.7701455354690552, 0.36036184430122375, 80.77392578125, -7.578125, 66.546875 -Lavender, 0.9019607901573181, 0.9019607901573181, 0.9803921580314636, 240, 0.07999998331069946, 0.9803921580314636, 240, 0.6666669845581055, 0.9411764740943909, 0.7818050980567932, 0.8031823635101318, 1.0180078744888306, 82.452392578125, 8.34375, -20.53125 -Lavender Blush, 1, 0.9411764740943909, 0.9607843160629272, 340.0000305175781, 0.058823518455028534, 1, 340, 1, 0.970588207244873, 0.8887804746627808, 0.9017353057861328, 0.9908458590507507, 91.4306640625, 13.015625, -0.921875 -Lawn Green, 0.48627451062202454, 0.9882352948188782, 0, 90.4761962890625, 0.9999998807907104, 0.9882352948188782, 90.47618865966797, 1, 0.4941176474094391, 0.4312170445919037, 0.7390342950820923, 0.11992476135492325, 85.9375, -81.125, 81.890625 -Lemon Chiffon, 1, 0.9803921580314636, 0.8039215803146362, 53.999969482421875, 0.19607838988304138, 1, 54.000003814697266, 1, 0.9019607901573181, 0.864437460899353, 0.9404037594795227, 0.7133886814117432, 95.196533203125, -8.34375, 43.84375 -Light Blue, 0.6784313917160034, 0.8470588326454163, 0.9019607901573181, 194.7368621826172, 0.24782603979110718, 0.9019607901573181, 194.73684692382812, 0.5327103734016418, 0.7901960611343384, 0.5606712102890015, 0.637069046497345, 0.8418401479721069, 67.95654296875, -14.46875, -20.40625 -Light Coral, 0.9411764740943909, 0.501960813999176, 0.501960813999176, 0, 0.466666579246521, 0.9411764740943909, 0, 0.7887322902679443, 0.7215686440467834, 0.47553157806396484, 0.3552677631378174, 0.2476925551891327, 50.152587890625, 63.46875, 40.25 -Light Cyan, 0.8784313797950745, 1, 1, 179.99993896484375, 0.12156860530376434, 1, 180, 1, 0.9392156600952148, 0.8454471826553345, 0.9458548426628113, 1.0838316679000854, 95.855712890625, -20.125, -6.578125 -Light Goldenrod, 0.9803921580314636, 0.9803921580314636, 0.8235294222831726, 59.99995422363281, 0.15999996662139893, 0.9803921580314636, 60, 0.8000002503395081, 0.9019607901573181, 0.8524099588394165, 0.9334931969642639, 0.7448301315307617, 94.427490234375, -11.75, 38.5 -Light Gray, 0.8274509906768799, 0.8274509906768799, 0.8274509906768799, 0, 0, 0.8274509906768799, 0, 0, 0.8274509906768799, 0.619132399559021, 0.6514056324958801, 0.7092204689979553, 68.06640625, 0.015625, 0 -Light Green, 0.5647059082984924, 0.9333333373069763, 0.5647059082984924, 120, 0.39495790004730225, 0.9333333373069763, 120, 0.7343750596046448, 0.7490196228027344, 0.47107797861099243, 0.69089674949646, 0.3723141551017761, 77.1240234375, -65.984375, 58.671875 -Light Pink, 1, 0.7137255072593689, 0.7568627595901489, 350.95892333984375, 0.2862744629383087, 1, 350.9588928222656, 1, 0.8568627834320068, 0.6759384274482727, 0.5856972932815552, 0.5818241834640503, 66.705322265625, 53.203125, 16.15625 -Light Salmon, 1, 0.6274510025978088, 0.47843137383461, 17.142854690551758, 0.5215685963630676, 1, 17.142858505249023, 1, 0.7392156720161438, 0.5732675194740295, 0.4781184494495392, 0.24616536498069763, 60.565185546875, 60.953125, 55.46875 -Light Sea Green, 0.125490203499794, 0.6980392336845398, 0.6666666865348816, 176.71231079101562, 0.8202245831489563, 0.6980392336845398, 176.7123260498047, 0.6952381134033203, 0.4117647111415863, 0.23767849802970886, 0.35047221183776855, 0.43531426787376404, 42.46826171875, -29.65625, -1.625 -Light Sky Blue, 0.529411792755127, 0.8078431487083435, 0.9803921580314636, 202.95652770996094, 0.4599999189376831, 0.9803921580314636, 202.95651245117188, 0.9200001358985901, 0.7549020051956177, 0.49310988187789917, 0.5619192719459534, 0.9866426587104797, 63.153076171875, 0.625, -51.046875 -Light Slate Gray, 0.46666666865348816, 0.5333333611488342, 0.6000000238418579, 210.00001525878906, 0.2222222089767456, 0.6000000238418579, 210, 0.14285717904567719, 0.5333333611488342, 0.2215970903635025, 0.23829501867294312, 0.33560386300086975, 25.946044921875, -0.921875, -13.140625 -Light Steel Blue, 0.6901960968971252, 0.7686274647712708, 0.8705882430076599, 213.91305541992188, 0.20720715820789337, 0.8705882430076599, 213.9130401611328, 0.410714328289032, 0.7803921699523926, 0.5082481503486633, 0.5398250222206116, 0.7682933807373047, 57.84912109375, 1.390625, -27.25 -Light Yellow, 1, 1, 0.8784313797950745, 59.999942779541016, 0.12156860530376434, 1, 60, 1, 0.9392156600952148, 0.9045210480690002, 0.9816260933876038, 0.8468302488327026, 98.626708984375, -9.96875, 31.296875 -Lime, 0, 1, 0, 120, 0.9999998807907104, 1, 120, 1, 0.5, 0.3575800061225891, 0.7151600122451782, 0.11919300258159637, 87.738037109375, -86.1875, 83.171875 -Lime Green, 0.19607843458652496, 0.8039215803146362, 0.19607843458652496, 120, 0.7560974955558777, 0.8039215803146362, 120, 0.6078431606292725, 0.5, 0.2372114062309265, 0.44568729400634766, 0.1036919504404068, 55.72509765625, -58.8125, 56.28125 -Linen, 0.9803921580314636, 0.9411764740943909, 0.9019607901573181, 29.999954223632812, 0.07999998331069946, 0.9803921580314636, 30, 0.6666669845581055, 0.9411764740943909, 0.8486459255218506, 0.8835818767547607, 0.8742563128471375, 89.73388671875, 4.125, 12.6875 -Magenta, 1, 0, 1, 300, 0.9999998807907104, 1, 300, 1, 0.5, 0.5928760170936584, 0.2848399877548218, 0.9695610404014587, 60.321044921875, 98.234375, -60.828125 -Maroon, 0.6901960968971252, 0.1882352977991104, 0.3764705955982208, 337.5, 0.727272629737854, 0.6901960968971252, 337.5, 0.5714285969734192, 0.43921568989753723, 0.2107411026954651, 0.12191140651702881, 0.12306557595729828, 22.320556640625, 42.5, 17.859375 -Web Maroon, 0.501960813999176, 0, 0, 0, 0.9999997615814209, 0.501960813999176, 0, 1, 0.250980406999588, 0.08903230726718903, 0.045907266438007355, 0.004173446912318468, 7.2998046875, 26.734375, 11.546875 -Medium Aquamarine, 0.4000000059604645, 0.8039215803146362, 0.6666666865348816, 159.61163330078125, 0.5024389624595642, 0.8039215803146362, 159.6116485595703, 0.5073891878128052, 0.6019607782363892, 0.3456289768218994, 0.4938696026802063, 0.45730581879615784, 56.951904296875, -45.140625, 18.890625 -Medium Blue, 0, 0, 0.8039215803146362, 240, 0.9999998807907104, 0.8039215803146362, 240, 1, 0.4019607901573181, 0.11014744639396667, 0.04405885189771652, 0.5801093578338623, 17.3583984375, 54.703125, -74.5 -Medium Orchid, 0.729411780834198, 0.3333333432674408, 0.8274509906768799, 288.09521484375, 0.597156286239624, 0.8274509906768799, 288.0952453613281, 0.5887851119041443, 0.5803921818733215, 0.35253477096557617, 0.2164035439491272, 0.6393042802810669, 33.404541015625, 61.03125, -53.953125 -Medium Purple, 0.5764706134796143, 0.43921568989753723, 0.8588235378265381, 259.62615966796875, 0.48858439922332764, 0.8588235378265381, 259.62615966796875, 0.597765326499939, 0.6490195989608765, 0.3060874342918396, 0.22905084490776062, 0.6980715990066528, 30.28564453125, 50.640625, -67.96875 -Medium Sea Green, 0.23529411852359772, 0.7019608020782471, 0.4431372582912445, 146.72268676757812, 0.6648043990135193, 0.7019608020782471, 146.72268676757812, 0.49790799617767334, 0.4686274528503418, 0.2096228152513504, 0.34391117095947266, 0.21151721477508545, 41.845703125, -43.359375, 32.015625 -Medium Slate Blue, 0.48235294222831726, 0.40784314274787903, 0.9333333373069763, 248.50746154785156, 0.5630251169204712, 0.9333333373069763, 248.50746154785156, 0.7976190447807312, 0.6705882549285889, 0.28545498847961426, 0.2028283178806305, 0.8327666521072388, 31.658935546875, 62.015625, -87.640625 -Medium Spring Green, 0, 0.9803921580314636, 0.6039215922355652, 156.95999145507812, 0.9999998807907104, 0.9803921580314636, 156.9600067138672, 1, 0.4901960790157318, 0.4001394212245941, 0.7069948315620422, 0.4210047423839569, 84.503173828125, -79.15625, 61.90625 -Medium Turquoise, 0.2823529541492462, 0.8196078538894653, 0.800000011920929, 177.81019592285156, 0.6555023193359375, 0.8196078538894653, 177.81021118164062, 0.5982533097267151, 0.5509803891181946, 0.36366453766822815, 0.5133431553840637, 0.6510230898857117, 60.260009765625, -36.171875, -5.53125 -Medium Violet Red, 0.7803921699523926, 0.08235294371843338, 0.5215686559677124, 322.2471923828125, 0.8944722414016724, 0.7803921699523926, 322.2471923828125, 0.8090909123420715, 0.4313725531101227, 0.28056198358535767, 0.14375197887420654, 0.23481225967407227, 30.4443359375, 54.640625, 9.328125 -Midnight Blue, 0.09803921729326248, 0.09803921729326248, 0.43921568989753723, 240, 0.7767854928970337, 0.43921568989753723, 240, 0.6350364685058594, 0.26862746477127075, 0.036719486117362976, 0.020713143050670624, 0.15531133115291595, 1.947021484375, 9.859375, -22.6875 -Mint Cream, 0.9607843160629272, 1, 0.9803921580314636, 149.99990844726562, 0.039215680211782455, 1, 150, 1, 0.9803921580314636, 0.9066698551177979, 0.978341281414032, 1.0452386140823364, 98.150634765625, -9.359375, 2.984375 -Misty Rose, 1, 0.8941176533699036, 0.8823529481887817, 5.999993801116943, 0.11764703691005707, 1, 6, 1, 0.9411764740943909, 0.8257196545600891, 0.821847140789032, 0.8272725939750671, 84.649658203125, 19.546875, 10.859375 -Moccasin, 1, 0.8941176533699036, 0.7098039388656616, 38.10809326171875, 0.290196031332016, 1, 38.10810852050781, 1, 0.8549019694328308, 0.773240864276886, 0.8008556365966797, 0.5508846044540405, 83.349609375, 11.421875, 47.65625 -Navajo White, 1, 0.8705882430076599, 0.6784313917160034, 35.85364532470703, 0.3215685784816742, 1, 35.85365676879883, 1, 0.8392156958580017, 0.7490472197532654, 0.7652256488800049, 0.5034854412078857, 80.487060546875, 16.515625, 49.828125 -Navy Blue, 0, 0, 0.501960813999176, 240, 0.9999997615814209, 0.501960813999176, 240, 1, 0.250980406999588, 0.03894620016217232, 0.015578435733914375, 0.20511648058891296, 2.47802734375, 17.421875, -32.34375 -Old Lace, 0.9921568632125854, 0.9607843160629272, 0.9019607901573181, 39.1303825378418, 0.09090907126665115, 0.9921568632125854, 39.13043212890625, 0.8518518805503845, 0.9470587968826294, 0.8744063377380371, 0.9190149903297424, 0.879738450050354, 92.852783203125, 1.09375, 17.171875 -Olive, 0.501960813999176, 0.501960813999176, 0, 59.99998474121094, 0.9999997615814209, 0.501960813999176, 60, 1, 0.250980406999588, 0.16621971130371094, 0.20028206706047058, 0.029902508482336998, 22.015380859375, -7.21875, 30.421875 -Olive Drab, 0.41960784792900085, 0.5568627715110779, 0.13725490868091583, 79.62619018554688, 0.7535209655761719, 0.5568627715110779, 79.62617492675781, 0.604519784450531, 0.34705883264541626, 0.16039887070655823, 0.2259306162595749, 0.05105489119887352, 25.665283203125, -23.078125, 31.984375 -Orange, 1, 0.6470588445663452, 0, 38.823524475097656, 0.9999998807907104, 1, 38.82353210449219, 1, 0.5, 0.5469968318939209, 0.48175865411758423, 0.06418181210756302, 61.279296875, 57.75, 70.78125 -Orange Red, 1, 0.2705882489681244, 0, 16.235292434692383, 0.9999998807907104, 1, 16.235294342041016, 1, 0.5, 0.4337330162525177, 0.2552310526371002, 0.02642732299864292, 53.61328125, 79.015625, 67.328125 -Orchid, 0.8549019694328308, 0.43921568989753723, 0.8392156958580017, 302.26416015625, 0.4862384796142578, 0.8549019694328308, 302.26416015625, 0.588888943195343, 0.6470588445663452, 0.46843424439430237, 0.313510537147522, 0.6718415021896362, 44.287109375, 67.21875, -39.28125 -Pale Goldenrod, 0.9333333373069763, 0.9098039269447327, 0.6666666865348816, 54.70585632324219, 0.2857142388820648, 0.9333333373069763, 54.70588302612305, 0.6666667461395264, 0.800000011920929, 0.7137202620506287, 0.7879424691200256, 0.4946836233139038, 81.73828125, -9.171875, 52.34375 -Pale Green, 0.5960784554481506, 0.9843137264251709, 0.5960784554481506, 120, 0.3944222331047058, 0.9843137264251709, 120, 0.9252336621284485, 0.7901960611343384, 0.5311088562011719, 0.7793415784835815, 0.41941505670547485, 85.955810546875, -72.625, 64.703125 -Pale Turquoise, 0.686274528503418, 0.9333333373069763, 0.9333333373069763, 179.99996948242188, 0.2647058367729187, 0.9333333373069763, 180, 0.6494846343994141, 0.8098039627075195, 0.6368032693862915, 0.7643305063247681, 0.9226345419883728, 80.8837890625, -31.0625, -9.59375 -Pale Violet Red, 0.8588235378265381, 0.43921568989753723, 0.5764706134796143, 340.37384033203125, 0.48858439922332764, 0.8588235378265381, 340.37384033203125, 0.597765326499939, 0.6490195989608765, 0.4027522802352905, 0.28758469223976135, 0.31025686860084534, 40.93017578125, 56.546875, 15.46875 -Papaya Whip, 1, 0.9372549057006836, 0.8352941274642944, 37.14282989501953, 0.16470585763454437, 1, 37.14285659790039, 1, 0.9176470637321472, 0.8411519527435303, 0.8779867887496948, 0.7544852495193481, 89.459228515625, 5.296875, 29.296875 -Peach Puff, 1, 0.8549019694328308, 0.7254902124404907, 28.285701751708984, 0.2745097577571869, 1, 28.285715103149414, 1, 0.8627451062202454, 0.7506852746009827, 0.7490838170051575, 0.5639030337333679, 78.857421875, 22.0625, 39.046875 -Peru, 0.8039215803146362, 0.5215686559677124, 0.24705882370471954, 29.57746124267578, 0.6926828622817993, 0.8039215803146362, 29.577468872070312, 0.5867769122123718, 0.5254902243614197, 0.34463950991630554, 0.3011631667613983, 0.08699263632297516, 38.299560546875, 37.796875, 45.1875 -Pink, 1, 0.7529411911964417, 0.7960784435272217, 349.5238037109375, 0.24705877900123596, 1, 349.5238037109375, 1, 0.8764705657958984, 0.7086877226829529, 0.6327421069145203, 0.6496396660804749, 69.879150390625, 47.5625, 11.625 -Plum, 0.8666666746139526, 0.6274510025978088, 0.8666666746139526, 300.0000305175781, 0.27601805329322815, 0.8666666746139526, 300, 0.4728682041168213, 0.7470588684082031, 0.5543830394744873, 0.4573570787906647, 0.7429462671279907, 52.9052734375, 51.359375, -33.234375 -Powder Blue, 0.6901960968971252, 0.8784313797950745, 0.9019607901573181, 186.6667022705078, 0.23478256165981293, 0.9019607901573181, 186.6666717529297, 0.519230842590332, 0.7960784435272217, 0.5883779525756836, 0.6825222969055176, 0.8491535186767578, 72.44873046875, -21.171875, -13.609375 -Purple, 0.6274510025978088, 0.125490203499794, 0.9411764740943909, 276.9230651855469, 0.8666665554046631, 0.9411764740943909, 276.923095703125, 0.8739495277404785, 0.5333333611488342, 0.3073701858520508, 0.1479761302471161, 0.8365147113800049, 32.94677734375, 73.046875, -87.984375 -Web Purple, 0.501960813999176, 0, 0.501960813999176, 300, 0.9999997615814209, 0.501960813999176, 300, 1, 0.250980406999588, 0.12797850370407104, 0.06148570030927658, 0.20928992331027985, 9.649658203125, 33.015625, -20.421875 -Rebecca Purple, 0.4000000059604645, 0.20000000298023224, 0.6000000238418579, 270, 0.666666567325592, 0.6000000238418579, 270, 0.5000000596046448, 0.4000000059604645, 0.12411271035671234, 0.07492164522409439, 0.309206485748291, 9.87548828125, 31.78125, -38.859375 -Red, 1, 0, 0, 0, 0.9999998807907104, 1, 0, 1, 0.5, 0.4124529957771301, 0.21267099678516388, 0.01933399960398674, 53.240966796875, 80.09375, 67.203125 -Rosy Brown, 0.7372549176216125, 0.5607843399047852, 0.5607843399047852, 0, 0.23936164379119873, 0.7372549176216125, 0, 0.25139662623405457, 0.6490195989608765, 0.35519424080848694, 0.32321077585220337, 0.3034681975841522, 36.712646484375, 24.875, 11.0625 -Royal Blue, 0.2549019753932953, 0.4117647111415863, 0.8823529481887817, 225, 0.7111109495162964, 0.8823529481887817, 225, 0.7272727489471436, 0.5686274766921997, 0.2081635594367981, 0.1666068732738495, 0.7333256006240845, 27.1484375, 52.328125, -80.0625 -Saddle Brown, 0.545098066329956, 0.2705882489681244, 0.07450980693101883, 24.9999942779541, 0.8633092045783997, 0.545098066329956, 25, 0.7594937086105347, 0.30980393290519714, 0.12894324958324432, 0.09793803095817566, 0.018272994086146355, 12.58544921875, 23.65625, 18.765625 -Salmon, 0.9803921580314636, 0.501960813999176, 0.4470588266849518, 6.1764726638793945, 0.5439999103546143, 0.9803921580314636, 6.176474094390869, 0.931506872177124, 0.7137255072593689, 0.501841127872467, 0.36982640624046326, 0.20410597324371338, 54.083251953125, 69.3125, 51.78125 -Sandy Brown, 0.95686274766922, 0.6431372761726379, 0.3764705955982208, 27.56756591796875, 0.6065572500228882, 0.95686274766922, 27.56757164001465, 0.8705880641937256, 0.6666666865348816, 0.5269815921783447, 0.46633121371269226, 0.17288833856582642, 57.208251953125, 49.90625, 59.21875 -Sea Green, 0.18039216101169586, 0.545098066329956, 0.34117648005485535, 146.45159912109375, 0.6690646409988403, 0.545098066329956, 146.4516143798828, 0.5027027726173401, 0.36274510622024536, 0.12078526616096497, 0.19733065366744995, 0.12186554074287415, 23.590087890625, -28.5, 20.1875 -Seashell, 1, 0.9607843160629272, 0.9333333373069763, 24.705839157104492, 0.06666665524244308, 1, 24.705883026123047, 1, 0.9666666984558105, 0.8932191729545593, 0.9273865818977356, 0.9406061172485352, 93.603515625, 5.25, 10.03125 -Sienna, 0.6274510025978088, 0.32156863808631897, 0.1764705926179886, 19.304340362548828, 0.7187498807907104, 0.6274510025978088, 19.30434799194336, 0.5609756708145142, 0.4019607901573181, 0.17989644408226013, 0.13699708878993988, 0.04178870469331741, 18.93310546875, 30.296875, 25.828125 -Silver, 0.7529411911964417, 0.7529411911964417, 0.7529411911964417, 0, 0, 0.7529411911964417, 0, 0, 0.7529411911964417, 0.5009996891021729, 0.5271151065826416, 0.5738986730575562, 55.926513671875, 0.015625, 0 -Sky Blue, 0.529411792755127, 0.8078431487083435, 0.9215686321258545, 197.40000915527344, 0.4255318343639374, 0.9215686321258545, 197.39999389648438, 0.7142857313156128, 0.7254902124404907, 0.4705203175544739, 0.5528834462165833, 0.8676709532737732, 61.65771484375, -10.375, -35.78125 -Slate Blue, 0.4156862795352936, 0.3529411852359772, 0.8039215803146362, 248.3478240966797, 0.5609755516052246, 0.8039215803146362, 248.3478240966797, 0.5348837375640869, 0.5784313678741455, 0.2061532735824585, 0.14783000946044922, 0.5950824618339539, 21.966552734375, 46.59375, -66.78125 -Slate Gray, 0.43921568989753723, 0.501960813999176, 0.5647059082984924, 210.00003051757812, 0.2222222089767456, 0.5647059082984924, 210, 0.12598426640033722, 0.501960813999176, 0.19433583319187164, 0.20896126329898834, 0.2938746213912964, 22.42431640625, -0.921875, -11.65625 -Snow, 1, 0.9803921580314636, 0.9803921580314636, 0, 0.019607840105891228, 1, 0, 1, 0.9901961088180542, 0.9267695546150208, 0.9653365612030029, 1.0416710376739502, 96.8994140625, 3.84375, 1.375 -Spring Green, 0, 1, 0.49803921580314636, 149.88235473632812, 0.9999998807907104, 1, 149.88235473632812, 1, 0.5, 0.3958713114261627, 0.7304764986038208, 0.3208603858947754, 87.8662109375, -84.515625, 74.84375 -Steel Blue, 0.27450981736183167, 0.5098039507865906, 0.7058823704719543, 207.27273559570312, 0.6111109852790833, 0.7058823704719543, 207.27272033691406, 0.4399999678134918, 0.4901961088180542, 0.1874300241470337, 0.20560768246650696, 0.46148544549942017, 24.560546875, 10.1875, -37.984375 -Tan, 0.8235294222831726, 0.7058823704719543, 0.5490196347236633, 34.285701751708984, 0.33333325386047363, 0.8235294222831726, 34.28571319580078, 0.4375000298023224, 0.686274528503418, 0.47633710503578186, 0.4823954105377197, 0.31605905294418335, 52.72216796875, 13.3125, 34.375 -Teal, 0, 0.501960813999176, 0.501960813999176, 179.99998474121094, 0.9999997615814209, 0.501960813999176, 180, 1, 0.250980406999588, 0.11613360047340393, 0.16995322704315186, 0.23084554076194763, 19.989013671875, -16.125, -4.75 -Thistle, 0.8470588326454163, 0.7490196228027344, 0.8470588326454163, 300.00006103515625, 0.11574071645736694, 0.8470588326454163, 300, 0.2427184134721756, 0.7980391979217529, 0.5934168100357056, 0.5681906342506409, 0.7278823256492615, 60.626220703125, 23.546875, -16.015625 -Tomato, 1, 0.38823530077934265, 0.27843138575553894, 9.130434036254883, 0.7215685248374939, 1, 9.130434036254883, 1, 0.6392157077789307, 0.4684373736381531, 0.3064501881599426, 0.09407974779605865, 54.351806640625, 77.125, 63.765625 -Turquoise, 0.250980406999588, 0.8784313797950745, 0.8156862854957581, 173.99998474121094, 0.7142855525016785, 0.8784313797950745, 174, 0.720720648765564, 0.5647059082984924, 0.4014909863471985, 0.5895079374313354, 0.6892006993293762, 69.00634765625, -46.453125, 3.671875 -Violet, 0.9333333373069763, 0.5098039507865906, 0.9333333373069763, 300, 0.4537814259529114, 0.9333333373069763, 300, 0.7605634331703186, 0.7215686440467834, 0.5867264270782471, 0.403179794549942, 0.8555747270584106, 54.876708984375, 77.96875, -48.9375 -Wheat, 0.9607843160629272, 0.8705882430076599, 0.7019608020782471, 39.09088897705078, 0.26938769221305847, 0.9607843160629272, 39.09090805053711, 0.7674418687820435, 0.8313725590705872, 0.7191405892372131, 0.7491186857223511, 0.5330684781074524, 78.302001953125, 8.078125, 42.4375 -White, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0.9504560232162476, 1, 1.0887540578842163, 100, 0, 0 -White Smoke, 0.9607843160629272, 0.9607843160629272, 0.9607843160629272, 0, 0, 0.9607843160629272, 0, 0, 0.9607843160629272, 0.8678600788116455, 0.9130986332893372, 0.9941397905349731, 92.205810546875, 0.015625, 0 -Yellow, 1, 1, 0, 59.99999237060547, 0.9999998807907104, 1, 60, 1, 0.5, 0.7700330018997192, 0.9278309941291809, 0.1385270059108734, 97.137451171875, -21.546875, 94.46875 -Yellow Green, 0.6039215922355652, 0.8039215803146362, 0.19607843458652496, 79.741943359375, 0.7560974955558777, 0.8039215803146362, 79.74193572998047, 0.6078431606292725, 0.5, 0.35733717679977417, 0.5076271295547485, 0.1093229204416275, 57.45849609375, -44.4375, 58.421875 +Name, RGB - R, RGB - G, RGB - B, HSB - H, HSB - S, HSB - B, HSL - H, HSL - S, HSL - L, XYZ - X, XYZ - Y, XYZ - Z, LAB - L, LAB - A, LAB - B, LUV - L, LUV - U, LUV - V +Alice Blue, 0.9411764740943909, 0.9725490212440491, 1, 208.00006103515625, 0.058823518455028534, 1, 208, 1, 0.970588207244873, 0.8754762411117554, 0.9287939667701721, 1.0789587497711182, 97.027587890625, -1.25, -4.484375, 97.17863464355469, -4.758118152618408, -6.4231486320495605 +Antique White, 0.9803921580314636, 0.9215686321258545, 0.843137264251709, 34.28568649291992, 0.13999997079372406, 0.9803921580314636, 34.28571319580078, 0.7777780294418335, 0.9117647409439087, 0.8139658570289612, 0.8464831709861755, 0.7632243633270264, 93.585205078125, 1.84375, 11.484375, 93.73136138916016, 10.011678695678711, 16.821168899536133 +Aqua, 0, 1, 1, 180, 0.9999998807907104, 1, 180, 1, 0.5, 0.5380030274391174, 0.7873290181159973, 1.0694199800491333, 91.11328125, -48.09375, -14.125, 91.11328887939453, -70.4778823852539, -15.202608108520508 +Aquamarine, 0.49803921580314636, 1, 0.8313725590705872, 159.84375, 0.5019606947898865, 1, 159.84375, 1, 0.7490196228027344, 0.5639011859893799, 0.807809591293335, 0.7489018440246582, 92.02880859375, -45.59375, 9.875, 92.03413391113281, -55.9193229675293, 22.36087417602539 +Azure, 0.9411764740943909, 1, 1, 179.9998779296875, 0.058823518455028534, 1, 180, 1, 0.970588207244873, 0.897400975227356, 0.9726435542106628, 1.0862669944763184, 98.907470703125, -5.03125, -1.734375, 98.93241882324219, -8.16883373260498, -1.7620676755905151 +Beige, 0.9607843160629272, 0.9607843160629272, 0.8627451062202454, 59.99992370605469, 0.10204079747200012, 0.9607843160629272, 60.000003814697266, 0.5555555820465088, 0.9117647409439087, 0.8322436213493347, 0.8988521099090576, 0.8065600991249084, 95.794677734375, -4.171875, 12.015625, 95.94913482666016, 1.3557952642440796, 18.790529251098633 +Bisque, 1, 0.8941176533699036, 0.7686274647712708, 32.5423583984375, 0.23137250542640686, 1, 32.54237365722656, 1, 0.884313702583313, 0.7894670963287354, 0.8073461651802063, 0.6363427639007568, 91.912841796875, 4.609375, 19.015625, 92.01346588134766, 18.426982879638672, 26.702899932861328 +Black, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -0, -0 +Blanched Almond, 1, 0.9215686321258545, 0.8039215803146362, 35.99998092651367, 0.19607838988304138, 1, 36, 1, 0.9019607901573181, 0.8196672201156616, 0.8508632779121399, 0.6984653472900391, 93.81103515625, 2.3125, 17.03125, 93.92029571533203, 13.779644012451172, 24.53878402709961 +Blue, 0, 0, 1, 240, 0.9999998807907104, 1, 240, 1, 0.5, 0.18042300641536713, 0.0721689984202385, 0.9502270221710205, 32.293701171875, 79.1875, -107.859375, 32.29566955566406, -9.404743194580078, -130.33950805664062 +Blue Violet, 0.5411764979362488, 0.16862745583057404, 0.886274516582489, 271.1475524902344, 0.8097344040870667, 0.886274516582489, 271.1475524902344, 0.7593361735343933, 0.5274509787559509, 0.25068020820617676, 0.12621363997459412, 0.7304641008377075, 42.156982421875, 69.765625, -74.59375, 42.187103271484375, 18.34722328186035, -113.15458679199219 +Brown, 0.6470588445663452, 0.16470588743686676, 0.16470588743686676, 0, 0.7454544305801392, 0.6470588445663452, 0, 0.5942029356956482, 0.4058823585510254, 0.16764701902866364, 0.09824936091899872, 0.032035328447818756, 37.493896484375, 49.65625, 30.578125, 37.52638244628906, 91.76915740966797, 19.795351028442383 +Burlywood, 0.8705882430076599, 0.7215686440467834, 0.529411792755127, 33.7930908203125, 0.39189180731773376, 0.8705882430076599, 33.7931022644043, 0.5686275362968445, 0.7000000476837158, 0.5163891315460205, 0.5156236290931702, 0.3014764189720154, 76.9287109375, 7.03125, 29.90625, 77.0184097290039, 27.81132698059082, 38.589744567871094 +Cadet Blue, 0.37254902720451355, 0.6196078658103943, 0.6274510025978088, 181.84617614746094, 0.4062499403953552, 0.6274510025978088, 181.84616088867188, 0.2549019753932953, 0.5, 0.23288553953170776, 0.2942303717136383, 0.377002090215683, 61.09619140625, -19.734375, -7.421875, 61.15318298339844, -29.095840454101562, -7.940064907073975 +Chartreuse, 0.49803921580314636, 1, 0, 90.1176528930664, 0.9999998807907104, 1, 90.11764526367188, 1, 0.5, 0.44511520862579346, 0.7602953314781189, 0.12329627573490143, 89.874267578125, -68.0625, 85.78125, 89.87305450439453, -60.90852737426758, 107.06683349609375 +Chocolate, 0.8235294222831726, 0.4117647111415863, 0.11764705926179886, 24.999996185302734, 0.8571427464485168, 0.8235294222831726, 25.000001907348633, 0.75, 0.47058823704719543, 0.31867295503616333, 0.23902495205402374, 0.04163479432463646, 55.865478515625, 37.125, 56.609375, 55.99004364013672, 86.28438568115234, 47.74782180786133 +Coral, 1, 0.49803921580314636, 0.3137255012989044, 16.114280700683594, 0.6862744688987732, 1, 16.11428451538086, 1, 0.656862735748291, 0.5028159618377686, 0.3702393174171448, 0.12085746228694916, 67.29736328125, 45.328125, 47.640625, 67.29497528076172, 101.03590393066406, 44.4131965637207 +Cornflower Blue, 0.3921568691730499, 0.5843137502670288, 0.929411768913269, 218.54014587402344, 0.5780590176582336, 0.929411768913269, 218.54014587402344, 0.791907548904419, 0.6607843041419983, 0.31282591819763184, 0.3031572103500366, 0.8430083990097046, 61.871337890625, 9.1875, -49.15625, 61.925682067871094, -22.94134521484375, -79.77806091308594 +Cornsilk, 1, 0.9725490212440491, 0.8627451062202454, 47.99995803833008, 0.13725487887859344, 1, 48, 1, 0.9313725233078003, 0.8772358298301697, 0.9356323480606079, 0.8112900257110596, 97.332763671875, -2.03125, 14.296875, 97.45571899414062, 5.646143436431885, 21.694337844848633 +Crimson, 0.8627451062202454, 0.0784313753247261, 0.23529411852359772, 348, 0.9090908169746399, 0.8627451062202454, 348, 0.8333333730697632, 0.47058823704719543, 0.3058439791202545, 0.1604711264371872, 0.05760817229747772, 46.97265625, 70.796875, 33.625, 47.03623962402344, 138.25340270996094, 19.648666381835938 +Cyan, 0, 1, 1, 180, 0.9999998807907104, 1, 180, 1, 0.5, 0.5380030274391174, 0.7873290181159973, 1.0694199800491333, 91.11328125, -48.09375, -14.125, 91.11328887939453, -70.4778823852539, -15.202608108520508 +Dark Blue, 0, 0, 0.545098066329956, 240, 0.9999997615814209, 0.545098066329956, 240, 1, 0.272549033164978, 0.04658212512731552, 0.018632797524333, 0.24533233046531677, 14.74609375, 50.40625, -68.65625, 14.752754211425781, -4.296110153198242, -59.539459228515625 +Dark Cyan, 0, 0.545098066329956, 0.545098066329956, 179.99998474121094, 0.9999997615814209, 0.545098066329956, 180, 1, 0.272549033164978, 0.13890314102172852, 0.20327484607696533, 0.2761059105396271, 52.191162109375, -30.578125, -9, 52.205467224121094, -40.38194274902344, -8.710710525512695 +Dark Goldenrod, 0.7215686440467834, 0.5254902243614197, 0.04313725605607033, 42.65895080566406, 0.9402172565460205, 0.7215686440467834, 42.658958435058594, 0.8871794939041138, 0.38235294818878174, 0.283547043800354, 0.2726714313030243, 0.04086246341466904, 59.197998046875, 9.78125, 62.65625, 59.220787048339844, 41.892578125, 59.634849548339844 +Dark Gray, 0.6627451181411743, 0.6627451181411743, 0.6627451181411743, 0, 0, 0.6627451181411743, 0, 0, 0.6627451181411743, 0.37709841132164, 0.3967552185058594, 0.4319688677787781, 69.183349609375, 0.015625, 0, 69.23779296875, 0, 0 +Dark Green, 0, 0.3921568691730499, 0, 120, 0.9999997019767761, 0.3921568691730499, 120, 1, 0.19607843458652496, 0.04556916654109955, 0.0911383330821991, 0.015189680270850658, 36.053466796875, -43.234375, 41.734375, 36.20254135131836, -34.2806282043457, 44.31380844116211 +Dark Khaki, 0.7411764860153198, 0.7176470756530762, 0.41960784792900085, 55.609737396240234, 0.4338623583316803, 0.7411764860153198, 55.60975646972656, 0.38317760825157166, 0.5803921818733215, 0.4057421386241913, 0.4574859142303467, 0.20598962903022766, 73.30322265625, -8.796875, 39.390625, 73.38209533691406, 7.597750663757324, 51.29442596435547 +Dark Magenta, 0.545098066329956, 0, 0.545098066329956, 300, 0.9999997615814209, 0.545098066329956, 300, 1, 0.272549033164978, 0.15307042002677917, 0.07354079931974411, 0.2503240406513214, 32.5927734375, 62.546875, -38.703125, 32.59975814819336, 45.43534469604492, -58.733245849609375 +Dark Olive Green, 0.3333333432674408, 0.41960784792900085, 0.18431372940540314, 82.00001525878906, 0.560747504234314, 0.41960784792900085, 82, 0.3896103799343109, 0.3019607961177826, 0.09517066180706024, 0.12651889026165009, 0.046292148530483246, 42.083740234375, -18.71875, 30.484375, 42.23397445678711, -10.579286575317383, 36.11909103393555 +Dark Orange, 1, 0.5490196347236633, 0, 32.9411735534668, 0.9999998807907104, 1, 32.94117736816406, 1, 0.5, 0.5062285661697388, 0.40022218227386475, 0.05059244483709335, 69.482421875, 36.84375, 75.5, 69.4853515625, 95.87791442871094, 65.39054870605469 +Dark Orchid, 0.6000000238418579, 0.19607843458652496, 0.800000011920929, 280.1298522949219, 0.7549018859863281, 0.800000011920929, 280.1298828125, 0.6062992215156555, 0.49803921580314636, 0.25173529982566833, 0.13413403928279877, 0.5837336778640747, 43.3349609375, 65.109375, -59.984375, 43.37964630126953, 29.86530113220215, -94.55174255371094 +Dark Red, 0.545098066329956, 0, 0, 0, 0.9999997615814209, 0.545098066329956, 0, 1, 0.272549033164978, 0.10648829489946365, 0.05490800365805626, 0.004991707392036915, 28.076171875, 51, 41.265625, 28.089637756347656, 92.33747863769531, 19.917953491210938 +Dark Salmon, 0.9137254953384399, 0.5882353186607361, 0.47843137383461, 15.135133743286133, 0.47639480233192444, 0.9137254953384399, 15.135137557983398, 0.7161290049552917, 0.6960784196853638, 0.4802568256855011, 0.4054543673992157, 0.23703771829605103, 69.78759765625, 28.015625, 27.859375, 69.85625457763672, 60.19567108154297, 30.309755325317383 +Dark Sea Green, 0.5607843399047852, 0.7372549176216125, 0.5607843399047852, 120, 0.23936164379119873, 0.7372549176216125, 120, 0.25139662623405457, 0.6490195989608765, 0.3426717221736908, 0.4378833770751953, 0.32625696063041687, 72.015380859375, -23.71875, 17.96875, 72.0867919921875, -22.59234619140625, 29.204618453979492 +Dark Slate Blue, 0.2823529541492462, 0.239215686917305, 0.545098066329956, 248.46153259277344, 0.5611510276794434, 0.545098066329956, 248.4615478515625, 0.39000004529953003, 0.3921568691730499, 0.08999692648649216, 0.06578757613897324, 0.252147376537323, 30.755615234375, 26.109375, -42.171875, 30.82803726196289, -0.5906588435173035, -58.25946044921875 +Dark Slate Gray, 0.18431372940540314, 0.30980393290519714, 0.30980393290519714, 179.99993896484375, 0.4050631523132324, 0.30980393290519714, 180, 0.2539682388305664, 0.24705883860588074, 0.053789474070072174, 0.06760461628437042, 0.08416478335857391, 31.14013671875, -11.640625, -3.703125, 31.25525665283203, -14.17424488067627, -3.0574874877929688 +Dark Turquoise, 0, 0.8078431487083435, 0.8196078538894653, 180.8612518310547, 0.9999998807907104, 0.8196078538894653, 180.86123657226562, 1, 0.40980392694473267, 0.3357378840446472, 0.48741617798805237, 0.6794284582138062, 75.189208984375, -40, -13.484375, 75.29029846191406, -57.92400360107422, -15.085222244262695 +Dark Violet, 0.5803921818733215, 0, 0.8274509906768799, 282.0852966308594, 0.9999998807907104, 0.8274509906768799, 282.0852966308594, 1, 0.41372549533843994, 0.23967167735099792, 0.10999131202697754, 0.6247087717056274, 39.544677734375, 76.265625, -70.234375, 39.57900619506836, 29.2673282623291, -105.64356231689453 +Deep Pink, 1, 0.0784313753247261, 0.5764706134796143, 327.574462890625, 0.9215685129165649, 1, 327.574462890625, 1, 0.5392156839370728, 0.46759656071662903, 0.23873063921928406, 0.29741615056991577, 55.96923828125, 84.515625, -5.640625, 55.960479736328125, 131.4710235595703, -24.355266571044922 +Deep Sky Blue, 0, 0.7490196228027344, 1, 195.05882263183594, 0.9999998807907104, 1, 195.05882263183594, 1, 0.5, 0.36672061681747437, 0.4447641968727112, 1.0123260021209717, 72.47314453125, -17.53125, -42.640625, 72.54578399658203, -49.2724609375, -66.99807739257812 +Dim Gray, 0.4117647111415863, 0.4117647111415863, 0.4117647111415863, 0, 0, 0.4117647111415863, 0, 0, 0.4117647111415863, 0.13426454365253448, 0.14126330614089966, 0.15380096435546875, 44.25048828125, 0.015625, 0, 44.41355895996094, -1.0589017620077357E-05, -2.1178035240154713E-05 +Dodger Blue, 0.11764705926179886, 0.5647059082984924, 1, 209.60000610351562, 0.8823528289794922, 1, 209.60000610351562, 1, 0.5588235259056091, 0.28550490736961365, 0.27438414096832275, 0.9837203025817871, 59.36279296875, 10, -63.40625, 59.37794494628906, -32.81703186035156, -102.25732421875 +Firebrick, 0.6980392336845398, 0.13333334028720856, 0.13333334028720856, 0, 0.808988630771637, 0.6980392336845398, 0, 0.6792452931404114, 0.4156862795352936, 0.19223062694072723, 0.10727573186159134, 0.02571427822113037, 39.080810546875, 55.859375, 37.640625, 39.117794036865234, 107.5474853515625, 23.19884490966797 +Floral White, 1, 0.9803921580314636, 0.9411764740943909, 39.99991989135742, 0.058823518455028534, 1, 40, 1, 0.970588207244873, 0.9115046262741089, 0.9592306017875671, 0.9612758755683899, 98.272705078125, 0.140625, 5.421875, 98.40166473388672, 3.415327310562134, 8.21953296661377 +Forest Green, 0.13333334028720856, 0.545098066329956, 0.13333334028720856, 120, 0.7553955316543579, 0.545098066329956, 120, 0.6069364547729492, 0.3392156958580017, 0.10180484503507614, 0.18919843435287476, 0.046282973140478134, 50.57373046875, -49.546875, 45, 50.593292236328125, -43.12392807006836, 55.74532699584961 +Fuchsia, 1, 0, 1, 300, 0.9999998807907104, 1, 300, 1, 0.5, 0.5928760170936584, 0.2848399877548218, 0.9695610404014587, 60.321044921875, 98.234375, -60.828125, 60.32350158691406, 84.07484436035156, -108.68161010742188 +Gainsboro, 0.8627451062202454, 0.8627451062202454, 0.8627451062202454, 0, 0, 0.8627451062202454, 0, 0, 0.8627451062202454, 0.6802351474761963, 0.715693473815918, 0.7792141437530518, 87.640380859375, 0.015625, 0, 87.76087951660156, -2.0923824195051566E-05, 0 +Ghost White, 0.9725490212440491, 0.9725490212440491, 1, 240, 0.02745097503066063, 1, 240, 1, 0.9862744808197021, 0.9032419919967651, 0.9431107044219971, 1.0802602767944336, 97.607421875, 1.328125, -3.578125, 97.7572021484375, -0.3925853669643402, -5.440537452697754 +Gold, 1, 0.843137264251709, 0, 50.58823013305664, 0.9999998807907104, 1, 50.588233947753906, 1, 0.5, 0.6554437875747681, 0.6986526250839233, 0.10033071041107178, 86.846923828125, -1.75, 87.0625, 86.93073272705078, 35.49911880493164, 92.07884216308594 +Goldenrod, 0.8549019694328308, 0.6470588445663452, 0.125490203499794, 42.903221130371094, 0.8532109260559082, 0.8549019694328308, 42.903228759765625, 0.7440000176429749, 0.4901960790157318, 0.426321417093277, 0.4192340672016144, 0.07212784886360168, 70.751953125, 8.453125, 68.71875, 70.81807708740234, 44.365760803222656, 69.98861694335938 +Gray, 0.7450980544090271, 0.7450980544090271, 0.7450980544090271, 0, 0, 0.7450980544090271, 0, 0, 0.7450980544090271, 0.4894065856933594, 0.5149176716804504, 0.5606186985969543, 76.89208984375, 0.015625, 0, 76.97594451904297, 1.8352495317230932E-05, 7.340998126892373E-05 +Web Gray, 0.501960813999176, 0.501960813999176, 0.501960813999176, 0, 0, 0.501960813999176, 0, 0, 0.501960813999176, 0.20516590774059296, 0.2158605009317398, 0.23501898348331451, 53.582763671875, 0, 0, 53.58501434326172, 1.2775662980857305E-05, 0 +Green, 0, 1, 0, 120, 0.9999998807907104, 1, 120, 1, 0.5, 0.3575800061225891, 0.7151600122451782, 0.11919300258159637, 87.738037109375, -86.1875, 83.171875, 87.73509216308594, -83.07742309570312, 107.3923110961914 +Web Green, 0, 0.501960813999176, 0, 120, 0.9999997615814209, 0.501960813999176, 120, 1, 0.250980406999588, 0.07718739658594131, 0.15437479317188263, 0.025729062035679817, 46.22802734375, -51.6875, 49.890625, 46.227664947509766, -43.77353286743164, 56.585086822509766 +Green Yellow, 0.6784313917160034, 1, 0.18431372940540314, 83.65385437011719, 0.8156861662864685, 1, 83.65383911132812, 1, 0.5921568870544434, 0.5350667238235474, 0.8060835003852844, 0.1542835831642151, 91.949462890625, -52.53125, 81.890625, 91.9571304321289, -41.033653259277344, 102.70511627197266 +Honeydew, 0.9411764740943909, 1, 0.9411764740943909, 120, 0.058823518455028534, 1, 120, 1, 0.970588207244873, 0.8741925954818726, 0.9633602499961853, 0.9640365839004517, 98.529052734375, -7.78125, 5.640625, 98.56559753417969, -7.5411787033081055, 9.748356819152832 +Hot Pink, 1, 0.4117647111415863, 0.7058823704719543, 330, 0.5882351994514465, 1, 330, 1, 0.7058823704719543, 0.5453130006790161, 0.3466355800628662, 0.46986567974090576, 65.41748046875, 64.390625, -10.625, 65.48588562011719, 91.12604522705078, -27.48790168762207 +Indian Red, 0.8039215803146362, 0.3607843220233917, 0.3607843220233917, 0, 0.5512194037437439, 0.8039215803146362, 0, 0.5305164456367493, 0.5823529362678528, 0.3093794584274292, 0.21409708261489868, 0.12625597417354584, 53.289794921875, 44.84375, 22.171875, 53.39500427246094, 82.95269012451172, 17.893566131591797 +Indigo, 0.29411765933036804, 0, 0.5098039507865906, 274.6153869628906, 0.9999997615814209, 0.5098039507865906, 274.6153869628906, 1, 0.2549019753932953, 0.06929568946361542, 0.031073689460754395, 0.21347758173942566, 20.4345703125, 51.65625, -53.359375, 20.468868255615234, 10.083295822143555, -61.334896087646484 +Ivory, 1, 1, 0.9411764740943909, 59.9998779296875, 0.058823518455028534, 1, 60, 1, 0.970588207244873, 0.9272476434707642, 0.9907166957855225, 0.9665235280990601, 99.627685546875, -2.625, 7.375, 99.63992309570312, 0.8216015696525574, 11.386540412902832 +Khaki, 0.9411764740943909, 0.9019607901573181, 0.5490196347236633, 53.99998474121094, 0.41666659712791443, 0.9411764740943909, 54.000003814697266, 0.7692306637763977, 0.7450980544090271, 0.6896663904190063, 0.7701455354690552, 0.36036184430122375, 90.191650390625, -8.96875, 44.8125, 90.32831573486328, 10.829050064086914, 60.95827865600586 +Lavender, 0.9019607901573181, 0.9019607901573181, 0.9803921580314636, 240, 0.07999998331069946, 0.9803921580314636, 240, 0.6666669845581055, 0.9411764740943909, 0.7818050980567932, 0.8031823635101318, 1.0180078744888306, 91.68701171875, 3.703125, -9.625, 91.82746124267578, -1.1404914855957031, -15.805614471435547 +Lavender Blush, 1, 0.9411764740943909, 0.9607843160629272, 340.0000305175781, 0.058823518455028534, 1, 340, 1, 0.970588207244873, 0.8887804746627808, 0.9017353057861328, 0.9908458590507507, 95.953369140625, 6.046875, -0.53125, 96.0687026977539, 8.275922775268555, -1.9832603931427002 +Lawn Green, 0.48627451062202454, 0.9882352948188782, 0, 90.4761962890625, 0.9999998807907104, 0.9882352948188782, 90.47618865966797, 1, 0.4941176474094391, 0.4312170445919037, 0.7390342950820923, 0.11992476135492325, 88.720703125, -67.796875, 84.8125, 88.87681579589844, -60.7806282043457, 105.94657897949219 +Lemon Chiffon, 1, 0.9803921580314636, 0.8039215803146362, 53.999969482421875, 0.19607838988304138, 1, 54.000003814697266, 1, 0.9019607901573181, 0.864437460899353, 0.9404037594795227, 0.7133886814117432, 97.52197265625, -5.21875, 22.21875, 97.64826202392578, 5.385188579559326, 33.384437561035156 +Light Blue, 0.6784313917160034, 0.8470588326454163, 0.9019607901573181, 194.7368621826172, 0.24782603979110718, 0.9019607901573181, 194.73684692382812, 0.5327103734016418, 0.7901960611343384, 0.5606712102890015, 0.637069046497345, 0.8418401479721069, 83.70361328125, -10.84375, -11.4375, 83.81293487548828, -22.274141311645508, -16.13959503173828 +Light Coral, 0.9411764740943909, 0.501960813999176, 0.501960813999176, 0, 0.466666579246521, 0.9411764740943909, 0, 0.7887322902679443, 0.7215686440467834, 0.47553157806396484, 0.3552677631378174, 0.2476925551891327, 66.10107421875, 42.640625, 19.46875, 66.15674591064453, 79.69701385498047, 17.19130516052246 +Light Cyan, 0.8784313797950745, 1, 1, 179.99993896484375, 0.12156860530376434, 1, 180, 1, 0.9392156600952148, 0.8454471826553345, 0.9458548426628113, 1.0838316679000854, 97.83935546875, -10.0625, -3.421875, 97.86742401123047, -16.39739227294922, -3.5370185375213623 +Light Goldenrod, 0.9803921580314636, 0.9803921580314636, 0.8235294222831726, 59.99995422363281, 0.15999996662139893, 0.9803921580314636, 60, 0.8000002503395081, 0.9019607901573181, 0.8524099588394165, 0.9334931969642639, 0.7448301315307617, 97.210693359375, -6.453125, 19.171875, 97.36919403076172, 2.126041889190674, 29.46488380432129 +Light Gray, 0.8274509906768799, 0.8274509906768799, 0.8274509906768799, 0, 0, 0.8274509906768799, 0, 0, 0.8274509906768799, 0.619132399559021, 0.6514056324958801, 0.7092204689979553, 84.4482421875, 0.015625, 0, 84.55612182617188, -4.031950084026903E-05, 0 +Light Green, 0.5647059082984924, 0.9333333373069763, 0.5647059082984924, 120, 0.39495790004730225, 0.9333333373069763, 120, 0.7343750596046448, 0.7490196228027344, 0.47107797861099243, 0.69089674949646, 0.3723141551017761, 86.419677734375, -46.15625, 36.8125, 86.54843139648438, -45.20298767089844, 58.432960510253906 +Light Pink, 1, 0.7137255072593689, 0.7568627595901489, 350.95892333984375, 0.2862744629383087, 1, 350.9588928222656, 1, 0.8568627834320068, 0.6759384274482727, 0.5856972932815552, 0.5818241834640503, 81.005859375, 28.046875, 5.109375, 81.05450439453125, 45.75114059448242, 2.127246379852295 +Light Salmon, 1, 0.6274510025978088, 0.47843137383461, 17.142854690551758, 0.5215685963630676, 1, 17.142858505249023, 1, 0.7392156720161438, 0.5732675194740295, 0.4781184494495392, 0.24616536498069763, 74.67041015625, 31.5, 34.765625, 74.70608520507812, 70.3686752319336, 37.76241683959961 +Light Sea Green, 0.125490203499794, 0.6980392336845398, 0.6666666865348816, 176.71231079101562, 0.8202245831489563, 0.6980392336845398, 176.7123260498047, 0.6952381134033203, 0.4117647111415863, 0.23767849802970886, 0.35047221183776855, 0.43531426787376404, 65.716552734375, -37.46875, -6.34375, 65.78541564941406, -49.63898849487305, -3.874094247817993 +Light Sky Blue, 0.529411792755127, 0.8078431487083435, 0.9803921580314636, 202.95652770996094, 0.4599999189376831, 0.9803921580314636, 202.95651245117188, 0.9200001358985901, 0.7549020051956177, 0.49310988187789917, 0.5619192719459534, 0.9866426587104797, 79.62646484375, -10.796875, -28.40625, 79.7229232788086, -32.99339294433594, -44.26582336425781 +Light Slate Gray, 0.46666666865348816, 0.5333333611488342, 0.6000000238418579, 210.00001525878906, 0.2222222089767456, 0.6000000238418579, 210, 0.14285717904567719, 0.5333333611488342, 0.2215970903635025, 0.23829501867294312, 0.33560386300086975, 55.87158203125, -2.390625, -11.125, 55.91667938232422, -9.65645980834961, -15.848763465881348 +Light Steel Blue, 0.6901960968971252, 0.7686274647712708, 0.8705882430076599, 213.91305541992188, 0.20720715820789337, 0.8705882430076599, 213.9130401611328, 0.410714328289032, 0.7803921699523926, 0.5082481503486633, 0.5398250222206116, 0.7682933807373047, 78.363037109375, -1.265625, -15.15625, 78.45153045654297, -11.73449993133545, -23.502105712890625 +Light Yellow, 1, 1, 0.8784313797950745, 59.999942779541016, 0.12156860530376434, 1, 60, 1, 0.9392156600952148, 0.9045210480690002, 0.9816260933876038, 0.8468302488327026, 99.27978515625, -5.15625, 15.03125, 99.28514862060547, 1.6665629148483276, 23.09687042236328 +Lime, 0, 1, 0, 120, 0.9999998807907104, 1, 120, 1, 0.5, 0.3575800061225891, 0.7151600122451782, 0.11919300258159637, 87.738037109375, -86.1875, 83.171875, 87.73509216308594, -83.07742309570312, 107.3923110961914 +Lime Green, 0.19607843458652496, 0.8039215803146362, 0.19607843458652496, 120, 0.7560974955558777, 0.8039215803146362, 120, 0.6078431606292725, 0.5, 0.2372114062309265, 0.44568729400634766, 0.1036919504404068, 72.509765625, -67.046875, 61.375, 72.60699462890625, -62.926719665527344, 81.343994140625 +Linen, 0.9803921580314636, 0.9411764740943909, 0.9019607901573181, 29.999954223632812, 0.07999998331069946, 0.9803921580314636, 30, 0.6666669845581055, 0.9411764740943909, 0.8486459255218506, 0.8835818767547607, 0.8742563128471375, 95.159912109375, 1.671875, 6, 95.31155395507812, 6.34827995300293, 8.826791763305664 +Magenta, 1, 0, 1, 300, 0.9999998807907104, 1, 300, 1, 0.5, 0.5928760170936584, 0.2848399877548218, 0.9695610404014587, 60.321044921875, 98.234375, -60.828125, 60.32350158691406, 84.07484436035156, -108.68161010742188 +Maroon, 0.6901960968971252, 0.1882352977991104, 0.3764705955982208, 337.5, 0.727272629737854, 0.6901960968971252, 337.5, 0.5714285969734192, 0.43921568989753723, 0.2107411026954651, 0.12191140651702881, 0.12306557595729828, 41.448974609375, 54.671875, 2.59375, 41.518306732177734, 82.11595153808594, -6.913197994232178 +Web Maroon, 0.501960813999176, 0, 0, 0, 0.9999997615814209, 0.501960813999176, 0, 1, 0.250980406999588, 0.08903230726718903, 0.045907266438007355, 0.004173446912318468, 25.537109375, 48.046875, 38.046875, 25.53540802001953, 83.94110107421875, 18.106775283813477 +Medium Aquamarine, 0.4000000059604645, 0.8039215803146362, 0.6666666865348816, 159.61163330078125, 0.5024389624595642, 0.8039215803146362, 159.6116485595703, 0.5073891878128052, 0.6019607782363892, 0.3456289768218994, 0.4938696026802063, 0.45730581879615784, 75.5859375, -38.296875, 8.234375, 75.6914291381836, -45.59864044189453, 18.4306640625 +Medium Blue, 0, 0, 0.8039215803146362, 240, 0.9999998807907104, 0.8039215803146362, 240, 1, 0.4019607901573181, 0.11014744639396667, 0.04405885189771652, 0.5801093578338623, 24.9267578125, 67.109375, -91.40625, 24.970291137695312, -7.271531581878662, -100.77560424804688 +Medium Orchid, 0.729411780834198, 0.3333333432674408, 0.8274509906768799, 288.09521484375, 0.597156286239624, 0.8274509906768799, 288.0952453613281, 0.5887851119041443, 0.5803921818733215, 0.35253477096557617, 0.2164035439491272, 0.6393042802810669, 53.55224609375, 59.078125, -47.359375, 53.64331817626953, 40.295440673828125, -80.39657592773438 +Medium Purple, 0.5764706134796143, 0.43921568989753723, 0.8588235378265381, 259.62615966796875, 0.48858439922332764, 0.8588235378265381, 259.62615966796875, 0.597765326499939, 0.6490195989608765, 0.3060874342918396, 0.22905084490776062, 0.6980715990066528, 54.852294921875, 36.953125, -50.078125, 54.97443389892578, 8.540892601013184, -82.2688980102539 +Medium Sea Green, 0.23529411852359772, 0.7019608020782471, 0.4431372582912445, 146.72268676757812, 0.6648043990135193, 0.7019608020782471, 146.72268676757812, 0.49790799617767334, 0.4686274528503418, 0.2096228152513504, 0.34391117095947266, 0.21151721477508545, 65.19775390625, -48.234375, 24.421875, 65.27183532714844, -49.348201751708984, 40.11897659301758 +Medium Slate Blue, 0.48235294222831726, 0.40784314274787903, 0.9333333373069763, 248.50746154785156, 0.5630251169204712, 0.9333333373069763, 248.50746154785156, 0.7976190447807312, 0.6705882549285889, 0.28545498847961426, 0.2028283178806305, 0.8327666521072388, 52.001953125, 41.09375, -65.40625, 52.155487060546875, -1.2601090669631958, -105.10879516601562 +Medium Spring Green, 0, 0.9803921580314636, 0.6039215922355652, 156.95999145507812, 0.9999998807907104, 0.9803921580314636, 156.9600067138672, 1, 0.4901960790157318, 0.4001394212245941, 0.7069948315620422, 0.4210047423839569, 87.188720703125, -70.53125, 32.3125, 87.33879089355469, -76.49688720703125, 57.130462646484375 +Medium Turquoise, 0.2823529541492462, 0.8196078538894653, 0.800000011920929, 177.81019592285156, 0.6555023193359375, 0.8196078538894653, 177.81021118164062, 0.5982533097267151, 0.5509803891181946, 0.36366453766822815, 0.5133431553840637, 0.6510230898857117, 76.776123046875, -37.328125, -8.359375, 76.88107299804688, -52.59000778198242, -7.10806941986084 +Medium Violet Red, 0.7803921699523926, 0.08235294371843338, 0.5215686559677124, 322.2471923828125, 0.8944722414016724, 0.7803921699523926, 322.2471923828125, 0.8090909123420715, 0.4313725531101227, 0.28056198358535767, 0.14375197887420654, 0.23481225967407227, 44.7265625, 70.90625, -15.21875, 44.76626968383789, 92.77569580078125, -32.870296478271484 +Midnight Blue, 0.09803921729326248, 0.09803921729326248, 0.43921568989753723, 240, 0.7767854928970337, 0.43921568989753723, 240, 0.6350364685058594, 0.26862746477127075, 0.036719486117362976, 0.020713143050670624, 0.15531133115291595, 15.802001953125, 31.59375, -49.390625, 15.85713005065918, -3.5571157932281494, -49.2978401184082 +Mint Cream, 0.9607843160629272, 1, 0.9803921580314636, 149.99990844726562, 0.039215680211782455, 1, 150, 1, 0.9803921580314636, 0.9066698551177979, 0.978341281414032, 1.0452386140823364, 99.114990234375, -4.390625, 1.421875, 99.15640258789062, -5.260156631469727, 2.677217960357666 +Misty Rose, 1, 0.8941176533699036, 0.8823529481887817, 5.999993801116943, 0.11764703691005707, 1, 6, 1, 0.9411764740943909, 0.8257196545600891, 0.821847140789032, 0.8272725939750671, 92.5537109375, 8.890625, 4.890625, 92.65631866455078, 16.148357391357422, 5.699342250823975 +Moccasin, 1, 0.8941176533699036, 0.7098039388656616, 38.10809326171875, 0.290196031332016, 1, 38.10810852050781, 1, 0.8549019694328308, 0.773240864276886, 0.8008556365966797, 0.5508846044540405, 91.6259765625, 2.625, 26.328125, 91.72323608398438, 19.523889541625977, 36.78556823730469 +Navajo White, 1, 0.8705882430076599, 0.6784313917160034, 35.85364532470703, 0.3215685784816742, 1, 35.85365676879883, 1, 0.8392156958580017, 0.7490472197532654, 0.7652256488800049, 0.5034854412078857, 90.008544921875, 4.6875, 28.234375, 90.10140991210938, 23.727821350097656, 38.62346267700195 +Navy Blue, 0, 0, 0.501960813999176, 240, 0.9999997615814209, 0.501960813999176, 240, 1, 0.250980406999588, 0.03894620016217232, 0.015578435733914375, 0.20511648058891296, 12.969970703125, 47.5, -64.703125, 12.971153259277344, -3.7772977352142334, -52.349239349365234 +Old Lace, 0.9921568632125854, 0.9607843160629272, 0.9019607901573181, 39.1303825378418, 0.09090907126665115, 0.9921568632125854, 39.13043212890625, 0.8518518805503845, 0.9470587968826294, 0.8744063377380371, 0.9190149903297424, 0.879738450050354, 96.624755859375, 0.1875, 8.140625, 96.780029296875, 5.471574783325195, 12.317169189453125 +Olive, 0.501960813999176, 0.501960813999176, 0, 59.99998474121094, 0.9999997615814209, 0.501960813999176, 60, 1, 0.250980406999588, 0.16621971130371094, 0.20028206706047058, 0.029902508482336998, 51.873779296875, -12.921875, 56.671875, 51.86907958984375, 4.114067554473877, 57.01655960083008 +Olive Drab, 0.41960784792900085, 0.5568627715110779, 0.13725490868091583, 79.62619018554688, 0.7535209655761719, 0.5568627715110779, 79.62617492675781, 0.604519784450531, 0.34705883264541626, 0.16039887070655823, 0.2259306162595749, 0.05105489119887352, 54.608154296875, -28.34375, 49.640625, 54.650672912597656, -17.44415855407715, 57.436561584472656 +Orange, 1, 0.6470588445663452, 0, 38.823524475097656, 0.9999998807907104, 1, 38.82353210449219, 1, 0.5, 0.5469968318939209, 0.48175865411758423, 0.06418181210756302, 74.908447265625, 24.015625, 78.9375, 74.93571472167969, 74.84410095214844, 73.99249267578125 +Orange Red, 1, 0.2705882489681244, 0, 16.235292434692383, 0.9999998807907104, 1, 16.235294342041016, 1, 0.5, 0.4337330162525177, 0.2552310526371002, 0.02642732299864292, 57.562255859375, 67.84375, 68.953125, 57.58158874511719, 151.04318237304688, 45.48124313354492 +Orchid, 0.8549019694328308, 0.43921568989753723, 0.8392156958580017, 302.26416015625, 0.4862384796142578, 0.8549019694328308, 302.26416015625, 0.588888943195343, 0.6470588445663452, 0.46843424439430237, 0.313510537147522, 0.6718415021896362, 62.664794921875, 55.328125, -34.4375, 62.80287170410156, 51.34286880493164, -61.82412338256836 +Pale Goldenrod, 0.9333333373069763, 0.9098039269447327, 0.6666666865348816, 54.70585632324219, 0.2857142388820648, 0.9333333373069763, 54.70588302612305, 0.6666667461395264, 0.800000011920929, 0.7137202620506287, 0.7879424691200256, 0.4946836233139038, 91.00341796875, -7.3125, 30.859375, 91.14110565185547, 6.9131622314453125, 44.52756881713867 +Pale Green, 0.5960784554481506, 0.9843137264251709, 0.5960784554481506, 120, 0.3944222331047058, 0.9843137264251709, 120, 0.9252336621284485, 0.7901960611343384, 0.5311088562011719, 0.7793415784835815, 0.41941505670547485, 90.606689453125, -48.125, 38.390625, 90.74983978271484, -47.46623229980469, 61.358543395996094 +Pale Turquoise, 0.686274528503418, 0.9333333373069763, 0.9333333373069763, 179.99996948242188, 0.2647058367729187, 0.9333333373069763, 180, 0.6494846343994141, 0.8098039627075195, 0.6368032693862915, 0.7643305063247681, 0.9226345419883728, 89.923095703125, -19.5625, -6.375, 90.06002044677734, -31.06871223449707, -6.701792240142822 +Pale Violet Red, 0.8588235378265381, 0.43921568989753723, 0.5764706134796143, 340.37384033203125, 0.48858439922332764, 0.8588235378265381, 340.37384033203125, 0.597765326499939, 0.6490195989608765, 0.4027522802352905, 0.28758469223976135, 0.31025686860084534, 60.43701171875, 45.609375, 0.25, 60.56787109375, 68.84209442138672, -7.892024993896484 +Papaya Whip, 1, 0.9372549057006836, 0.8352941274642944, 37.14282989501953, 0.16470585763454437, 1, 37.14285659790039, 1, 0.9176470637321472, 0.8411519527435303, 0.8779867887496948, 0.7544852495193481, 94.964599609375, 1.453125, 14.53125, 95.07611083984375, 11.003774642944336, 21.25665283203125 +Peach Puff, 1, 0.8549019694328308, 0.7254902124404907, 28.285701751708984, 0.2745097577571869, 1, 28.285715103149414, 1, 0.8627451062202454, 0.7506852746009827, 0.7490838170051575, 0.5639030337333679, 89.263916015625, 8.25, 21.015625, 89.35005187988281, 25.183385848999023, 28.48662567138672 +Peru, 0.8039215803146362, 0.5215686559677124, 0.24705882370471954, 29.57746124267578, 0.6926828622817993, 0.8039215803146362, 29.577468872070312, 0.5867769122123718, 0.5254902243614197, 0.34463950991630554, 0.3011631667613983, 0.08699263632297516, 61.71875, 21.265625, 47.96875, 61.75445556640625, 57.19938659667969, 48.754154205322266 +Pink, 1, 0.7529411911964417, 0.7960784435272217, 349.5238037109375, 0.24705877900123596, 1, 349.5238037109375, 1, 0.8764705657958984, 0.7086877226829529, 0.6327421069145203, 0.6496396660804749, 83.5205078125, 24.234375, 3.390625, 83.58644104003906, 38.572566986083984, 0.4393051862716675 +Plum, 0.8666666746139526, 0.6274510025978088, 0.8666666746139526, 300.0000305175781, 0.27601805329322815, 0.8666666746139526, 300, 0.4728682041168213, 0.7470588684082031, 0.5543830394744873, 0.4573570787906647, 0.7429462671279907, 73.297119140625, 32.421875, -21.890625, 73.37370300292969, 30.628082275390625, -39.59230422973633 +Powder Blue, 0.6901960968971252, 0.8784313797950745, 0.9019607901573181, 186.6667022705078, 0.23478256165981293, 0.9019607901573181, 186.6666717529297, 0.519230842590332, 0.7960784435272217, 0.5883779525756836, 0.6825222969055176, 0.8491535186767578, 86.0107421875, -14.03125, -7.984375, 86.13241577148438, -24.475399017333984, -10.110276222229004 +Purple, 0.6274510025978088, 0.125490203499794, 0.9411764740943909, 276.9230651855469, 0.8666665554046631, 0.9411764740943909, 276.923095703125, 0.8739495277404785, 0.5333333611488342, 0.3073701858520508, 0.1479761302471161, 0.8365147113800049, 45.294189453125, 78.671875, -77.25, 45.355735778808594, 27.28308868408203, -120.23531341552734 +Web Purple, 0.501960813999176, 0, 0.501960813999176, 300, 0.9999997615814209, 0.501960813999176, 300, 1, 0.250980406999588, 0.12797850370407104, 0.06148570030927658, 0.20928992331027985, 29.78515625, 58.9375, -36.484375, 29.784244537353516, 41.51127243041992, -53.66067123413086 +Rebecca Purple, 0.4000000059604645, 0.20000000298023224, 0.6000000238418579, 270, 0.666666567325592, 0.6000000238418579, 270, 0.5000000596046448, 0.4000000059604645, 0.12411271035671234, 0.07492164522409439, 0.309206485748291, 32.8369140625, 42.84375, -47.203125, 32.90205383300781, 12.983814239501953, -67.75232696533203 +Red, 1, 0, 0, 0, 0.9999998807907104, 1, 0, 1, 0.5, 0.4124529957771301, 0.21267099678516388, 0.01933399960398674, 53.240966796875, 80.09375, 67.203125, 53.24058532714844, 175.01475524902344, 37.752098083496094 +Rosy Brown, 0.7372549176216125, 0.5607843399047852, 0.5607843399047852, 0, 0.23936164379119873, 0.7372549176216125, 0, 0.25139662623405457, 0.6490195989608765, 0.35519424080848694, 0.32321077585220337, 0.3034681975841522, 63.57421875, 16.953125, 6.578125, 63.60736846923828, 28.56975555419922, 6.162703990936279 +Royal Blue, 0.2549019753932953, 0.4117647111415863, 0.8823529481887817, 225, 0.7111109495162964, 0.8823529481887817, 225, 0.7272727489471436, 0.5686274766921997, 0.2081635594367981, 0.1666068732738495, 0.7333256006240845, 47.69287109375, 26.359375, -65.265625, 47.829627990722656, -17.509973526000977, -101.21499633789062 +Saddle Brown, 0.545098066329956, 0.2705882489681244, 0.07450980693101883, 24.9999942779541, 0.8633092045783997, 0.545098066329956, 25, 0.7594937086105347, 0.30980393290519714, 0.12894324958324432, 0.09793803095817566, 0.018272994086146355, 37.420654296875, 26.546875, 40.890625, 37.46978759765625, 55.634742736816406, 31.63748550415039 +Salmon, 0.9803921580314636, 0.501960813999176, 0.4470588266849518, 6.1764726638793945, 0.5439999103546143, 0.9803921580314636, 6.176474094390869, 0.931506872177124, 0.7137255072593689, 0.501841127872467, 0.36982640624046326, 0.20410597324371338, 67.193603515625, 45.015625, 29.234375, 67.26400756835938, 90.50045013427734, 27.37546157836914 +Sandy Brown, 0.95686274766922, 0.6431372761726379, 0.3764705955982208, 27.56756591796875, 0.6065572500228882, 0.95686274766922, 27.56757164001465, 0.8705880641937256, 0.6666666865348816, 0.5269815921783447, 0.46633121371269226, 0.17288833856582642, 73.870849609375, 22.890625, 46.859375, 73.95448303222656, 61.838199615478516, 51.560733795166016 +Sea Green, 0.18039216101169586, 0.545098066329956, 0.34117648005485535, 146.45159912109375, 0.6690646409988403, 0.545098066329956, 146.4516143798828, 0.5027027726173401, 0.36274510622024536, 0.12078526616096497, 0.19733065366744995, 0.12186554074287415, 51.507568359375, -39.75, 20.203125, 51.5340576171875, -38.62210464477539, 31.473846435546875 +Seashell, 1, 0.9607843160629272, 0.9333333373069763, 24.705839157104492, 0.06666665524244308, 1, 24.705883026123047, 1, 0.9666666984558105, 0.8932191729545593, 0.9273865818977356, 0.9406061172485352, 96.9970703125, 2.328125, 4.59375, 97.12144470214844, 6.145484924316406, 6.558441162109375 +Sienna, 0.6274510025978088, 0.32156863808631897, 0.1764705926179886, 19.304340362548828, 0.7187498807907104, 0.6274510025978088, 19.30434799194336, 0.5609756708145142, 0.4019607901573181, 0.17989644408226013, 0.13699708878993988, 0.04178870469331741, 43.719482421875, 29.421875, 35.609375, 43.799156188964844, 60.948280334472656, 30.77882194519043 +Silver, 0.7529411911964417, 0.7529411911964417, 0.7529411911964417, 0, 0, 0.7529411911964417, 0, 0, 0.7529411911964417, 0.5009996891021729, 0.5271151065826416, 0.5738986730575562, 77.6123046875, 0, 0, 77.70436096191406, -1.8526163330534473E-05, 3.7052326661068946E-05 +Sky Blue, 0.529411792755127, 0.8078431487083435, 0.9215686321258545, 197.40000915527344, 0.4255318343639374, 0.9215686321258545, 197.39999389648438, 0.7142857313156128, 0.7254902124404907, 0.4705203175544739, 0.5528834462165833, 0.8676709532737732, 79.11376953125, -14.78125, -21.203125, 79.20706939697266, -33.22006607055664, -31.48775863647461 +Slate Blue, 0.4156862795352936, 0.3529411852359772, 0.8039215803146362, 248.3478240966797, 0.5609755516052246, 0.8039215803146362, 248.3478240966797, 0.5348837375640869, 0.5784313678741455, 0.2061532735824585, 0.14783000946044922, 0.5950824618339539, 45.21484375, 36.078125, -57.796875, 45.335533142089844, -1.1291123628616333, -89.71842193603516 +Slate Gray, 0.43921568989753723, 0.501960813999176, 0.5647059082984924, 210.00003051757812, 0.2222222089767456, 0.5647059082984924, 210, 0.12598426640033722, 0.501960813999176, 0.19433583319187164, 0.20896126329898834, 0.2938746213912964, 52.801513671875, -2.296875, -10.578125, 52.83562469482422, -9.07613468170166, -14.885616302490234 +Snow, 1, 0.9803921580314636, 0.9803921580314636, 0, 0.019607840105891228, 1, 0, 1, 0.9901961088180542, 0.9267695546150208, 0.9653365612030029, 1.0416710376739502, 98.516845703125, 1.828125, 0.640625, 98.64389038085938, 2.8199665546417236, 0.6083775162696838 +Spring Green, 0, 1, 0.49803921580314636, 149.88235473632812, 0.9999998807907104, 1, 149.88235473632812, 1, 0.5, 0.3958713114261627, 0.7304764986038208, 0.3208603858947754, 88.470458984375, -76.90625, 47.015625, 88.47042846679688, -79.66142272949219, 75.3046875 +Steel Blue, 0.27450981736183167, 0.5098039507865906, 0.7058823704719543, 207.27273559570312, 0.6111109852790833, 0.7058823704719543, 207.27272033691406, 0.4399999678134918, 0.4901961088180542, 0.1874300241470337, 0.20560768246650696, 0.46148544549942017, 52.447509765625, -4.15625, -32.09375, 52.46539306640625, -25.111114501953125, -48.36060333251953 +Tan, 0.8235294222831726, 0.7058823704719543, 0.5490196347236633, 34.285701751708984, 0.33333325386047363, 0.8235294222831726, 34.28571319580078, 0.4375000298023224, 0.686274528503418, 0.47633710503578186, 0.4823954105377197, 0.31605905294418335, 74.90234375, 5.015625, 24.34375, 74.97576904296875, 21.605274200439453, 32.13224411010742 +Teal, 0, 0.501960813999176, 0.501960813999176, 179.99998474121094, 0.9999997615814209, 0.501960813999176, 180, 1, 0.250980406999588, 0.11613360047340393, 0.16995322704315186, 0.23084554076194763, 48.25439453125, -28.84375, -8.484375, 48.25414276123047, -37.32551574707031, -8.05141544342041 +Thistle, 0.8470588326454163, 0.7490196228027344, 0.8470588326454163, 300.00006103515625, 0.11574071645736694, 0.8470588326454163, 300, 0.2427184134721756, 0.7980391979217529, 0.5934168100357056, 0.5681906342506409, 0.7278823256492615, 79.98046875, 13.171875, -9.1875, 80.07772064208984, 12.722251892089844, -16.445825576782227 +Tomato, 1, 0.38823530077934265, 0.27843138575553894, 9.130434036254883, 0.7215685248374939, 1, 9.130434036254883, 1, 0.6392157077789307, 0.4684373736381531, 0.3064501881599426, 0.09407974779605865, 62.152099609375, 57.984375, 46.5, 62.206817626953125, 123.37556457519531, 38.35554122924805 +Turquoise, 0.250980406999588, 0.8784313797950745, 0.8156862854957581, 173.99998474121094, 0.7142855525016785, 0.8784313797950745, 174, 0.720720648765564, 0.5647059082984924, 0.4014909863471985, 0.5895079374313354, 0.6892006993293762, 81.14013671875, -44.03125, -4.046875, 81.26454162597656, -59.018863677978516, 0.7302815914154053 +Violet, 0.9333333373069763, 0.5098039507865906, 0.9333333373069763, 300, 0.4537814259529114, 0.9333333373069763, 300, 0.7605634331703186, 0.7215686440467834, 0.5867264270782471, 0.403179794549942, 0.8555747270584106, 69.62890625, 56.15625, -36.671875, 69.6954116821289, 51.85017776489258, -67.02557373046875 +Wheat, 0.9607843160629272, 0.8705882430076599, 0.7019608020782471, 39.09088897705078, 0.26938769221305847, 0.9607843160629272, 39.09090805053711, 0.7674418687820435, 0.8313725590705872, 0.7191405892372131, 0.7491186857223511, 0.5330684781074524, 89.22119140625, 1.515625, 23.921875, 89.3516845703125, 16.694942474365234, 33.73115921020508 +White, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0.9504560232162476, 1, 1.0887540578842163, 100, 0, 0, 100, 2.384185791015625E-05, 9.5367431640625E-05 +White Smoke, 0.9607843160629272, 0.9607843160629272, 0.9607843160629272, 0, 0, 0.9607843160629272, 0, 0, 0.9607843160629272, 0.8678600788116455, 0.9130986332893372, 0.9941397905349731, 96.380615234375, 0.015625, 0, 96.5374984741211, 0, 4.603266643243842E-05 +Yellow, 1, 1, 0, 59.99999237060547, 0.9999998807907104, 1, 60, 1, 0.5, 0.7700330018997192, 0.9278309941291809, 0.1385270059108734, 97.137451171875, -21.546875, 94.46875, 97.1395034790039, 7.704753398895264, 106.77960968017578 +Yellow Green, 0.6039215922355652, 0.8039215803146362, 0.19607843458652496, 79.741943359375, 0.7560974955558777, 0.8039215803146362, 79.74193572998047, 0.6078431606292725, 0.5, 0.35733717679977417, 0.5076271295547485, 0.1093229204416275, 76.446533203125, -37.875, 66.53125, 76.53504943847656, -25.493520736694336, 81.70164489746094 diff --git a/Unicolour.Tests/Utils/TestColour.cs b/Unicolour.Tests/Utils/TestColour.cs index dd569c73..7e1641bd 100644 --- a/Unicolour.Tests/Utils/TestColour.cs +++ b/Unicolour.Tests/Utils/TestColour.cs @@ -13,7 +13,10 @@ internal class TestColour public ColourTriplet? Hsb { get; init; } public ColourTriplet? Xyz { get; init; } public ColourTriplet? Lab { get; init; } + public ColourTriplet? Luv { get; init; } public Tolerances? Tolerances { get; init; } + public bool IsRgbConstrained { get; init; } = true; + public bool IsRgbLinearConstrained { get; init; } = true; /* * both ColorMine and SixLabors behave strangely with HSL @@ -32,5 +35,5 @@ internal class TestColour internal record Tolerances { - public double Rgb, RgbLinear, Hsb, Hsl, Xyz, Lab; + public double Rgb, RgbLinear, Hsb, Hsl, Xyz, Lab, Luv; } diff --git a/Unicolour.Tests/Utils/TestColours.cs b/Unicolour.Tests/Utils/TestColours.cs index e2f5ec54..7de3394b 100644 --- a/Unicolour.Tests/Utils/TestColours.cs +++ b/Unicolour.Tests/Utils/TestColours.cs @@ -18,13 +18,14 @@ internal static class TestColours public static readonly List RandomHslColours = new(); public static readonly List RandomXyzColours = new(); public static readonly List RandomLabColours = new(); + public static readonly List RandomLuvColours = new(); public static readonly List RandomOklabColours = new(); static TestColours() { NamedColours = File.ReadAllLines(Path.Combine("Utils", "NamedColours.csv")) .Skip(1).Select(CreateNamedColour).ToList(); - + for (var i = 0; i < 1000; i++) { RandomHexColours.Add(GenerateRandomHex()); @@ -34,6 +35,7 @@ static TestColours() RandomHslColours.Add(GetRandomHsl()); RandomXyzColours.Add(GetRandomXyz()); RandomLabColours.Add(GetRandomLab()); + RandomLuvColours.Add(GetRandomLuv()); RandomOklabColours.Add(GetRandomOklab()); } } @@ -44,7 +46,9 @@ static TestColours() internal static ColourTriplet GetRandomHsl() => new(Random.NextDouble() * 360, Random.NextDouble(), Random.NextDouble()); internal static ColourTriplet GetRandomXyz() => new(Random.NextDouble(), Random.NextDouble(), Random.NextDouble()); internal static ColourTriplet GetRandomLab() => new(Random.NextDouble() * 100, Random.NextDouble() * 256 - 128, Random.NextDouble() * 256 - 128); + internal static ColourTriplet GetRandomLuv() => new(Random.NextDouble() * 100, Random.NextDouble() * 200 - 100, Random.NextDouble() * 200 - 100); internal static ColourTriplet GetRandomOklab() => new(Random.NextDouble(), Random.NextDouble(), Random.NextDouble()); + internal static double GetRandomAlpha() => Random.NextDouble(); diff --git a/Unicolour/ColourTriplet.cs b/Unicolour/ColourTriplet.cs index e02dd548..0c8d6f5e 100644 --- a/Unicolour/ColourTriplet.cs +++ b/Unicolour/ColourTriplet.cs @@ -6,4 +6,5 @@ public record ColourTriplet(double First, double Second, double Third) public double Second { get; } = Second; public double Third { get; } = Third; public (double, double, double) Tuple => (First, Second, Third); + public override string ToString() => Tuple.ToString(); } \ No newline at end of file diff --git a/Unicolour/Conversion.cs b/Unicolour/Conversion.cs index 68156bec..c2e61fd7 100644 --- a/Unicolour/Conversion.cs +++ b/Unicolour/Conversion.cs @@ -7,9 +7,11 @@ internal static class Conversion // https://en.wikipedia.org/wiki/HSL_and_HSV#From_RGB public static Hsb RgbToHsb(Rgb rgb) { - var r = rgb.R; - var g = rgb.G; - var b = rgb.B; + // this is just a transformation from Cartesian coordinates to cylindrical coordinates + // so ensure values are within the mappable range + var r = rgb.ConstrainedR; + var g = rgb.ConstrainedG; + var b = rgb.ConstrainedB; var components = new[] {r, g, b}; var xMax = components.Max(); @@ -27,40 +29,15 @@ public static Hsb RgbToHsb(Rgb rgb) var saturation = brightness == 0 ? 0 : chroma / brightness; return new Hsb(hue, saturation, brightness, false); } - - // https://en.wikipedia.org/wiki/HSL_and_HSV#From_RGB - public static Hsl RgbToHsl(Rgb rgb) - { - var r = rgb.R; - var g = rgb.G; - var b = rgb.B; - - var components = new[] {r, g, b}; - var xMax = components.Max(); - var xMin = components.Min(); - var chroma = xMax - xMin; - var lightness = (xMax + xMin) / 2.0; - double hue; - if (chroma == 0.0) hue = 0; - else if (xMax == r) hue = 60 * (0 + ((g - b) / chroma)); - else if (xMax == g) hue = 60 * (2 + ((b - r) / chroma)); - else if (xMax == b) hue = 60 * (4 + ((r - g) / chroma)); - else throw new InvalidOperationException(); - hue = hue < 0 ? 360 + hue : hue; - var saturation = lightness > 0.0 && lightness < 1.0 - ? (xMax - lightness) / Math.Min(lightness, 1 - lightness) - : 0.0; - - return new Hsl(hue, saturation, lightness, false); - } - // https://en.wikipedia.org/wiki/HSL_and_HSV#HSV_to_RGB public static Rgb HsbToRgb(Hsb hsb, Configuration config) { - var hue = hsb.H; - var saturation = hsb.S; - var brightness = hsb.B; + // this is just a transformation from cylindrical coordinates to Cartesian coordinates + // so ensure values are within the mappable range + var hue = hsb.ConstrainedH; + var saturation = hsb.ConstrainedS; + var brightness = hsb.ConstrainedB; var chroma = brightness * saturation; var h = hue / 60; @@ -85,10 +62,15 @@ public static Rgb HsbToRgb(Hsb hsb, Configuration config) // https://en.wikipedia.org/wiki/HSL_and_HSV#HSV_to_HSL public static Hsl HsbToHsl(Hsb hsb) { - var hue = hsb.H; - var lightness = hsb.B * (1 - hsb.S / 2); + // this is just a transformation between 2 different cylindrical coordinates systems + // so ensure values are within the mappable range + var hue = hsb.ConstrainedH; + var hsbSaturation = hsb.ConstrainedS; + var brightness = hsb.ConstrainedB; + + var lightness = brightness * (1 - hsbSaturation / 2); var saturation = lightness is > 0.0 and < 1.0 - ? (hsb.B - lightness) / Math.Min(lightness, 1 - lightness) + ? (brightness - lightness) / Math.Min(lightness, 1 - lightness) : 0; return new Hsl(hue, saturation, lightness, hsb.HasHue); @@ -97,9 +79,13 @@ public static Hsl HsbToHsl(Hsb hsb) // https://en.wikipedia.org/wiki/HSL_and_HSV#HSL_to_HSV public static Hsb HslToHsb(Hsl hsl) { - var hue = hsl.H; - var lightness = hsl.L; - var brightness = lightness + hsl.S * Math.Min(lightness, 1 - lightness); + // this is just a transformation between 2 different cylindrical coordinates systems + // so ensure values are within the mappable range + var hue = hsl.ConstrainedH; + var hslSaturation = hsl.ConstrainedS; + var lightness = hsl.ConstrainedL; + + var brightness = lightness + hslSaturation * Math.Min(lightness, 1 - lightness); var saturation = brightness > 0.0 ? 2 * (1 - lightness / brightness) : 0; @@ -189,6 +175,51 @@ public static Xyz LabToXyz(Lab lab, Configuration config) return new Xyz(x, y, z); } + // https://en.wikipedia.org/wiki/CIELUV#The_forward_transformation + public static Luv XyzToLuv(Xyz xyz, Configuration config) + { + double U(double x, double y, double z) => 4 * x / (x + 15 * y + 3 * z); + double V(double x, double y, double z) => 9 * y / (x + 15 * y + 3 * z); + + var (xRef, yRef, zRef) = config.XyzWhitePoint; + var uPrime = U(xyz.X * 100, xyz.Y * 100, xyz.Z * 100); + var uPrimeRef = U(xRef, yRef, zRef); + var vPrime = V(xyz.X * 100, xyz.Y * 100, xyz.Z * 100); + var vPrimeRef = V(xRef, yRef, zRef); + + var yRatio = xyz.Y * 100 / yRef; + var l = yRatio > Math.Pow(6.0 / 29.0, 3) ? 116 * CubeRoot(yRatio) - 16 : Math.Pow(29 / 3.0, 3) * yRatio; + var u = 13 * l * (uPrime - uPrimeRef); + var v = 13 * l * (vPrime - vPrimeRef); + + double ZeroNaN(double value) => double.IsNaN(value) ? 0.0 : value; + return new Luv(ZeroNaN(l), ZeroNaN(u), ZeroNaN(v)); + } + + // https://en.wikipedia.org/wiki/CIELUV#The_reverse_transformation + public static Xyz LuvToXyz(Luv luv, Configuration config) + { + var l = luv.L; + var u = luv.U; + var v = luv.V; + + double U(double x, double y, double z) => 4 * x / (x + 15 * y + 3 * z); + double V(double x, double y, double z) => 9 * y / (x + 15 * y + 3 * z); + + var (xRef, yRef, zRef) = config.XyzWhitePoint; + var uPrimeRef = U(xRef, yRef, zRef); + var uPrime = u / (13 * l) + uPrimeRef; + var vPrimeRef = V(xRef, yRef, zRef); + var vPrime = v / (13 * l) + vPrimeRef; + + var y = (l > 8 ? yRef * Math.Pow((l + 16) / 116.0, 3) : yRef * l * Math.Pow(3 / 29.0, 3)) / 100.0; + var x = y * ((9 * uPrime) / (4 * vPrime)); + var z = y * ((12 - 3 * uPrime - 20 * vPrime) / (4 * vPrime)); + + double ZeroNaN(double value) => double.IsNaN(value) || double.IsInfinity(value) ? 0.0 : value; + return new Xyz(ZeroNaN(x), ZeroNaN(y), ZeroNaN(z)); + } + // https://bottosson.github.io/posts/oklab/#converting-from-xyz-to-oklab public static Oklab XyzToOklab(Xyz xyz, Configuration config) { diff --git a/Unicolour/Hsb.cs b/Unicolour/Hsb.cs index 213587cd..f4448152 100644 --- a/Unicolour/Hsb.cs +++ b/Unicolour/Hsb.cs @@ -7,11 +7,11 @@ public record Hsb public double S { get; } public double B { get; } public ColourTriplet Triplet => new(H, S, B); - - public double ClampedH => H.Clamp(0.0, 360.0); - public double ClampedS => S.Clamp(0.0, 1.0); - public double ClampedB => B.Clamp(0.0, 1.0); - public ColourTriplet ClampedTriplet => new(ClampedH, ClampedS, ClampedB); + + public double ConstrainedH => H.Modulo(360.0); + public double ConstrainedS => S.Clamp(0.0, 1.0); + public double ConstrainedB => B.Clamp(0.0, 1.0); + public ColourTriplet ConstrainedTriplet => new(ConstrainedH, ConstrainedS, ConstrainedB); // RGB(0,0,0) is black, but has no explicit hue (and don't want to assume red) // HSB(0,0,0) is black, but want to acknowledge the explicit red hue of 0 diff --git a/Unicolour/Hsl.cs b/Unicolour/Hsl.cs index 7cddcb2f..1fb34d50 100644 --- a/Unicolour/Hsl.cs +++ b/Unicolour/Hsl.cs @@ -8,10 +8,10 @@ public record Hsl public double L { get; } public ColourTriplet Triplet => new(H, S, L); - public double ClampedH => H.Clamp(0.0, 360.0); - public double ClampedS => S.Clamp(0.0, 1.0); - public double ClampedL => L.Clamp(0.0, 1.0); - public ColourTriplet ClampedTriplet => new(ClampedH, ClampedS, ClampedL); + public double ConstrainedH => H.Modulo(360.0); + public double ConstrainedS => S.Clamp(0.0, 1.0); + public double ConstrainedL => L.Clamp(0.0, 1.0); + public ColourTriplet ConstrainedTriplet => new(ConstrainedH, ConstrainedS, ConstrainedL); public bool HasHue => explicitHue || S > 0.0 && L is > 0.0 and < 1.0; diff --git a/Unicolour/Interpolation.cs b/Unicolour/Interpolation.cs index 97ba9f10..3836e853 100644 --- a/Unicolour/Interpolation.cs +++ b/Unicolour/Interpolation.cs @@ -53,6 +53,15 @@ public static Unicolour InterpolateLab(this Unicolour startColour, Unicolour end return Unicolour.FromLab(startColour.Config, l, a, b, alpha); } + public static Unicolour InterpolateLuv(this Unicolour startColour, Unicolour endColour, double distance) + { + GuardConfiguration(startColour, endColour); + + var (l, u, v) = InterpolateTriplet(startColour.Luv.Triplet, endColour.Luv.Triplet, distance); + var alpha = Interpolate(startColour.Alpha.A, endColour.Alpha.A, distance); + return Unicolour.FromLuv(startColour.Config, l, u, v, alpha); + } + public static Unicolour InterpolateOklab(this Unicolour startColour, Unicolour endColour, double distance) { GuardConfiguration(startColour, endColour); diff --git a/Unicolour/Luv.cs b/Unicolour/Luv.cs new file mode 100644 index 00000000..05a591a4 --- /dev/null +++ b/Unicolour/Luv.cs @@ -0,0 +1,16 @@ +namespace Wacton.Unicolour; + +public record Luv(double L, double U, double V) +{ + public double L { get; } = L; + public double U { get; } = U; + public double V { get; } = V; + public ColourTriplet Triplet => new(L, U, V); + + public override string ToString() + { + var prefixU = U > 0 ? "+" : string.Empty; + var prefixV = V > 0 ? "+" : string.Empty; + return $"{Math.Round(L, 2)} {prefixU}{Math.Round(U, 2)} {prefixV}{Math.Round(V, 2)}"; + } +} \ No newline at end of file diff --git a/Unicolour/Rgb.cs b/Unicolour/Rgb.cs index 91ba2bb3..0ab95abf 100644 --- a/Unicolour/Rgb.cs +++ b/Unicolour/Rgb.cs @@ -7,14 +7,14 @@ public record Rgb public double B { get; } public ColourTriplet Triplet => new(R, G, B); - public double ClampedR => R.Clamp(0.0, 1.0); - public double ClampedG => G.Clamp(0.0, 1.0); - public double ClampedB => B.Clamp(0.0, 1.0); - public ColourTriplet ClampedTriplet => new(ClampedR, ClampedG, ClampedB); + public double ConstrainedR => R.Clamp(0.0, 1.0); + public double ConstrainedG => G.Clamp(0.0, 1.0); + public double ConstrainedB => B.Clamp(0.0, 1.0); + public ColourTriplet ConstrainedTriplet => new(ConstrainedR, ConstrainedG, ConstrainedB); - public int R255 => (int) Math.Round(ClampedR * 255); - public int G255 => (int) Math.Round(ClampedG * 255); - public int B255 => (int) Math.Round(ClampedB * 255); + public int R255 => (int) Math.Round(ConstrainedR * 255); + public int G255 => (int) Math.Round(ConstrainedG * 255); + public int B255 => (int) Math.Round(ConstrainedB * 255); public ColourTriplet Triplet255 => new(R255, G255, B255); private readonly Func inverseCompand; @@ -22,6 +22,11 @@ public record Rgb public double GLinear => inverseCompand(G); public double BLinear => inverseCompand(B); public ColourTriplet TripletLinear => new(RLinear, GLinear, BLinear); + + public double ConstrainedRLinear => RLinear.Clamp(0.0, 1.0); + public double ConstrainedGLinear => GLinear.Clamp(0.0, 1.0); + public double ConstrainedBLinear => BLinear.Clamp(0.0, 1.0); + public ColourTriplet ConstrainedTripletLinear => new(ConstrainedRLinear, ConstrainedGLinear, ConstrainedBLinear); public string Hex => $"#{R255:X2}{G255:X2}{B255:X2}"; diff --git a/Unicolour/Unicolour.cs b/Unicolour/Unicolour.cs index 2121f9a6..0f32badb 100644 --- a/Unicolour/Unicolour.cs +++ b/Unicolour/Unicolour.cs @@ -8,6 +8,7 @@ public partial class Unicolour : IEquatable private Hsl? hsl; private Xyz? xyz; private Lab? lab; + private Luv? luv; private Oklab? oklab; public Rgb Rgb => Get(() => rgb, ColourSpace.Rgb)!; @@ -15,6 +16,7 @@ public partial class Unicolour : IEquatable public Hsl Hsl => Get(() => hsl, ColourSpace.Hsl)!; public Xyz Xyz => Get(() => xyz, ColourSpace.Xyz)!; public Lab Lab => Get(() => lab, ColourSpace.Lab)!; + public Luv Luv => Get(() => luv, ColourSpace.Luv)!; public Oklab Oklab => Get(() => oklab, ColourSpace.Oklab)!; public Alpha Alpha { get; } public Configuration Config { get; } @@ -57,6 +59,7 @@ private bool ColourSpaceEquals(Unicolour other) ColourSpace.Hsl => Hsl.Equals(other.Hsl), ColourSpace.Xyz => Xyz.Equals(other.Xyz), ColourSpace.Lab => Lab.Equals(other.Lab), + ColourSpace.Luv => Luv.Equals(other.Luv), ColourSpace.Oklab => Oklab.Equals(other.Oklab), _ => throw new ArgumentOutOfRangeException() }; @@ -73,6 +76,7 @@ public override int GetHashCode() ColourSpace.Hsl => Hsl.GetHashCode() * 397, ColourSpace.Xyz => Xyz.GetHashCode() * 397, ColourSpace.Lab => Lab.GetHashCode() * 397, + ColourSpace.Luv => Luv.GetHashCode() * 397, ColourSpace.Oklab => Oklab.GetHashCode() * 397, _ => throw new ArgumentOutOfRangeException() }; diff --git a/Unicolour/Unicolour.csproj b/Unicolour/Unicolour.csproj index 58e3bb39..c25e3416 100644 --- a/Unicolour/Unicolour.csproj +++ b/Unicolour/Unicolour.csproj @@ -15,9 +15,9 @@ netstandard2.0 True Resources\Unicolour.png - 1.3.0 - colour color converter RGB HSB HSV HSL XYZ LAB Oklab color-spaces colour-spaces interpolation comparison contrast luminance deltaE - Add Oklab support + 1.4.0 + colour color converter RGB HSB HSV HSL XYZ LAB LUV Oklab color-spaces colour-spaces interpolation comparison contrast luminance deltaE + Add LUV support Resources\Unicolour.ico LICENSE diff --git a/Unicolour/UnicolourConstructors.cs b/Unicolour/UnicolourConstructors.cs index 47693f0b..92a655ee 100644 --- a/Unicolour/UnicolourConstructors.cs +++ b/Unicolour/UnicolourConstructors.cs @@ -7,6 +7,7 @@ public partial class Unicolour private Unicolour(Configuration config, Hsl hsl, Alpha alpha) : this(config, alpha, ColourSpace.Hsl) => this.hsl = hsl; private Unicolour(Configuration config, Xyz xyz, Alpha alpha) : this(config, alpha, ColourSpace.Xyz) => this.xyz = xyz; private Unicolour(Configuration config, Lab lab, Alpha alpha) : this(config, alpha, ColourSpace.Lab) => this.lab = lab; + private Unicolour(Configuration config, Luv luv, Alpha alpha) : this(config, alpha, ColourSpace.Luv) => this.luv = luv; private Unicolour(Configuration config, Oklab oklab, Alpha alpha) : this(config, alpha, ColourSpace.Oklab) => this.oklab = oklab; public static Unicolour FromHex(string hex) => FromHex(Configuration.Default, hex); @@ -46,6 +47,11 @@ public static Unicolour FromHex(Configuration config, string hex) public static Unicolour FromLab(double l, double a, double b, double alpha = 1.0) => FromLab(Configuration.Default, l, a, b, alpha); public static Unicolour FromLab(Configuration config, double l, double a, double b, double alpha = 1.0) => new(config, new Lab(l, a, b), new Alpha(alpha)); + public static Unicolour FromLuv((double l, double u, double v) tuple, double alpha = 1.0) => FromLuv(Configuration.Default, tuple.l, tuple.u, tuple.v, alpha); + public static Unicolour FromLuv(Configuration config, (double l, double u, double v) tuple, double alpha = 1.0) => FromLuv(config, tuple.l, tuple.u, tuple.v, alpha); + public static Unicolour FromLuv(double l, double u, double v, double alpha = 1.0) => FromLuv(Configuration.Default, l, u, v, alpha); + public static Unicolour FromLuv(Configuration config, double l, double u, double v, double alpha = 1.0) => new(config, new Luv(l, u, v), new Alpha(alpha)); + public static Unicolour FromOklab((double l, double a, double b) tuple, double alpha = 1.0) => FromOklab(Configuration.Default, tuple.l, tuple.a, tuple.b, alpha); public static Unicolour FromOklab(Configuration config, (double l, double a, double b) tuple, double alpha = 1.0) => FromOklab(config, tuple.l, tuple.a, tuple.b, alpha); public static Unicolour FromOklab(double l, double a, double b, double alpha = 1.0) => FromOklab(Configuration.Default, l, a, b, alpha); diff --git a/Unicolour/UnicolourSpaces.cs b/Unicolour/UnicolourSpaces.cs index b8a300dd..0b5c384c 100644 --- a/Unicolour/UnicolourSpaces.cs +++ b/Unicolour/UnicolourSpaces.cs @@ -2,7 +2,7 @@ public partial class Unicolour { - private enum ColourSpace { Rgb, Hsb, Hsl, Xyz, Lab, Oklab } + private enum ColourSpace { Rgb, Hsb, Hsl, Xyz, Lab, Luv, Oklab } private readonly Dictionary> conversions = new(); @@ -14,6 +14,7 @@ private void SetupConversions() {ColourSpace.Hsl, () => hsl = Conversion.HsbToHsl(Hsb)}, {ColourSpace.Xyz, () => xyz = Conversion.RgbToXyz(Rgb, Config)}, {ColourSpace.Lab, () => lab = Conversion.XyzToLab(Xyz, Config)}, + {ColourSpace.Luv, () => luv = Conversion.XyzToLuv(Xyz, Config)}, {ColourSpace.Oklab, () => oklab = Conversion.XyzToOklab(Xyz, Config)} }); @@ -23,6 +24,7 @@ private void SetupConversions() {ColourSpace.Hsl, () => hsl = Conversion.HsbToHsl(Hsb)}, {ColourSpace.Xyz, () => xyz = Conversion.RgbToXyz(Rgb, Config)}, {ColourSpace.Lab, () => lab = Conversion.XyzToLab(Xyz, Config)}, + {ColourSpace.Luv, () => luv = Conversion.XyzToLuv(Xyz, Config)}, {ColourSpace.Oklab, () => oklab = Conversion.XyzToOklab(Xyz, Config)} }); @@ -32,6 +34,7 @@ private void SetupConversions() {ColourSpace.Hsb, () => hsb = Conversion.HslToHsb(Hsl)}, {ColourSpace.Xyz, () => xyz = Conversion.RgbToXyz(Rgb, Config)}, {ColourSpace.Lab, () => lab = Conversion.XyzToLab(Xyz, Config)}, + {ColourSpace.Luv, () => luv = Conversion.XyzToLuv(Xyz, Config)}, {ColourSpace.Oklab, () => oklab = Conversion.XyzToOklab(Xyz, Config)} }); @@ -41,6 +44,7 @@ private void SetupConversions() {ColourSpace.Hsb, () => hsb = Conversion.RgbToHsb(Rgb)}, {ColourSpace.Hsl, () => hsl = Conversion.HsbToHsl(Hsb)}, {ColourSpace.Lab, () => lab = Conversion.XyzToLab(Xyz, Config)}, + {ColourSpace.Luv, () => luv = Conversion.XyzToLuv(Xyz, Config)}, {ColourSpace.Oklab, () => oklab = Conversion.XyzToOklab(Xyz, Config)} }); @@ -50,6 +54,17 @@ private void SetupConversions() {ColourSpace.Hsb, () => hsb = Conversion.RgbToHsb(Rgb)}, {ColourSpace.Hsl, () => hsl = Conversion.HsbToHsl(Hsb)}, {ColourSpace.Xyz, () => xyz = Conversion.LabToXyz(Lab, Config)}, + {ColourSpace.Luv, () => luv = Conversion.XyzToLuv(Xyz, Config)}, + {ColourSpace.Oklab, () => oklab = Conversion.XyzToOklab(Xyz, Config)} + }); + + conversions.Add(ColourSpace.Luv, new Dictionary + { + {ColourSpace.Rgb, () => rgb = Conversion.XyzToRgb(Xyz, Config)}, + {ColourSpace.Hsb, () => hsb = Conversion.RgbToHsb(Rgb)}, + {ColourSpace.Hsl, () => hsl = Conversion.HsbToHsl(Hsb)}, + {ColourSpace.Xyz, () => xyz = Conversion.LuvToXyz(Luv, Config)}, + {ColourSpace.Lab, () => lab = Conversion.XyzToLab(Xyz, Config)}, {ColourSpace.Oklab, () => oklab = Conversion.XyzToOklab(Xyz, Config)} }); @@ -59,7 +74,8 @@ private void SetupConversions() {ColourSpace.Hsb, () => hsb = Conversion.RgbToHsb(Rgb)}, {ColourSpace.Hsl, () => hsl = Conversion.HsbToHsl(Hsb)}, {ColourSpace.Xyz, () => xyz = Conversion.OklabToXyz(Oklab, Config)}, - {ColourSpace.Lab, () => lab = Conversion.XyzToLab(Xyz, Config)} + {ColourSpace.Lab, () => lab = Conversion.XyzToLab(Xyz, Config)}, + {ColourSpace.Luv, () => luv = Conversion.XyzToLuv(Xyz, Config)} }); }