diff --git a/src/Q42.HueApi.ColorConverters.Tests/OriginalWithModel/ColorConverterTests-Arrays.cs b/src/Q42.HueApi.ColorConverters.Tests/Gamut/ColorConverterTests-Arrays.cs
similarity index 100%
rename from src/Q42.HueApi.ColorConverters.Tests/OriginalWithModel/ColorConverterTests-Arrays.cs
rename to src/Q42.HueApi.ColorConverters.Tests/Gamut/ColorConverterTests-Arrays.cs
diff --git a/src/Q42.HueApi.ColorConverters.Tests/OriginalWithModel/ColorConverterTests.cs b/src/Q42.HueApi.ColorConverters.Tests/Gamut/ColorConverterTests.cs
similarity index 95%
rename from src/Q42.HueApi.ColorConverters.Tests/OriginalWithModel/ColorConverterTests.cs
rename to src/Q42.HueApi.ColorConverters.Tests/Gamut/ColorConverterTests.cs
index 685c0d72..7295f346 100644
--- a/src/Q42.HueApi.ColorConverters.Tests/OriginalWithModel/ColorConverterTests.cs
+++ b/src/Q42.HueApi.ColorConverters.Tests/Gamut/ColorConverterTests.cs
@@ -1,5 +1,6 @@
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-using Q42.HueApi.ColorConverters.OriginalWithModel;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Q42.HueApi.ColorConverters.Gamut;
+using Q42.HueApi.Models.Gamut;
using System;
namespace Q42.HueApi.ColorConverters.Tests
@@ -17,12 +18,12 @@ public void ColorConversionWhitePoint()
foreach (string model in models)
{
// Make sure that Philips' white point resolves to #FFFFFF for all lights.
- var rgb = HueColorConverter.XYToRgb(CIE1931Point.PhilipsWhite, model);
+ var rgb = HueColorConverter.XYToRgb(CIE1931Point.PhilipsWhite, CIE1931Gamut.ForModel(model));
Assert.AreEqual(rgb.R, 1.0, 0.0001);
Assert.AreEqual(rgb.G, 1.0, 0.0001);
Assert.AreEqual(rgb.B, 1.0, 0.0001);
- var xy = HueColorConverter.RgbToXY(new RGBColor(1.0, 1.0, 1.0), model);
+ var xy = HueColorConverter.RgbToXY(new RGBColor(1.0, 1.0, 1.0), CIE1931Gamut.ForModel(model));
AssertAreEqual(CIE1931Point.PhilipsWhite, xy, 0.0001);
}
}
@@ -51,8 +52,8 @@ public void ColorsOutsideGamutAdjustedToInBeInGamutOnConversion()
// A color green outside Gamut A.
CIE1931Point greenOutsideGamut = new CIE1931Point(0.21, 0.75);
- var a = HueColorConverter.XYToRgb(gamutGreen, "LST001");
- var b = HueColorConverter.XYToRgb(greenOutsideGamut, "LST001");
+ var a = HueColorConverter.XYToRgb(gamutGreen, CIE1931Gamut.ForModel("LST001"));
+ var b = HueColorConverter.XYToRgb(greenOutsideGamut, CIE1931Gamut.ForModel("LST001"));
// Points should be equal, since the green outside the gamut should
// be adjusted the the nearest green in-gamut.
@@ -76,7 +77,7 @@ public void CompareColorConversionWithReference()
var referenceXy = ReferenceColorConverter.XyFromColor(red, green, blue);
// LCT001 uses Gamut B, which is the gamut used in the reference implementation.
- var actualXy = HueColorConverter.RgbToXY(new RGBColor(red, green, blue), "LCT001");
+ var actualXy = HueColorConverter.RgbToXY(new RGBColor(red, green, blue), CIE1931Gamut.ForModel("LCT001"));
AssertAreEqual(referenceXy, actualXy, 0.0001);
}
@@ -115,8 +116,8 @@ public void ColorConversionRoundtripInsideGamut()
|| !ReferenceColorConverter.CheckPointInLampsReach(originalXy)
|| !CIE1931Gamut.PhilipsWideGamut.Contains(originalXy));
- RGBColor rgb = HueColorConverter.XYToRgb(originalXy, "LCT001");
- var xy = HueColorConverter.RgbToXY(rgb, "LCT001");
+ RGBColor rgb = HueColorConverter.XYToRgb(originalXy, CIE1931Gamut.ForModel("LCT001"));
+ var xy = HueColorConverter.RgbToXY(rgb, CIE1931Gamut.ForModel("LCT001"));
AssertAreEqual(originalXy, xy, 0.0001);
}
@@ -139,8 +140,8 @@ public void ColorConversionRoundtripAllPoints()
}
while (originalXy.x + originalXy.y >= 1.0);
- RGBColor rgb = HueColorConverter.XYToRgb(originalXy, "LCT001");
- var xy = HueColorConverter.RgbToXY(rgb, "LCT001");
+ RGBColor rgb = HueColorConverter.XYToRgb(originalXy, CIE1931Gamut.ForModel("LCT001"));
+ var xy = HueColorConverter.RgbToXY(rgb, CIE1931Gamut.ForModel("LCT001"));
// We expect a point that is both inside the lamp's gamut and the "wide gamut"
// used for XYZ->RGB and RGB->XYZ conversion.
diff --git a/src/Q42.HueApi.ColorConverters.Tests/HueColorConverterTests.cs b/src/Q42.HueApi.ColorConverters.Tests/HueColorConverterTests.cs
index 178dcae3..75e92dac 100644
--- a/src/Q42.HueApi.ColorConverters.Tests/HueColorConverterTests.cs
+++ b/src/Q42.HueApi.ColorConverters.Tests/HueColorConverterTests.cs
@@ -5,7 +5,8 @@
using System.Text;
using System.Threading.Tasks;
using System.Drawing;
-using Q42.HueApi.ColorConverters.OriginalWithModel;
+using Q42.HueApi.Models.Gamut;
+using Q42.HueApi.ColorConverters.Gamut;
namespace Q42.HueApi.ColorConverters.Tests
{
@@ -32,7 +33,7 @@ private Bitmap DrawBitmap(string model)
for (int y = 0; y < dimension; y++)
{
CIE1931Point point = new CIE1931Point(x / (dimension * 1.0), y / (dimension * 1.0));
- var rgb = HueColorConverter.XYToRgb(point, model);
+ var rgb = HueColorConverter.XYToRgb(point, CIE1931Gamut.ForModel(model));
Color c;
if (point.x + point.y > 1.0)
diff --git a/src/Q42.HueApi.ColorConverters.Tests/Q42.HueApi.ColorConverters.Tests.csproj b/src/Q42.HueApi.ColorConverters.Tests/Q42.HueApi.ColorConverters.Tests.csproj
index 9e37bba3..7dee7a43 100644
--- a/src/Q42.HueApi.ColorConverters.Tests/Q42.HueApi.ColorConverters.Tests.csproj
+++ b/src/Q42.HueApi.ColorConverters.Tests/Q42.HueApi.ColorConverters.Tests.csproj
@@ -55,8 +55,8 @@
-
-
+
+
diff --git a/src/Q42.HueApi.ColorConverters/OriginalWithModel/Extensions/LightCommandExtensions.cs b/src/Q42.HueApi.ColorConverters/Gamut/Extensions/LightCommandExtensions.cs
similarity index 67%
rename from src/Q42.HueApi.ColorConverters/OriginalWithModel/Extensions/LightCommandExtensions.cs
rename to src/Q42.HueApi.ColorConverters/Gamut/Extensions/LightCommandExtensions.cs
index 891cd789..ffb82db3 100644
--- a/src/Q42.HueApi.ColorConverters/OriginalWithModel/Extensions/LightCommandExtensions.cs
+++ b/src/Q42.HueApi.ColorConverters/Gamut/Extensions/LightCommandExtensions.cs
@@ -1,20 +1,21 @@
-using System;
+using Q42.HueApi.Models.Gamut;
+using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
-namespace Q42.HueApi.ColorConverters.OriginalWithModel
+namespace Q42.HueApi.ColorConverters.Gamut
{
public static class LightCommandExtensions
{
- public static LightCommand SetColor(this LightCommand lightCommand, RGBColor color, string model = "LCT001")
+ public static LightCommand SetColor(this LightCommand lightCommand, RGBColor color, CIE1931Gamut? gamut)
{
if (lightCommand == null)
throw new ArgumentNullException(nameof(lightCommand));
- var point = HueColorConverter.RgbToXY(color, model);
+ var point = HueColorConverter.RgbToXY(color, gamut);
return lightCommand.SetColor(point.x, point.y);
}
}
diff --git a/src/Q42.HueApi.ColorConverters/Gamut/Extensions/LightExtensions.cs b/src/Q42.HueApi.ColorConverters/Gamut/Extensions/LightExtensions.cs
new file mode 100644
index 00000000..5bfd9173
--- /dev/null
+++ b/src/Q42.HueApi.ColorConverters/Gamut/Extensions/LightExtensions.cs
@@ -0,0 +1,22 @@
+using Q42.HueApi.Models.Gamut;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Q42.HueApi.ColorConverters.Gamut
+{
+ public static class LightExtensions
+ {
+ public static string ToHex(this Light light)
+ {
+ return light.ToHex(light.Capabilities?.Control?.ColorGamut);
+ }
+
+ public static string ToHex(this Light light, CIE1931Gamut? gamut)
+ {
+ return HueColorConverter.HexFromState(light.State, gamut);
+ }
+ }
+}
diff --git a/src/Q42.HueApi.ColorConverters/Gamut/Extensions/StateExtensions.cs b/src/Q42.HueApi.ColorConverters/Gamut/Extensions/StateExtensions.cs
new file mode 100644
index 00000000..7c7067b9
--- /dev/null
+++ b/src/Q42.HueApi.ColorConverters/Gamut/Extensions/StateExtensions.cs
@@ -0,0 +1,22 @@
+using Q42.HueApi.Models.Gamut;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Q42.HueApi.ColorConverters.Gamut
+{
+ public static class StateExtensions
+ {
+ public static string ToHex(this State state, CIE1931Gamut? gamut)
+ {
+ return HueColorConverter.HexFromState(state, gamut);
+ }
+
+ public static RGBColor ToRgb(this State state, CIE1931Gamut? gamut)
+ {
+ return HueColorConverter.RgbFromState(state, gamut);
+ }
+ }
+}
diff --git a/src/Q42.HueApi.ColorConverters/OriginalWithModel/HueColorConverter.cs b/src/Q42.HueApi.ColorConverters/Gamut/HueColorConverter.cs
similarity index 90%
rename from src/Q42.HueApi.ColorConverters/OriginalWithModel/HueColorConverter.cs
rename to src/Q42.HueApi.ColorConverters/Gamut/HueColorConverter.cs
index bd8cf807..067abd0f 100644
--- a/src/Q42.HueApi.ColorConverters/OriginalWithModel/HueColorConverter.cs
+++ b/src/Q42.HueApi.ColorConverters/Gamut/HueColorConverter.cs
@@ -1,11 +1,12 @@
-using Q42.HueApi.ColorConverters;
+using Q42.HueApi.ColorConverters;
+using Q42.HueApi.Models.Gamut;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
-namespace Q42.HueApi.ColorConverters.OriginalWithModel
+namespace Q42.HueApi.ColorConverters.Gamut
{
///
/// Used to convert colors between XY and RGB
@@ -16,7 +17,7 @@ namespace Q42.HueApi.ColorConverters.OriginalWithModel
///
internal static class HueColorConverter
{
- public static CIE1931Point RgbToXY(RGBColor color, string model)
+ public static CIE1931Point RgbToXY(RGBColor color, CIE1931Gamut? gamut)
{
// Apply gamma correction. Convert non-linear RGB colour components
// to linear color intensity levels.
@@ -61,13 +62,10 @@ public static CIE1931Point RgbToXY(RGBColor color, string model)
xyPoint = new CIE1931Point(X / (X + Y + Z), Y / (X + Y + Z));
}
- if (model != null)
+ if (gamut.HasValue)
{
- //Check if the given XY value is within the colourreach of our lamps.
- CIE1931Gamut gamut = CIE1931Gamut.ForModel(model);
-
// The point, adjusted it to the nearest point that is within the gamut of the lamp, if neccessary.
- return gamut.NearestContainedPoint(xyPoint);
+ return gamut.Value.NearestContainedPoint(xyPoint);
}
return xyPoint;
}
@@ -78,9 +76,9 @@ public static CIE1931Point RgbToXY(RGBColor color, string model)
///
///
///
- public static string HexFromState(State state, string model)
+ public static string HexFromState(State state, CIE1931Gamut? gamut)
{
- var rgb = RgbFromState(state, model);
+ var rgb = RgbFromState(state, gamut);
return rgb.ToHex();
}
@@ -90,7 +88,7 @@ public static string HexFromState(State state, string model)
///
///
///
- public static RGBColor RgbFromState(State state, string model)
+ public static RGBColor RgbFromState(State state, CIE1931Gamut? gamut)
{
if (state == null)
throw new ArgumentNullException(nameof(state));
@@ -98,22 +96,20 @@ public static RGBColor RgbFromState(State state, string model)
return new RGBColor(0,0,0);
if (state.ColorCoordinates != null && state.ColorCoordinates.Length == 2) //Based on XY value
{
- var color = XYToRgb(new CIE1931Point(state.ColorCoordinates[0], state.ColorCoordinates[1]), model);
+ var color = XYToRgb(new CIE1931Point(state.ColorCoordinates[0], state.ColorCoordinates[1]), gamut);
return color;
}
return new RGBColor(1, 1, 1); ;
}
- public static RGBColor XYToRgb(CIE1931Point point, string model)
+ public static RGBColor XYToRgb(CIE1931Point point, CIE1931Gamut? gamut)
{
- if (model != null)
+ if (gamut.HasValue)
{
- CIE1931Gamut gamut = CIE1931Gamut.ForModel(model);
-
// If the color is outside the lamp's gamut, adjust to the nearest color
// inside the lamp's gamut.
- point = gamut.NearestContainedPoint(point);
+ point = gamut.Value.NearestContainedPoint(point);
}
// Also adjust it to be in the Philips "Wide Gamut" if not already.
diff --git a/src/Q42.HueApi.ColorConverters/OriginalWithModel/CIE1931Gamut.cs b/src/Q42.HueApi.ColorConverters/OriginalWithModel/CIE1931Gamut.cs
deleted file mode 100644
index d3d46927..00000000
--- a/src/Q42.HueApi.ColorConverters/OriginalWithModel/CIE1931Gamut.cs
+++ /dev/null
@@ -1,200 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace Q42.HueApi.ColorConverters.OriginalWithModel
-{
- ///
- /// Represents a gamut with red, green and blue primaries in CIE1931 color space.
- ///
- internal struct CIE1931Gamut
- {
- public readonly CIE1931Point Red;
- public readonly CIE1931Point Green;
- public readonly CIE1931Point Blue;
-
- public CIE1931Gamut(CIE1931Point red, CIE1931Point green, CIE1931Point blue)
- {
- this.Red = red;
- this.Green = green;
- this.Blue = blue;
- }
-
- public static readonly CIE1931Gamut PhilipsWideGamut = new CIE1931Gamut(
- red: new CIE1931Point(0.700607, 0.299301),
- green: new CIE1931Point(0.172416, 0.746797),
- blue: new CIE1931Point(0.135503, 0.039879)
- );
-
- public static CIE1931Gamut ForModel(string modelId)
- {
- // Details from http://www.developers.meethue.com/documentation/supported-lights
-
- List gamutA = new List() {
- "LLC001" /* Monet, Renoir, Mondriaan (gen II) */,
- "LLC005" /* Bloom (gen II) */,
- "LLC006" /* Iris (gen III) */,
- "LLC007" /* Bloom, Aura (gen III) */,
- "LLC010" /* Iris */,
- "LLC011" /* Hue Bloom */,
- "LLC012" /* Hue Bloom */,
- "LLC013" /* Storylight */,
- "LST001" /* Light Strips */,
- "LLC014" /* Bloom, Aura (gen III) */
- };
-
- List gamutB = new List() {
- "LCT001" /* Hue A19 */,
- "LCT007" /* Hue A19 */,
- "LCT002" /* Hue BR30 */,
- "LCT003" /* Hue GU10 */,
- "LLM001" /* Color Light Module */
- };
-
- List gamutC = new List() {
- "LLC020" /* Hue Go */,
- "LST002" /* Hue LightStrips Plus */,
- "LCT011" /* Hue BR30 */,
- "LCT012" /* Hue color candle */,
- "LCT010" /* Hue A19 gen 3 */,
- "LCT014" /* Hue A19 gen 3 */,
- "LCT015" /* Hue A19 gen 3 */,
- "LCT016" /* Hue A19 gen 3 */
- };
-
- if (gamutA.Contains(modelId))
- {
- return new CIE1931Gamut(
- red: new CIE1931Point(0.704, 0.296),
- green: new CIE1931Point(0.2151, 0.7106),
- blue: new CIE1931Point(0.138, 0.08)
- );
- }
- else if (gamutB.Contains(modelId))
- {
- return new CIE1931Gamut(
- red: new CIE1931Point(0.675, 0.322),
- green: new CIE1931Point(0.409, 0.518),
- blue: new CIE1931Point(0.167, 0.04)
- );
- }
- else if (gamutC.Contains(modelId))
- {
- return new CIE1931Gamut(
- red: new CIE1931Point(0.692, 0.308),
- green: new CIE1931Point(0.17, 0.7),
- blue: new CIE1931Point(0.153, 0.048)
- );
- }
- else
- {
- // A gamut containing all colors (and then some!)
- return new CIE1931Gamut(
- red: new CIE1931Point(1.0F, 0.0F),
- green: new CIE1931Point(0.0F, 1.0F),
- blue: new CIE1931Point(0.0F, 0.0F)
- );
- }
- }
-
- public bool Contains(CIE1931Point point)
- {
- // Arrangement of points in color space:
- //
- // ^ G
- // y|
- // | R
- // | B
- // .------------------->
- // x
- //
- return IsBelow(Blue, Green, point) &&
- IsBelow(Green, Red, point) &&
- IsAbove(Red, Blue, point);
- }
-
- private static bool IsBelow(CIE1931Point a, CIE1931Point b, CIE1931Point point)
- {
- double slope = (a.y - b.y) / (a.x - b.x);
- double intercept = a.y - slope * a.x;
-
- double maxY = point.x * slope + intercept;
- return point.y <= maxY;
- }
-
- private static bool IsAbove(CIE1931Point blue, CIE1931Point red, CIE1931Point point)
- {
- double slope = (blue.y - red.y) / (blue.x - red.x);
- double intercept = blue.y - slope * blue.x;
-
- double minY = point.x * slope + intercept;
- return point.y >= minY;
- }
-
- public CIE1931Point NearestContainedPoint(CIE1931Point point)
- {
- if (Contains(point))
- {
- // If this gamut already contains the point, then no adjustment is required.
- return point;
- }
-
- // Find the closest point on each line in the triangle.
- CIE1931Point pAB = GetClosestPointOnLine(Red, Green, point);
- CIE1931Point pAC = GetClosestPointOnLine(Red, Blue, point);
- CIE1931Point pBC = GetClosestPointOnLine(Green, Blue, point);
-
- //Get the distances per point and see which point is closer to our Point.
- double dAB = GetDistanceBetweenTwoPoints(point, pAB);
- double dAC = GetDistanceBetweenTwoPoints(point, pAC);
- double dBC = GetDistanceBetweenTwoPoints(point, pBC);
-
- double lowest = dAB;
- CIE1931Point closestPoint = pAB;
-
- if (dAC < lowest)
- {
- lowest = dAC;
- closestPoint = pAC;
- }
- if (dBC < lowest)
- {
- lowest = dBC;
- closestPoint = pBC;
- }
- return closestPoint;
- }
-
- private static CIE1931Point GetClosestPointOnLine(CIE1931Point a, CIE1931Point b, CIE1931Point p)
- {
- CIE1931Point AP = new CIE1931Point(p.x - a.x, p.y - a.y);
- CIE1931Point AB = new CIE1931Point(b.x - a.x, b.y - a.y);
-
- double ab2 = AB.x * AB.x + AB.y * AB.y;
- double ap_ab = AP.x * AB.x + AP.y * AB.y;
-
- double t = ap_ab / ab2;
-
- // Bound to ends of line between A and B.
- if (t < 0.0f)
- {
- t = 0.0f;
- }
- else if (t > 1.0f)
- {
- t = 1.0f;
- }
-
- return new CIE1931Point(a.x + AB.x * t, a.y + AB.y * t);
- }
-
- private static double GetDistanceBetweenTwoPoints(CIE1931Point one, CIE1931Point two)
- {
- double dx = one.x - two.x; // horizontal difference
- double dy = one.y - two.y; // vertical difference
- return Math.Sqrt(dx * dx + dy * dy);
- }
- }
-}
diff --git a/src/Q42.HueApi.ColorConverters/OriginalWithModel/Extensions/LightExtensions.cs b/src/Q42.HueApi.ColorConverters/OriginalWithModel/Extensions/LightExtensions.cs
deleted file mode 100644
index 3b9653fa..00000000
--- a/src/Q42.HueApi.ColorConverters/OriginalWithModel/Extensions/LightExtensions.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace Q42.HueApi.ColorConverters.OriginalWithModel
-{
- public static class LightExtensions
- {
- public static string ToHex(this Light light)
- {
- return HueColorConverter.HexFromState(light.State, light.ModelId);
- }
- }
-}
diff --git a/src/Q42.HueApi.ColorConverters/OriginalWithModel/Extensions/StateExtensions.cs b/src/Q42.HueApi.ColorConverters/OriginalWithModel/Extensions/StateExtensions.cs
deleted file mode 100644
index bc0197a2..00000000
--- a/src/Q42.HueApi.ColorConverters/OriginalWithModel/Extensions/StateExtensions.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace Q42.HueApi.ColorConverters.OriginalWithModel
-{
- public static class StateExtensions
- {
- public static string ToHex(this State state, string model = "LCT001")
- {
- return HueColorConverter.HexFromState(state, model);
- }
-
- public static RGBColor ToRgb(this State state, string model = "LCT001")
- {
- return HueColorConverter.RgbFromState(state, model);
- }
- }
-}
diff --git a/src/Q42.HueApi.Tests/App.config b/src/Q42.HueApi.Tests/App.config
index 5ed0560a..e48e929f 100644
--- a/src/Q42.HueApi.Tests/App.config
+++ b/src/Q42.HueApi.Tests/App.config
@@ -1,10 +1,10 @@
-
-
-
+
+
+
diff --git a/src/Q42.HueApi/Converters/GamutConverter.cs b/src/Q42.HueApi/Converters/GamutConverter.cs
new file mode 100644
index 00000000..8139baab
--- /dev/null
+++ b/src/Q42.HueApi/Converters/GamutConverter.cs
@@ -0,0 +1,74 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using Q42.HueApi.Models.Gamut;
+
+namespace Q42.HueApi.Converters
+{
+ internal class GamutConverter : JsonConverter
+ {
+ public override bool CanConvert(Type objectType)
+ {
+ return objectType == typeof(CIE1931Gamut?) || objectType == typeof(CIE1931Gamut);
+ }
+
+ public override bool CanRead
+ {
+ get { return true; }
+ }
+
+ public override bool CanWrite
+ {
+ get { return true; }
+ }
+
+ public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
+ {
+ if (reader.TokenType == JsonToken.StartArray)
+ {
+
+ JArray array = JArray.Load(reader);
+ var gammutValues = array.ToObject>>();
+
+ if (gammutValues.Count == 3)
+ {
+ var red = new CIE1931Point(gammutValues[0][0], gammutValues[0][1]);
+ var green = new CIE1931Point(gammutValues[1][0], gammutValues[1][1]);
+ var blue = new CIE1931Point(gammutValues[2][0], gammutValues[2][1]);
+
+ return new CIE1931Gamut(red, green, blue);
+ }
+ }
+
+ return null;
+ }
+
+ public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
+ {
+ CIE1931Gamut gammut;
+
+ if (value == null)
+ {
+ writer.WriteNull();
+ return;
+ }
+
+ if (value is CIE1931Gamut?)
+ gammut = ((CIE1931Gamut?)value).Value;
+ else
+ gammut = (CIE1931Gamut)value;
+
+ var red = new List() { gammut.Red.x, gammut.Red.y };
+ var green = new List() { gammut.Green.x, gammut.Green.y };
+ var blue = new List() { gammut.Blue.x, gammut.Blue.y };
+
+ var result = new List>() { red, green, blue };
+
+ writer.WriteValue(result);
+ }
+ }
+}
diff --git a/src/Q42.HueApi/Models/Gamut/CIE1931Gamut.cs b/src/Q42.HueApi/Models/Gamut/CIE1931Gamut.cs
new file mode 100644
index 00000000..9d6711a4
--- /dev/null
+++ b/src/Q42.HueApi/Models/Gamut/CIE1931Gamut.cs
@@ -0,0 +1,213 @@
+using Newtonsoft.Json;
+using Q42.HueApi.Converters;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Q42.HueApi.Models.Gamut
+{
+ ///
+ /// Represents a gamut with red, green and blue primaries in CIE1931 color space.
+ ///
+ [JsonConverter(typeof(GamutConverter))]
+ public struct CIE1931Gamut
+ {
+ public readonly CIE1931Point Red;
+ public readonly CIE1931Point Green;
+ public readonly CIE1931Point Blue;
+
+ public CIE1931Gamut(CIE1931Point red, CIE1931Point green, CIE1931Point blue)
+ {
+ this.Red = red;
+ this.Green = green;
+ this.Blue = blue;
+ }
+
+ public static readonly CIE1931Gamut PhilipsWideGamut = new CIE1931Gamut(
+ red: new CIE1931Point(0.700607, 0.299301),
+ green: new CIE1931Point(0.172416, 0.746797),
+ blue: new CIE1931Point(0.135503, 0.039879)
+ );
+
+ public static CIE1931Gamut ModelTypeA => new CIE1931Gamut(
+ red: new CIE1931Point(0.704, 0.296),
+ green: new CIE1931Point(0.2151, 0.7106),
+ blue: new CIE1931Point(0.138, 0.08)
+ );
+
+ public static CIE1931Gamut ModelTypeB => new CIE1931Gamut(
+ red: new CIE1931Point(0.675, 0.322),
+ green: new CIE1931Point(0.409, 0.518),
+ blue: new CIE1931Point(0.167, 0.04)
+ );
+
+ public static CIE1931Gamut ModelTypeC => new CIE1931Gamut(
+ red: new CIE1931Point(0.692, 0.308),
+ green: new CIE1931Point(0.17, 0.7),
+ blue: new CIE1931Point(0.153, 0.048)
+ );
+
+ public static CIE1931Gamut All => new CIE1931Gamut(
+ red: new CIE1931Point(1.0F, 0.0F),
+ green: new CIE1931Point(0.0F, 1.0F),
+ blue: new CIE1931Point(0.0F, 0.0F)
+ );
+
+
+ [Obsolete("List of Models is no longer updated by Philips. Use The Capabilities.GamutType or Gamut from the Light")]
+ public static CIE1931Gamut ForModel(string modelId)
+ {
+ // Details from http://www.developers.meethue.com/documentation/supported-lights
+
+ List gamutA = new List() {
+ "LLC001" /* Monet, Renoir, Mondriaan (gen II) */,
+ "LLC005" /* Bloom (gen II) */,
+ "LLC006" /* Iris (gen III) */,
+ "LLC007" /* Bloom, Aura (gen III) */,
+ "LLC010" /* Iris */,
+ "LLC011" /* Hue Bloom */,
+ "LLC012" /* Hue Bloom */,
+ "LLC013" /* Storylight */,
+ "LST001" /* Light Strips */,
+ "LLC014" /* Bloom, Aura (gen III) */
+ };
+
+ List gamutB = new List() {
+ "LCT001" /* Hue A19 */,
+ "LCT007" /* Hue A19 */,
+ "LCT002" /* Hue BR30 */,
+ "LCT003" /* Hue GU10 */,
+ "LLM001" /* Color Light Module */
+ };
+
+ List gamutC = new List() {
+ "LLC020" /* Hue Go */,
+ "LST002" /* Hue LightStrips Plus */,
+ "LCT011" /* Hue BR30 */,
+ "LCT012" /* Hue color candle */,
+ "LCT010" /* Hue A19 gen 3 */,
+ "LCT014" /* Hue A19 gen 3 */,
+ "LCT015" /* Hue A19 gen 3 */,
+ "LCT016" /* Hue A19 gen 3 */
+ };
+
+ if (gamutA.Contains(modelId))
+ {
+ return CIE1931Gamut.ModelTypeA;
+ }
+ else if (gamutB.Contains(modelId))
+ {
+ return CIE1931Gamut.ModelTypeB;
+ }
+ else if (gamutC.Contains(modelId))
+ {
+ return CIE1931Gamut.ModelTypeC;
+ }
+ else
+ {
+ // A gamut containing all colors (and then some!)
+ return CIE1931Gamut.All;
+ }
+ }
+
+ public bool Contains(CIE1931Point point)
+ {
+ // Arrangement of points in color space:
+ //
+ // ^ G
+ // y|
+ // | R
+ // | B
+ // .------------------->
+ // x
+ //
+ return IsBelow(Blue, Green, point) &&
+ IsBelow(Green, Red, point) &&
+ IsAbove(Red, Blue, point);
+ }
+
+ private static bool IsBelow(CIE1931Point a, CIE1931Point b, CIE1931Point point)
+ {
+ double slope = (a.y - b.y) / (a.x - b.x);
+ double intercept = a.y - slope * a.x;
+
+ double maxY = point.x * slope + intercept;
+ return point.y <= maxY;
+ }
+
+ private static bool IsAbove(CIE1931Point blue, CIE1931Point red, CIE1931Point point)
+ {
+ double slope = (blue.y - red.y) / (blue.x - red.x);
+ double intercept = blue.y - slope * blue.x;
+
+ double minY = point.x * slope + intercept;
+ return point.y >= minY;
+ }
+
+ public CIE1931Point NearestContainedPoint(CIE1931Point point)
+ {
+ if (Contains(point))
+ {
+ // If this gamut already contains the point, then no adjustment is required.
+ return point;
+ }
+
+ // Find the closest point on each line in the triangle.
+ CIE1931Point pAB = GetClosestPointOnLine(Red, Green, point);
+ CIE1931Point pAC = GetClosestPointOnLine(Red, Blue, point);
+ CIE1931Point pBC = GetClosestPointOnLine(Green, Blue, point);
+
+ //Get the distances per point and see which point is closer to our Point.
+ double dAB = GetDistanceBetweenTwoPoints(point, pAB);
+ double dAC = GetDistanceBetweenTwoPoints(point, pAC);
+ double dBC = GetDistanceBetweenTwoPoints(point, pBC);
+
+ double lowest = dAB;
+ CIE1931Point closestPoint = pAB;
+
+ if (dAC < lowest)
+ {
+ lowest = dAC;
+ closestPoint = pAC;
+ }
+ if (dBC < lowest)
+ {
+ lowest = dBC;
+ closestPoint = pBC;
+ }
+ return closestPoint;
+ }
+
+ private static CIE1931Point GetClosestPointOnLine(CIE1931Point a, CIE1931Point b, CIE1931Point p)
+ {
+ CIE1931Point AP = new CIE1931Point(p.x - a.x, p.y - a.y);
+ CIE1931Point AB = new CIE1931Point(b.x - a.x, b.y - a.y);
+
+ double ab2 = AB.x * AB.x + AB.y * AB.y;
+ double ap_ab = AP.x * AB.x + AP.y * AB.y;
+
+ double t = ap_ab / ab2;
+
+ // Bound to ends of line between A and B.
+ if (t < 0.0f)
+ {
+ t = 0.0f;
+ }
+ else if (t > 1.0f)
+ {
+ t = 1.0f;
+ }
+
+ return new CIE1931Point(a.x + AB.x * t, a.y + AB.y * t);
+ }
+
+ private static double GetDistanceBetweenTwoPoints(CIE1931Point one, CIE1931Point two)
+ {
+ double dx = one.x - two.x; // horizontal difference
+ double dy = one.y - two.y; // vertical difference
+ return Math.Sqrt(dx * dx + dy * dy);
+ }
+ }
+}
diff --git a/src/Q42.HueApi.ColorConverters/OriginalWithModel/CIE1931Point.cs b/src/Q42.HueApi/Models/Gamut/CIE1931Point.cs
similarity index 80%
rename from src/Q42.HueApi.ColorConverters/OriginalWithModel/CIE1931Point.cs
rename to src/Q42.HueApi/Models/Gamut/CIE1931Point.cs
index 57efac7b..0852cdf5 100644
--- a/src/Q42.HueApi.ColorConverters/OriginalWithModel/CIE1931Point.cs
+++ b/src/Q42.HueApi/Models/Gamut/CIE1931Point.cs
@@ -1,15 +1,15 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
-namespace Q42.HueApi.ColorConverters.OriginalWithModel
+namespace Q42.HueApi.Models.Gamut
{
- ///
- /// Represents a point in CIE1931 color space.
- ///
- internal struct CIE1931Point
+ ///
+ /// Represents a point in CIE1931 color space.
+ ///
+ public struct CIE1931Point
{
///
/// The D65 White Point.
diff --git a/src/Q42.HueApi/Models/Light.cs b/src/Q42.HueApi/Models/Light.cs
index 86b518f8..ed4001d7 100644
--- a/src/Q42.HueApi/Models/Light.cs
+++ b/src/Q42.HueApi/Models/Light.cs
@@ -1,5 +1,6 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
+using Q42.HueApi.Models.Gamut;
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
@@ -81,10 +82,47 @@ public class StreamingLightCapabilities
[DataContract]
public class LightCapabilities
{
+ [DataMember(Name = "certified")]
+ public bool Certified { get; set; }
+
+ [DataMember(Name = "control")]
+ public Control Control { get; set; }
+
[DataMember(Name = "streaming")]
public StreamingLightCapabilities Streaming { get; set; }
}
+ [DataContract]
+ public class Control
+ {
+ [DataMember(Name = "mindimlevel")]
+ public int? MinDimLevel { get; set; }
+
+ [DataMember(Name = "maxlumen")]
+ public int? MaxLumen { get; set; }
+
+ ///
+ /// A, B or C
+ ///
+ [DataMember(Name = "colorgamuttype")]
+ public string ColorGamutType { get; set; }
+
+ [DataMember(Name = "colorgamut")]
+ public CIE1931Gamut? ColorGamut { get; set; }
+
+ [DataMember(Name = "ct")]
+ public ColorTemperature ColorTemperature { get; set; }
+ }
+
+ [DataContract]
+ public class ColorTemperature
+ {
+ [DataMember(Name = "min")]
+ public int Min { get; set; }
+ [DataMember(Name = "max")]
+ public int Max { get; set; }
+ }
+
[DataContract]
public class LightConfig
{