prepare-modules.mjs 2.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162
  1. // Split long source blocks into stepped facade modules without changing street orientation.
  2. import { readFileSync, writeFileSync } from 'node:fs'
  3. import { Cartesian3, Cartographic, Matrix4, Math as CM } from 'cesium'
  4. const file = 'tools/city-massing/district/plan.json',
  5. p = JSON.parse(readFileSync(file))
  6. const transform = Matrix4.fromArray(p.transform)
  7. const toGeo = (q) => {
  8. const c = Cartographic.fromCartesian(Matrix4.multiplyByPoint(transform, new Cartesian3(...q), new Cartesian3()))
  9. return { lon: CM.toDegrees(c.longitude), lat: CM.toDegrees(c.latitude) }
  10. }
  11. const modules = [],
  12. samples = []
  13. for (const b of p.buildings) {
  14. const n = b.archetype === 'midrise' ? 1 : Math.max(1, Math.ceil(b.width / (b.archetype === 'house' ? 16 : 28)))
  15. for (let part = 0; part < n; part++) {
  16. const width = b.width / n,
  17. depth = b.depth,
  18. localOffset = -b.width / 2 + width * (part + 0.5),
  19. u = [Math.cos(b.heading), Math.sin(b.heading)],
  20. v = [-u[1], u[0]]
  21. const center = [b.centerENU[0] + u[0] * localOffset, b.centerENU[1] + u[1] * localOffset, 0]
  22. const footprint = [
  23. [-width / 2, -depth / 2],
  24. [width / 2, -depth / 2],
  25. [width / 2, depth / 2],
  26. [-width / 2, depth / 2],
  27. ].map(([x, y]) => [center[0] + u[0] * x + v[0] * y, center[1] + u[1] * x + v[1] * y, 0])
  28. const id = b.id + '-part' + part
  29. const points = []
  30. const cols = Math.ceil(width / 5),
  31. rows = Math.ceil(depth / 5)
  32. for (let y = 0; y <= rows; y++)
  33. for (let x = 0; x <= cols; x++) {
  34. let dx = -width / 2 + (width * x) / cols,
  35. dy = -depth / 2 + (depth * y) / rows
  36. points.push(toGeo([center[0] + u[0] * dx + v[0] * dy, center[1] + u[1] * dx + v[1] * dy, 0]))
  37. }
  38. const sampleIndices = points.map((a) => {
  39. samples.push({ ...a, building: id })
  40. return samples.length - 1
  41. })
  42. modules.push({
  43. id,
  44. sourceId: b.id,
  45. source: b.source,
  46. width,
  47. depth,
  48. heading: b.heading,
  49. centerENU: center,
  50. footprintENU: footprint,
  51. center: toGeo(center),
  52. archetype: b.archetype,
  53. floors: b.floors,
  54. part,
  55. sampleIndices,
  56. })
  57. }
  58. }
  59. p.modules = modules
  60. p.samples = samples
  61. writeFileSync(file, JSON.stringify(p, null, 2))
  62. console.log({ sourceBuildings: p.buildings.length, modules: modules.length, samples: samples.length })