private Bitmap DrawBitmap(string model)
        {
            var gamut = CIE1931Gamut.ForModel(model);

            int dimension = 500;
            Bitmap b = new Bitmap(dimension, dimension);
            for (int x = 0; x < dimension; x++)
            {
                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);

                    Color c;
                    if (point.x + point.y > 1.0)
                    {
                        c = Color.Black;
                    }
                    else if (gamut.Contains(point))
                    {
                        c = Color.FromArgb((int)(rgb.R * 255.999), (int)(rgb.G * 255.999), (int)(rgb.B * 255.999));
                    }
                    else
                    {
                        c = Color.FromArgb((int)(rgb.R * 127.999), (int)(rgb.G * 127.999), (int)(rgb.B * 127.999));
                    }

                    // CIE1931 charts are drawn with y-increasing being upwards, not downwards as in bitmaps.
                    b.SetPixel(x, (dimension - 1) - y, c);
                }
            }
            return b;
        }
Пример #2
0
        public void ColorConversionRoundtripAllPoints()
        {
            // Use a consistent seed for test reproducability
            Random r = new Random(0);

            for (int trial = 0; trial < 1000; trial++)
            {
                CIE1931Point originalXy;

                // Randomly generate a test color that is at a valid CIE1931 coordinate.
                do
                {
                    originalXy = new CIE1931Point(r.NextDouble(), r.NextDouble());
                }while (originalXy.x + originalXy.y >= 1.0);

                RGBColor rgb = HueColorConverter.XYToRgb(originalXy, "LCT001");
                var      xy  = HueColorConverter.RgbToXY(rgb, "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.
                // Conversion from XY to RGB
                var expectedXy = CIE1931Gamut.ForModel("LCT001").NearestContainedPoint(originalXy);
                expectedXy = CIE1931Gamut.PhilipsWideGamut.NearestContainedPoint(expectedXy);

                // RGB to XY
                expectedXy = CIE1931Gamut.ForModel("LCT001").NearestContainedPoint(expectedXy);

                AssertAreEqual(expectedXy, xy, 0.0001);
            }
        }
Пример #3
0
        private Bitmap DrawBitmap(string model)
        {
            var gamut = CIE1931Gamut.ForModel(model);

            int    dimension = 500;
            Bitmap b         = new Bitmap(dimension, dimension);

            for (int x = 0; x < dimension; x++)
            {
                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);

                    Color c;
                    if (point.x + point.y > 1.0)
                    {
                        c = Color.Black;
                    }
                    else if (gamut.Contains(point))
                    {
                        c = Color.FromArgb((int)(rgb.R * 255.999), (int)(rgb.G * 255.999), (int)(rgb.B * 255.999));
                    }
                    else
                    {
                        c = Color.FromArgb((int)(rgb.R * 127.999), (int)(rgb.G * 127.999), (int)(rgb.B * 127.999));
                    }

                    // CIE1931 charts are drawn with y-increasing being upwards, not downwards as in bitmaps.
                    b.SetPixel(x, (dimension - 1) - y, c);
                }
            }
            return(b);
        }
Пример #4
0
        /// <summary>
        /// Find the distance between two points.
        /// </summary>
        /// <param name="one"></param>
        /// <param name="two"></param>
        /// <returns>the distance between point one and two</returns>
        private static double GetDistanceBetweenTwoPoints(CIE1931Point one, CIE1931Point two)
        {
            double dx   = one.x - two.x; // horizontal difference
            double dy   = one.y - two.y; // vertical difference
            double dist = Math.Sqrt(dx * dx + dy * dy);

            return(dist);
        }
