// Split long source blocks into stepped facade modules without changing street orientation. import { readFileSync, writeFileSync } from 'node:fs' import { Cartesian3, Cartographic, Matrix4, Math as CM } from 'cesium' const file = 'tools/city-massing/district/plan.json', p = JSON.parse(readFileSync(file)) const transform = Matrix4.fromArray(p.transform) const toGeo = (q) => { const c = Cartographic.fromCartesian(Matrix4.multiplyByPoint(transform, new Cartesian3(...q), new Cartesian3())) return { lon: CM.toDegrees(c.longitude), lat: CM.toDegrees(c.latitude) } } const modules = [], samples = [] for (const b of p.buildings) { const n = b.archetype === 'midrise' ? 1 : Math.max(1, Math.ceil(b.width / (b.archetype === 'house' ? 16 : 28))) for (let part = 0; part < n; part++) { const width = b.width / n, depth = b.depth, localOffset = -b.width / 2 + width * (part + 0.5), u = [Math.cos(b.heading), Math.sin(b.heading)], v = [-u[1], u[0]] const center = [b.centerENU[0] + u[0] * localOffset, b.centerENU[1] + u[1] * localOffset, 0] const footprint = [ [-width / 2, -depth / 2], [width / 2, -depth / 2], [width / 2, depth / 2], [-width / 2, depth / 2], ].map(([x, y]) => [center[0] + u[0] * x + v[0] * y, center[1] + u[1] * x + v[1] * y, 0]) const id = b.id + '-part' + part const points = [] const cols = Math.ceil(width / 5), rows = Math.ceil(depth / 5) for (let y = 0; y <= rows; y++) for (let x = 0; x <= cols; x++) { let dx = -width / 2 + (width * x) / cols, dy = -depth / 2 + (depth * y) / rows points.push(toGeo([center[0] + u[0] * dx + v[0] * dy, center[1] + u[1] * dx + v[1] * dy, 0])) } const sampleIndices = points.map((a) => { samples.push({ ...a, building: id }) return samples.length - 1 }) modules.push({ id, sourceId: b.id, source: b.source, width, depth, heading: b.heading, centerENU: center, footprintENU: footprint, center: toGeo(center), archetype: b.archetype, floors: b.floors, part, sampleIndices, }) } } p.modules = modules p.samples = samples writeFileSync(file, JSON.stringify(p, null, 2)) console.log({ sourceBuildings: p.buildings.length, modules: modules.length, samples: samples.length })