@kerros/viewer 0.0.0-stage → 0.1.0-rc.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +31 -2
- package/dist/adapters/basemap.js +11 -0
- package/dist/adapters/basemap.js.map +1 -0
- package/dist/adapters/persistence.js +121 -0
- package/dist/adapters/persistence.js.map +1 -0
- package/dist/adapters/status.js +18 -0
- package/dist/adapters/status.js.map +1 -0
- package/dist/components/ElevatorPanel.js +120 -0
- package/dist/components/ElevatorPanel.js.map +1 -0
- package/dist/components/Icons.js +57 -0
- package/dist/components/Icons.js.map +1 -0
- package/dist/components/Inspector.js +1184 -0
- package/dist/components/Inspector.js.map +1 -0
- package/dist/components/NavigatePanel.js +301 -0
- package/dist/components/NavigatePanel.js.map +1 -0
- package/dist/components/NavigationGraph.js +234 -0
- package/dist/components/NavigationGraph.js.map +1 -0
- package/dist/components/StructureExplorer.js +510 -0
- package/dist/components/StructureExplorer.js.map +1 -0
- package/dist/components/StructureView.js +286 -0
- package/dist/components/StructureView.js.map +1 -0
- package/dist/components/controls.js +170 -0
- package/dist/components/controls.js.map +1 -0
- package/dist/components/graphLayout.js +27 -0
- package/dist/components/graphLayout.js.map +1 -0
- package/dist/host.js +169 -0
- package/dist/host.js.map +1 -0
- package/dist/i18n.js +88 -0
- package/dist/i18n.js.map +1 -0
- package/dist/index.js +171 -0
- package/dist/index.js.map +1 -0
- package/dist/map/CabinMaterials.js +81 -0
- package/dist/map/CabinMaterials.js.map +1 -0
- package/dist/map/CabinMirror.js +43 -0
- package/dist/map/CabinMirror.js.map +1 -0
- package/dist/map/Doors.js +80 -0
- package/dist/map/Doors.js.map +1 -0
- package/dist/map/ElevatorCabin.js +93 -0
- package/dist/map/ElevatorCabin.js.map +1 -0
- package/dist/map/FixtureLights.js +117 -0
- package/dist/map/FixtureLights.js.map +1 -0
- package/dist/map/MapCanvas.js +2161 -0
- package/dist/map/MapCanvas.js.map +1 -0
- package/dist/map/Passenger.js +69 -0
- package/dist/map/Passenger.js.map +1 -0
- package/dist/map/SceneLayer.js +1534 -0
- package/dist/map/SceneLayer.js.map +1 -0
- package/dist/map/UndergroundContext.js +153 -0
- package/dist/map/UndergroundContext.js.map +1 -0
- package/dist/map/ambience.js +194 -0
- package/dist/map/ambience.js.map +1 -0
- package/dist/map/architecture.js +305 -0
- package/dist/map/architecture.js.map +1 -0
- package/dist/map/bathMusic.js +92 -0
- package/dist/map/bathMusic.js.map +1 -0
- package/dist/map/cameraAim.js +33 -0
- package/dist/map/cameraAim.js.map +1 -0
- package/dist/map/elevators.js +56 -0
- package/dist/map/elevators.js.map +1 -0
- package/dist/map/excavation.js +52 -0
- package/dist/map/excavation.js.map +1 -0
- package/dist/map/exteriors.js +25 -0
- package/dist/map/exteriors.js.map +1 -0
- package/dist/map/features.js +218 -0
- package/dist/map/features.js.map +1 -0
- package/dist/map/fences.js +29 -0
- package/dist/map/fences.js.map +1 -0
- package/dist/map/journey.js +112 -0
- package/dist/map/journey.js.map +1 -0
- package/dist/map/levels.js +6 -0
- package/dist/map/levels.js.map +1 -0
- package/dist/map/lighting.js +142 -0
- package/dist/map/lighting.js.map +1 -0
- package/dist/map/loadBasemap.js +26 -0
- package/dist/map/loadBasemap.js.map +1 -0
- package/dist/map/materials.js +183 -0
- package/dist/map/materials.js.map +1 -0
- package/dist/map/poolLighting.js +39 -0
- package/dist/map/poolLighting.js.map +1 -0
- package/dist/map/projection.js +24 -0
- package/dist/map/projection.js.map +1 -0
- package/dist/map/rampGeometry.js +38 -0
- package/dist/map/rampGeometry.js.map +1 -0
- package/dist/map/route.js +107 -0
- package/dist/map/route.js.map +1 -0
- package/dist/map/stairSurfaces.js +115 -0
- package/dist/map/stairSurfaces.js.map +1 -0
- package/dist/map/surfaces.js +136 -0
- package/dist/map/surfaces.js.map +1 -0
- package/dist/map/textures.js +95 -0
- package/dist/map/textures.js.map +1 -0
- package/dist/map/underground.js +34 -0
- package/dist/map/underground.js.map +1 -0
- package/dist/map/walk.js +433 -0
- package/dist/map/walk.js.map +1 -0
- package/dist/map/walkContext.js +46 -0
- package/dist/map/walkContext.js.map +1 -0
- package/dist/map/walkDimensions.js +7 -0
- package/dist/map/walkDimensions.js.map +1 -0
- package/dist/map/walkJourney.js +85 -0
- package/dist/map/walkJourney.js.map +1 -0
- package/dist/map/walkSurfaces.js +106 -0
- package/dist/map/walkSurfaces.js.map +1 -0
- package/dist/map/wallJoins.js +70 -0
- package/dist/map/wallJoins.js.map +1 -0
- package/dist/map/water.js +274 -0
- package/dist/map/water.js.map +1 -0
- package/dist/model/authoring.js +80 -0
- package/dist/model/authoring.js.map +1 -0
- package/dist/model/boundaries.js +548 -0
- package/dist/model/boundaries.js.map +1 -0
- package/dist/model/coverage.js +30 -0
- package/dist/model/coverage.js.map +1 -0
- package/dist/model/document.js +52 -0
- package/dist/model/document.js.map +1 -0
- package/dist/model/doors.js +47 -0
- package/dist/model/doors.js.map +1 -0
- package/dist/model/factory.js +56 -0
- package/dist/model/factory.js.map +1 -0
- package/dist/model/geometry.js +394 -0
- package/dist/model/geometry.js.map +1 -0
- package/dist/model/inference.js +182 -0
- package/dist/model/inference.js.map +1 -0
- package/dist/model/materials.js +11 -0
- package/dist/model/materials.js.map +1 -0
- package/dist/model/mutations.js +157 -0
- package/dist/model/mutations.js.map +1 -0
- package/dist/model/navigation.js +275 -0
- package/dist/model/navigation.js.map +1 -0
- package/dist/model/ontology.js +109 -0
- package/dist/model/ontology.js.map +1 -0
- package/dist/model/passages.js +5 -0
- package/dist/model/passages.js.map +1 -0
- package/dist/model/portals.js +42 -0
- package/dist/model/portals.js.map +1 -0
- package/dist/model/precision.js +11 -0
- package/dist/model/precision.js.map +1 -0
- package/dist/model/project.js +17 -0
- package/dist/model/project.js.map +1 -0
- package/dist/model/ramps.js +10 -0
- package/dist/model/ramps.js.map +1 -0
- package/dist/model/roomNavigation.js +87 -0
- package/dist/model/roomNavigation.js.map +1 -0
- package/dist/model/routeClearance.js +72 -0
- package/dist/model/routeClearance.js.map +1 -0
- package/dist/model/spaces.js +104 -0
- package/dist/model/spaces.js.map +1 -0
- package/dist/model/stairGeometry.js +40 -0
- package/dist/model/stairGeometry.js.map +1 -0
- package/dist/model/stairTraversal.js +48 -0
- package/dist/model/stairTraversal.js.map +1 -0
- package/dist/model/topology.js +63 -0
- package/dist/model/topology.js.map +1 -0
- package/dist/model/types.js +35 -0
- package/dist/model/types.js.map +1 -0
- package/dist/model/validate.js +240 -0
- package/dist/model/validate.js.map +1 -0
- package/dist/model/vertical.js +129 -0
- package/dist/model/vertical.js.map +1 -0
- package/dist/model/walls.js +71 -0
- package/dist/model/walls.js.map +1 -0
- package/dist/styles.css +4931 -0
- package/dist/theme/index.js +34 -0
- package/dist/theme/index.js.map +1 -0
- package/dist/types/adapters/basemap.d.ts +2 -0
- package/dist/types/adapters/persistence.d.ts +29 -0
- package/dist/types/adapters/status.d.ts +4 -0
- package/dist/types/components/ElevatorPanel.d.ts +18 -0
- package/dist/types/components/Icons.d.ts +8 -0
- package/dist/types/components/Inspector.d.ts +38 -0
- package/dist/types/components/NavigatePanel.d.ts +27 -0
- package/dist/types/components/NavigationGraph.d.ts +10 -0
- package/dist/types/components/StructureExplorer.d.ts +8 -0
- package/dist/types/components/StructureView.d.ts +22 -0
- package/dist/types/components/controls.d.ts +52 -0
- package/dist/types/components/graphLayout.d.ts +9 -0
- package/dist/types/i18n.d.ts +85 -0
- package/dist/types/import/analysisPreview.d.ts +27 -0
- package/dist/types/import/brief.d.ts +22 -0
- package/dist/types/import/checkpoint.d.ts +49 -0
- package/dist/types/import/instructions.d.ts +7 -0
- package/dist/types/import/usage.d.ts +10 -0
- package/dist/types/map/CabinMaterials.d.ts +18 -0
- package/dist/types/map/CabinMirror.d.ts +13 -0
- package/dist/types/map/Doors.d.ts +11 -0
- package/dist/types/map/ElevatorCabin.d.ts +20 -0
- package/dist/types/map/FixtureLights.d.ts +35 -0
- package/dist/types/map/MapCanvas.d.ts +87 -0
- package/dist/types/map/Passenger.d.ts +18 -0
- package/dist/types/map/SceneLayer.d.ts +306 -0
- package/dist/types/map/UndergroundContext.d.ts +18 -0
- package/dist/types/map/ambience.d.ts +29 -0
- package/dist/types/map/architecture.d.ts +8 -0
- package/dist/types/map/bathMusic.d.ts +14 -0
- package/dist/types/map/cameraAim.d.ts +20 -0
- package/dist/types/map/elevators.d.ts +16 -0
- package/dist/types/map/excavation.d.ts +9 -0
- package/dist/types/map/exteriors.d.ts +3 -0
- package/dist/types/map/features.d.ts +33 -0
- package/dist/types/map/fences.d.ts +4 -0
- package/dist/types/map/journey.d.ts +36 -0
- package/dist/types/map/levels.d.ts +4 -0
- package/dist/types/map/lighting.d.ts +103 -0
- package/dist/types/map/loadBasemap.d.ts +4 -0
- package/dist/types/map/materials.d.ts +38 -0
- package/dist/types/map/poolLighting.d.ts +11 -0
- package/dist/types/map/projection.d.ts +9 -0
- package/dist/types/map/rampGeometry.d.ts +8 -0
- package/dist/types/map/route.d.ts +59 -0
- package/dist/types/map/stairSurfaces.d.ts +29 -0
- package/dist/types/map/surfaces.d.ts +36 -0
- package/dist/types/map/textures.d.ts +8 -0
- package/dist/types/map/underground.d.ts +30 -0
- package/dist/types/map/walk.d.ts +232 -0
- package/dist/types/map/walkContext.d.ts +11 -0
- package/dist/types/map/walkDimensions.d.ts +5 -0
- package/dist/types/map/walkJourney.d.ts +33 -0
- package/dist/types/map/walkSurfaces.d.ts +27 -0
- package/dist/types/map/wallJoins.d.ts +6 -0
- package/dist/types/map/water.d.ts +12 -0
- package/dist/types/model/authoring.d.ts +38 -0
- package/dist/types/model/boundaries.d.ts +41 -0
- package/dist/types/model/coverage.d.ts +8 -0
- package/dist/types/model/document.d.ts +9 -0
- package/dist/types/model/doors.d.ts +19 -0
- package/dist/types/model/factory.d.ts +5 -0
- package/dist/types/model/geometry.d.ts +99 -0
- package/dist/types/model/host.d.ts +154 -0
- package/dist/types/model/inference.d.ts +46 -0
- package/dist/types/model/live.d.ts +60 -0
- package/dist/types/model/materials.d.ts +28 -0
- package/dist/types/model/mutations.d.ts +178 -0
- package/dist/types/model/navigation.d.ts +70 -0
- package/dist/types/model/ontology.d.ts +43 -0
- package/dist/types/model/passages.d.ts +2 -0
- package/dist/types/model/portals.d.ts +9 -0
- package/dist/types/model/precision.d.ts +11 -0
- package/dist/types/model/project.d.ts +8 -0
- package/dist/types/model/ramps.d.ts +7 -0
- package/dist/types/model/roomNavigation.d.ts +15 -0
- package/dist/types/model/routeClearance.d.ts +11 -0
- package/dist/types/model/spaces.d.ts +46 -0
- package/dist/types/model/stairGeometry.d.ts +27 -0
- package/dist/types/model/stairTraversal.d.ts +8 -0
- package/dist/types/model/topology.d.ts +14 -0
- package/dist/types/model/types.d.ts +369 -0
- package/dist/types/model/validate.d.ts +38 -0
- package/dist/types/model/vertical.d.ts +145 -0
- package/dist/types/model/walls.d.ts +71 -0
- package/dist/types/schema/index.d.ts +25 -0
- package/dist/types/theme/index.d.ts +55 -0
- package/dist/types/viewer/FloorViewer.d.ts +76 -0
- package/dist/types/viewer/ViewerWorkspace.d.ts +2 -0
- package/dist/types/viewer/host.d.ts +11 -0
- package/dist/types/viewer/index.d.ts +2 -0
- package/dist/types/viewer/panels.d.ts +12 -0
- package/dist/viewer/FloorViewer.js +104 -0
- package/dist/viewer/FloorViewer.js.map +1 -0
- package/dist/viewer/ViewerWorkspace.js +370 -0
- package/dist/viewer/ViewerWorkspace.js.map +1 -0
- package/dist/viewer/panels.js +35 -0
- package/dist/viewer/panels.js.map +1 -0
- package/package.json +58 -4
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"boundaries.js","sources":["../../../../src/model/boundaries.ts"],"sourcesContent":["// The shared 2D boundary model. Barriers ARE physical edges; only unwalled edges need another record.\n// Space loops reference these edges. Coordinates in rings are generated compatibility/render caches.\nimport polygonClipping from 'polygon-clipping';\nimport { validateRings } from './validate';\nimport type { Barrier, BoundaryUse, Point, ProjectDocument, Ring, SiteObject, VirtualBoundary } from './types';\nimport { isArea, uid } from './types';\nimport {\n addBarrier,\n centroid,\n closeRing,\n distance,\n footprint,\n openRing,\n pointInRing,\n rectangle,\n removeBarrier,\n ringArea,\n segmentProjection,\n} from './geometry';\nimport {\n CLIP_SCALE,\n GEOMETRY_EPS as EPS,\n MIN_FACE_AREA,\n MIN_RING_EDGE,\n MIN_SPACE_AREA,\n MIN_WALL_LENGTH,\n} from './precision';\n\nexport type BoundaryEdge = Barrier | VirtualBoundary;\nexport interface BoundaryRegion {\n loops: BoundaryUse[][];\n /** Centreline polygon, including holes. Net footprints also subtract the physical wall bodies. */\n rings: Ring[];\n}\nexport const boundaryEdges = (p: ProjectDocument): BoundaryEdge[] => [...p.barriers, ...(p.virtualBoundaries ?? [])];\nexport const followsBoundaries = (o: SiteObject) => o.geometry?.mode === 'boundaries';\nconst ends = (p: ProjectDocument, e: BoundaryEdge): [Point, Point] => {\n const a = p.junctions.find(j => j.id === e.startId),\n b = p.junctions.find(j => j.id === e.endId);\n if (!a || !b) throw new Error('A space boundary references a missing junction.');\n return [a.position, b.position];\n};\nconst reverse = (loop: BoundaryUse[]) => [...loop].reverse().map(u => ({ edgeId: u.edgeId, reversed: !u.reversed }));\nconst signedArea = (ring: Ring) =>\n ring.reduce((s, a, i) => {\n const b = ring[(i + 1) % ring.length];\n return s + a[0] * b[1] - b[0] * a[1];\n }, 0) / 2;\nconst area = (rings: Ring[]) => ringArea(rings[0]) - rings.slice(1).reduce((s, r) => s + ringArea(r), 0);\nconst totalArea = (polys: Ring[][]) => polys.reduce((s, pg) => s + area(pg), 0);\nconst quantize = (rings: Ring[]) =>\n rings.map(r =>\n closeRing(r).map(\n q => [Math.round(q[0] * CLIP_SCALE) / CLIP_SCALE, Math.round(q[1] * CLIP_SCALE) / CLIP_SCALE] as Point,\n ),\n );\n/** Integer input coordinates avoid binary near-duplicates in the clipper's event queue. The\n * output keeps much finer precision so independently computed shared intersections agree. */\nfunction difference(subject: Ring[], ...clips: Ring[][]): Ring[][] {\n const scaled = (rings: Ring[]) =>\n rings.map(r => closeRing(r).map(q => [Math.round(q[0] * CLIP_SCALE), Math.round(q[1] * CLIP_SCALE)] as Point));\n const input = scaled(subject),\n operands = clips.map(scaled);\n let result: Ring[][];\n try {\n result = polygonClipping.difference(input, ...operands) as Ring[][];\n } catch {\n // An equivalent Boolean evaluation avoids a clipper event-queue failure with many nearly\n // coincident wall corners. Do not quantize intermediate results or discard failed polygons.\n result = [input];\n for (const operand of operands) {\n if (!result.length) break;\n result = polygonClipping.difference(result, operand) as Ring[][];\n }\n }\n return result.map(pg =>\n pg.map(r =>\n r.map(\n q => [Math.round((q[0] / CLIP_SCALE) * 1e10) / 1e10, Math.round((q[1] / CLIP_SCALE) * 1e10) / 1e10] as Point,\n ),\n ),\n );\n}\n\n/** Keep every reference correct when a physical/virtual edge is split or replaced. */\nexport function replaceBoundaryUses(p: ProjectDocument, edgeId: string, replacement: BoundaryUse[]) {\n for (const o of p.objects)\n if (o.geometry?.mode === 'boundaries')\n o.geometry.loops = o.geometry.loops.map(loop =>\n loop.flatMap(u => (u.edgeId !== edgeId ? [u] : u.reversed ? reverse(replacement) : replacement)),\n );\n}\n\nexport function boundaryRings(p: ProjectDocument, loops: BoundaryUse[][], floorId: string | null): Ring[] {\n const edges = new Map(boundaryEdges(p).map(e => [e.id, e]));\n return loops.map(loop => {\n if (loop.length < 3) throw new Error('A space boundary must form a closed loop.');\n const ring: Ring = [];\n for (let i = 0; i < loop.length; i++) {\n const u = loop[i],\n next = loop[(i + 1) % loop.length];\n const e = edges.get(u.edgeId),\n n = edges.get(next.edgeId);\n if (!e || !n || e.floorId !== floorId || n.floorId !== floorId)\n throw new Error('A space boundary references a missing edge or another floor.');\n if ((u.reversed ? e.startId : e.endId) !== (next.reversed ? n.endId : n.startId))\n throw new Error('Space boundary edges must share their junctions in order.');\n ring.push(ends(p, e)[u.reversed ? 1 : 0]);\n }\n return closeRing(ring);\n });\n}\n\nfunction splitEdge(p: ProjectDocument, edge: BoundaryEdge, cuts: { t: number; id: string }[]) {\n const [a, b] = ends(p, edge),\n length = distance(a, b);\n const interior = cuts.filter(c => c.t > EPS / length && c.t < 1 - EPS / length).sort((x, y) => x.t - y.t);\n const unique = interior.filter((c, i) => i === 0 || c.id !== interior[i - 1].id);\n if (!unique.length) return;\n const stops = [{ t: 0, id: edge.startId }, ...unique, { t: 1, id: edge.endId }];\n const openings = p.objects.filter(o => o.barrierId === edge.id);\n for (const c of unique)\n for (const o of openings)\n if (Math.abs((o.offset ?? 0) - c.t * length) < o.width / 2 - EPS)\n throw new Error('A junction cannot split an opening.');\n const physical = 'kind' in edge;\n const pieces = stops.slice(1).map((end, i) => {\n const start = stops[i];\n if ((end.t - start.t) * length < (physical ? MIN_WALL_LENGTH : MIN_RING_EDGE) - EPS)\n throw new Error(\n `That crossing would leave a boundary segment too short. \"${'name' in edge ? edge.name : edge.id}\" would be ${((end.t - start.t) * length).toFixed(4)} m long.`,\n );\n return { ...edge, id: i === 0 ? edge.id : uid(), startId: start.id, endId: end.id };\n });\n for (const o of openings) {\n const at = (o.offset ?? 0) / length;\n const i = stops.slice(1).findIndex(s => at <= s.t + EPS);\n o.barrierId = pieces[i].id;\n o.offset = (o.offset ?? 0) - stops[i].t * length;\n }\n replaceBoundaryUses(\n p,\n edge.id,\n pieces.map(e => ({ edgeId: e.id })),\n );\n Object.assign(edge, pieces[0]);\n if (physical) p.barriers.push(...(pieces.slice(1) as Barrier[]));\n else (p.virtualBoundaries ??= []).push(...pieces.slice(1));\n}\n\n/** Planarize a floor: one junction at each intersection, with overlaps represented once.\n * Exact calculated intersections are shared; the input grid is not applied again here. */\nexport function normalizeBoundaries(p: ProjectDocument, floorId: string | null) {\n const edges = boundaryEdges(p).filter(e => e.floorId === floorId);\n const junctions = p.junctions.filter(j => j.floorId === floorId);\n const canonical = new Map<string, string>();\n const representatives: typeof junctions = [];\n for (const j of junctions) {\n const earlier = representatives.find(k => distance(k.position, j.position) <= EPS);\n if (earlier) canonical.set(j.id, earlier.id);\n else representatives.push(j);\n }\n for (const e of edges) {\n e.startId = canonical.get(e.startId) ?? e.startId;\n e.endId = canonical.get(e.endId) ?? e.endId;\n }\n p.junctions = p.junctions.filter(j => !canonical.has(j.id));\n const positions = new Map(p.junctions.map(j => [j.id, j.position]));\n // Resolve endpoints once, before the broad-phase pair scan. Looking each one up in the\n // full junction list inside the nested loop made planarization cubic in floor size.\n const spans = edges.map(e => ({\n points: [positions.get(e.startId)!, positions.get(e.endId)!] as [Point, Point],\n box: bounds([positions.get(e.startId)!, positions.get(e.endId)!]),\n }));\n const cuts = new Map<string, { t: number; id: string }[]>();\n const cut = (e: BoundaryEdge, at: Point) => {\n const [a, b] = ends(p, e),\n hit = segmentProjection(at, a, b);\n let j = p.junctions.find(j => j.floorId === floorId && distance(j.position, at) <= EPS);\n if (!j) {\n j = { id: uid(), floorId, position: at };\n p.junctions.push(j);\n }\n const list = cuts.get(e.id) ?? [];\n list.push({ t: hit.t, id: j.id });\n cuts.set(e.id, list);\n };\n for (let i = 0; i < edges.length; i++)\n for (let k = i + 1; k < edges.length; k++) {\n if (!boundsMeet(spans[i].box, spans[k].box)) continue;\n const one = edges[i],\n two = edges[k],\n [a, b] = spans[i].points,\n [c, d] = spans[k].points;\n const ux = b[0] - a[0],\n uy = b[1] - a[1],\n vx = d[0] - c[0],\n vy = d[1] - c[1];\n const det = ux * vy - uy * vx;\n const sharedEnd =\n one.startId === two.startId ||\n one.startId === two.endId ||\n one.endId === two.startId ||\n one.endId === two.endId;\n // Lines with a shared endpoint already have their intersection. Solving again for almost\n // parallel edges amplifies cancellation and can invent a second, microscopic cut beside it.\n // Still check endpoint projections below: collinear edges can overlap beyond the junction.\n if (!sharedEnd && Math.abs(det) > 1e-12 * Math.hypot(ux, uy) * Math.hypot(vx, vy)) {\n const t = ((c[0] - a[0]) * vy - (c[1] - a[1]) * vx) / det;\n const s = ((c[0] - a[0]) * uy - (c[1] - a[1]) * ux) / det;\n if (\n t >= -EPS / Math.hypot(ux, uy) &&\n t <= 1 + EPS / Math.hypot(ux, uy) &&\n s >= -EPS / Math.hypot(vx, vy) &&\n s <= 1 + EPS / Math.hypot(vx, vy)\n ) {\n const at: Point = [a[0] + Math.max(0, Math.min(1, t)) * ux, a[1] + Math.max(0, Math.min(1, t)) * uy];\n cut(one, at);\n cut(two, at);\n }\n } else {\n for (const at of [a, b])\n if (segmentProjection(at, c, d).distance <= EPS) {\n cut(one, at);\n cut(two, at);\n }\n for (const at of [c, d])\n if (segmentProjection(at, a, b).distance <= EPS) {\n cut(one, at);\n cut(two, at);\n }\n }\n }\n for (const e of edges) splitEdge(p, e, cuts.get(e.id) ?? []);\n // Physical edges precede virtual ones, so drawing a wall on a virtual division builds that wall.\n const byEnds = new Map<string, BoundaryEdge>();\n const removed = new Set<string>();\n for (const e of boundaryEdges(p).filter(e => e.floorId === floorId)) {\n const key = [e.startId, e.endId].sort().join('|'),\n kept = byEnds.get(key);\n if (!kept) {\n byEnds.set(key, e);\n continue;\n }\n const flipped = e.startId !== kept.startId;\n replaceBoundaryUses(p, e.id, [{ edgeId: kept.id, reversed: flipped }]);\n for (const o of p.objects.filter(o => o.barrierId === e.id)) {\n o.barrierId = kept.id;\n if (flipped) {\n o.offset = distance(...ends(p, kept)) - (o.offset ?? 0);\n if (o.kind === 'door') {\n o.doorHinge = o.doorHinge === 'right' ? 'left' : 'right';\n o.doorSwing = o.doorSwing === -1 ? 1 : -1;\n }\n }\n }\n removed.add(e.id);\n }\n p.barriers = p.barriers.filter(e => !removed.has(e.id));\n if (p.virtualBoundaries) p.virtualBoundaries = p.virtualBoundaries.filter(e => !removed.has(e.id));\n const used = new Set(boundaryEdges(p).flatMap(e => [e.startId, e.endId]));\n p.junctions = p.junctions.filter(j => !canonical.has(j.id) || used.has(j.id));\n}\n\n/** Bounded faces of the shared graph. Bridges do not divide a face; nested components form holes. */\nexport function boundaryRegions(p: ProjectDocument, floorId: string | null): BoundaryRegion[] {\n const edges = boundaryEdges(p).filter(e => e.floorId === floorId);\n const outgoing = new Map<string, { edge: BoundaryEdge; to: string }[]>();\n for (const e of edges)\n for (const [a, b] of [\n [e.startId, e.endId],\n [e.endId, e.startId],\n ]) {\n const list = outgoing.get(a) ?? [];\n list.push({ edge: e, to: b });\n outgoing.set(a, list);\n }\n const entered = new Map<string, number>(),\n low = new Map<string, number>(),\n bridges = new Set<string>();\n let time = 0;\n const visit = (at: string, parent?: string) => {\n entered.set(at, ++time);\n low.set(at, time);\n for (const { edge, to } of outgoing.get(at) ?? []) {\n if (edge.id === parent) continue;\n if (!entered.has(to)) {\n visit(to, edge.id);\n low.set(at, Math.min(low.get(at)!, low.get(to)!));\n if (low.get(to)! > entered.get(at)!) bridges.add(edge.id);\n } else low.set(at, Math.min(low.get(at)!, entered.get(to)!));\n }\n };\n for (const at of outgoing.keys()) if (!entered.has(at)) visit(at);\n const positions = new Map(p.junctions.map(j => [j.id, j.position]));\n for (const [at, list] of outgoing) {\n const a = positions.get(at)!;\n outgoing.set(\n at,\n list\n .filter(x => !bridges.has(x.edge.id))\n .sort((x, y) => {\n const b = positions.get(x.to)!,\n c = positions.get(y.to)!;\n return Math.atan2(b[1] - a[1], b[0] - a[0]) - Math.atan2(c[1] - a[1], c[0] - a[0]);\n }),\n );\n }\n const seen = new Set<string>(),\n cycles: { loop: BoundaryUse[]; ring: Ring }[] = [];\n for (const e of edges.filter(e => !bridges.has(e.id)))\n for (const reversed of [false, true]) {\n let use: BoundaryUse = { edgeId: e.id, reversed },\n current = e;\n const first = `${e.id}:${reversed}`,\n loop: BoundaryUse[] = [],\n ring: Ring = [];\n if (seen.has(first)) continue;\n for (let step = 0; step <= edges.length * 2; step++) {\n const key = `${use.edgeId}:${!!use.reversed}`;\n if (seen.has(key)) break;\n seen.add(key);\n loop.push(use);\n const from = use.reversed ? current.endId : current.startId,\n to = use.reversed ? current.startId : current.endId;\n ring.push(positions.get(from)!);\n const list = outgoing.get(to)!;\n const back = list.findIndex(x => x.edge.id === current.id);\n const next = list[(back + list.length - 1) % list.length];\n current = next.edge;\n use = { edgeId: current.id, reversed: current.endId === to };\n if (`${use.edgeId}:${!!use.reversed}` === first) break;\n }\n if (signedArea(ring) >= MIN_FACE_AREA) cycles.push({ loop, ring: closeRing(ring) });\n }\n const parent = cycles.map(\n (child, i) =>\n cycles\n .map((candidate, j) => ({ j, candidate }))\n .filter(\n ({ candidate, j }) =>\n j !== i &&\n ringArea(candidate.ring) > ringArea(child.ring) + EPS &&\n child.ring.every(q => pointInRing(q, candidate.ring)) &&\n !child.loop.some(u => candidate.loop.some(v => v.edgeId === u.edgeId)),\n )\n .sort((a, b) => ringArea(a.candidate.ring) - ringArea(b.candidate.ring))[0]?.j,\n );\n return cycles.map((c, i) => {\n const holes = cycles.filter((_, j) => parent[j] === i);\n return {\n loops: [c.loop, ...holes.map(h => reverse(h.loop))],\n rings: [c.ring, ...holes.map(h => closeRing([...openRing(h.ring)].reverse()))],\n };\n });\n}\n\nfunction wallBodies(p: ProjectDocument, floorId: string | null): Ring[][] {\n // Subdividing a straight wall must not change its swept body through separate rounding at\n // each new endpoint. Reconstitute collinear runs for clipping, retaining the individual IDs.\n const positions = new Map(p.junctions.map(j => [j.id, j.position]));\n const runs = p.barriers\n .filter(b => b.floorId === floorId)\n .map(b => ({\n points: [positions.get(b.startId)!, positions.get(b.endId)!] as [Point, Point],\n thickness: b.thickness,\n }));\n for (let i = 0; i < runs.length; i++) {\n let merged = true;\n while (merged) {\n merged = false;\n for (let k = i + 1; k < runs.length; k++) {\n const one = runs[i],\n two = runs[k];\n if (one.thickness !== two.thickness) continue;\n const [a, b] = one.points,\n [c, d] = two.points;\n const ab = distance(a, b),\n cd = distance(c, d);\n if (Math.abs((b[0] - a[0]) * (d[1] - c[1]) - (b[1] - a[1]) * (d[0] - c[0])) > EPS * ab * cd) continue;\n const pair = [\n [a, c, b, d],\n [a, d, b, c],\n [b, c, a, d],\n [b, d, a, c],\n ].find(([x, y]) => distance(x, y) <= EPS);\n if (!pair) continue;\n one.points = [pair[2], pair[3]];\n runs.splice(k, 1);\n merged = true;\n break;\n }\n }\n }\n return runs.map(({ points: [a, c], thickness }) => {\n // Canonical orientation makes reversed strokes use exactly the same clipping coordinates.\n if (a[0] > c[0] || (a[0] === c[0] && a[1] > c[1])) [a, c] = [c, a];\n const len = distance(a, c);\n return quantize([\n rectangle(\n [(a[0] + c[0]) / 2, (a[1] + c[1]) / 2],\n len + thickness,\n thickness,\n (Math.atan2(c[1] - a[1], c[0] - a[0]) * 180) / Math.PI,\n ),\n ]);\n });\n}\nfunction tidy(ring: Ring): Ring {\n const out: Ring = [];\n for (const q of openRing(ring)) if (!out.length || distance(out.at(-1)!, q) >= EPS) out.push(q);\n if (out.length > 1 && distance(out[0], out.at(-1)!) < EPS) out.pop();\n return closeRing(out);\n}\ntype Bounds = { minX: number; minY: number; maxX: number; maxY: number };\nconst bounds = (ring: Ring): Bounds => {\n let minX = Infinity,\n minY = Infinity,\n maxX = -Infinity,\n maxY = -Infinity;\n for (const [x, y] of ring) {\n minX = Math.min(minX, x);\n minY = Math.min(minY, y);\n maxX = Math.max(maxX, x);\n maxY = Math.max(maxY, y);\n }\n return { minX, minY, maxX, maxY };\n};\nconst boundsMeet = (a: Bounds, b: Bounds) =>\n a.minX <= b.maxX + EPS && a.maxX + EPS >= b.minX && a.minY <= b.maxY + EPS && a.maxY + EPS >= b.minY;\n\n/** Short-lived evaluation scope: coordinates are fixed during one refresh/validation pass. Reuse\n * wall bodies across its spaces, without caching a mutable transaction draft between edits. */\nfunction netEvaluator(p: ProjectDocument) {\n const floors = new Map<string | null, { rings: Ring[]; box: Bounds }[]>();\n return (floorId: string | null, rings: Ring[], allowEmpty = false): Ring[] => {\n let solids = floors.get(floorId);\n if (!solids) {\n solids = wallBodies(p, floorId).map(rings => ({ rings, box: bounds(rings[0]) }));\n floors.set(floorId, solids);\n }\n const box = bounds(rings[0]);\n const nearby = solids.filter(s => boundsMeet(s.box, box)).map(s => s.rings);\n return netRings(rings, nearby, allowEmpty);\n };\n}\nfunction netRings(rings: Ring[], solids: Ring[][], allowEmpty = false): Ring[] {\n const pieces = solids.length ? difference(rings, ...solids) : [quantize(rings)];\n const usable = pieces.filter(pg => area(pg) >= MIN_SPACE_AREA);\n if (!usable.length && allowEmpty) return [];\n if (!usable.length)\n throw new Error(\n `A space needs at least ${MIN_SPACE_AREA} m² of usable area. Largest region after subtracting walls: ${Math.max(0, ...pieces.map(pg => area(pg))).toFixed(3)} m².`,\n );\n if (usable.length !== 1)\n throw new Error(\n `The wall would leave ${usable.length} disconnected usable regions (${usable.map(pg => area(pg).toFixed(2)).join(', ')} m²). Extend it to a boundary to divide the space.`,\n );\n return usable[0].map(tidy);\n}\nexport function derivedSpaceRings(p: ProjectDocument, o: SiteObject): Ring[] {\n if (o.geometry?.mode !== 'boundaries') return footprint(o);\n return netEvaluator(p)(o.floorId, boundaryRings(p, o.geometry.loops, o.floorId));\n}\nfunction cacheRings(o: SiteObject, rings: Ring[]) {\n o.rings = rings;\n o.position = centroid(rings[0]);\n o.rotation = 0;\n const xs = rings[0].map(q => q[0]),\n ys = rings[0].map(q => q[1]);\n o.width = Math.max(...xs) - Math.min(...xs);\n o.depth = Math.max(...ys) - Math.min(...ys);\n}\n\nexport function bindSpaceToRegion(p: ProjectDocument, o: SiteObject, region: BoundaryRegion) {\n if (!isArea(o.kind)) throw new Error('Only a drawn area can use shared boundaries.');\n o.geometry = { mode: 'boundaries', loops: structuredClone(region.loops) };\n cacheRings(o, derivedSpaceRings(p, o));\n}\nexport function boundaryRegionAt(p: ProjectDocument, floorId: string | null, point: Point): BoundaryRegion | null {\n return (\n boundaryRegions(p, floorId)\n .filter(r => pointInRing(point, r.rings[0]) && !r.rings.slice(1).some(h => pointInRing(point, h)))\n .sort((a, b) => area(a.rings) - area(b.rings))[0] ?? null\n );\n}\n\nfunction junctionAt(p: ProjectDocument, floorId: string | null, point: Point): string {\n const existing = p.junctions.find(j => j.floorId === floorId && distance(j.position, point) <= EPS);\n if (existing) return existing.id;\n const id = uid();\n p.junctions.push({ id, floorId, position: point });\n return id;\n}\nexport function addVirtualBoundary(p: ProjectDocument, floorId: string | null, a: Point, b: Point): string {\n if (distance(a, b) < MIN_RING_EDGE) throw new Error('A space boundary is too short.');\n const startId = junctionAt(p, floorId, a),\n endId = junctionAt(p, floorId, b);\n const existing = boundaryEdges(p).find(\n e =>\n e.floorId === floorId &&\n ((e.startId === startId && e.endId === endId) || (e.startId === endId && e.endId === startId)),\n );\n if (existing) return existing.id;\n const id = uid();\n (p.virtualBoundaries ??= []).push({ id, floorId, startId, endId });\n return id;\n}\n\n/** The drawing tool intentionally opens any collinear wall span. Ordinary addVirtualBoundary\n * remains non-destructive for generated space outlines and model normalization. */\nexport function drawVirtualBoundary(p: ProjectDocument, floorId: string | null, a: Point, b: Point): string {\n const length = distance(a, b);\n if (length < MIN_RING_EDGE) throw new Error('A space boundary is too short.');\n const onLine = (q: Point) => Math.abs((b[0] - a[0]) * (q[1] - a[1]) - (b[1] - a[1]) * (q[0] - a[0])) / length <= EPS;\n for (const wall of [...p.barriers].filter(w => w.floorId === floorId)) {\n const [start, end] = ends(p, wall);\n if (!onLine(start) || !onLine(end)) continue;\n const cuts = [a, b].flatMap(q => {\n const hit = segmentProjection(q, start, end);\n return hit.distance <= EPS && hit.t > 0 && hit.t < 1 ? [{ t: hit.t, id: junctionAt(p, floorId, hit.point) }] : [];\n });\n splitEdge(p, wall, cuts);\n }\n for (const wall of [...p.barriers].filter(w => w.floorId === floorId)) {\n if (!ends(p, wall).every(q => segmentProjection(q, a, b).distance <= EPS)) continue;\n // Retain the edge identity and room loops, even when no room has claimed it yet.\n (p.virtualBoundaries ??= []).push({ id: wall.id, floorId, startId: wall.startId, endId: wall.endId });\n removeBarrier(p, wall.id);\n }\n return addVirtualBoundary(p, floorId, a, b);\n}\n\n/** Explicit conversion: retain the drawn shape with shared virtual edges, or adopt its enclosing\n * walls on request. Legacy documents remain independent until this operation is chosen. */\nexport function connectSpace(p: ProjectDocument, objectId: string, source: 'outline' | 'walls' = 'outline') {\n const o = p.objects.find(o => o.id === objectId);\n if (!o || !isArea(o.kind)) throw new Error('Select an area to connect to boundaries.');\n if (source === 'walls') {\n normalizeBoundaries(p, o.floorId);\n // A concave outline's label position can lie outside it. An explicit adoption uses the\n // region containing the greatest part of its footprint, and refuses ambiguous choices.\n const sourceRings = footprint(o),\n sourceArea = area(sourceRings);\n const candidates = boundaryRegions(p, o.floorId)\n .map(region => ({\n region,\n overlap: sourceArea - totalArea(difference(sourceRings, region.rings)),\n }))\n .filter(c => c.overlap > EPS)\n .sort((a, b) => b.overlap - a.overlap);\n if (candidates.length > 1 && Math.abs(candidates[0].overlap - candidates[1].overlap) < EPS)\n throw new Error('More than one region matches this outline. Use Space from walls inside the intended region.');\n const region = candidates[0]?.region;\n if (!region) throw new Error('No closed wall boundary surrounds this space.');\n bindSpaceToRegion(p, o, region);\n return;\n }\n const loops = footprint(o).map((ring, ri) => {\n let points = openRing(ring);\n if (signedArea(points) > 0 !== (ri === 0)) points = [...points].reverse();\n return points.map((a, i) => {\n const b = points[(i + 1) % points.length],\n edgeId = addVirtualBoundary(p, o.floorId, a, b);\n const e = boundaryEdges(p).find(e => e.id === edgeId)!;\n return { edgeId, reversed: distance(ends(p, e)[0], a) > EPS };\n });\n });\n o.geometry = { mode: 'boundaries', loops };\n normalizeBoundaries(p, o.floorId);\n cacheRings(o, derivedSpaceRings(p, o));\n}\nexport function disconnectSpace(p: ProjectDocument, objectId: string) {\n const o = p.objects.find(o => o.id === objectId);\n if (o) o.geometry = { mode: 'independent' };\n}\n\nexport function addBoundaryHole(p: ProjectDocument, o: SiteObject, ring: Ring) {\n if (o.geometry?.mode !== 'boundaries') throw new Error('Select a space with shared boundaries.');\n let points = openRing(ring);\n if (signedArea(points) > 0) points = [...points].reverse();\n const loop = points.map((a, i) => {\n const edgeId = addVirtualBoundary(p, o.floorId, a, points[(i + 1) % points.length]);\n const e = boundaryEdges(p).find(e => e.id === edgeId)!;\n return { edgeId, reversed: distance(ends(p, e)[0], a) > EPS };\n });\n o.geometry.loops.push(loop);\n}\n\nexport function splitBoundarySpace(p: ProjectDocument, o: SiteObject, a: Point, b: Point, wall = true): string {\n if (o.geometry?.mode !== 'boundaries') throw new Error('This space has no shared boundaries.');\n const length = distance(a, b);\n if (length < EPS) throw new Error('Draw a line across the space.');\n const dx = (b[0] - a[0]) / length,\n dy = (b[1] - a[1]) / length;\n const hits: number[] = [];\n for (const closed of boundaryRings(p, o.geometry.loops, o.floorId)) {\n const r = openRing(closed);\n for (let i = 0; i < r.length; i++) {\n const c = r[i],\n d = r[(i + 1) % r.length],\n ex = d[0] - c[0],\n ey = d[1] - c[1];\n const det = dx * ey - dy * ex;\n if (Math.abs(det) < 1e-12) continue;\n const t = ((c[0] - a[0]) * dy - (c[1] - a[1]) * dx) / det;\n if (t >= -EPS && t <= 1 + EPS) hits.push(((c[0] - a[0]) * ey - (c[1] - a[1]) * ex) / det);\n }\n }\n hits.sort((x, y) => x - y);\n const unique = hits.filter((x, i) => i === 0 || x - hits[i - 1] > EPS);\n for (let i = 0; i + 1 < unique.length; i += 2) {\n const start: Point = [a[0] + unique[i] * dx, a[1] + unique[i] * dy];\n const end: Point = [a[0] + unique[i + 1] * dx, a[1] + unique[i + 1] * dy];\n if (wall) {\n const edge = addBarrier(p, start, end, o.floorId, 'wall');\n if (edge) {\n edge.name = 'Partition';\n edge.thickness = 0.18;\n }\n } else addVirtualBoundary(p, o.floorId, start, end);\n }\n normalizeBoundaries(p, o.floorId);\n const created = refreshBoundarySpaces(p);\n if (!created.length) throw new Error('The cut must divide the space into usable regions.');\n return created[0];\n}\n\n/** Join centreline loops before deriving the net footprint, including the former wall's floor area. */\nexport function mergeBoundaryGeometry(p: ProjectDocument, keep: SiteObject, absorbed: SiteObject): boolean {\n if (keep.geometry?.mode !== 'boundaries' || absorbed.geometry?.mode !== 'boundaries')\n throw new Error('Connect both spaces to shared boundaries before merging their boundaries.');\n const a = keep.geometry.loops.flat(),\n b = absorbed.geometry.loops.flat();\n const shared = new Set(\n a.filter(u => b.some(v => v.edgeId === u.edgeId && !!v.reversed !== !!u.reversed)).map(u => u.edgeId),\n );\n if (!shared.size) return false;\n if (p.barriers.some(e => shared.has(e.id))) throw new Error('Remove the dividing wall before merging these spaces.');\n const edges = new Map(boundaryEdges(p).map(e => [e.id, e]));\n const remaining = [...a, ...b].filter(u => !shared.has(u.edgeId));\n const loops: BoundaryUse[][] = [];\n while (remaining.length) {\n const loop = [remaining.shift()!];\n const first = edges.get(loop[0].edgeId)!;\n const start = loop[0].reversed ? first.endId : first.startId;\n let at = loop[0].reversed ? first.startId : first.endId;\n while (at !== start) {\n const index = remaining.findIndex(u => {\n const e = edges.get(u.edgeId)!;\n return (u.reversed ? e.endId : e.startId) === at;\n });\n if (index < 0) throw new Error('The merged space boundary is not closed.');\n const u = remaining.splice(index, 1)[0],\n e = edges.get(u.edgeId)!;\n loop.push(u);\n at = u.reversed ? e.startId : e.endId;\n }\n loops.push(loop);\n }\n loops.sort(\n (a, b) =>\n signedArea(openRing(boundaryRings(p, [b], keep.floorId)[0])) -\n signedArea(openRing(boundaryRings(p, [a], keep.floorId)[0])),\n );\n keep.geometry = { mode: 'boundaries', loops };\n p.virtualBoundaries = (p.virtualBoundaries ?? []).filter(\n e =>\n !shared.has(e.id) ||\n p.objects.some(\n o =>\n o.id !== absorbed.id &&\n o.geometry?.mode === 'boundaries' &&\n o.geometry.loops.some(l => l.some(u => u.edgeId === e.id)),\n ),\n );\n cacheRings(keep, derivedSpaceRings(p, keep));\n return true;\n}\n\n/** A removed wall leaves the semantic division as a virtual boundary. */\nexport function preserveBoundary(p: ProjectDocument, edge: BoundaryEdge) {\n if (\n !p.objects.some(\n o => o.geometry?.mode === 'boundaries' && o.geometry.loops.some(l => l.some(u => u.edgeId === edge.id)),\n )\n )\n return;\n if (!(p.virtualBoundaries ?? []).some(e => e.id === edge.id))\n (p.virtualBoundaries ??= []).push({ id: edge.id, floorId: edge.floorId, startId: edge.startId, endId: edge.endId });\n}\n\n/** Regenerate by explicit loops, subdividing identities only when a new boundary divides a space.\n * No old/new footprint-overlap matching is involved in ordinary wall movements. */\nexport function refreshBoundarySpaces(p: ProjectDocument, topologyChanged?: Set<string | null>): string[] {\n const created: string[] = [],\n regions = new Map<string | null, BoundaryRegion[]>();\n const net = netEvaluator(p);\n for (const o of [...p.objects]) {\n if (o.geometry?.mode !== 'boundaries') continue;\n const envelope = boundaryRings(p, o.geometry.loops, o.floorId);\n // Moving a vertex changes the shape of a face, not its identity. Only a changed edge graph\n // needs face discovery and polygon containment against every other region on the floor.\n if (topologyChanged && !topologyChanged.has(o.floorId)) {\n cacheRings(o, net(o.floorId, envelope));\n continue;\n }\n if (!regions.has(o.floorId)) regions.set(o.floorId, boundaryRegions(p, o.floorId));\n const inside = regions.get(o.floorId)!.filter(r => totalArea(difference(r.rings, envelope)) < EPS);\n if (!inside.length) throw new Error('The space boundaries no longer form a closed region.');\n inside.sort((a, b) => area(b.rings) - area(a.rings));\n // Interior walls can eliminate a tiny region, but a labelled space must never disappear silently.\n const usable = inside\n .map(r => ({ region: r, rings: net(o.floorId, r.rings, inside.length > 1) }))\n .filter(r => r.rings.length)\n .sort((a, b) => area(b.rings) - area(a.rings));\n if (!usable.length) throw new Error('The walls would remove all usable area from this space.');\n for (let i = 0; i < usable.length; i++) {\n const target = i === 0 ? o : { ...structuredClone(o), id: uid(), name: `${o.name} ${i + 1}`, feedId: undefined };\n target.geometry = { mode: 'boundaries', loops: structuredClone(usable[i].region.loops) };\n cacheRings(target, usable[i].rings);\n if (i > 0) {\n p.objects.push(target);\n created.push(target.id);\n for (const z of p.zones ?? []) if (z.spaceIds.includes(o.id)) z.spaceIds.push(target.id);\n }\n }\n }\n return created;\n}\n\n/** One synchronization gate for UI gestures, host mutations, history and persistence. */\nexport function synchronizeGeometry(before: ProjectDocument, p: ProjectDocument) {\n // A host may install the frozen result of another transaction in its draft (e.g. a constrained\n // drag). Thaw those collections before the normalization pass, without touching its input.\n for (const key of ['barriers', 'junctions', 'objects', 'virtualBoundaries', 'zones'] as const) {\n const collection = p[key];\n if (collection && (Object.isFrozen(collection) || collection.some(e => Object.isFrozen(e))))\n Object.assign(p, { [key]: structuredClone(collection) });\n }\n const edges = boundaryEdges(p),\n ids = new Set(edges.map(e => e.id));\n for (const e of boundaryEdges(before))\n if (!ids.has(e.id)) {\n preserveBoundary(p, e);\n if ((p.virtualBoundaries ?? []).some(v => v.id === e.id))\n for (const id of [e.startId, e.endId]) {\n if (!p.junctions.some(j => j.id === id)) {\n const old = before.junctions.find(j => j.id === id);\n if (old) p.junctions.push(structuredClone(old));\n }\n }\n }\n const signature = (doc: ProjectDocument, floor: string | null) =>\n JSON.stringify([\n boundaryEdges(doc)\n .filter(e => e.floorId === floor)\n .map(e => [e.id, e.startId, e.endId]),\n doc.junctions.filter(j => j.floorId === floor).map(j => [j.id, j.position]),\n ]);\n const floors = new Set(boundaryEdges(p).map(e => e.floorId));\n for (const floor of floors) if (signature(before, floor) !== signature(p, floor)) normalizeBoundaries(p, floor);\n const topology = (doc: ProjectDocument, floor: string | null) =>\n JSON.stringify(\n boundaryEdges(doc)\n .filter(e => e.floorId === floor)\n .map(e => [e.id, e.startId, e.endId]),\n );\n const changed = new Set([...floors].filter(floor => topology(before, floor) !== topology(p, floor)));\n refreshBoundarySpaces(p, changed);\n}\n\n/** Validate the explicit topology and its cache. Independent polygons retain their existing rules. */\nexport function validateSpaceBoundaries(p: ProjectDocument) {\n const occupied = new Set<string>();\n const faces = new Map<string | null, Set<string>>();\n const net = netEvaluator(p);\n const loopKey = (loops: BoundaryUse[][]) =>\n loops\n .flat()\n .map(u => `${u.edgeId}:${!!u.reversed}`)\n .sort()\n .join('|');\n for (const o of p.objects) {\n if (o.geometry === undefined) continue;\n if (!o.geometry || !['independent', 'boundaries'].includes(o.geometry.mode))\n throw new Error('Unknown space geometry mode.');\n if (o.geometry.mode === 'independent') continue;\n if (\n !isArea(o.kind) ||\n !Array.isArray(o.geometry.loops) ||\n !o.geometry.loops.length ||\n o.geometry.loops.some(\n l =>\n !Array.isArray(l) ||\n l.some(\n u => !u || typeof u.edgeId !== 'string' || (u.reversed !== undefined && typeof u.reversed !== 'boolean'),\n ),\n )\n )\n throw new Error('Malformed space boundary loops.');\n const rings = boundaryRings(p, o.geometry.loops, o.floorId);\n const issue = validateRings(rings, EPS, MIN_FACE_AREA);\n if (issue) throw new Error(issue);\n if (signedArea(openRing(rings[0])) <= 0 || rings.slice(1).some(r => signedArea(openRing(r)) >= 0))\n throw new Error('Space boundary loops must keep the space on their left.');\n if (!faces.has(o.floorId)) faces.set(o.floorId, new Set(boundaryRegions(p, o.floorId).map(r => loopKey(r.loops))));\n if (!faces.get(o.floorId)!.has(loopKey(o.geometry.loops)))\n throw new Error('A connected space must follow one face of the boundary network.');\n for (const loop of o.geometry.loops)\n for (const u of loop) {\n const key = `${u.edgeId}:${!!u.reversed}`;\n if (occupied.has(key)) throw new Error('Two spaces cannot occupy the same side of a shared boundary.');\n occupied.add(key);\n }\n const calculated = net(o.floorId, rings);\n if (\n !o.rings ||\n calculated.length !== o.rings.length ||\n calculated.some(\n (r, ri) => r.length !== o.rings![ri].length || r.some((q, i) => distance(q, o.rings![ri][i]) > EPS),\n )\n )\n throw new Error('The space footprint is stale. Update it through a geometry transaction.');\n const xs = calculated[0].map(q => q[0]),\n ys = calculated[0].map(q => q[1]);\n if (\n distance(o.position, centroid(calculated[0])) > EPS ||\n Math.abs(o.width - (Math.max(...xs) - Math.min(...xs))) > EPS ||\n Math.abs(o.depth - (Math.max(...ys) - Math.min(...ys))) > EPS ||\n o.rotation !== 0\n )\n throw new Error('A boundary-driven space has stale calculated dimensions.');\n }\n}\n"],"names":["boundaryEdges","p","followsBoundaries","o","_a","ends","e","a","j","b","reverse","loop","u","signedArea","ring","s","area","rings","ringArea","totalArea","polys","pg","quantize","r","closeRing","q","CLIP_SCALE","difference","subject","clips","scaled","input","operands","result","polygonClipping","operand","replaceBoundaryUses","edgeId","replacement","boundaryRings","loops","floorId","edges","i","next","n","splitEdge","edge","cuts","length","distance","interior","c","EPS","x","y","unique","stops","openings","physical","pieces","end","start","MIN_WALL_LENGTH","MIN_RING_EDGE","uid","at","normalizeBoundaries","junctions","canonical","representatives","earlier","k","positions","spans","bounds","cut","hit","segmentProjection","list","boundsMeet","one","two","d","ux","uy","vx","vy","det","t","byEnds","removed","key","kept","flipped","used","boundaryRegions","outgoing","entered","low","bridges","time","visit","parent","to","seen","cycles","reversed","use","current","first","step","from","back","MIN_FACE_AREA","child","candidate","pointInRing","v","holes","_","h","openRing","wallBodies","runs","merged","ab","cd","pair","thickness","len","rectangle","tidy","out","minX","minY","maxX","maxY","netEvaluator","floors","allowEmpty","solids","box","nearby","netRings","usable","MIN_SPACE_AREA","derivedSpaceRings","footprint","cacheRings","centroid","xs","ys","bindSpaceToRegion","region","isArea","boundaryRegionAt","point","junctionAt","existing","id","addVirtualBoundary","startId","endId","drawVirtualBoundary","onLine","wall","w","removeBarrier","connectSpace","objectId","source","sourceRings","sourceArea","candidates","ri","points","disconnectSpace","addBoundaryHole","splitBoundarySpace","dx","dy","hits","closed","ex","ey","addBarrier","created","refreshBoundarySpaces","mergeBoundaryGeometry","keep","absorbed","_b","shared","remaining","index","l","preserveBoundary","topologyChanged","regions","net","envelope","inside","target","z","synchronizeGeometry","before","collection","ids","old","signature","doc","floor","topology","changed","validateSpaceBoundaries","occupied","faces","loopKey","issue","validateRings","calculated"],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { objectPosition as g, barrierEnds as k, pointInRing as A, distance as E, segmentsCross as M } from "./geometry.js";
|
|
2
|
+
import { isSpace as v, isOpening as F } from "./types.js";
|
|
3
|
+
const O = 12, R = 70;
|
|
4
|
+
function P(i, o) {
|
|
5
|
+
var f;
|
|
6
|
+
const a = o.coverageRange ?? O, l = (o.coverageAngle ?? R) / 2, t = g(i, o), u = o.rotation ?? 0, r = i.barriers.filter((n) => n.floorId === o.floorId), h = new Map(r.map((n) => [n.id, k(i, n)])), e = [];
|
|
7
|
+
for (const n of i.objects) {
|
|
8
|
+
if (n.id === o.id || n.floorId !== o.floorId || !v(n.kind) && !F(n.kind)) continue;
|
|
9
|
+
if ((f = n.rings) != null && f.length && A(t, n.rings[0])) {
|
|
10
|
+
e.push(n.id);
|
|
11
|
+
continue;
|
|
12
|
+
}
|
|
13
|
+
const s = g(i, n), c = E(t, s);
|
|
14
|
+
if (c < 0.01 || c > a) continue;
|
|
15
|
+
const b = Math.atan2(s[1] - t[1], s[0] - t[0]) * 180 / Math.PI;
|
|
16
|
+
if (Math.abs(((b - u) % 360 + 540) % 360 - 180) > l) continue;
|
|
17
|
+
r.some((d) => {
|
|
18
|
+
if (d.id === n.barrierId) return !1;
|
|
19
|
+
const [p, I] = h.get(d.id);
|
|
20
|
+
return M(t, s, p, I);
|
|
21
|
+
}) || e.push(n.id);
|
|
22
|
+
}
|
|
23
|
+
return e;
|
|
24
|
+
}
|
|
25
|
+
export {
|
|
26
|
+
R as DEFAULT_FOV,
|
|
27
|
+
O as DEFAULT_RANGE,
|
|
28
|
+
P as coverageOf
|
|
29
|
+
};
|
|
30
|
+
//# sourceMappingURL=coverage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coverage.js","sources":["../../../../src/model/coverage.ts"],"sourcesContent":["// What a camera can see, read off the plan.\n//\n// A camera carries a facing, a reach and a field of view, and the plan already says where the walls\n// and the rooms are — so which spaces and doorways fall inside that cone is a computation, not\n// something anyone should be typing in by hand. `watchedIds` is the field it lands in; the library\n// never interprets it, and what a host does with \"this camera covers that door\" is its own business.\n//\n// Deliberately coarse in one respect: a space counts by its centre, so a long corridor whose middle\n// is out of reach does not count even though its near end is visible. Naming the rule is better than\n// a partial-visibility fraction nobody can check by eye.\nimport { barrierEnds, distance, objectPosition, pointInRing, segmentsCross } from './geometry';\nimport { isOpening, isSpace, type ProjectDocument, type SiteObject } from './types';\n\nexport const DEFAULT_RANGE = 12;\nexport const DEFAULT_FOV = 70;\n\n/** Ids of the spaces and openings inside `camera`'s cone with an unobstructed line to it.\n *\n * Sightlines stop at walls, with one exception: the opening being tested is itself a hole in the\n * wall it sits on, so that wall cannot be what hides it. */\nexport function coverageOf(project: ProjectDocument, camera: SiteObject): string[] {\n const range = camera.coverageRange ?? DEFAULT_RANGE;\n const half = (camera.coverageAngle ?? DEFAULT_FOV) / 2;\n const from = objectPosition(project, camera);\n const facing = camera.rotation ?? 0;\n const walls = project.barriers.filter(b => b.floorId === camera.floorId);\n const ends = new Map(walls.map(b => [b.id, barrierEnds(project, b)]));\n const seen: string[] = [];\n for (const o of project.objects) {\n if (o.id === camera.id || o.floorId !== camera.floorId) continue;\n if (!isSpace(o.kind) && !isOpening(o.kind)) continue;\n // The room the camera stands in is covered whatever the cone says — the camera is inside it,\n // and its centre may well be behind the lens.\n if (o.rings?.length && pointInRing(from, o.rings[0])) {\n seen.push(o.id);\n continue;\n }\n const to = objectPosition(project, o);\n const reach = distance(from, to);\n if (reach < 0.01 || reach > range) continue;\n const bearing = (Math.atan2(to[1] - from[1], to[0] - from[0]) * 180) / Math.PI;\n const off = Math.abs(((((bearing - facing) % 360) + 540) % 360) - 180);\n if (off > half) continue;\n const blocked = walls.some(b => {\n if (b.id === o.barrierId) return false; // the opening is the hole in this wall\n const [p, q] = ends.get(b.id)!;\n return segmentsCross(from, to, p, q);\n });\n if (!blocked) seen.push(o.id);\n }\n return seen;\n}\n"],"names":["DEFAULT_RANGE","DEFAULT_FOV","coverageOf","project","camera","range","half","from","objectPosition","facing","walls","b","ends","barrierEnds","seen","o","isSpace","isOpening","_a","pointInRing","to","reach","distance","bearing","q","segmentsCross"],"mappings":";;AAaO,MAAMA,IAAgB,IAChBC,IAAc;AAMpB,SAASC,EAAWC,GAA0BC,GAA8B;;AACjF,QAAMC,IAAQD,EAAO,iBAAiBJ,GAChCM,KAAQF,EAAO,iBAAiBH,KAAe,GAC/CM,IAAOC,EAAeL,GAASC,CAAM,GACrCK,IAASL,EAAO,YAAY,GAC5BM,IAAQP,EAAQ,SAAS,OAAO,OAAKQ,EAAE,YAAYP,EAAO,OAAO,GACjEQ,IAAO,IAAI,IAAIF,EAAM,IAAI,CAAAC,MAAK,CAACA,EAAE,IAAIE,EAAYV,GAASQ,CAAC,CAAC,CAAC,CAAC,GAC9DG,IAAiB,CAAA;AACvB,aAAWC,KAAKZ,EAAQ,SAAS;AAE/B,QADIY,EAAE,OAAOX,EAAO,MAAMW,EAAE,YAAYX,EAAO,WAC3C,CAACY,EAAQD,EAAE,IAAI,KAAK,CAACE,EAAUF,EAAE,IAAI,EAAG;AAG5C,SAAIG,IAAAH,EAAE,UAAF,QAAAG,EAAS,UAAUC,EAAYZ,GAAMQ,EAAE,MAAM,CAAC,CAAC,GAAG;AACpD,MAAAD,EAAK,KAAKC,EAAE,EAAE;AACd;AAAA,IACF;AACA,UAAMK,IAAKZ,EAAeL,GAASY,CAAC,GAC9BM,IAAQC,EAASf,GAAMa,CAAE;AAC/B,QAAIC,IAAQ,QAAQA,IAAQhB,EAAO;AACnC,UAAMkB,IAAW,KAAK,MAAMH,EAAG,CAAC,IAAIb,EAAK,CAAC,GAAGa,EAAG,CAAC,IAAIb,EAAK,CAAC,CAAC,IAAI,MAAO,KAAK;AAE5E,QADY,KAAK,MAAQgB,IAAUd,KAAU,MAAO,OAAO,MAAO,GAAG,IAC3DH,EAAM;AAMhB,IALgBI,EAAM,KAAK,CAAAC,MAAK;AAC9B,UAAIA,EAAE,OAAOI,EAAE,UAAW,QAAO;AACjC,YAAM,CAAC,GAAGS,CAAC,IAAIZ,EAAK,IAAID,EAAE,EAAE;AAC5B,aAAOc,EAAclB,GAAMa,GAAI,GAAGI,CAAC;AAAA,IACrC,CAAC,KACaV,EAAK,KAAKC,EAAE,EAAE;AAAA,EAC9B;AACA,SAAOD;AACT;"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { validateProject as f } from "./validate.js";
|
|
2
|
+
async function u(s) {
|
|
3
|
+
return new Promise((o, t) => {
|
|
4
|
+
const e = new FileReader();
|
|
5
|
+
e.onload = () => o(String(e.result)), e.onerror = () => t(e.error), e.readAsDataURL(s);
|
|
6
|
+
});
|
|
7
|
+
}
|
|
8
|
+
async function g(s, o) {
|
|
9
|
+
const t = {};
|
|
10
|
+
for (const e of new Set(s.drawings.map((r) => r.assetId))) {
|
|
11
|
+
const r = await o.get(e);
|
|
12
|
+
if (!r) throw new Error("A reference drawing is missing. Reattach it or remove its reference before exporting.");
|
|
13
|
+
t[e] = await u(r);
|
|
14
|
+
}
|
|
15
|
+
return JSON.stringify({ ...s, embeddedAssets: t }, null, 2);
|
|
16
|
+
}
|
|
17
|
+
async function m(s, o) {
|
|
18
|
+
const t = JSON.parse(s), e = f(t), r = t.embeddedAssets ?? {}, i = [];
|
|
19
|
+
for (const n of new Set(e.drawings.map((d) => d.assetId))) {
|
|
20
|
+
const d = r[n];
|
|
21
|
+
if (typeof d != "string" || !/^data:image\/(png|jpeg|webp);base64,/.test(d))
|
|
22
|
+
throw new Error("Project export is missing a supported reference image.");
|
|
23
|
+
const c = await fetch(d);
|
|
24
|
+
i.push([n, await c.blob()]);
|
|
25
|
+
}
|
|
26
|
+
const a = structuredClone(e);
|
|
27
|
+
delete a.embeddedAssets;
|
|
28
|
+
for (const [n, d] of i) {
|
|
29
|
+
const c = crypto.randomUUID();
|
|
30
|
+
await o.put(c, d), a.drawings.filter((p) => p.assetId === n).forEach((p) => {
|
|
31
|
+
p.assetId = c;
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
return a.id = crypto.randomUUID(), a.updatedAt = (/* @__PURE__ */ new Date()).toISOString(), a;
|
|
35
|
+
}
|
|
36
|
+
function w(s) {
|
|
37
|
+
const o = JSON.parse(s), t = f(o), e = o.embeddedAssets ?? {}, r = structuredClone(t);
|
|
38
|
+
return delete r.embeddedAssets, { project: r, assets: { get: async (a) => {
|
|
39
|
+
const n = e[a];
|
|
40
|
+
if (!(typeof n != "string" || !/^data:image\/(png|jpeg|webp);base64,/.test(n)))
|
|
41
|
+
return (await fetch(n)).blob();
|
|
42
|
+
}, put: async () => {
|
|
43
|
+
}, delete: async () => {
|
|
44
|
+
} } };
|
|
45
|
+
}
|
|
46
|
+
export {
|
|
47
|
+
u as blobDataUrl,
|
|
48
|
+
g as exportProject,
|
|
49
|
+
m as importProject,
|
|
50
|
+
w as parseExport
|
|
51
|
+
};
|
|
52
|
+
//# sourceMappingURL=document.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"document.js","sources":["../../../../src/model/document.ts"],"sourcesContent":["// Document serialisation: export/import with embedded reference images, and read-only parsing of an\n// export. Import-side-effect-free — no browser storage, no React, no MapLibre; safe for any host\n// (node-side tests, schema consumers). Every path in and out runs the full rule set in validate.ts,\n// so a ProjectDocument obtained here is valid by construction.\nimport type { AssetRepository, ProjectDocument } from './types';\nimport { validateProject } from './validate';\n\nexport async function blobDataUrl(blob: Blob): Promise<string> {\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => resolve(String(reader.result));\n reader.onerror = () => reject(reader.error);\n reader.readAsDataURL(blob);\n });\n}\nexport async function exportProject(project: ProjectDocument, assets: AssetRepository): Promise<string> {\n const embeddedAssets: Record<string, string> = {};\n for (const id of new Set(project.drawings.map(d => d.assetId))) {\n const blob = await assets.get(id);\n if (!blob) throw new Error('A reference drawing is missing. Reattach it or remove its reference before exporting.');\n embeddedAssets[id] = await blobDataUrl(blob);\n }\n return JSON.stringify({ ...project, embeddedAssets }, null, 2);\n}\nexport async function importProject(json: string, assets: AssetRepository): Promise<ProjectDocument> {\n const value: unknown = JSON.parse(json);\n const project = validateProject(value);\n const embedded = (value as { embeddedAssets?: Record<string, unknown> }).embeddedAssets ?? {};\n const decoded: [string, Blob][] = [];\n for (const id of new Set(project.drawings.map(d => d.assetId))) {\n const data = embedded[id];\n if (typeof data !== 'string' || !/^data:image\\/(png|jpeg|webp);base64,/.test(data))\n throw new Error('Project export is missing a supported reference image.');\n const res = await fetch(data);\n decoded.push([id, await res.blob()]);\n }\n const copy = structuredClone(project);\n delete (copy as ProjectDocument & { embeddedAssets?: unknown }).embeddedAssets;\n for (const [id, blob] of decoded) {\n const assetId = crypto.randomUUID();\n await assets.put(assetId, blob);\n copy.drawings\n .filter(d => d.assetId === id)\n .forEach(d => {\n d.assetId = assetId;\n });\n }\n copy.id = crypto.randomUUID();\n copy.updatedAt = new Date().toISOString();\n return copy;\n}\n/** Display semantics: ids and updatedAt untouched, nothing written anywhere — unlike importProject, which rewrites both for import-for-editing. Unsupported embedded entries resolve to undefined at get time rather than throwing. */\nexport function parseExport(json: string): { project: ProjectDocument; assets: AssetRepository } {\n const value: unknown = JSON.parse(json);\n const project = validateProject(value);\n const embedded = (value as { embeddedAssets?: Record<string, unknown> }).embeddedAssets ?? {};\n const copy = structuredClone(project);\n delete (copy as ProjectDocument & { embeddedAssets?: unknown }).embeddedAssets;\n const get = async (id: string): Promise<Blob | undefined> => {\n const data = embedded[id];\n if (typeof data !== 'string' || !/^data:image\\/(png|jpeg|webp);base64,/.test(data)) return undefined;\n return (await fetch(data)).blob();\n };\n return { project: copy, assets: { get, put: async () => {}, delete: async () => {} } };\n}\n"],"names":["blobDataUrl","blob","resolve","reject","reader","exportProject","project","assets","embeddedAssets","id","d","importProject","json","value","validateProject","embedded","decoded","data","res","copy","assetId","parseExport"],"mappings":";AAOA,eAAsBA,EAAYC,GAA6B;AAC7D,SAAO,IAAI,QAAQ,CAACC,GAASC,MAAW;AACtC,UAAMC,IAAS,IAAI,WAAA;AACnB,IAAAA,EAAO,SAAS,MAAMF,EAAQ,OAAOE,EAAO,MAAM,CAAC,GACnDA,EAAO,UAAU,MAAMD,EAAOC,EAAO,KAAK,GAC1CA,EAAO,cAAcH,CAAI;AAAA,EAC3B,CAAC;AACH;AACA,eAAsBI,EAAcC,GAA0BC,GAA0C;AACtG,QAAMC,IAAyC,CAAA;AAC/C,aAAWC,KAAM,IAAI,IAAIH,EAAQ,SAAS,IAAI,CAAAI,MAAKA,EAAE,OAAO,CAAC,GAAG;AAC9D,UAAMT,IAAO,MAAMM,EAAO,IAAIE,CAAE;AAChC,QAAI,CAACR,EAAM,OAAM,IAAI,MAAM,uFAAuF;AAClH,IAAAO,EAAeC,CAAE,IAAI,MAAMT,EAAYC,CAAI;AAAA,EAC7C;AACA,SAAO,KAAK,UAAU,EAAE,GAAGK,GAAS,gBAAAE,EAAA,GAAkB,MAAM,CAAC;AAC/D;AACA,eAAsBG,EAAcC,GAAcL,GAAmD;AACnG,QAAMM,IAAiB,KAAK,MAAMD,CAAI,GAChCN,IAAUQ,EAAgBD,CAAK,GAC/BE,IAAYF,EAAuD,kBAAkB,CAAA,GACrFG,IAA4B,CAAA;AAClC,aAAWP,KAAM,IAAI,IAAIH,EAAQ,SAAS,IAAI,CAAA,MAAK,EAAE,OAAO,CAAC,GAAG;AAC9D,UAAMW,IAAOF,EAASN,CAAE;AACxB,QAAI,OAAOQ,KAAS,YAAY,CAAC,uCAAuC,KAAKA,CAAI;AAC/E,YAAM,IAAI,MAAM,wDAAwD;AAC1E,UAAMC,IAAM,MAAM,MAAMD,CAAI;AAC5B,IAAAD,EAAQ,KAAK,CAACP,GAAI,MAAMS,EAAI,KAAA,CAAM,CAAC;AAAA,EACrC;AACA,QAAMC,IAAO,gBAAgBb,CAAO;AACpC,SAAQa,EAAwD;AAChE,aAAW,CAACV,GAAIR,CAAI,KAAKe,GAAS;AAChC,UAAMI,IAAU,OAAO,WAAA;AACvB,UAAMb,EAAO,IAAIa,GAASnB,CAAI,GAC9BkB,EAAK,SACF,OAAO,CAAAT,MAAKA,EAAE,YAAYD,CAAE,EAC5B,QAAQ,CAAAC,MAAK;AACZ,MAAAA,EAAE,UAAUU;AAAA,IACd,CAAC;AAAA,EACL;AACA,SAAAD,EAAK,KAAK,OAAO,WAAA,GACjBA,EAAK,aAAY,oBAAI,KAAA,GAAO,YAAA,GACrBA;AACT;AAEO,SAASE,EAAYT,GAAqE;AAC/F,QAAMC,IAAiB,KAAK,MAAMD,CAAI,GAChCN,IAAUQ,EAAgBD,CAAK,GAC/BE,IAAYF,EAAuD,kBAAkB,CAAA,GACrFM,IAAO,gBAAgBb,CAAO;AACpC,gBAAQa,EAAwD,gBAMzD,EAAE,SAASA,GAAM,QAAQ,EAAE,KALtB,OAAOV,MAA0C;AAC3D,UAAMQ,IAAOF,EAASN,CAAE;AACxB,QAAI,SAAOQ,KAAS,YAAY,CAAC,uCAAuC,KAAKA,CAAI;AACjF,cAAQ,MAAM,MAAMA,CAAI,GAAG,KAAA;AAAA,EAC7B,GACuC,KAAK,YAAY;AAAA,EAAC,GAAG,QAAQ,YAAY;AAAA,EAAC,IAAE;AACrF;"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { objectPosition as u, objectRotation as l, add as f, rotate as b, barrierEnds as k } from "./geometry.js";
|
|
2
|
+
function S(t, i) {
|
|
3
|
+
const h = u(t, i), r = l(t, i), n = i.doorHinge === "right" ? -1 : 1, a = (i.doorSwing ?? 1) * n, c = f(h, b([-n * i.width / 2, 0], r)), e = (s) => f(c, b([n * i.width, 0], r + a * s));
|
|
4
|
+
return { hinge: c, swing: a, tip: e, arc: Array.from({ length: 16 }, (s, d) => e(d * 6)) };
|
|
5
|
+
}
|
|
6
|
+
function T(t, i, h) {
|
|
7
|
+
const r = t.barriers.find((s) => s.id === i.barrierId);
|
|
8
|
+
if (!r) return i.doorSwing ?? 1;
|
|
9
|
+
const [n, a] = k(t, r), c = Math.hypot(a[0] - n[0], a[1] - n[1]), e = ((a[0] - n[0]) * (h[1] - n[1]) - (a[1] - n[1]) * (h[0] - n[0])) / c;
|
|
10
|
+
return Math.abs(e) <= Math.max(0.08, r.thickness / 2) ? i.doorSwing ?? 1 : e > 0 ? 1 : -1;
|
|
11
|
+
}
|
|
12
|
+
function p(t, i) {
|
|
13
|
+
if (i.doorType === "sliding") return [];
|
|
14
|
+
if (i.doorType !== "double") return [S(t, i)];
|
|
15
|
+
const h = u(t, i), r = l(t, i);
|
|
16
|
+
return ["left", "right"].map(
|
|
17
|
+
(n, a) => S(t, {
|
|
18
|
+
...i,
|
|
19
|
+
barrierId: void 0,
|
|
20
|
+
position: f(h, b([(a ? 1 : -1) * i.width / 4, 0], r)),
|
|
21
|
+
rotation: r,
|
|
22
|
+
width: i.width / 2,
|
|
23
|
+
doorHinge: n
|
|
24
|
+
})
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
function x(t, i, h) {
|
|
28
|
+
var m;
|
|
29
|
+
if (i.doorType !== "sliding")
|
|
30
|
+
return p(t, i).flatMap((g) => [
|
|
31
|
+
g.arc,
|
|
32
|
+
[g.hinge, g.tip(h === void 0 ? 90 : h ? 72 : 5)]
|
|
33
|
+
]);
|
|
34
|
+
const r = u(t, i), n = l(t, i), a = ((m = t.barriers.find((g) => g.id === i.barrierId)) == null ? void 0 : m.thickness) ?? 0.2, c = i.doorSwing ?? 1, e = i.doorHinge === "right" ? 1 : -1, s = (g, M) => f(r, b([g, M], n)), d = c * (a / 2 + 0.08), y = h ? e * i.width : 0, w = e * i.width * 0.7, v = -e * i.width * 0.15, o = d + c * 0.2;
|
|
35
|
+
return [
|
|
36
|
+
[s(-i.width / 2 + y, d), s(i.width / 2 + y, d)],
|
|
37
|
+
[s(v, o), s(w, o)],
|
|
38
|
+
[s(w - e * 0.12, o - 0.09), s(w, o), s(w - e * 0.12, o + 0.09)]
|
|
39
|
+
];
|
|
40
|
+
}
|
|
41
|
+
export {
|
|
42
|
+
S as doorGeometry,
|
|
43
|
+
p as doorLeaves,
|
|
44
|
+
x as doorSymbol,
|
|
45
|
+
T as draggedDoorSwing
|
|
46
|
+
};
|
|
47
|
+
//# sourceMappingURL=doors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doors.js","sources":["../../../../src/model/doors.ts"],"sourcesContent":["import type { Point, ProjectDocument, SiteObject } from './types';\nimport { add, barrierEnds, objectPosition, objectRotation, rotate } from './geometry';\n\n/** One hinge and signed rotation for plan symbols, drag previews and the animated 3D leaf. */\nexport function doorGeometry(project: ProjectDocument, door: SiteObject) {\n const position = objectPosition(project, door),\n rotation = objectRotation(project, door);\n const hand = door.doorHinge === 'right' ? -1 : 1;\n const swing = (door.doorSwing ?? 1) * hand;\n const hinge = add(position, rotate([(-hand * door.width) / 2, 0], rotation));\n const tip = (degrees: number) => add(hinge, rotate([hand * door.width, 0], rotation + swing * degrees));\n return { hinge, swing, tip, arc: Array.from({ length: 16 }, (_, i) => tip(i * 6)) };\n}\n\n/** A small dead zone retains the original side during an ordinary along-wall drag. */\nexport function draggedDoorSwing(project: ProjectDocument, door: SiteObject, pointer: Point): 1 | -1 {\n const barrier = project.barriers.find(b => b.id === door.barrierId);\n if (!barrier) return door.doorSwing ?? 1;\n const [a, b] = barrierEnds(project, barrier);\n const length = Math.hypot(b[0] - a[0], b[1] - a[1]);\n const side = ((b[0] - a[0]) * (pointer[1] - a[1]) - (b[1] - a[1]) * (pointer[0] - a[0])) / length;\n return Math.abs(side) <= Math.max(0.08, barrier.thickness / 2) ? (door.doorSwing ?? 1) : side > 0 ? 1 : -1;\n}\n\n/** All moving leaves, shared by the plan and 3D renderer. Double doors meet in the centre. */\nexport function doorLeaves(project: ProjectDocument, door: SiteObject) {\n if (door.doorType === 'sliding') return [];\n if (door.doorType !== 'double') return [doorGeometry(project, door)];\n const position = objectPosition(project, door),\n rotation = objectRotation(project, door);\n return (['left', 'right'] as const).map((doorHinge, i) =>\n doorGeometry(project, {\n ...door,\n barrierId: undefined,\n position: add(position, rotate([((i ? 1 : -1) * door.width) / 4, 0], rotation)),\n rotation,\n width: door.width / 2,\n doorHinge,\n }),\n );\n}\n\n/** Architectural symbol (or a feed's current state), including a rail and travel arrow for sliders. */\nexport function doorSymbol(project: ProjectDocument, door: SiteObject, open?: boolean): Point[][] {\n if (door.doorType !== 'sliding')\n return doorLeaves(project, door).flatMap(leaf => [\n leaf.arc,\n [leaf.hinge, leaf.tip(open === undefined ? 90 : open ? 72 : 5)],\n ]);\n const position = objectPosition(project, door),\n rotation = objectRotation(project, door);\n const thickness = project.barriers.find(b => b.id === door.barrierId)?.thickness ?? 0.2;\n const side = door.doorSwing ?? 1,\n travel = door.doorHinge === 'right' ? 1 : -1;\n const at = (x: number, y: number) => add(position, rotate([x, y], rotation));\n const y = side * (thickness / 2 + 0.08),\n offset = open ? travel * door.width : 0;\n const head = travel * door.width * 0.7,\n tail = -travel * door.width * 0.15,\n arrowY = y + side * 0.2;\n return [\n [at(-door.width / 2 + offset, y), at(door.width / 2 + offset, y)],\n [at(tail, arrowY), at(head, arrowY)],\n [at(head - travel * 0.12, arrowY - 0.09), at(head, arrowY), at(head - travel * 0.12, arrowY + 0.09)],\n ];\n}\n"],"names":["doorGeometry","project","door","position","objectPosition","rotation","objectRotation","hand","swing","hinge","add","rotate","tip","degrees","_","i","draggedDoorSwing","pointer","barrier","b","a","barrierEnds","length","side","doorLeaves","doorHinge","doorSymbol","open","leaf","thickness","_a","travel","at","x","y","offset","head","tail","arrowY"],"mappings":"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"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
function l(e, i = "Untitled site", t = "Site ground level = 0 m") {
|
|
2
|
+
return {
|
|
3
|
+
schemaVersion: 1,
|
|
4
|
+
id: crypto.randomUUID(),
|
|
5
|
+
name: i,
|
|
6
|
+
description: i,
|
|
7
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8
|
+
origin: e,
|
|
9
|
+
datum: t,
|
|
10
|
+
buildings: [{ id: "building-main", name: "Main building" }],
|
|
11
|
+
floors: [{ id: "floor-ground", buildingId: "building-main", name: "Ground floor", elevation: 0, height: 3.5 }],
|
|
12
|
+
junctions: [],
|
|
13
|
+
barriers: [],
|
|
14
|
+
objects: [],
|
|
15
|
+
drawings: []
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
function c(e, i, t, o) {
|
|
19
|
+
const r = {
|
|
20
|
+
door: [1, 0.2, 2.1],
|
|
21
|
+
window: [1.2, 0.15, 1.6],
|
|
22
|
+
gate: [4, 0.15, 2],
|
|
23
|
+
turnstile: [1, 1, 1.1],
|
|
24
|
+
elevator: [2.6, 2.6, 2.5],
|
|
25
|
+
stairs: [2.5, 4.5, 2.7],
|
|
26
|
+
office: [6, 3, 2.6],
|
|
27
|
+
container: [6, 2.4, 2.6],
|
|
28
|
+
storage: [8, 6, 0.1],
|
|
29
|
+
camera: [0.5, 0.5, 2.4],
|
|
30
|
+
sensor: [0.35, 0.35, 0.25],
|
|
31
|
+
light: [1.2, 0.3, 2.8],
|
|
32
|
+
alarm: [0.4, 0.4, 0.4],
|
|
33
|
+
equipment: [1.4, 0.9, 1.4]
|
|
34
|
+
}, [n, a, s] = r[e] ?? [1, 1, 0];
|
|
35
|
+
return {
|
|
36
|
+
id: crypto.randomUUID(),
|
|
37
|
+
kind: e,
|
|
38
|
+
floorId: t,
|
|
39
|
+
name: o ?? e[0].toUpperCase() + e.slice(1),
|
|
40
|
+
position: i,
|
|
41
|
+
rotation: 0,
|
|
42
|
+
width: n,
|
|
43
|
+
depth: a,
|
|
44
|
+
height: s,
|
|
45
|
+
symbol: e === "poi" ? "personnel" : void 0,
|
|
46
|
+
coverageAngle: e === "camera" ? 70 : void 0,
|
|
47
|
+
coverageRange: e === "camera" ? 12 : void 0,
|
|
48
|
+
servedFloorIds: e === "stairs" || e === "elevator" ? t ? [t] : [] : void 0,
|
|
49
|
+
...e === "light" ? { light: { kelvin: 4e3, intensity: 45, range: 12 } } : {}
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
export {
|
|
53
|
+
c as createObject,
|
|
54
|
+
l as emptyProject
|
|
55
|
+
};
|
|
56
|
+
//# sourceMappingURL=factory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"factory.js","sources":["../../../../src/model/factory.ts"],"sourcesContent":["// Core domain factories for building indoor-mapping documents from scratch — no app defaults, no\n// demo data. Part of the schema contract so library users can construct valid projects and objects.\nimport type { ObjectKind, Origin, Point, ProjectDocument, SiteObject } from './types';\n\n/** An empty, valid single-floor project anchored at an explicit EPSG:3067 origin (with optional site bearing). */\nexport function emptyProject(\n origin: Origin,\n name = 'Untitled site',\n datum = 'Site ground level = 0 m',\n): ProjectDocument {\n return {\n schemaVersion: 1,\n id: crypto.randomUUID(),\n name,\n description: name,\n updatedAt: new Date().toISOString(),\n origin,\n datum,\n buildings: [{ id: 'building-main', name: 'Main building' }],\n floors: [{ id: 'floor-ground', buildingId: 'building-main', name: 'Ground floor', elevation: 0, height: 3.5 }],\n junctions: [],\n barriers: [],\n objects: [],\n drawings: [],\n };\n}\n/** Create a site object of the given kind at a position, seeded with sensible default dimensions. */\nexport function createObject(kind: ObjectKind, position: Point, floorId: string | null, name?: string): SiteObject {\n const sizes: Partial<Record<ObjectKind, [number, number, number]>> = {\n door: [1, 0.2, 2.1],\n window: [1.2, 0.15, 1.6],\n gate: [4, 0.15, 2],\n turnstile: [1, 1, 1.1],\n elevator: [2.6, 2.6, 2.5],\n stairs: [2.5, 4.5, 2.7],\n office: [6, 3, 2.6],\n container: [6, 2.4, 2.6],\n storage: [8, 6, 0.1],\n camera: [0.5, 0.5, 2.4],\n sensor: [0.35, 0.35, 0.25],\n light: [1.2, 0.3, 2.8],\n alarm: [0.4, 0.4, 0.4],\n equipment: [1.4, 0.9, 1.4],\n };\n const [width, depth, height] = sizes[kind] ?? [1, 1, 0];\n return {\n id: crypto.randomUUID(),\n kind,\n floorId,\n name: name ?? kind[0].toUpperCase() + kind.slice(1),\n position,\n rotation: 0,\n width,\n depth,\n height,\n symbol: kind === 'poi' ? 'personnel' : undefined,\n coverageAngle: kind === 'camera' ? 70 : undefined,\n coverageRange: kind === 'camera' ? 12 : undefined,\n servedFloorIds: kind === 'stairs' || kind === 'elevator' ? (floorId ? [floorId] : []) : undefined,\n ...(kind === 'light' ? { light: { kelvin: 4000, intensity: 45, range: 12 } } : {}),\n };\n}\n"],"names":["emptyProject","origin","name","datum","createObject","kind","position","floorId","sizes","width","depth","height"],"mappings":"AAKO,SAASA,EACdC,GACAC,IAAO,iBACPC,IAAQ,2BACS;AACjB,SAAO;AAAA,IACL,eAAe;AAAA,IACf,IAAI,OAAO,WAAA;AAAA,IACX,MAAAD;AAAA,IACA,aAAaA;AAAA,IACb,YAAW,oBAAI,KAAA,GAAO,YAAA;AAAA,IACtB,QAAAD;AAAA,IACA,OAAAE;AAAA,IACA,WAAW,CAAC,EAAE,IAAI,iBAAiB,MAAM,iBAAiB;AAAA,IAC1D,QAAQ,CAAC,EAAE,IAAI,gBAAgB,YAAY,iBAAiB,MAAM,gBAAgB,WAAW,GAAG,QAAQ,KAAK;AAAA,IAC7G,WAAW,CAAA;AAAA,IACX,UAAU,CAAA;AAAA,IACV,SAAS,CAAA;AAAA,IACT,UAAU,CAAA;AAAA,EAAC;AAEf;AAEO,SAASC,EAAaC,GAAkBC,GAAiBC,GAAwBL,GAA2B;AACjH,QAAMM,IAA+D;AAAA,IACnE,MAAM,CAAC,GAAG,KAAK,GAAG;AAAA,IAClB,QAAQ,CAAC,KAAK,MAAM,GAAG;AAAA,IACvB,MAAM,CAAC,GAAG,MAAM,CAAC;AAAA,IACjB,WAAW,CAAC,GAAG,GAAG,GAAG;AAAA,IACrB,UAAU,CAAC,KAAK,KAAK,GAAG;AAAA,IACxB,QAAQ,CAAC,KAAK,KAAK,GAAG;AAAA,IACtB,QAAQ,CAAC,GAAG,GAAG,GAAG;AAAA,IAClB,WAAW,CAAC,GAAG,KAAK,GAAG;AAAA,IACvB,SAAS,CAAC,GAAG,GAAG,GAAG;AAAA,IACnB,QAAQ,CAAC,KAAK,KAAK,GAAG;AAAA,IACtB,QAAQ,CAAC,MAAM,MAAM,IAAI;AAAA,IACzB,OAAO,CAAC,KAAK,KAAK,GAAG;AAAA,IACrB,OAAO,CAAC,KAAK,KAAK,GAAG;AAAA,IACrB,WAAW,CAAC,KAAK,KAAK,GAAG;AAAA,EAAA,GAErB,CAACC,GAAOC,GAAOC,CAAM,IAAIH,EAAMH,CAAI,KAAK,CAAC,GAAG,GAAG,CAAC;AACtD,SAAO;AAAA,IACL,IAAI,OAAO,WAAA;AAAA,IACX,MAAAA;AAAA,IACA,SAAAE;AAAA,IACA,MAAML,KAAQG,EAAK,CAAC,EAAE,gBAAgBA,EAAK,MAAM,CAAC;AAAA,IAClD,UAAAC;AAAA,IACA,UAAU;AAAA,IACV,OAAAG;AAAA,IACA,OAAAC;AAAA,IACA,QAAAC;AAAA,IACA,QAAQN,MAAS,QAAQ,cAAc;AAAA,IACvC,eAAeA,MAAS,WAAW,KAAK;AAAA,IACxC,eAAeA,MAAS,WAAW,KAAK;AAAA,IACxC,gBAAgBA,MAAS,YAAYA,MAAS,aAAcE,IAAU,CAACA,CAAO,IAAI,CAAA,IAAM;AAAA,IACxF,GAAIF,MAAS,UAAU,EAAE,OAAO,EAAE,QAAQ,KAAM,WAAW,IAAI,OAAO,GAAA,EAAG,IAAM,CAAA;AAAA,EAAC;AAEpF;"}
|