@ha4us/hue.adapter
Version:
Adapter for the hue system to ha4us
192 lines (191 loc) • 6.5 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
var tinycolor = require("tinycolor2");
var GAMUT_A = [[0.703, 0.296], [0.214, 0.709], [0.139, 0.081]];
var GAMUT_B = [[0.674, 0.322], [0.408, 0.517], [0.168, 0.041]];
var GAMUT_C = [[0.692, 0.308], [0.17, 0.7], [0.153, 0.048]];
var GAMUT_DEFAULT = [[1.0, 0.0], [0.0, 1.0], [0.0, 0.0]];
exports.MODEL_GAMUTS = {
LLC001: GAMUT_A,
LLC005: GAMUT_A,
LLC006: GAMUT_A,
LLC007: GAMUT_A,
LLC010: GAMUT_A,
LLC011: GAMUT_A,
LLC012: GAMUT_A,
LLC014: GAMUT_A,
LLC013: GAMUT_A,
LST001: GAMUT_A,
LCT001: GAMUT_B,
LCT002: GAMUT_B,
LCT003: GAMUT_B,
LCT004: GAMUT_B,
LLM001: GAMUT_B,
LCT005: GAMUT_B,
LCT006: GAMUT_B,
LCT007: GAMUT_B,
LCT010: GAMUT_C,
LCT011: GAMUT_C,
LCT012: GAMUT_C,
LCT014: GAMUT_C,
LCT015: GAMUT_C,
LCT016: GAMUT_C,
LLC020: GAMUT_C,
LST002: GAMUT_C,
};
var XY = /** @class */ (function () {
function XY(xy) {
this.x = xy[0];
this.y = xy[1];
}
return XY;
}());
var HueLimit = /** @class */ (function () {
function HueLimit(gamut) {
this.red = new XY(gamut[0]);
this.green = new XY(gamut[1]);
this.blue = new XY(gamut[2]);
}
return HueLimit;
}());
function _crossProduct(p1, p2) {
return p1.x * p2.y - p1.y * p2.x;
}
function _isInColorGamut(p, lampLimits) {
var v1 = new XY([
lampLimits.green.x - lampLimits.red.x,
lampLimits.green.y - lampLimits.red.y,
]);
var v2 = new XY([
lampLimits.blue.x - lampLimits.red.x,
lampLimits.blue.y - lampLimits.red.y,
]);
var q = new XY([p.x - lampLimits.red.x, p.y - lampLimits.red.y]);
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;
}
/**
* Find the closest point on a line. This point will be reproducible by the limits.
*
* @param start The point where the line starts.
* @param stop The point where the line ends.
* @param point The point which is close to the line.
* @return A point that is on the line specified, and closest to the XY provided.
*/
function _getClosestPoint(start, stop, point) {
var AP = new XY([point.x - start.x, point.y - start.y]);
var AB = new XY([stop.x - start.x, stop.y - start.y]);
var ab2 = AB.x * AB.x + AB.y * AB.y;
var apAb = AP.x * AB.x + AP.y * AB.y;
var t = apAb / ab2;
if (t < 0.0) {
t = 0.0;
}
else if (t > 1.0) {
t = 1.0;
}
return new XY([start.x + AB.x * t, start.y + AB.y * t]);
}
function _getDistanceBetweenPoints(pOne, pTwo) {
var dx = pOne.x - pTwo.x;
var dy = pOne.y - pTwo.y;
return Math.sqrt(dx * dx + dy * dy);
}
/**
* When a color is outside the limits, find the closest point on each line in the CIE 1931 'triangle'.
* @param point The point that is outside the limits
* @param limits The limits of the bulb (red, green and blue XY points).
* @return [description]
*/
function _resolveXYPointForLamp(point, limits) {
var pAB = _getClosestPoint(limits.red, limits.green, point);
var pAC = _getClosestPoint(limits.blue, limits.red, point);
var pBC = _getClosestPoint(limits.green, limits.blue, point);
var dAB = _getDistanceBetweenPoints(point, pAB);
var dAC = _getDistanceBetweenPoints(point, pAC);
var dBC = _getDistanceBetweenPoints(point, pBC);
var lowest = dAB;
var closestPoint = pAB;
if (dAC < lowest) {
lowest = dAC;
closestPoint = pAC;
}
if (dBC < lowest) {
closestPoint = pBC;
}
return closestPoint;
}
function gammaCorrection(value) {
return value > 0.04045
? Math.pow((value + 0.055) / (1.0 + 0.055), 2.4)
: value / 12.92;
}
var XY2RGBConverter = /** @class */ (function () {
function XY2RGBConverter(colorgamut) {
this.limit = new HueLimit(colorgamut);
}
XY2RGBConverter.prototype.toRGB = function (xy, brightness) {
var point = new XY(xy);
// 4th - 2: check for model gamut
if (!_isInColorGamut(point, this.limit)) {
point = _resolveXYPointForLamp(xy, this.limit);
}
var z = 1 - point.x - point.y;
var Y = brightness;
var X = (Y / point.y) * point.x;
var Z = (Y / point.y) * z;
// Convert to RGB using Wide RGB D65 conversion
var rgb = [
X * 1.656492 - Y * 0.354851 - Z * 0.255038,
-X * 0.707196 + Y * 1.655397 + Z * 0.036152,
X * 0.051713 - Y * 0.121364 + Z * 1.01153,
];
// Apply reverse gamma correction.
rgb = rgb.map(function (_x) {
return _x <= 0.0031308
? 12.92 * _x
: (1.0 + 0.055) * Math.pow(_x, 1.0 / 2.4) - 0.055;
});
// Bring all negative components to zero.
rgb = rgb.map(function (_x) {
return Math.max(0, _x);
});
// If one component is greater than 1, weight components by that value.
var max = Math.max(rgb[0], rgb[1], rgb[2]);
if (max > 1) {
rgb = rgb.map(function (_x) {
return _x / max;
});
}
rgb = rgb.map(function (_x) {
return Math.floor(_x * 255);
});
var r = rgb[0], g = rgb[1], b = rgb[2];
return tinycolor({ r: r, g: g, b: b }).toHexString();
};
XY2RGBConverter.prototype.toXY = function (rgbColor) {
var _a = tinycolor(rgbColor).toRgb(), r = _a.r, g = _a.g, b = _a.b;
// 1st + 2nd : Normalize a scale 1 + apply Gamma Correction
r = gammaCorrection(r / 255);
g = gammaCorrection(g / 255);
b = gammaCorrection(b / 255);
// 3rd: Wide RGB D65
var X = r * 0.4360747 + g * 0.3850649 + b * 0.0930804;
var Y = r * 0.2225045 + g * 0.7168786 + b * 0.0406169;
var Z = r * 0.0139322 + g * 0.0971045 + b * 0.7141733;
// 4th: calculate xy
var cx = X / (X + Y + Z);
var cy = Y / (X + Y + Z);
cx = isNaN(cx) ? 0.0 : cx;
cy = isNaN(cy) ? 0.0 : cy;
var xyPoint = new XY([cx, cy]);
// 4th - 2: check for model gamut
if (!_isInColorGamut(xyPoint, this.limit)) {
xyPoint = _resolveXYPointForLamp(xyPoint, this.limit);
}
return [xyPoint.x, xyPoint.y];
};
return XY2RGBConverter;
}());
exports.XY2RGBConverter = XY2RGBConverter;