Пример #5
0
        public void GamutContainsWorksCorrectly()
        {
            Random r = new Random(0);

            for (int trial = 0; trial < 1000; trial++)
            {
                var point  = new CIE1931Point(r.NextDouble(), r.NextDouble());
                var gamutB = CIE1931Gamut.ForModel("LCT001");

                Assert.AreEqual(ReferenceColorConverter.CheckPointInLampsReach(point), gamutB.Contains(point));
            }
        }
        public static CIE1931Point RgbToXY(RGBColor color, CIE1931Gamut?gamut)
        {
            // Apply gamma correction. Convert non-linear RGB colour components
            // to linear color intensity levels.
            double r = InverseGamma(color.R);
            double g = InverseGamma(color.G);
            double b = InverseGamma(color.B);

            // Hue bulbs (depending on the type) can display colors outside the sRGB gamut supported
            // by most computer screens.
            // To make sure all colors are selectable by the user, Philips in its implementation
            // decided to interpret all RGB colors as if they were from a wide (non-sRGB) gamut.
            // The effect of this is to map colors in sRGB to a broader gamut of colors the hue lights
            // can produce.
            //
            // This also results in some deviation of color on screen vs color in real-life.
            //
            // The Philips implementation describes the matrix below with the comment
            // "Wide Gamut D65", but the values suggest this is infact not a standard
            // gamut but some custom gamut.
            //
            // The coordinates of this gamut have been identified as follows:
            //  red: (0.700607, 0.299301)
            //  green: (0.172416, 0.746797)
            //  blue: (0.135503, 0.039879)
            //
            // (substitute r = 1, g = 1, b = 1 in sequence into array below and convert
            //  from XYZ to xyY coordinates).
            // The plotted chart can be seen here: http://imgur.com/zelKnSk
            //
            // Also of interest, the white point is not D65 (0.31271, 0.32902), but a slightly
            // shifted version at (0.322727, 0.32902). This might be because true D65 is slightly
            // outside Gamut B (the position of D65 in the linked chart is slightly inaccurate).
            double X = r * 0.664511f + g * 0.154324f + b * 0.162028f;
            double Y = r * 0.283881f + g * 0.668433f + b * 0.047685f;
            double Z = r * 0.000088f + g * 0.072310f + b * 0.986039f;

            CIE1931Point xyPoint = new CIE1931Point(0.0, 0.0);

            if ((X + Y + Z) > 0.0)
            {
                // Convert from CIE XYZ to CIE xyY coordinates.
                xyPoint = new CIE1931Point(X / (X + Y + Z), Y / (X + Y + Z));
            }

            if (gamut.HasValue)
            {
                // The point, adjusted it to the nearest point that is within the gamut of the lamp, if neccessary.
                return(gamut.Value.NearestContainedPoint(xyPoint));
            }
            return(xyPoint);
        }
Пример #7
0
        public void ColorsOutsideGamutAdjustedToInBeInGamut()
        {
            // This green is in the gamut of LST001, but not LCT001.
            CIE1931Point outsideGreen = new CIE1931Point(0.18, 0.72);

            CIE1931Point gamutAGreen = new CIE1931Point(0.2151, 0.7106);
            CIE1931Point gamutBGreen = new CIE1931Point(0.409, 0.518);
            CIE1931Point gamutCGreen = new CIE1931Point(0.17, 0.7);

            AssertAreEqual(gamutAGreen, CIE1931Gamut.ForModel("LST001").NearestContainedPoint(outsideGreen), 0.0001);
            AssertAreEqual(gamutBGreen, CIE1931Gamut.ForModel("LCT001").NearestContainedPoint(outsideGreen), 0.0001);
            AssertAreEqual(gamutCGreen, CIE1931Gamut.ForModel("LST002").NearestContainedPoint(outsideGreen), 0.0001);
        }
