// MARD 色库预设 + 预处理(Typed Array 结构,供匹配引擎高速读取) const { MARD_PALETTE, MARD_SMART_ORDER } = require('./palette-data') const { hexToRgb, rgbToOklabInto, luma } = require('./color') // 颜色匹配模式:darkMax/lightMin 为二维码深/浅模块允许的亮度范围 const MATCH_MODES = [ { id: 'rich', label: '色彩优先', hint: '更像原图', darkMax: 0.4, lightMin: 0.74 }, { id: 'balanced', label: '均衡', hint: '推荐', darkMax: 0.36, lightMin: 0.78 }, { id: 'strict', label: '扫码优先', hint: '最稳', darkMax: 0.3, lightMin: 0.84 }, ] const byId = new Map(MARD_PALETTE.map((c) => [c.id, c])) const pick = (ids) => ids.map((id) => byId.get(id)).filter(Boolean) const STANDARD_221 = MARD_PALETTE.filter((c) => /^[A-HM]\d+$/.test(c.id)) const PRESETS = [ { id: '221', label: '标准 221 色', short: '221 色', detail: 'MARD 标准色,最常见', list: () => STANDARD_221 }, { id: '291', label: '完整 291 色', short: '291 色', detail: '含扩展色,颜色最全', list: () => MARD_PALETTE }, ...[144, 96, 72, 48, 24].map((n) => ({ id: String(n), label: `精选 ${n} 色`, short: `${n} 色`, detail: n <= 48 ? '入门套装,买豆更省' : '常用色,兼顾效果与成本', list: () => pick(MARD_SMART_ORDER.slice(0, n)), })), ] const cache = {} function preparePalette(presetId) { if (cache[presetId]) return cache[presetId] const preset = PRESETS.find((p) => p.id === presetId) || PRESETS[0] const list = preset.list() const n = list.length const rgb = new Uint8Array(n * 3) const lab = new Float32Array(n * 3) const lum = new Float32Array(n) list.forEach((c, i) => { const [r, g, b] = hexToRgb(c.hex) rgb[i * 3] = r; rgb[i * 3 + 1] = g; rgb[i * 3 + 2] = b rgbToOklabInto(r, g, b, lab, i * 3) lum[i] = luma(r, g, b) }) // 色与色之间的距离矩阵(291² ≈ 8.5 万项,一次性计算) const pair = new Float32Array(n * n) for (let i = 0; i < n; i += 1) { for (let j = i + 1; j < n; j += 1) { const dl = lab[i * 3] - lab[j * 3] const da = lab[i * 3 + 1] - lab[j * 3 + 1] const db = lab[i * 3 + 2] - lab[j * 3 + 2] const d = Math.sqrt(dl * dl + da * da + db * db) pair[i * n + j] = d pair[j * n + i] = d } } const prepared = { id: preset.id, label: preset.label, short: preset.short, n, ids: list.map((c) => c.id), hexes: list.map((c) => c.hex), rgb, lab, lum, pair, subsets: {}, luts: {}, } cache[presetId] = prepared return prepared } module.exports = { MATCH_MODES, PRESETS, preparePalette }