← back to Dw Pairs Well

lib/paint.js

60 lines

// Nearest Sherwin-Williams + Dunn-Edwards paint match for any hex.
// Reads from color_reference (already populated with hex + Lab).

const { hexToLab } = require('./color');

// All seven paint brands stocked in color_reference. The frontend picks which
// to display per request via ?brands=sw,de,bm — server always returns all so
// switching is instant (no extra fetch).
const BRANDS = ['sherwin-williams', 'dunn-edwards', 'benjamin-moore', 'behr', 'ppg', 'farrow-ball', 'ral'];
const BRAND_LABELS = {
  'sherwin-williams': 'SW',
  'dunn-edwards':     'DE',
  'benjamin-moore':   'BM',
  'behr':             'Behr',
  'ppg':              'PPG',
  'farrow-ball':      'F&B',
  'ral':              'RAL'
};
const cache = new Map();   // hex -> { sherwin-williams: {...}, dunn-edwards: {...} }

async function nearestPaints(pool, hex) {
  if (!hex) return null;
  var key = String(hex).toLowerCase();
  if (cache.has(key)) return cache.get(key);

  var lab = hexToLab(hex);
  if (!lab) return null;

  var sql = `
    SELECT DISTINCT ON (brand) brand, code, name, hex,
           ((lab_l - $1) * (lab_l - $1)
          + (lab_a - $2) * (lab_a - $2)
          + (lab_b - $3) * (lab_b - $3)) AS dist_sq
    FROM color_reference
    WHERE brand = ANY($4)
    ORDER BY brand, dist_sq
  `;
  var r = await pool.query(sql, [lab.L, lab.a, lab.b, BRANDS]);
  var out = {};
  for (var i = 0; i < r.rows.length; i++) {
    var row = r.rows[i];
    out[row.brand] = { code: row.code, name: row.name, hex: row.hex };
  }
  cache.set(key, out);
  return out;
}

// Bulk variant — pass an array of hexes, get back map hex -> paints
async function nearestPaintsBatch(pool, hexes) {
  var uniq = Array.from(new Set((hexes || []).filter(Boolean).map(function (h) { return String(h).toLowerCase(); })));
  var result = {};
  for (var i = 0; i < uniq.length; i++) {
    var h = uniq[i];
    result[h] = await nearestPaints(pool, h);
  }
  return result;
}

module.exports = { nearestPaints, nearestPaintsBatch, BRANDS, BRAND_LABELS };