UNPKG

node-red-contrib-knx-ultimate

Version:

Control your KNX intallation via Node-Red! A bunch of KNX nodes, with integrated Philips HUE control and ETS group address importer. Easy to use and highly configurable.

116 lines (101 loc) 4.3 kB
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 {object} lampGamut Hue bulb gamut range (the lamp provides a gamut object red:{x:1,y:1}, etc) * @returns {number[]} */ calculateXYFromRGB(red, green, blue, lampGamut) { red /= 255; green /= 255; blue /= 255; const r = red > 0.04045 ? ((red + 0.055) / 1.055) ** 2.4000000953674316 : red / 12.92; const g = green > 0.04045 ? ((green + 0.055) / 1.055) ** 2.4000000953674316 : green / 12.92; const b = blue > 0.04045 ? ((blue + 0.055) / 1.055) ** 2.4000000953674316 : blue / 12.92; const x = r * 0.664511 + g * 0.154324 + b * 0.162028; const y = r * 0.283881 + g * 0.668433 + b * 0.047685; const z = r * 8.8E-5 + g * 0.07231 + b * 0.986039; const 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; } if (lampGamut !== null && lampGamut !== undefined) { const colorPoints = [[lampGamut.red.x, lampGamut.red.y], [lampGamut.green.x, lampGamut.green.y], [lampGamut.blue.x, lampGamut.blue.y]]; const inReachOfLamps = checkPointInLampsReach(xy, lampGamut); if (!inReachOfLamps) { const pAB = getClosestPointToPoints(colorPoints[0], colorPoints[1], xy); const pAC = getClosestPointToPoints(colorPoints[2], colorPoints[0], xy); const pBC = getClosestPointToPoints(colorPoints[1], colorPoints[2], xy); const dAB = getDistanceBetweenTwoPoints(xy, pAB); const dAC = getDistanceBetweenTwoPoints(xy, pAC); const dBC = getDistanceBetweenTwoPoints(xy, pBC); let lowest = dAB; let 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]); // Check boundary (min 0, max 1) xy[0] = xy[0] < 0 ? 0 : xy[0]; xy[0] = xy[0] > 1 ? 1 : xy[0]; xy[1] = xy[1] < 0 ? 0 : xy[1]; xy[1] = xy[1] > 1 ? 1 : xy[1]; return { x: xy[0], y: xy[1] }; }, }; function crossProduct(point1, point2) { return point1[0] * point2[1] - point1[1] * point2[0]; } function checkPointInLampsReach(point, lampGamutObject) { if (point != null && lampGamutObject != null) { const { red } = lampGamutObject; const { green } = lampGamutObject; const { blue } = lampGamutObject; const v1 = [green.x - red.x, green.y - red.y]; const v2 = [blue.x - red.x, blue.y - red.y]; const q = [point[0] - red.x, point[1] - red.y]; const s = crossProduct(q, v2) / crossProduct(v1, v2); const t = crossProduct(v1, q) / crossProduct(v1, v2); return s >= 0.0 && t >= 0.0 && s + t <= 1.0; } return false; } function getClosestPointToPoints(pointA, pointB, pointP) { if (pointA != null && pointB != null && pointP != null) { const pointAP = [pointP[0] - pointA[0], pointP[1] - pointA[1]]; const pointAB = [pointB[0] - pointA[0], pointB[1] - pointA[1]]; const ab2 = pointAB[0] * pointAB[0] + pointAB[1] * pointAB[1]; const apAb = pointAP[0] * pointAB[0] + pointAP[1] * pointAB[1]; let 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]; } return null; } function getDistanceBetweenTwoPoints(pointA, pointB) { const dx = pointA[0] - pointB[0]; const dy = pointA[1] - pointB[1]; const dist = Math.sqrt(dx * dx + dy * dy); return dist; } function precision(d) { return Math.round(10000.0 * d) / 10000.0; }