public override LightCommand GetLightCommand(IDictionary<string, string> values, PhilipsHueDevice bulb)
        {
            string hexColor;
            if (!values.TryGetValue("Color", out hexColor))
            {
                throw new ArgumentException("Color");
            }

            // TODO: http://www.developers.meethue.com/documentation/color-conversions-rgb-xy
            var rgb = new RGBColor(hexColor);
            var xy = RgbToCieConverter.Convert(bulb.Model, rgb.R, rgb.G, rgb.B);

            if (Equals(xy, default(RgbToCieConverter.CieResult)))
            {
                return new LightCommand();
            }

            var command = new LightCommand
            {
                On = true,
                ColorCoordinates = new[] {xy.X, xy.Y}
            };

            TimeSpan transitionTime;
            if (TryGetTransitionTime(values, out transitionTime))
            {
                command.TransitionTime = transitionTime;
            }

            return command;
        }
		public static CIE1931Point RgbToXY(RGBColor color, string model)
		{
            // 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 (model != null)
            {
                //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 xyPoint;
		}
		/*****************************************************************************************
		The code below is based on http://www.developers.meethue.com/documentation/color-conversions-rgb-xy
		Converted to C# by Niels Laute
		*****************************************************************************************/

		public static CGPoint CalculateXY(RGBColor color, string model)
		{
			//CGColorRef cgColor = [color CGColor];

			//const CGFloat* components = CGColorGetComponents(cgColor);
			//long numberOfComponents = CGColorGetNumberOfComponents(cgColor);

			// Default to white
			float red = 1.0f;
			float green = 1.0f;
			float blue = 1.0f;

			//if (numberOfComponents == 4)
			//{
			// Full color
			red = color.R;
			green = color.G;
			blue = color.B;
			//}
			//else if (numberOfComponents == 2)
			//{
			//    // Greyscale color
			//    red = green = blue = color.A;
			//}

			// Apply gamma correction
			float r;
			float g;
			float b;

			if (red > 0.04045f)
			{
				r = (float)Math.Pow((red + 0.055f) / (1.0f + 0.055f), 2.4f);
			}
			else
			{
				r = red / 12.92f;
			}

			if (green > 0.04045f)
			{
				g = (float)Math.Pow((green + 0.055f) / (1.0f + 0.055f), 2.4f);
			}
			else
			{
				g = green / 12.92f;
			}

			if (blue > 0.04045f)
			{
				b = (float)Math.Pow((blue + 0.055f) / (1.0f + 0.055f), 2.4f);
			}
			else
			{
				b = blue / 12.92f;
			}


			// Wide gamut conversion D65
			float X = r * 0.664511f + g * 0.154324f + b * 0.162028f;
			float Y = r * 0.283881f + g * 0.668433f + b * 0.047685f;
			float Z = r * 0.000088f + g * 0.072310f + b * 0.986039f;

			float cx = 0.0f;

			if((X + Y + Z) != 0)
				cx = X / (X + Y + Z);

			float cy = 0.0f;
			if((X + Y + Z) != 0)
				cy = Y / (X + Y + Z);

			//Check if the given XY value is within the colourreach of our lamps.

			CGPoint xyPoint = new CGPoint(cx, cy);
			List<CGPoint> colorPoints = ColorPointsForModel(model);
			bool inReachOfLamps = CheckPointInLampsReach(xyPoint, colorPoints);

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

				//Find the closest point on each line in the triangle.
				CGPoint pAB = GetClosestPointToPoints(colorPoints[0], colorPoints[1], xyPoint);
				CGPoint pAC = GetClosestPointToPoints(colorPoints[2], colorPoints[0], xyPoint);
				CGPoint pBC = GetClosestPointToPoints(colorPoints[1], colorPoints[2], xyPoint);

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

				float lowest = dAB;
				CGPoint 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 = (float)closestPoint.x;
				cy = (float)closestPoint.y;
			}

			return new CGPoint(cx, cy);
		}
	public static LightCommand SetColor(this LightCommand lightCommand, RGBColor color, string model)
	{
		if (lightCommand == null)
			throw new ArgumentNullException("lightCommand");

		var point = HueColorConverter.CalculateXY(color, model);
		return lightCommand.SetColor(point.x, point.y);
	}
Exemple #5
0
        /*****************************************************************************************
        *  The code below is based on http://www.developers.meethue.com/documentation/color-conversions-rgb-xy
        *  Converted to C# by Niels Laute
        *****************************************************************************************/

        public static CGPoint CalculateXY(RGBColor color, string model)
        {
            //CGColorRef cgColor = [color CGColor];

            //const CGFloat* components = CGColorGetComponents(cgColor);
            //long numberOfComponents = CGColorGetNumberOfComponents(cgColor);

            // Default to white
            float red   = 1.0f;
            float green = 1.0f;
            float blue  = 1.0f;

            //if (numberOfComponents == 4)
            //{
            // Full color
            red   = color.R;
            green = color.G;
            blue  = color.B;
            //}
            //else if (numberOfComponents == 2)
            //{
            //    // Greyscale color
            //    red = green = blue = color.A;
            //}

            // Apply gamma correction
            float r;
            float g;
            float b;

            if (red > 0.04045f)
            {
                r = (float)Math.Pow((red + 0.055f) / (1.0f + 0.055f), 2.4f);
            }
            else
            {
                r = red / 12.92f;
            }

            if (green > 0.04045f)
            {
                g = (float)Math.Pow((green + 0.055f) / (1.0f + 0.055f), 2.4f);
            }
            else
            {
                g = green / 12.92f;
            }

            if (blue > 0.04045f)
            {
                b = (float)Math.Pow((blue + 0.055f) / (1.0f + 0.055f), 2.4f);
            }
            else
            {
                b = blue / 12.92f;
            }


            // Wide gamut conversion D65
            float X = r * 0.664511f + g * 0.154324f + b * 0.162028f;
            float Y = r * 0.283881f + g * 0.668433f + b * 0.047685f;
            float Z = r * 0.000088f + g * 0.072310f + b * 0.986039f;

            float cx = 0.0f;

            if ((X + Y + Z) != 0)
            {
                cx = X / (X + Y + Z);
            }

            float cy = 0.0f;

            if ((X + Y + Z) != 0)
            {
                cy = Y / (X + Y + Z);
            }

            //Check if the given XY value is within the colourreach of our lamps.

            CGPoint        xyPoint        = new CGPoint(cx, cy);
            List <CGPoint> colorPoints    = ColorPointsForModel(model);
            bool           inReachOfLamps = CheckPointInLampsReach(xyPoint, colorPoints);

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

                //Find the closest point on each line in the triangle.
                CGPoint pAB = GetClosestPointToPoints(colorPoints[0], colorPoints[1], xyPoint);
                CGPoint pAC = GetClosestPointToPoints(colorPoints[2], colorPoints[0], xyPoint);
                CGPoint pBC = GetClosestPointToPoints(colorPoints[1], colorPoints[2], xyPoint);

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

                float   lowest       = dAB;
                CGPoint 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 = (float)closestPoint.x;
                cy = (float)closestPoint.y;
            }

            return(new CGPoint(cx, cy));
        }