Пример #8
0
        public void ColorsOutsideGamutAdjustedToInBeInGamut()
        {
            // This green is in the gamut of LST001, but not LCT001.
            CIE1931Point outsideGreen = new CIE1931Point(0.18, 0.72);

            CIE1931Point gamutAGreen = new CIE1931Point(0.2151, 0.7106);
            CIE1931Point gamutBGreen = new CIE1931Point(0.409, 0.518);
            CIE1931Point gamutCGreen = new CIE1931Point(0.17, 0.7);

            AssertAreEqual(gamutAGreen, CIE1931Gamut.ForModel("LST001").NearestContainedPoint(outsideGreen), 0.0001);
            AssertAreEqual(gamutBGreen, CIE1931Gamut.ForModel("LCT001").NearestContainedPoint(outsideGreen), 0.0001);
            AssertAreEqual(gamutCGreen, CIE1931Gamut.ForModel("LST002").NearestContainedPoint(outsideGreen), 0.0001);
        }
        public static RGBColor XYToRgb(CIE1931Point point, CIE1931Gamut?gamut)
        {
            if (gamut.HasValue)
            {
                // If the color is outside the lamp's gamut, adjust to the nearest color
                // inside the lamp's gamut.
                point = gamut.Value.NearestContainedPoint(point);
            }

            // Also adjust it to be in the Philips "Wide Gamut" if not already.
            // The wide gamut used for XYZ->RGB conversion does not quite contain all colors
            // all of the hue bulbs support.
            point = CIE1931Gamut.PhilipsWideGamut.NearestContainedPoint(point);

            // Convert from xyY to XYZ coordinates.
            double Y = 1.0; // Luminance
            double X = (Y / point.y) * point.x;
            double Z = (Y / point.y) * point.z;

            // The Philips implementation comments this matrix with "sRGB D65 conversion"
            // However, this is not the XYZ -> RGB conversion matrix for sRGB. Instead
            // the matrix that is the inverse of that in RgbToXY() is used.
            // See comment in RgbToXY() for more info.
            double r = X * 1.656492 - Y * 0.354851 - Z * 0.255038;
            double g = -X * 0.707196 + Y * 1.655397 + Z * 0.036152;
            double b = X * 0.051713 - Y * 0.121364 + Z * 1.011530;

            // Downscale color components so that largest component has an intensity of 1.0,
            // as we can't display colors brighter than that.
            double maxComponent = Math.Max(Math.Max(r, g), b);

            if (maxComponent > 1.0)
            {
                r /= maxComponent;
                g /= maxComponent;
                b /= maxComponent;
            }

            // We now have the (linear) amounts of R, G and B corresponding to the specified XY coordinates.
            // Since displays are non-linear, we must apply a gamma correction to get the pixel value.
            // For example, a pixel red value of 1.0 (255) is more than twice as bright as 0.5 (127).
            // We need to correct for this non-linearity.
            r = Gamma(r);
            g = Gamma(g);
            b = Gamma(b);

            // Philips applies a second round of downscaling here, but that should be unnecessary given
            // gamma returns a value between 0.0 and 1.0 for every input between 0.0 and 1.0.
            return(new RGBColor(r, g, b));
        }
Пример #10
0
        public void ColorsOutsideGamutAdjustedToInBeInGamutOnConversion()
        {
            // The green primary of Gamut A.
            CIE1931Point gamutGreen = new CIE1931Point(0.2151, 0.7106);

            // 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");

            // Points should be equal, since the green outside the gamut should
            // be adjusted the the nearest green in-gamut.
            Assert.AreEqual(a.R, b.R);
            Assert.AreEqual(a.G, b.G);
            Assert.AreEqual(a.B, b.B);
        }
Пример #11
0
        public void ColorsOutsideGamutAdjustedToInBeInGamutOnConversion()
        {
            // The green primary of Gamut A.
            CIE1931Point gamutGreen = new CIE1931Point(0.2151, 0.7106);

            // 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");

            // Points should be equal, since the green outside the gamut should
            // be adjusted the the nearest green in-gamut.
            Assert.AreEqual(a.R, b.R);
            Assert.AreEqual(a.G, b.G);
            Assert.AreEqual(a.B, b.B);
        }
Пример #12
0
        /// <summary>
        ///  Method to see if the given XY value is within the reach of the lamps.
        /// </summary>
        /// <param name="p">p the point containing the X,Y value</param>
        /// <returns>true if within reach, false otherwise.</returns>
        public static bool CheckPointInLampsReach(CIE1931Point p)
        {
            CIE1931Point v1 = new CIE1931Point(Lime.x - Red.x, Lime.y - Red.y);
            CIE1931Point v2 = new CIE1931Point(Blue.x - Red.x, Blue.y - Red.y);

            CIE1931Point q = new CIE1931Point(p.x - Red.x, p.y - Red.y);

            double s = ReferenceColorConverter.CrossProduct(q, v2) / ReferenceColorConverter.CrossProduct(v1, v2);
            double t = ReferenceColorConverter.CrossProduct(v1, q) / ReferenceColorConverter.CrossProduct(v1, v2);

            if ((s >= 0.0f) && (t >= 0.0f) && (s + t <= 1.0f))
            {
                return(true);
            }
            else
            {
                return(false);
            }
        }
