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));
            }
        }
        /// <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);
            }
        }
        public void CompareColorConversionWithReference()
        {
            // Use a consistent seed for test reproducability
            Random r = new Random(0);

            for (int trial = 0; trial < 1000; trial++)
            {
                double red   = r.NextDouble();
                double green = r.NextDouble();
                double blue  = r.NextDouble();

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

                AssertAreEqual(referenceXy, actualXy, 0.0001);
            }
        }
        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);
            }
        }
        /// <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));
        }