// Select a seated CAD BIM subset for a batched export. // The full 48k plan cannot be exported in one Blender pass, so batches are cut by tier and mask; // every batch keeps the same schema so the exporter and verifier need no changes. import { readFileSync, writeFileSync } from 'node:fs' // Accept both `--key=value` and `--key value`; a bare `--key` means true. const argv = process.argv.slice(2) const args = {} for (let i = 0; i < argv.length; i++) { if (!argv[i].startsWith('--')) continue const key = argv[i].slice(2) const eq = key.indexOf('=') if (eq >= 0) { args[key.slice(0, eq)] = key.slice(eq + 1) } else if (i + 1 < argv.length && !argv[i + 1].startsWith('--')) { args[key] = argv[++i] } else { args[key] = true } } const source = JSON.parse(readFileSync('tools/city-massing/city/cad-bim/seated-plan.json', 'utf8')) let buildings = source.buildings // `--exclude ` removes buildings already exported in an earlier batch, so // successive batches tile the seated plan without duplication. let excludedIds = new Set() if (args.exclude) { for (const path of String(args.exclude).split(',')) { const manifest = JSON.parse(readFileSync(path, 'utf8')) for (const cell of manifest.cells) for (const id of cell.buildingIds) excludedIds.add(id) } buildings = buildings.filter((b) => !excludedIds.has(b.id)) } if (args.tier) buildings = buildings.filter((b) => b.tier === args.tier) if (args.mask) buildings = buildings.filter((b) => Number(b.maskId) === Number(args.mask)) if (args.max) { // Deterministic pick so a re-run selects the same buildings. const hash = (id) => { let value = 2166136261 for (const ch of id) value = ((value ^ ch.charCodeAt(0)) * 16777619) >>> 0 return value } buildings = [...buildings].sort((a, b) => hash(a.id) - hash(b.id)).slice(0, Number(args.max)) } if (!buildings.length) throw new Error('selection is empty; check --tier/--mask/--max/--exclude') const out = { ...source, buildings, batch: { tier: args.tier || 'all', mask: args.mask || 'all', selected: buildings.length, excludedByEarlierBatches: excludedIds.size, note: 'One batch of the seated CAD BIM plan for a bounded Blender export.', }, } const outFile = args.out || 'tools/city-massing/city/cad-bim/seated-batch.json' writeFileSync(outFile, JSON.stringify(out, null, 2)) console.log(JSON.stringify({ out: outFile, selected: buildings.length, byKind: Object.fromEntries([...new Set(buildings.map((b) => b.kind))].map((k) => [k, buildings.filter((b) => b.kind === k).length])), byMask: Object.fromEntries([...new Set(buildings.map((b) => String(b.maskId)))].map((k) => [k, buildings.filter((b) => String(b.maskId) === k).length])), }))