first commit
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
import { trace } from 'potrace'
|
||||
import sharp from 'sharp'
|
||||
|
||||
const assetsDir = path.resolve(process.cwd(), 'src', 'assets')
|
||||
const outJson = path.resolve(process.cwd(), 'src', 'data', 'slotClipPaths.json')
|
||||
|
||||
// theme slot definitions to inspect (matching stripThemes.js layout)
|
||||
const themesToTrace = {
|
||||
pink: { file: '1.png', slots: [ { x:80,y:160,width:440,height:290 }, { x:80,y:615,width:440,height:290 }, { x:80,y:1070,width:440,height:290 } ] },
|
||||
ticket: { file: '8.png', slots: [ { x:80,y:160,width:440,height:290 }, { x:80,y:615,width:440,height:290 }, { x:80,y:1070,width:440,height:290 } ] },
|
||||
music: { file: '9.png', slots: [ { x:78,y:50,width:444,height:333 }, { x:78,y:545,width:444,height:333 }, { x:78,y:1040,width:444,height:333 } ] },
|
||||
bowtie: { file: '12.png', slots: [ { x:77,y:160,width:380,height:271 }, { x:77,y:549,width:380,height:271 }, { x:77,y:939,width:380,height:271 } ] },
|
||||
}
|
||||
|
||||
async function traceSlot(themeId, fileName, slot, index, options) {
|
||||
const src = path.join(assetsDir, fileName)
|
||||
const tmp = path.join(process.cwd(), 'tmp', `${themeId}-slot-${index}.png`)
|
||||
await fs.promises.mkdir(path.dirname(tmp), { recursive: true })
|
||||
// crop the slot area with a small padding to capture edges
|
||||
const pad = 2
|
||||
await sharp(src)
|
||||
.extract({ left: Math.max(0, slot.x - pad), top: Math.max(0, slot.y - pad), width: slot.width + pad*2, height: slot.height + pad*2 })
|
||||
.resize(slot.width, slot.height)
|
||||
.toFile(tmp)
|
||||
|
||||
const traceOptions = Object.assign({ background: 'transparent' }, options || {})
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
trace(tmp, traceOptions, (err, svg) => {
|
||||
if (err) return reject(err)
|
||||
// extract path d attribute
|
||||
const m = svg.match(/<path[^>]*d="([^"]+)"/)
|
||||
const d = m ? m[1] : null
|
||||
resolve(d)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const out = {}
|
||||
// parse CLI args for tuning options: --turdSize=20 --threshold=128 --optTolerance=0.4
|
||||
const argv = process.argv.slice(2)
|
||||
const cli = {}
|
||||
for (const a of argv) {
|
||||
const parts = a.replace(/^--/, '').split('=')
|
||||
cli[parts[0]] = parts[1] === undefined ? true : parts[1]
|
||||
}
|
||||
const options = {}
|
||||
if (cli.turdSize) options.turdSize = parseInt(cli.turdSize, 10)
|
||||
if (cli.threshold) options.threshold = parseInt(cli.threshold, 10)
|
||||
if (cli.optTolerance) options.optTolerance = parseFloat(cli.optTolerance)
|
||||
if (cli.turnPolicy) options.turnPolicy = cli.turnPolicy
|
||||
if (cli.background) options.background = cli.background
|
||||
|
||||
console.log('Tracing with options:', options)
|
||||
|
||||
for (const [themeId, info] of Object.entries(themesToTrace)) {
|
||||
const paths = []
|
||||
for (let i = 0; i < info.slots.length; i++) {
|
||||
try {
|
||||
process.stdout.write(`Tracing ${themeId} slot ${i}... `)
|
||||
const d = await traceSlot(themeId, info.file, info.slots[i], i, options)
|
||||
paths.push(d)
|
||||
console.log('ok')
|
||||
} catch (err) {
|
||||
console.error('failed', err.message)
|
||||
paths.push(null)
|
||||
}
|
||||
}
|
||||
out[themeId] = paths
|
||||
}
|
||||
await fs.promises.writeFile(outJson, JSON.stringify(out, null, 2), 'utf8')
|
||||
console.log('Wrote', outJson)
|
||||
}
|
||||
|
||||
main().catch(err => { console.error(err); process.exit(1) })
|
||||
Reference in New Issue
Block a user