Initial Commit
This commit is contained in:
105
node_modules/color-space/hsm.js
generated
vendored
Normal file
105
node_modules/color-space/hsm.js
generated
vendored
Normal file
@@ -0,0 +1,105 @@
|
||||
/**
|
||||
* http://seer.ufrgs.br/rita/article/viewFile/rita_v16_n2_p141/7428
|
||||
*
|
||||
* @module color-space/hsm
|
||||
*/
|
||||
import rgb from './rgb.js';
|
||||
|
||||
var hsm = {
|
||||
name: 'hsm',
|
||||
min: [0, 0, 0],
|
||||
max: [1, 1, 1],
|
||||
channel: ['hue', 'saturation', 'mixture']
|
||||
};
|
||||
|
||||
export default hsm
|
||||
|
||||
|
||||
/**
|
||||
* HSM to RGB
|
||||
*
|
||||
* @param {Array<number>} hsm Channel values
|
||||
*
|
||||
* @return {Array<number>} RGB channel values
|
||||
*/
|
||||
hsm.rgb = function ([h, s, m]) {
|
||||
// This implementation uses an alternate derivation (with help of GPTs), since the one in paper is incorrect.
|
||||
// The idea is that the original RGB is recovered as:
|
||||
// [r, g, b] = m + d,
|
||||
// where d = s * D(m) * [cos(omega)*u + sin(omega)*v],
|
||||
// with omega = 2π * h.
|
||||
// We choose reference vector u as the normalized version of (3, -4, -4) (this is
|
||||
// consistent with the forward computation of hue in the paper) and then take v to be
|
||||
// an orthonormal vector in the same plane. The plane is defined by the constraint
|
||||
// 4*(r-m) + 2*(g-m) + (b-m) = 0.
|
||||
|
||||
// 1. Compute normalization denominator D(m) (FIXED: piecewise terms from forward transform)
|
||||
let D;
|
||||
if (m >= 0 && m <= 1 / 7) D = Math.sqrt((0 - m) ** 2 + (0 - m) ** 2 + (7 - m) ** 2);
|
||||
else if (m > 1 / 7 && m <= 3 / 7) D = Math.sqrt((0 - m) ** 2 + ((7 * m - 1) / 2 - m) ** 2 + (1 - m) ** 2);
|
||||
else if (m > 3 / 7 && m <= 0.5) D = Math.sqrt(((7 * m - 3) / 2 - m) ** 2 + (1 - m) ** 2 + (1 - m) ** 2);
|
||||
else if (m > 0.5 && m <= 4 / 7) D = Math.sqrt((7 * m / 4 - m) ** 2 + (0 - m) ** 2 + (0 - m) ** 2);
|
||||
else if (m > 4 / 7 && m <= 6 / 7) D = Math.sqrt((1 - m) ** 2 + ((7 * m - 4) / 2 - m) ** 2 + (0 - m) ** 2);
|
||||
else if (m > 6 / 7 && m <= 1) D = Math.sqrt((1 - m) ** 2 + (1 - m) ** 2 + (7 * m - 6 - m) ** 2);
|
||||
else D = 1;
|
||||
|
||||
// 2. Compute chromatic magnitude (FIXED: include D(m))
|
||||
const R = s * D;
|
||||
|
||||
// 3. Precompute coefficients (FIXED: orthonormal basis vectors)
|
||||
const cosTheta = Math.cos(2 * Math.PI * h);
|
||||
const sinTheta = Math.sin(2 * Math.PI * h);
|
||||
const u_r = 3 / Math.sqrt(41), v_r = -4 / Math.sqrt(861); // FIXED: correct u/v components
|
||||
const u_g = -4 / Math.sqrt(41), v_g = 19 / Math.sqrt(861);
|
||||
const u_b = -4 / Math.sqrt(41), v_b = -22 / Math.sqrt(861);
|
||||
|
||||
// 4. Compute deviations (FIXED: use u/v basis)
|
||||
const dr = R * (u_r * cosTheta + v_r * sinTheta);
|
||||
const dg = R * (u_g * cosTheta + v_g * sinTheta);
|
||||
const db = R * (u_b * cosTheta + v_b * sinTheta);
|
||||
|
||||
// 5. Reconstruct RGB (FIXED: add m directly, clamp values)
|
||||
const r = Math.max(0, Math.min(1, m + dr)) * 255;
|
||||
const g = Math.max(0, Math.min(1, m + dg)) * 255;
|
||||
const b = Math.max(0, Math.min(1, m + db)) * 255;
|
||||
|
||||
return [r, g, b];
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* RGB to HSM
|
||||
*
|
||||
* @param {Array<number>} rgb Channel values
|
||||
*
|
||||
* @return {Array<number>} HSM channel values
|
||||
*/
|
||||
rgb.hsm = function ([r, g, b]) {
|
||||
r /= 255, g /= 255, b /= 255;
|
||||
|
||||
let m = (4 * r + 2 * g + b) / 7;
|
||||
|
||||
// distance in the deviation space
|
||||
let dr = r - m, dg = g - m, db = b - m;
|
||||
let d = Math.sqrt(dr * dr + dg * dg + db * db);
|
||||
|
||||
// calc hue based on the angle in the deviation plane.
|
||||
let theta = Math.acos(((3 * dr - 4 * dg - 4 * db) / Math.sqrt(41 * (dr * dr + dg * dg + db * db))) || 0);
|
||||
let h = (b <= g) ? theta / (2 * Math.PI) : 1 - theta / (2 * Math.PI);
|
||||
|
||||
|
||||
// Calculate saturation (s) based on the value of m
|
||||
let s;
|
||||
|
||||
if (0 <= m && m <= 1 / 7) s = d / Math.sqrt((0 - m) ** 2 + (0 - m) ** 2 + (7 - m) ** 2);
|
||||
else if (1 / 7 < m && m <= 3 / 7) s = d / Math.sqrt((0 - m) ** 2 + ((7 * m - 1) / 2 - m) ** 2 + (1 - m) ** 2);
|
||||
else if (3 / 7 < m && m <= 1 / 2) s = d / Math.sqrt(((7 * m - 3) / 2 - m) ** 2 + (1 - m) ** 2 + (1 - m) ** 2);
|
||||
else if (1 / 2 < m && m <= 4 / 7) s = d / Math.sqrt(((7 * m) / 4 - m) ** 2 + (0 - m) ** 2 + (0 - m) ** 2);
|
||||
else if (4 / 7 < m && m <= 6 / 7) s = d / Math.sqrt((1 - m) ** 2 + ((7 * m - 4) / 2 - m) ** 2 + (0 - m) ** 2);
|
||||
else if (6 / 7 < m && m < 1) s = d / Math.sqrt((1 - m) ** 2 + (1 - m) ** 2 + ((7 * m - 6) - m) ** 2);
|
||||
else s = 0;
|
||||
|
||||
// s = Math.max(0, Math.min(1, s))
|
||||
|
||||
return [h, s, m]
|
||||
};
|
||||
Reference in New Issue
Block a user