diff-versions.mjs 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. // 对比同一画板的新旧 JSON:尺寸、导出标记数、切图文件、标题区结构、字体、色板
  2. import { readFileSync, readdirSync, existsSync } from 'node:fs'
  3. import { join } from 'node:path'
  4. const oldDir = '.scratch/lanhu-cockpit-asset-replacement/research/raw'
  5. const newDir = '.scratch/lanhu-cockpit-asset-replacement/research/raw-v2'
  6. function load(dir, file) {
  7. return JSON.parse(readFileSync(join(dir, file), 'utf8'))
  8. }
  9. function hex(c) {
  10. const h = (v) => Math.round(v * 255).toString(16).padStart(2, '0')
  11. return `#${h(c.r)}${h(c.g)}${h(c.b)}`
  12. }
  13. function summarize(j) {
  14. const ab = j.artboard
  15. let exportMarked = 0
  16. let ddsMarked = 0
  17. const exported = []
  18. const fonts = new Map()
  19. const colors = new Map()
  20. const images = []
  21. function walk(node, path) {
  22. const p = path ? `${path}/${node.name}` : node.name
  23. if (node.hasExportImage) {
  24. exportMarked += 1
  25. exported.push({ path: p, w: Math.round(node.frame?.width ?? 0), h: Math.round(node.frame?.height ?? 0), type: node.type })
  26. }
  27. if (node.hasExportDDSImage) ddsMarked += 1
  28. if (node.type === 'textLayer' && node.text?.style?.font?.name) {
  29. const f = node.text.style.font
  30. const k = `${f.name}|${f.size}`
  31. fonts.set(k, (fonts.get(k) || 0) + 1)
  32. }
  33. for (const f of node.style?.fills || []) {
  34. if (f.type === 'color' && f.color) {
  35. const c = hex(f.color)
  36. colors.set(c, (colors.get(c) || 0) + 1)
  37. }
  38. const url = f.ddsImage?.imageUrl || f.image?.imageUrl || f.imageUrl
  39. if (f.type === 'image' && url) images.push({ p, url })
  40. }
  41. for (const ch of node.layers || []) walk(ch, p)
  42. }
  43. for (const l of ab.layers || []) walk(l, '')
  44. const titleNode = (function find(n) {
  45. if (n.name === 'title') return n
  46. for (const c of n.layers || []) {
  47. const r = find(c)
  48. if (r) return r
  49. }
  50. return null
  51. })(ab)
  52. return {
  53. name: ab.name,
  54. frame: `${ab.frame.width}x${ab.frame.height}`,
  55. exportMarked,
  56. ddsMarked,
  57. assets: (j.assets || []).length,
  58. assetUrls: j.assets || [],
  59. fonts: fonts.size,
  60. fontList: [...fonts.keys()].slice(0, 8),
  61. colors: colors.size,
  62. topColors: [...colors.entries()].sort((a, b) => b[1] - a[1]).slice(0, 8).map(([c]) => c),
  63. images: images.length,
  64. titleChildren: titleNode ? (titleNode.layers || []).map((c) => `${c.type}:${c.name} ${Math.round(c.frame?.width ?? 0)}x${Math.round(c.frame?.height ?? 0)}`) : [],
  65. exported: exported.slice(0, 40),
  66. }
  67. }
  68. const files = readdirSync(newDir).filter((f) => f.endsWith('.json'))
  69. for (const file of files) {
  70. const n = summarize(load(newDir, file))
  71. console.log(`\n=== NEW ${n.name} ${n.frame} | exportMarked=${n.exportMarked} dds=${n.ddsMarked} assets=${n.assets} fonts=${n.fonts} colors=${n.colors} images=${n.images}`)
  72. console.log(' fonts:', n.fontList.join(', '))
  73. console.log(' colors:', n.topColors.join(' '))
  74. console.log(' title:', n.titleChildren.join(' | '))
  75. console.log(' exported layers:')
  76. for (const e of n.exported) console.log(` ${e.w}x${e.h} ${e.type} | ${e.path}`)
  77. const oldPath = join(oldDir, file)
  78. if (existsSync(oldPath)) {
  79. const o = summarize(load(oldDir, file))
  80. console.log(` --- OLD ${o.name} ${o.frame} | exportMarked=${o.exportMarked} dds=${o.ddsMarked} assets=${o.assets} fonts=${o.fonts} colors=${o.colors} images=${o.images}`)
  81. console.log(' --- OLD title:', o.titleChildren.join(' | '))
  82. const oldSet = new Set(o.exported.map((e) => e.path))
  83. const added = n.exported.filter((e) => !oldSet.has(e.path))
  84. console.log(' --- NEW export layers (not in old):', added.length)
  85. for (const e of added) console.log(` + ${e.w}x${e.h} ${e.type} | ${e.path}`)
  86. }
  87. }