Пример #13
0
        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 <IList <IList <double> > >();

                if (gammutValues != null && 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);
        }
Пример #14
0
        /// <summary>
        /// Find the closest point on a line.
        /// This point will be within reach of the lamp.
        /// </summary>
        /// <param name="A">A the point where the line starts</param>
        /// <param name="B">B the point where the line ends</param>
        /// <param name="P">P the point which is close to a line.</param>
        /// <returns> the point which is on the line.</returns>
        private static CIE1931Point GetClosestPointToPoint(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;

            if (t < 0.0f)
            {
                t = 0.0f;
            }
            else if (t > 1.0f)
            {
                t = 1.0f;
            }

            CIE1931Point newPoint = new CIE1931Point(A.x + AB.x * t, A.y + AB.y * t);

            return(newPoint);
        }
Пример #15
0
        public void ColorConversionRoundtripInsideGamut()
        {
            // Use a consistent seed for test reproducability
            Random r = new Random(0);

            for (int trial = 0; trial < 1000; trial++)
            {
                CIE1931Point originalXy;

                // Randomly generate a test color that is at a valid CIE1931 coordinate.
                do
                {
                    originalXy = new CIE1931Point(r.NextDouble(), r.NextDouble());
                }while (originalXy.x + originalXy.y >= 1.0 ||
                        !ReferenceColorConverter.CheckPointInLampsReach(originalXy) ||
                        !CIE1931Gamut.PhilipsWideGamut.Contains(originalXy));

                RGBColor rgb = HueColorConverter.XYToRgb(originalXy, "LCT001");
                var      xy  = HueColorConverter.RgbToXY(rgb, "LCT001");

                AssertAreEqual(originalXy, xy, 0.0001);
            }
        }
Пример #16
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="light"></param>
        /// <param name="xy"></param>
        /// <param name="gamut">The gamut to use</param>
        /// <param name="timeSpan"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public static void SetColor(this EntertainmentLight light, CancellationToken cancellationToken, CIE1931Point xy, CIE1931Gamut gamut, TimeSpan timeSpan = default)
        {
            var rgb = HueColorConverter.XYToRgb(xy, gamut);

            light.SetState(cancellationToken, rgb, null, timeSpan);
        }
Пример #17
0
        public void ColorConversionRoundtripInsideGamut()
        {
            // Use a consistent seed for test reproducability
            Random r = new Random(0);

            for (int trial = 0; trial < 1000; trial++)
            {
                CIE1931Point originalXy;

                // Randomly generate a test color that is at a valid CIE1931 coordinate.
                do
                {
                    originalXy = new CIE1931Point(r.NextDouble(), r.NextDouble());
                }
                while (originalXy.x + originalXy.y >= 1.0 
                    || !ReferenceColorConverter.CheckPointInLampsReach(originalXy)
                    || !CIE1931Gamut.PhilipsWideGamut.Contains(originalXy));

                RGBColor rgb = HueColorConverter.XYToRgb(originalXy, "LCT001");
                var xy = HueColorConverter.RgbToXY(rgb, "LCT001");
                
                AssertAreEqual(originalXy, xy, 0.0001);
            }
        }
Пример #18
0
 private void AssertAreEqual(CIE1931Point expected, CIE1931Point actual, double delta)
 {
     Assert.AreEqual(expected.x, actual.x, delta);
     Assert.AreEqual(expected.y, actual.y, delta);
     Assert.AreEqual(expected.z, actual.z, delta);
 }
