UNPKG

@q42philips/hue-color-converter

Version:
135 lines (120 loc) 4.55 kB
var colorPointsGamut_A = [[0.703, 0.296], [0.214, 0.709], [0.139, 0.081]]; var colorPointsGamut_B = [[0.674, 0.322], [0.408, 0.517], [0.168, 0.041]]; var colorPointsGamut_C = [[0.692, 0.308], [0.17, 0.7], [0.153, 0.048]]; var colorPointsDefault = [[1.0, 0.0], [0.0, 1.0], [0.0, 0.0]]; var GAMUT_A_BULBS_LIST = ["LLC001", "LLC005", "LLC006", "LLC007", "LLC010", "LLC011", "LLC012", "LLC014", "LLC013", "LST001"]; var GAMUT_B_BULBS_LIST = ["LCT001", "LCT002", "LCT003", "LCT004", "LLM001", "LCT005", "LCT006", "LCT007"]; var GAMUT_C_BULBS_LIST = ["LLC020", "LST002"]; var MULTI_SOURCE_LUMINAIRES = ["HBL001", "HBL002", "HBL003", "HIL001", "HIL002", "HEL001", "HEL002"]; module.exports = { /** * Calculate XY color points for a given RGB value. * @param {number} red RGB red value (0-255) * @param {number} green RGB green value (0-255) * @param {number} blue RGB blue value (0-255) * @param {string} model Hue bulb model * @returns {number[]} */ calculateXY: function(red, green, blue, model) { red = red / 255; green = green / 255; blue = blue / 255; var r = red > 0.04045 ? Math.pow(((red + 0.055) / 1.055), 2.4000000953674316) : red / 12.92; var g = green > 0.04045 ? Math.pow(((green + 0.055) / 1.055), 2.4000000953674316) : green / 12.92; var b = blue > 0.04045 ? Math.pow(((blue + 0.055) / 1.055), 2.4000000953674316) : blue / 12.92; var x = r * 0.664511 + g * 0.154324 + b * 0.162028; var y = r * 0.283881 + g * 0.668433 + b * 0.047685; var z = r * 8.8E-5 + g * 0.07231 + b * 0.986039; var xy = [x / (x + y + z), y / (x + y + z)]; if (isNaN(xy[0])) { xy[0] = 0.0; } if (isNaN(xy[1])) { xy[1] = 0.0; } var colorPoints = colorPointsForModel(model); var inReachOfLamps = checkPointInLampsReach(xy, colorPoints); if (!inReachOfLamps) { var pAB = getClosestPointToPoints(colorPoints[0], colorPoints[1], xy); var pAC = getClosestPointToPoints(colorPoints[2], colorPoints[0], xy); var pBC = getClosestPointToPoints(colorPoints[1], colorPoints[2], xy); var dAB = getDistanceBetweenTwoPoints(xy, pAB); var dAC = getDistanceBetweenTwoPoints(xy, pAC); var dBC = getDistanceBetweenTwoPoints(xy, pBC); var lowest = dAB; var closestPoint = pAB; if (dAC < dAB) { lowest = dAC; closestPoint = pAC; } if (dBC < lowest) { closestPoint = pBC; } xy[0] = closestPoint[0]; xy[1] = closestPoint[1]; } xy[0] = precision(xy[0]); xy[1] = precision(xy[1]); return xy; } }; function colorPointsForModel(model) { if (model == null) { model = " "; } if (GAMUT_B_BULBS_LIST.indexOf(model) == -1 && MULTI_SOURCE_LUMINAIRES.indexOf(model) == -1) { if(GAMUT_A_BULBS_LIST.indexOf(model) >= 0) { return colorPointsGamut_A; } else if(GAMUT_C_BULBS_LIST.indexOf(model) >= 0) { return colorPointsGamut_C; } else { return colorPointsDefault; } } else { return colorPointsGamut_B; } } function checkPointInLampsReach(point, colorPoints) { if (point != null && colorPoints != null) { var red = colorPoints[0]; var green = colorPoints[1]; var blue = colorPoints[2]; var v1 = [green[0] - red[0], green[1] - red[1]]; var v2 = [blue[0] - red[0], blue[1] - red[1]]; var q = [point[0] - red[0], point[1] - red[1]]; var s = crossProduct(q, v2) / crossProduct(v1, v2); var t = crossProduct(v1, q) / crossProduct(v1, v2); return s >= 0.0 && t >= 0.0 && s + t <= 1.0; } else { return false; } } function crossProduct(point1, point2) { return point1[0] * point2[1] - point1[1] * point2[0]; } function getClosestPointToPoints(pointA, pointB, pointP) { if (pointA != null && pointB != null && pointP != null) { var pointAP = [pointP[0] - pointA[0], pointP[1] - pointA[1]]; var pointAB = [pointB[0] - pointA[0], pointB[1] - pointA[1]]; var ab2 = pointAB[0] * pointAB[0] + pointAB[1] * pointAB[1]; var apAb = pointAP[0] * pointAB[0] + pointAP[1] * pointAB[1]; var t = apAb / ab2; if(t < 0.0) { t = 0.0; } else if(t > 1.0) { t = 1.0; } return [pointA[0] + pointAB[0] * t, pointA[1] + pointAB[1] * t]; } else { return null; } } function getDistanceBetweenTwoPoints(pointA, pointB) { var dx = pointA[0] - pointB[0]; var dy = pointA[1] - pointB[1]; var dist = Math.sqrt(dx * dx + dy * dy); return dist; } function precision(d) { return Math.round(10000.0 * d) / 10000.0; }