File size: 2,655 Bytes
9de7b70
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
// 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 }