Пример #19
0
        /// <summary>
        ///  Get XY from red,green,blue ints
        /// </summary>
        /// <param name="red"></param>
        /// <param name="green"></param>
        /// <param name="blue"></param>
        /// <returns></returns>
        public static CIE1931Point XyFromColor(double red, double green, double blue)
        {
            double r = (red > 0.04045f) ? Math.Pow((red + 0.055f) / (1.0f + 0.055f), 2.4f) : (red / 12.92f);
            double g = (green > 0.04045f) ? Math.Pow((green + 0.055f) / (1.0f + 0.055f), 2.4f) : (green / 12.92f);
            double b = (blue > 0.04045f) ? Math.Pow((blue + 0.055f) / (1.0f + 0.055f), 2.4f) : (blue / 12.92f);

            //double X = r * 0.4360747f + g * 0.3850649f + b * 0.0930804f;
            //double Y = r * 0.2225045f + g * 0.7168786f + b * 0.0406169f;
            //double Z = r * 0.0139322f + g * 0.0971045f + b * 0.7141733f;
            double X = r * 0.664511f + g * 0.154324f + b * 0.162028f;
            double Y = r * 0.283881f + g * 0.668433f + b * 0.047685f;
            double Z = r * 0.000088f + g * 0.072310f + b * 0.986039f;



            double cx = X / (X + Y + Z);
            double cy = Y / (X + Y + Z);

            if (Double.IsNaN(cx))
            {
                cx = 0.0f;
            }

            if (Double.IsNaN(cy))
            {
                cy = 0.0f;
            }

            //Check if the given XY value is within the colourreach of our lamps.
            CIE1931Point xyPoint        = new CIE1931Point(cx, cy);
            bool         inReachOfLamps = ReferenceColorConverter.CheckPointInLampsReach(xyPoint);

            if (!inReachOfLamps)
            {
                //It seems the colour is out of reach
                //let's find the closes colour we can produce with our lamp and send this XY value out.

                //Find the closest point on each line in the triangle.
                CIE1931Point pAB = ReferenceColorConverter.GetClosestPointToPoint(Red, Lime, xyPoint);
                CIE1931Point pAC = ReferenceColorConverter.GetClosestPointToPoint(Blue, Red, xyPoint);
                CIE1931Point pBC = ReferenceColorConverter.GetClosestPointToPoint(Lime, Blue, xyPoint);

                //Get the distances per point and see which point is closer to our Point.
                double dAB = ReferenceColorConverter.GetDistanceBetweenTwoPoints(xyPoint, pAB);
                double dAC = ReferenceColorConverter.GetDistanceBetweenTwoPoints(xyPoint, pAC);
                double dBC = ReferenceColorConverter.GetDistanceBetweenTwoPoints(xyPoint, pBC);

                double       lowest       = dAB;
                CIE1931Point closestPoint = pAB;

                if (dAC < lowest)
                {
                    lowest       = dAC;
                    closestPoint = pAC;
                }
                if (dBC < lowest)
                {
                    lowest       = dBC;
                    closestPoint = pBC;
                }

                //Change the xy value to a value which is within the reach of the lamp.
                cx = closestPoint.x;
                cy = closestPoint.y;
            }

            return(new CIE1931Point(cx, cy));
        }
Пример #20
0
        public void GamutContainsWorksCorrectly()
        {
            Random r = new Random(0);

            for (int trial = 0; trial < 1000; trial++)
            {
                var point = new CIE1931Point(r.NextDouble(), r.NextDouble());
                var gamutB = CIE1931Gamut.ForModel("LCT001");

                Assert.AreEqual(ReferenceColorConverter.CheckPointInLampsReach(point), gamutB.Contains(point));
            }
        }
Пример #21
0
        /// <summary>
        /// Find the closest point on a line.
        /// This point will be within reach of the lamp.
        /// </summary>
        /// <param name="A">A the point where the line starts</param>
        /// <param name="B">B the point where the line ends</param>
        /// <param name="P">P the point which is close to a line.</param>
        /// <returns> the point which is on the line.</returns>
        private static CIE1931Point GetClosestPointToPoint(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;

            if (t < 0.0f)
                t = 0.0f;
            else if (t > 1.0f)
                t = 1.0f;

            CIE1931Point newPoint = new CIE1931Point(A.x + AB.x * t, A.y + AB.y * t);
            return newPoint;
        }
