diff --git a/mobile/app/pods/components/lights-tab/component.js b/mobile/app/pods/components/lights-tab/component.js index 3642a95..22c9db8 100644 --- a/mobile/app/pods/components/lights-tab/component.js +++ b/mobile/app/pods/components/lights-tab/component.js @@ -69,7 +69,7 @@ export default Component.extend({ rgbPreview: observer('rgb', function () { let rgb = this.get('rgb'), - xy = this.rgbToXy(rgb[0], rgb[1], rgb[2]); + xy = rgbToCie(rgb[0], rgb[1], rgb[2]); this.set('colorLoopOn', false); @@ -85,6 +85,30 @@ export default Component.extend({ $('.color').css('background', 'rgb(' + rgb[0] + ',' + rgb[1] + ',' + rgb[2] + ')'); }), + onActiveLightsChange: on('init', observer('activeLights.[]', function () { + let lightsData = this.get('lightsData'), + xy = null, + setRGB = true; + + this.get('activeLights').forEach((i) => { + let light = lightsData[i]; + + if (xy !== null && xy[0] !== light.state.xy[0] && xy[1] !== light.state.xy[1]) { + setRGB = false; + } + + xy = light.state.xy; + }); + + if (setRGB && xy) { + let rgb = cieToRgb(xy[0], xy[1]); + + $('.color').css('background', 'rgb(' + Math.abs(rgb[0]) + ',' + Math.abs(rgb[1]) + ',' + Math.abs(rgb[2]) + ')'); + } else { + $('.color').css('background', 'rgb(' + 255 + ',' + 255 + ',' + 255 + ')'); + } + })), + // determines whether the lights are on/off for the lights switch lightsOnChange: on('init', observer('lightsData.@each.state.on', 'activeLights.[]', function () { if (!this.get('strobeOn')) { @@ -250,92 +274,5 @@ export default Component.extend({ dimmerOnClass: computed('dimmerOn', function () { return this.get('dimmerOn') ? 'dimmerOn' : null; - }), - - // **************** STROBE LIGHT FINISH **************** - // http://www.developers.meethue.com/documentation/color-conversions-rgb-xy - rgbToXy(red, green, blue) { - let X, Y, Z, x, y; - - // normalize - red = Number((red / 255)); - green = Number((green / 255)); - blue = Number((blue / 255)); - - // gamma correction - red = (red > 0.04045) ? Math.pow((red + 0.055) / (1.0 + 0.055), 2.4) : (red / 12.92); - green = (green > 0.04045) ? Math.pow((green + 0.055) / (1.0 + 0.055), 2.4) : (green / 12.92); - blue = (blue > 0.04045) ? Math.pow((blue + 0.055) / (1.0 + 0.055), 2.4) : (blue / 12.92); - - // RGB to XYZ - X = red * 0.664511 + green * 0.154324 + blue * 0.162028; - Y = red * 0.283881 + green * 0.668433 + blue * 0.047685; - Z = red * 0.000088 + green * 0.072310 + blue * 0.986039; - - x = X / (X + Y + Z); - y = Y / (X + Y + Z); - - return [x, y]; - }, - - xyToRgb(x, y) { - let r, g, b, X, Y = 1.0, Z; - - X = (Y / y) * x; - Z = (Y / y) * (1 - x - y); - - r = X * 1.656492 - Y * 0.354851 - Z * 0.255038; - g = X * -0.707196 + Y * 1.655397 + Z * 0.036152; - b = X * 0.051713 - Y * 0.121364 + Z * 1.011530; - - if (r > b && r > g && r > 1.0) { - // red is too big - g = g / r; - b = b / r; - r = 1.0; - } else if (g > b && g > r && g > 1.0) { - // green is too big - r = r / g; - b = b / g; - g = 1.0; - } else if (b > r && b > g && b > 1.0) { - // blue is too big - r = r / b; - g = g / b; - b = 1.0; - } - - r = (r <= 0.0031308) ? 12.92 * r : 1.055 * Math.pow(r, (1.0 / 2.4)) - 0.055; - g = (g <= 0.0031308) ? 12.92 * g : 1.055 * Math.pow(g, (1.0 / 2.4)) - 0.055; - b = (b <= 0.0031308) ? 12.92 * b : 1.055 * Math.pow(b, (1.0 / 2.4)) - 0.055; - - if (r > b && r > g) { - // red is biggest - if (r > 1.0) { - g = g / r; - b = b / r; - r = 1.0; - } - } else if (g > b && g > r) { - // green is biggest - if (g > 1.0) { - r = r / g; - b = b / g; - g = 1.0; - } - } else if (b > r && b > g) { - // blue is biggest - if (b > 1.0) { - r = r / b; - g = g / b; - b = 1.0; - } - } - - r = r * 255; - g = g * 255; - b = b * 255; - - return [r, g, b]; - } + }) }); diff --git a/mobile/ember-cli-build.js b/mobile/ember-cli-build.js index 79d30d2..52985b6 100644 --- a/mobile/ember-cli-build.js +++ b/mobile/ember-cli-build.js @@ -11,6 +11,7 @@ module.exports = function(defaults) { }); app.import('vendor/dancer.js'); + app.import('vendor/cie-rgb-converter.js'); app.import('bower_components/intro.js/intro.js'); app.import('bower_components/intro.js/introjs.css'); diff --git a/mobile/vendor/cie-rgb-converter.js b/mobile/vendor/cie-rgb-converter.js new file mode 100644 index 0000000..eabd049 --- /dev/null +++ b/mobile/vendor/cie-rgb-converter.js @@ -0,0 +1,141 @@ +/* +With these functions you can convert the CIE color space to the RGB color space and vice versa. + +The developer documentation for Philips Hue provides the formulas used in the code below: +https://developers.meethue.com/documentation/color-conversions-rgb-xy + +I've used the formulas and Objective-C example code and transfered it to JavaScript. + + +Examples: + +var rgb = cie_to_rgb(0.6611, 0.2936) +var cie = rgb_to_cie(255, 39, 60) + +------------------------------------------------------------------------------------ + +The MIT License (MIT) + +Copyright (c) 2017 www.usolved.net +Published under https://github.com/usolved/cie-rgb-converter + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + + + + +/** + * Converts CIE color space to RGB color space + * @param {Number} x + * @param {Number} y + * @param {Number} brightness - Ranges from 1 to 254 + * @return {Array} Array that contains the color values for red, green and blue + */ +function cieToRgb(x, y, brightness) { + //Set to maximum brightness if no custom value was given (Not the slick ECMAScript 6 way for compatibility reasons) + if (brightness === undefined) { + brightness = 254; + } + + var z = 1.0 - x - y; + var Y = (brightness / 254).toFixed(2); + var X = (Y / y) * x; + var Z = (Y / y) * z; + + //Convert to RGB using Wide RGB D65 conversion + var red = X * 1.656492 - Y * 0.354851 - Z * 0.255038; + var green = -X * 0.707196 + Y * 1.655397 + Z * 0.036152; + var blue = X * 0.051713 - Y * 0.121364 + Z * 1.011530; + + //If red, green or blue is larger than 1.0 set it back to the maximum of 1.0 + if (red > blue && red > green && red > 1.0) { + + green = green / red; + blue = blue / red; + red = 1.0; + } + else if (green > blue && green > red && green > 1.0) { + + red = red / green; + blue = blue / green; + green = 1.0; + } + else if (blue > red && blue > green && blue > 1.0) { + + red = red / blue; + green = green / blue; + blue = 1.0; + } + + //Reverse gamma correction + red = red <= 0.0031308 ? 12.92 * red : (1.0 + 0.055) * Math.pow(red, (1.0 / 2.4)) - 0.055; + green = green <= 0.0031308 ? 12.92 * green : (1.0 + 0.055) * Math.pow(green, (1.0 / 2.4)) - 0.055; + blue = blue <= 0.0031308 ? 12.92 * blue : (1.0 + 0.055) * Math.pow(blue, (1.0 / 2.4)) - 0.055; + + + //Convert normalized decimal to decimal + red = Math.round(red * 255); + green = Math.round(green * 255); + blue = Math.round(blue * 255); + + if (isNaN(red)) + red = 0; + + if (isNaN(green)) + green = 0; + + if (isNaN(blue)) + blue = 0; + + + return [red, green, blue]; +} + + +/** + * Converts RGB color space to CIE color space + * @param {Number} red + * @param {Number} green + * @param {Number} blue + * @return {Array} Array that contains the CIE color values for x and y + */ +function rgbToCie(red, green, blue) { + var X, Y, Z, x, y; + + // normalize + red = Number((red / 255)); + green = Number((green / 255)); + blue = Number((blue / 255)); + + // gamma correction + red = (red > 0.04045) ? Math.pow((red + 0.055) / (1.0 + 0.055), 2.4) : (red / 12.92); + green = (green > 0.04045) ? Math.pow((green + 0.055) / (1.0 + 0.055), 2.4) : (green / 12.92); + blue = (blue > 0.04045) ? Math.pow((blue + 0.055) / (1.0 + 0.055), 2.4) : (blue / 12.92); + + // RGB to XYZ + X = red * 0.664511 + green * 0.154324 + blue * 0.162028; + Y = red * 0.283881 + green * 0.668433 + blue * 0.047685; + Z = red * 0.000088 + green * 0.072310 + blue * 0.986039; + + x = X / (X + Y + Z); + y = Y / (X + Y + Z); + + return [x, y]; +} \ No newline at end of file diff --git a/web/app/pods/components/lights-tab/component.js b/web/app/pods/components/lights-tab/component.js index 875c30f..c4001dd 100644 --- a/web/app/pods/components/lights-tab/component.js +++ b/web/app/pods/components/lights-tab/component.js @@ -1,13 +1,6 @@ import Ember from 'ember'; -const { - Component, - observer, - computed, - on, - run: { later, once }, - $ -} = Ember; +const { Component, observer, computed, on, run: { later, once }, $ } = Ember; export default Component.extend({ classNames: ['col-sm-10', 'col-sm-offset-1', 'col-xs-12'], @@ -70,7 +63,7 @@ export default Component.extend({ rgbPreview: observer('rgb', function () { let rgb = this.get('rgb'), - xy = this.rgbToXy(rgb[0], rgb[1], rgb[2]); + xy = rgbToCie(rgb[0], rgb[1], rgb[2]); this.set('colorLoopOn', false); @@ -86,6 +79,30 @@ export default Component.extend({ $('.color').css('background', 'rgb(' + rgb[0] + ',' + rgb[1] + ',' + rgb[2] + ')'); }), + onActiveLightsChange: on('init', observer('activeLights.[]', function () { + let lightsData = this.get('lightsData'), + xy = null, + setRGB = true; + + this.get('activeLights').forEach((i) => { + let light = lightsData[i]; + + if (xy !== null && xy[0] !== light.state.xy[0] && xy[1] !== light.state.xy[1]) { + setRGB = false; + } + + xy = light.state.xy; + }); + + if (setRGB && xy) { + let rgb = cieToRgb(xy[0], xy[1]); + + $('.color').css('background', 'rgb(' + Math.abs(rgb[0]) + ',' + Math.abs(rgb[1]) + ',' + Math.abs(rgb[2]) + ')'); + } else { + $('.color').css('background', 'rgb(' + 255 + ',' + 255 + ',' + 255 + ')'); + } + })), + // determines whether the lights are on/off for the lights switch lightsOnChange: on('init', observer('lightsData.@each.state.on', 'activeLights.[]', function () { if (!this.get('strobeOn')) { @@ -257,92 +274,5 @@ export default Component.extend({ toggleDimmer() { this.sendAction('toggleDimmer'); } - }, - - // **************** STROBE LIGHT FINISH **************** - // http://www.developers.meethue.com/documentation/color-conversions-rgb-xy - rgbToXy(red, green, blue) { - let X, Y, Z, x, y; - - // normalize - red = Number((red / 255)); - green = Number((green / 255)); - blue = Number((blue / 255)); - - // gamma correction - red = (red > 0.04045) ? Math.pow((red + 0.055) / (1.0 + 0.055), 2.4) : (red / 12.92); - green = (green > 0.04045) ? Math.pow((green + 0.055) / (1.0 + 0.055), 2.4) : (green / 12.92); - blue = (blue > 0.04045) ? Math.pow((blue + 0.055) / (1.0 + 0.055), 2.4) : (blue / 12.92); - - // RGB to XYZ - X = red * 0.664511 + green * 0.154324 + blue * 0.162028; - Y = red * 0.283881 + green * 0.668433 + blue * 0.047685; - Z = red * 0.000088 + green * 0.072310 + blue * 0.986039; - - x = X / (X + Y + Z); - y = Y / (X + Y + Z); - - return [x, y]; - }, - - xyToRgb(x, y) { - let r, g, b, X, Y = 1.0, Z; - - X = (Y / y) * x; - Z = (Y / y) * (1 - x - y); - - r = X * 1.656492 - Y * 0.354851 - Z * 0.255038; - g = X * -0.707196 + Y * 1.655397 + Z * 0.036152; - b = X * 0.051713 - Y * 0.121364 + Z * 1.011530; - - if (r > b && r > g && r > 1.0) { - // red is too big - g = g / r; - b = b / r; - r = 1.0; - } else if (g > b && g > r && g > 1.0) { - // green is too big - r = r / g; - b = b / g; - g = 1.0; - } else if (b > r && b > g && b > 1.0) { - // blue is too big - r = r / b; - g = g / b; - b = 1.0; - } - - r = (r <= 0.0031308) ? 12.92 * r : 1.055 * Math.pow(r, (1.0 / 2.4)) - 0.055; - g = (g <= 0.0031308) ? 12.92 * g : 1.055 * Math.pow(g, (1.0 / 2.4)) - 0.055; - b = (b <= 0.0031308) ? 12.92 * b : 1.055 * Math.pow(b, (1.0 / 2.4)) - 0.055; - - if (r > b && r > g) { - // red is biggest - if (r > 1.0) { - g = g / r; - b = b / r; - r = 1.0; - } - } else if (g > b && g > r) { - // green is biggest - if (g > 1.0) { - r = r / g; - b = b / g; - g = 1.0; - } - } else if (b > r && b > g) { - // blue is biggest - if (b > 1.0) { - r = r / b; - g = g / b; - b = 1.0; - } - } - - r = r * 255; - g = g * 255; - b = b * 255; - - return [r, g, b]; } }); diff --git a/web/ember-cli-build.js b/web/ember-cli-build.js index 5cb0345..a33a175 100644 --- a/web/ember-cli-build.js +++ b/web/ember-cli-build.js @@ -15,7 +15,8 @@ module.exports = function(defaults) { }); app.import('vendor/dancer.js'); - + app.import('vendor/cie-rgb-converter.js'); + app.import('bower_components/bootstrap-sass/assets/javascripts/bootstrap/tooltip.js'); app.import('bower_components/intro.js/intro.js'); app.import('bower_components/intro.js/introjs.css'); diff --git a/web/public/assets/images/huegasm.png b/web/public/assets/images/huegasm.png index d437a37..6490342 100644 Binary files a/web/public/assets/images/huegasm.png and b/web/public/assets/images/huegasm.png differ diff --git a/web/public/assets/images/logo.png b/web/public/assets/images/logo.png index cc9d381..3fc8b5e 100644 Binary files a/web/public/assets/images/logo.png and b/web/public/assets/images/logo.png differ diff --git a/web/vendor/cie-rgb-converter.js b/web/vendor/cie-rgb-converter.js new file mode 100644 index 0000000..eabd049 --- /dev/null +++ b/web/vendor/cie-rgb-converter.js @@ -0,0 +1,141 @@ +/* +With these functions you can convert the CIE color space to the RGB color space and vice versa. + +The developer documentation for Philips Hue provides the formulas used in the code below: +https://developers.meethue.com/documentation/color-conversions-rgb-xy + +I've used the formulas and Objective-C example code and transfered it to JavaScript. + + +Examples: + +var rgb = cie_to_rgb(0.6611, 0.2936) +var cie = rgb_to_cie(255, 39, 60) + +------------------------------------------------------------------------------------ + +The MIT License (MIT) + +Copyright (c) 2017 www.usolved.net +Published under https://github.com/usolved/cie-rgb-converter + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + + + + +/** + * Converts CIE color space to RGB color space + * @param {Number} x + * @param {Number} y + * @param {Number} brightness - Ranges from 1 to 254 + * @return {Array} Array that contains the color values for red, green and blue + */ +function cieToRgb(x, y, brightness) { + //Set to maximum brightness if no custom value was given (Not the slick ECMAScript 6 way for compatibility reasons) + if (brightness === undefined) { + brightness = 254; + } + + var z = 1.0 - x - y; + var Y = (brightness / 254).toFixed(2); + var X = (Y / y) * x; + var Z = (Y / y) * z; + + //Convert to RGB using Wide RGB D65 conversion + var red = X * 1.656492 - Y * 0.354851 - Z * 0.255038; + var green = -X * 0.707196 + Y * 1.655397 + Z * 0.036152; + var blue = X * 0.051713 - Y * 0.121364 + Z * 1.011530; + + //If red, green or blue is larger than 1.0 set it back to the maximum of 1.0 + if (red > blue && red > green && red > 1.0) { + + green = green / red; + blue = blue / red; + red = 1.0; + } + else if (green > blue && green > red && green > 1.0) { + + red = red / green; + blue = blue / green; + green = 1.0; + } + else if (blue > red && blue > green && blue > 1.0) { + + red = red / blue; + green = green / blue; + blue = 1.0; + } + + //Reverse gamma correction + red = red <= 0.0031308 ? 12.92 * red : (1.0 + 0.055) * Math.pow(red, (1.0 / 2.4)) - 0.055; + green = green <= 0.0031308 ? 12.92 * green : (1.0 + 0.055) * Math.pow(green, (1.0 / 2.4)) - 0.055; + blue = blue <= 0.0031308 ? 12.92 * blue : (1.0 + 0.055) * Math.pow(blue, (1.0 / 2.4)) - 0.055; + + + //Convert normalized decimal to decimal + red = Math.round(red * 255); + green = Math.round(green * 255); + blue = Math.round(blue * 255); + + if (isNaN(red)) + red = 0; + + if (isNaN(green)) + green = 0; + + if (isNaN(blue)) + blue = 0; + + + return [red, green, blue]; +} + + +/** + * Converts RGB color space to CIE color space + * @param {Number} red + * @param {Number} green + * @param {Number} blue + * @return {Array} Array that contains the CIE color values for x and y + */ +function rgbToCie(red, green, blue) { + var X, Y, Z, x, y; + + // normalize + red = Number((red / 255)); + green = Number((green / 255)); + blue = Number((blue / 255)); + + // gamma correction + red = (red > 0.04045) ? Math.pow((red + 0.055) / (1.0 + 0.055), 2.4) : (red / 12.92); + green = (green > 0.04045) ? Math.pow((green + 0.055) / (1.0 + 0.055), 2.4) : (green / 12.92); + blue = (blue > 0.04045) ? Math.pow((blue + 0.055) / (1.0 + 0.055), 2.4) : (blue / 12.92); + + // RGB to XYZ + X = red * 0.664511 + green * 0.154324 + blue * 0.162028; + Y = red * 0.283881 + green * 0.668433 + blue * 0.047685; + Z = red * 0.000088 + green * 0.072310 + blue * 0.986039; + + x = X / (X + Y + Z); + y = Y / (X + Y + Z); + + return [x, y]; +} \ No newline at end of file