Пример #22
0
        /// <summary>
        /// Find the distance between two points.
        /// </summary>
        /// <param name="one"></param>
        /// <param name="two"></param>
        /// <returns>the distance between point one and two</returns>
        private static double GetDistanceBetweenTwoPoints(CIE1931Point one, CIE1931Point two)
        {
            double dx = one.x - two.x; // horizontal difference
            double dy = one.y - two.y; // vertical difference
            double dist = Math.Sqrt(dx * dx + dy * dy);

            return dist;
        }
Пример #23
0
 /// <summary>
 /// Calculates crossProduct of two 2D vectors / points.
 /// </summary>
 /// <param name="p1"> p1 first point used as vector</param>
 /// <param name="p2">p2 second point used as vector</param>
 /// <returns>crossProduct of vectors</returns>
 private static double CrossProduct(CIE1931Point p1, CIE1931Point p2)
 {
     return (p1.x * p2.y - p1.y * p2.x);
 }
Пример #24
0
 /// <summary>
 /// Calculates crossProduct of two 2D vectors / points.
 /// </summary>
 /// <param name="p1"> p1 first point used as vector</param>
 /// <param name="p2">p2 second point used as vector</param>
 /// <returns>crossProduct of vectors</returns>
 private static double CrossProduct(CIE1931Point p1, CIE1931Point p2)
 {
     return(p1.x * p2.y - p1.y * p2.x);
 }
Пример #25
0
        /// <summary>
        ///  Method to see if the given XY value is within the reach of the lamps.
        /// </summary>
        /// <param name="p">p the point containing the X,Y value</param>
        /// <returns>true if within reach, false otherwise.</returns>
        public static bool CheckPointInLampsReach(CIE1931Point p)
        {
            CIE1931Point v1 = new CIE1931Point(Lime.x - Red.x, Lime.y - Red.y);
            CIE1931Point v2 = new CIE1931Point(Blue.x - Red.x, Blue.y - Red.y);

            CIE1931Point q = new CIE1931Point(p.x - Red.x, p.y - Red.y);

            double s = ReferenceColorConverter.CrossProduct(q, v2) / ReferenceColorConverter.CrossProduct(v1, v2);
            double t = ReferenceColorConverter.CrossProduct(v1, q) / ReferenceColorConverter.CrossProduct(v1, v2);

            if ((s >= 0.0f) && (t >= 0.0f) && (s + t <= 1.0f))
            {
                return true;
            }
            else
            {
                return false;
            }
        }
Пример #26
0
        /// <summary>
        ///  Get XY from red,green,blue ints
        /// </summary>
        /// <param name="red"></param>
        /// <param name="green"></param>
        /// <param name="blue"></param>
        /// <returns></returns>
        public static CIE1931Point XyFromColor(double red, double green, double blue)
        {
            double r = (red > 0.04045f) ? Math.Pow((red + 0.055f) / (1.0f + 0.055f), 2.4f) : (red / 12.92f);
            double g = (green > 0.04045f) ? Math.Pow((green + 0.055f) / (1.0f + 0.055f), 2.4f) : (green / 12.92f);
            double b = (blue > 0.04045f) ? Math.Pow((blue + 0.055f) / (1.0f + 0.055f), 2.4f) : (blue / 12.92f);

            //double X = r * 0.4360747f + g * 0.3850649f + b * 0.0930804f;
            //double Y = r * 0.2225045f + g * 0.7168786f + b * 0.0406169f;
            //double Z = r * 0.0139322f + g * 0.0971045f + b * 0.7141733f;
            double X = r * 0.664511f + g * 0.154324f + b * 0.162028f;
            double Y = r * 0.283881f + g * 0.668433f + b * 0.047685f;
            double Z = r * 0.000088f + g * 0.072310f + b * 0.986039f;



            double cx = X / (X + Y + Z);
            double cy = Y / (X + Y + Z);

            if (Double.IsNaN(cx))
            {
                cx = 0.0f;
            }

            if (Double.IsNaN(cy))
            {
                cy = 0.0f;
            }

            //Check if the given XY value is within the colourreach of our lamps.
            CIE1931Point xyPoint = new CIE1931Point(cx, cy);
            bool inReachOfLamps = ReferenceColorConverter.CheckPointInLampsReach(xyPoint);

            if (!inReachOfLamps)
            {
                //It seems the colour is out of reach
                //let's find the closes colour we can produce with our lamp and send this XY value out.

                //Find the closest point on each line in the triangle.
                CIE1931Point pAB = ReferenceColorConverter.GetClosestPointToPoint(Red, Lime, xyPoint);
                CIE1931Point pAC = ReferenceColorConverter.GetClosestPointToPoint(Blue, Red, xyPoint);
                CIE1931Point pBC = ReferenceColorConverter.GetClosestPointToPoint(Lime, Blue, xyPoint);

                //Get the distances per point and see which point is closer to our Point.
                double dAB = ReferenceColorConverter.GetDistanceBetweenTwoPoints(xyPoint, pAB);
                double dAC = ReferenceColorConverter.GetDistanceBetweenTwoPoints(xyPoint, pAC);
                double dBC = ReferenceColorConverter.GetDistanceBetweenTwoPoints(xyPoint, pBC);

                double lowest = dAB;
                CIE1931Point closestPoint = pAB;

                if (dAC < lowest)
                {
                    lowest = dAC;
                    closestPoint = pAC;
                }
                if (dBC < lowest)
                {
                    lowest = dBC;
                    closestPoint = pBC;
                }

                //Change the xy value to a value which is within the reach of the lamp.
                cx = closestPoint.x;
                cy = closestPoint.y;
            }

            return new CIE1931Point(cx, cy);
        }
Пример #27
0
 private void AssertAreEqual(CIE1931Point expected, CIE1931Point actual, double delta)
 {
     Assert.AreEqual(expected.x, actual.x, delta);
     Assert.AreEqual(expected.y, actual.y, delta);
     Assert.AreEqual(expected.z, actual.z, delta);
 }
Пример #28
0
        public void ColorConversionRoundtripAllPoints()
        {
            // Use a consistent seed for test reproducability
            Random r = new Random(0);

            for (int trial = 0; trial < 1000; trial++)
            {
                CIE1931Point originalXy;

                // Randomly generate a test color that is at a valid CIE1931 coordinate.
                do
                {
                    originalXy = new CIE1931Point(r.NextDouble(), r.NextDouble());
                }
                while (originalXy.x + originalXy.y >= 1.0);

                RGBColor rgb = HueColorConverter.XYToRgb(originalXy, "LCT001");
                var xy = HueColorConverter.RgbToXY(rgb, "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. 
                // Conversion from XY to RGB
                var expectedXy = CIE1931Gamut.ForModel("LCT001").NearestContainedPoint(originalXy);
                expectedXy = CIE1931Gamut.PhilipsWideGamut.NearestContainedPoint(expectedXy);

                // RGB to XY
                expectedXy = CIE1931Gamut.ForModel("LCT001").NearestContainedPoint(expectedXy);

                AssertAreEqual(expectedXy, xy, 0.0001);
            }
        }
Пример #29
0
        /// <summary>
        /// Set state on a single light
        /// </summary>
        /// <param name="light"></param>
        /// <param name="xy"></param>
        /// <param name="gamut"></param>
        /// <param name="brightness"></param>
        /// <param name="timeSpan"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public static void SetState(this EntertainmentLight light, CancellationToken cancellationToken, CIE1931Point xy, CIE1931Gamut gamut, double?brightness = null, TimeSpan timeSpan = default(TimeSpan))
        {
            var rgb = HueColorConverter.XYToRgb(xy, gamut);

            //Create a new transition for this light
            Transition transition = new Transition(rgb, brightness, timeSpan);

            light.Transition = transition;

            //Start the transition
            transition.Start(light.State.RGBColor, light.State.Brightness, cancellationToken);
        }