@kerros/viewer 0.0.0-stage → 0.1.0-rc.1
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 +52 -4
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import u from "polygon-clipping";
|
|
2
|
+
import { openRing as d, closeRing as h, barrierEnds as w, distance as x, rectangle as A, pointInRing as E } from "./geometry.js";
|
|
3
|
+
const I = (n, s) => (Math.atan2(s[1] - n[1], s[0] - n[0]) * 180 / Math.PI % 180 + 180) % 180, p = (n, s) => {
|
|
4
|
+
const e = ((s - n) % 180 + 180) % 180;
|
|
5
|
+
return e > 90 ? e - 180 : e;
|
|
6
|
+
}, C = 4, D = (n) => n.map((s, e) => [s, n[(e + 1) % n.length]]), M = /* @__PURE__ */ new WeakMap();
|
|
7
|
+
function O(n, s) {
|
|
8
|
+
let e = M.get(n);
|
|
9
|
+
e || M.set(n, e = /* @__PURE__ */ new Map());
|
|
10
|
+
const c = e.get(s);
|
|
11
|
+
if (c) return c;
|
|
12
|
+
let i = n.objects.filter((t) => t.floorId === s && t.kind === "zone" && !t.parentId && t.rings).flatMap((t) => t.rings.map(d));
|
|
13
|
+
if (!i.length) {
|
|
14
|
+
const t = n.objects.filter((o) => {
|
|
15
|
+
var r;
|
|
16
|
+
return o.floorId === s && o.kind === "room" && ((r = o.rings) == null ? void 0 : r.length);
|
|
17
|
+
});
|
|
18
|
+
if (t.length)
|
|
19
|
+
try {
|
|
20
|
+
const o = t.map((a) => h(a.rings[0])), r = o.map((a) => [a]);
|
|
21
|
+
for (const a of n.barriers) {
|
|
22
|
+
if (a.floorId !== s || a.kind !== "wall") continue;
|
|
23
|
+
const [l, g] = w(n, a), m = x(l, g);
|
|
24
|
+
if (m < 1e-6) continue;
|
|
25
|
+
const z = Math.atan2(g[1] - l[1], g[0] - l[0]) * 180 / Math.PI;
|
|
26
|
+
r.push([
|
|
27
|
+
h(A([(l[0] + g[0]) / 2, (l[1] + g[1]) / 2], m + a.thickness, a.thickness, z))
|
|
28
|
+
]);
|
|
29
|
+
}
|
|
30
|
+
const y = (a) => a.map((l) => l.map((g) => [Math.round(g[0] * 1e4) / 1e4, Math.round(g[1] * 1e4) / 1e4])), R = r.map(y);
|
|
31
|
+
i = u.union(...R).filter((a) => o.some((l) => l.some((g) => E(g, d(a[0]))))).map((a) => d(a[0]));
|
|
32
|
+
} catch {
|
|
33
|
+
try {
|
|
34
|
+
i = u.union(
|
|
35
|
+
...t.map((r) => [h(r.rings[0])])
|
|
36
|
+
).map((r) => d(r[0]));
|
|
37
|
+
} catch {
|
|
38
|
+
i = [];
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return i.length || (i = n.objects.filter((t) => t.kind === "building" && t.rings).flatMap((t) => t.rings.map(d))), e.set(s, i), i;
|
|
43
|
+
}
|
|
44
|
+
function b(n) {
|
|
45
|
+
const s = [];
|
|
46
|
+
for (const [c, f] of n) {
|
|
47
|
+
const i = x(c, f);
|
|
48
|
+
if (i < 0.5) continue;
|
|
49
|
+
const t = I(c, f), o = s.find((r) => Math.abs(p(r.seed, t)) <= C);
|
|
50
|
+
o ? (o.drift += i * p(o.seed, t), o.weight += i) : s.push({ seed: t, weight: i, drift: 0 });
|
|
51
|
+
}
|
|
52
|
+
const e = s.sort((c, f) => f.weight - c.weight)[0];
|
|
53
|
+
return e ? ((e.seed + e.drift / e.weight) % 180 + 180) % 180 : null;
|
|
54
|
+
}
|
|
55
|
+
function F(n, s) {
|
|
56
|
+
let e = k.get(n);
|
|
57
|
+
e || k.set(n, e = /* @__PURE__ */ new Map());
|
|
58
|
+
const c = s ?? "", f = e.get(c);
|
|
59
|
+
if (f !== void 0) return f;
|
|
60
|
+
const i = b(O(n, s).flatMap(D)) ?? b(n.barriers.filter((t) => t.floorId === s).map((t) => w(n, t))) ?? 0;
|
|
61
|
+
return e.set(c, i), i;
|
|
62
|
+
}
|
|
63
|
+
const k = /* @__PURE__ */ new WeakMap();
|
|
64
|
+
export {
|
|
65
|
+
C as ALIGNED,
|
|
66
|
+
p as axisDelta,
|
|
67
|
+
I as axisOf,
|
|
68
|
+
O as floorOutline,
|
|
69
|
+
F as mainAxis
|
|
70
|
+
};
|
|
71
|
+
//# sourceMappingURL=walls.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"walls.js","sources":["../../../../src/model/walls.ts"],"sourcesContent":["// Wall alignment. A wall in a real building almost never points wherever the pointer happened to be:\n// it runs with the building, squares across it, or continues a wall that is already there. This\n// module works out which of those a given spot calls for, so the drawing tools can hold to it.\n//\n// Everything here is in local metres, which `toLocal` has already rotated by the site origin's\n// bearing — so 0° is the site's own grid rather than true north, and a building laid out along its\n// street needs no reference angle at all beyond the default.\nimport polygonClipping from 'polygon-clipping';\nimport { boundaryEdges, type BoundaryEdge } from './boundaries';\nimport { drawBarrier } from './authoring';\nimport { transact } from './validate';\nimport {\n OPENING_MIN_SEGMENT,\n barrierEnds,\n barrierJoinPoint,\n barrierStrokeIssue,\n closeRing,\n distance,\n openRing,\n pointInRing,\n rectangle,\n segmentProjection,\n snapPoint,\n} from './geometry';\nimport type { Point, ProjectDocument, Ring } from './types';\n\ninterface CornerNeighbour {\n point: Point;\n /** Other ends of unchanged edges meeting this neighbour. */\n references: Point[];\n}\n\n/** Hold the moving corner to parallels/perpendiculars of the unchanged neighbouring edges.\n * Two nearby constraints determine a corner exactly; projecting onto just one cannot restore a\n * rectangle. Prefer their intersection over a one-line projection, within the same screen reach. */\nfunction snapCorner(raw: Point, tolerance: number, neighbours: CornerNeighbour[]): Point | undefined {\n const lines: { anchor: Point; direction: Point; neighbour: number }[] = [];\n let best = tolerance,\n projected: Point | undefined;\n for (const [neighbour, { point: anchor, references }] of neighbours.entries()) {\n for (const reference of references) {\n const length = distance(anchor, reference);\n if (length < 1e-6) continue;\n const dx = (reference[0] - anchor[0]) / length,\n dy = (reference[1] - anchor[1]) / length;\n for (const direction of [\n [dx, dy],\n [-dy, dx],\n ] as Point[]) {\n const along = (raw[0] - anchor[0]) * direction[0] + (raw[1] - anchor[1]) * direction[1];\n const point: Point = [anchor[0] + along * direction[0], anchor[1] + along * direction[1]];\n const error = distance(raw, point);\n if (error >= tolerance) continue;\n lines.push({ anchor, direction, neighbour });\n if (error < best) {\n best = error;\n projected = point;\n }\n }\n }\n }\n best = tolerance;\n let intersection: Point | undefined;\n for (let i = 0; i < lines.length; i++) {\n const a = lines[i];\n for (let j = i + 1; j < lines.length; j++) {\n const b = lines[j];\n if (a.neighbour === b.neighbour) continue;\n const denominator = a.direction[0] * b.direction[1] - a.direction[1] * b.direction[0];\n if (Math.abs(denominator) < 1e-6) continue;\n const dx = b.anchor[0] - a.anchor[0],\n dy = b.anchor[1] - a.anchor[1];\n const along = (dx * b.direction[1] - dy * b.direction[0]) / denominator;\n const point: Point = [a.anchor[0] + along * a.direction[0], a.anchor[1] + along * a.direction[1]];\n const error = distance(raw, point);\n if (error < best) {\n best = error;\n intersection = point;\n }\n }\n }\n return intersection ?? projected;\n}\n\n// A gesture reads one immutable project snapshot. Index adjacency once so corner constraints only\n// inspect local neighbours on pointer moves, without traversing the full plan for each neighbour.\nconst dragIndexes = new WeakMap<\n ProjectDocument,\n {\n positions: Map<string, Point>;\n incident: Map<string, BoundaryEdge[]>;\n }\n>();\nfunction dragIndex(project: ProjectDocument) {\n const cached = dragIndexes.get(project);\n if (cached) return cached;\n const positions = new Map(project.junctions.map(j => [j.id, j.position]));\n const incident = new Map<string, BoundaryEdge[]>();\n for (const edge of boundaryEdges(project)) {\n for (const id of [edge.startId, edge.endId]) {\n const edges = incident.get(id) ?? [];\n edges.push(edge);\n incident.set(id, edges);\n }\n }\n const index = { positions, incident };\n dragIndexes.set(project, index);\n return index;\n}\n\n/** A drag snaps in the geometry's frame. Rounding x/y independently moves an imported wall's\n * junction off its centreline, even when the pointer follows that centreline exactly. */\nexport function snapDragPoint(\n project: ProjectDocument,\n floorId: string | null,\n kind: 'junction' | 'barrier' | 'ring',\n id: string,\n raw: Point,\n snapping: boolean,\n tolerance: number,\n ringIndex = 0,\n vertexIndex = 0,\n): Point {\n if (kind === 'barrier') {\n const wall = boundaryEdges(project).find(b => b.id === id);\n if (!wall) return raw;\n const [a, b] = barrierEnds(project, wall),\n length = distance(a, b);\n const nx = -(b[1] - a[1]) / length,\n ny = (b[0] - a[0]) / length;\n let slide = (raw[0] - (a[0] + b[0]) / 2) * nx + (raw[1] - (a[1] + b[1]) / 2) * ny;\n if (snapping) {\n const desired = slide;\n let best = tolerance,\n held: number | undefined;\n const axis = mainAxis(project, floorId);\n const angles = [axisOf(a, b), ...Array.from({ length: 12 }, (_, i) => axis + i * 15)];\n // Solve the wall's normal displacement for each neighbouring segment's alignment.\n // The neighbours' remote ends stay fixed; their shared ends move with this wall.\n for (const other of boundaryEdges(project)) {\n if (other.id === wall.id) continue;\n const moving = [other.startId, other.endId].find(j => j === wall.startId || j === wall.endId);\n const fixedId = [other.startId, other.endId].find(j => j !== wall.startId && j !== wall.endId);\n if (!moving || !fixedId) continue;\n const at = moving === wall.startId ? a : b;\n const fixed = project.junctions.find(j => j.id === fixedId)!.position;\n for (const angle of angles) {\n const ux = Math.cos((angle * Math.PI) / 180),\n uy = Math.sin((angle * Math.PI) / 180);\n const denominator = nx * uy - ny * ux;\n if (Math.abs(denominator) < 1e-6) continue;\n const offset = ((fixed[0] - at[0]) * uy - (fixed[1] - at[1]) * ux) / denominator;\n const error = Math.abs(offset - desired);\n if (error < best) {\n best = error;\n held = offset;\n }\n }\n }\n slide = held ?? Math.round(slide * 2) / 2;\n }\n return [(a[0] + b[0]) / 2 + nx * slide, (a[1] + b[1]) / 2 + ny * slide];\n }\n if (!snapping) return raw;\n if (kind === 'ring') {\n const ring = openRing(project.objects.find(o => o.id === id)?.rings?.[ringIndex] ?? []);\n const neighbours: CornerNeighbour[] = [];\n if (ring.length >= 3 && vertexIndex >= 0 && vertexIndex < ring.length) {\n for (const step of [-1, 1]) {\n neighbours.push({\n point: ring[(vertexIndex + step + ring.length) % ring.length],\n references: [ring[(vertexIndex + 2 * step + ring.length) % ring.length]],\n });\n }\n }\n return snapCorner(raw, tolerance, neighbours) ?? [Math.round(raw[0] * 2) / 2, Math.round(raw[1] * 2) / 2];\n }\n const index = dragIndex(project);\n const incident = index.incident.get(id) ?? [];\n const incoming = new Set(incident.map(b => b.id));\n const others = {\n ...project,\n junctions: project.junctions.filter(j => j.id !== id),\n barriers: project.barriers.filter(b => !incoming.has(b.id)),\n virtualBoundaries: project.virtualBoundaries?.filter(b => !incoming.has(b.id)),\n };\n // Receiving junctions and walls take priority over the grid. Do not snap onto the point being\n // dragged or weld it back onto one of its own changing segments.\n const nearby = snapPoint(others, floorId, raw, tolerance, undefined, false);\n if (nearby.label) return nearby.point;\n const neighbours = [...new Set(incident.map(edge => (edge.startId === id ? edge.endId : edge.startId)))];\n const corner = snapCorner(\n raw,\n tolerance,\n neighbours.map(neighbour => ({\n point: index.positions.get(neighbour)!,\n references: (index.incident.get(neighbour) ?? [])\n .filter(edge => !incoming.has(edge.id))\n .map(edge => index.positions.get(edge.startId === neighbour ? edge.endId : edge.startId)!),\n })),\n );\n if (corner) return corner;\n let best = tolerance,\n aligned: Point | undefined;\n for (const wall of incident) {\n const [a, b] = barrierEnds(project, wall),\n dx = b[0] - a[0],\n dy = b[1] - a[1];\n const t = ((raw[0] - a[0]) * dx + (raw[1] - a[1]) * dy) / (dx * dx + dy * dy);\n const at: Point = [a[0] + t * dx, a[1] + t * dy];\n const d = distance(raw, at);\n if (d < best) {\n best = d;\n aligned = at;\n }\n }\n if (aligned) return aligned;\n const neighbour = incident\n .map(b => project.junctions.find(j => j.id === (b.startId === id ? b.endId : b.startId))!)\n .sort((a, b) => distance(raw, a.position) - distance(raw, b.position))[0];\n return snapPoint(others, floorId, raw, tolerance, neighbour?.position, true, mainAxis(project, floorId)).point;\n}\n\n/** A segment's direction as an *axis* in [0, 180). A wall and the same wall drawn backwards describe\n * one line, so 10° and 190° have to compare equal or half of every alignment test misses. */\nexport const axisOf = (a: Point, b: Point): number => {\n const deg = (Math.atan2(b[1] - a[1], b[0] - a[0]) * 180) / Math.PI;\n return ((deg % 180) + 180) % 180;\n};\n/** The smallest rotation carrying axis `from` onto axis `to`, in (-90, 90]. */\nexport const axisDelta = (from: number, to: number): number => {\n const d = (((to - from) % 180) + 180) % 180;\n return d > 90 ? d - 180 : d;\n};\n/** Two directions count as the same when they are within this many degrees. Wide enough to absorb a\n * footprint traced by hand or imported from a cadastral outline, narrow enough that a wall set at a\n * deliberate angle still reads as its own direction. */\nexport const ALIGNED = 4;\n\nconst ringEdges = (ring: Ring): [Point, Point][] =>\n ring.map((p, i) => [p, ring[(i + 1) % ring.length]] as [Point, Point]);\n\nconst outlines = new WeakMap<ProjectDocument, Map<string | null, Ring[]>>();\n\n/** The rings describing a floor's outline: its own top-level zones when it has them, else the union\n * of the rooms drawn on it, else the site's building footprints. Shared with `exteriorWalls` so\n * \"the outline\" means one thing across the app.\n *\n * The room union is what a floor without a zone over it actually has. An imported plan is exactly\n * that — rooms traced off a drawing, no covering zone and no site footprint — and skipping straight\n * to the building objects returned nothing for it, so not one of its walls counted as exterior:\n * no exterior material, no facade preset, and nothing for a stacked view to draw a storey's edge\n * from. Cached per document, which is immutable between edits, because the union is not free. */\nexport function floorOutline(project: ProjectDocument, floorId: string | null): Ring[] {\n let cache = outlines.get(project);\n if (!cache) outlines.set(project, (cache = new Map()));\n const hit = cache.get(floorId);\n if (hit) return hit;\n const zones = project.objects.filter(o => o.floorId === floorId && o.kind === 'zone' && !o.parentId && o.rings);\n let rings: Ring[] = zones.flatMap(o => o.rings!.map(openRing));\n if (!rings.length) {\n const rooms = project.objects.filter(o => o.floorId === floorId && o.kind === 'room' && o.rings?.length);\n if (rooms.length) {\n try {\n // The rooms AND the walls between them. Rooms alone do not touch — there is a wall in every\n // gap — so their union is one polygon per room rather than one building, and an outline per\n // room makes every interior partition look like it sits on the outside of something. The\n // floor's real footprint is what you could stand on plus what encloses it, and unioning the\n // wall bodies in is what closes those gaps into a single plate.\n const roomRings = rooms.map(o => closeRing(o.rings![0]));\n const solids: Ring[][] = roomRings.map(r => [r]);\n for (const b of project.barriers) {\n // Walls only. A fence is drawn on a floor but is not part of its plate, and unioning one\n // in makes the site boundary the building's longest edge — which is then what every new\n // wall squares itself to.\n if (b.floorId !== floorId || b.kind !== 'wall') continue;\n const [a, c] = barrierEnds(project, b);\n const len = distance(a, c);\n if (len < 1e-6) continue;\n const angle = (Math.atan2(c[1] - a[1], c[0] - a[0]) * 180) / Math.PI;\n // Run on by half a thickness at each end, as spaces.ts sweeps them, or every outer corner\n // keeps a square notch where the two walls only overlap across their inner quarter.\n solids.push([\n closeRing(rectangle([(a[0] + c[0]) / 2, (a[1] + c[1]) / 2], len + b.thickness, b.thickness, angle)),\n ]);\n }\n // Snapped to a tenth of a millimetre first. Two walls meeting at a corner arrive here with\n // coordinates that differ in the fifteenth decimal, and the clipper answers that by failing\n // to close a ring at all — which silently cost this floor its whole outline.\n const snap = (pg: Ring[]): Ring[] =>\n pg.map(r => r.map(q => [Math.round(q[0] * 1e4) / 1e4, Math.round(q[1] * 1e4) / 1e4] as Point));\n const clean = solids.map(snap);\n const merged = polygonClipping.union(...(clean as [Ring[], ...Ring[][]])) as unknown as Ring[][];\n // Outer rings only: a courtyard is a hole in the plate, not a second outline, and a stairwell\n // void certainly is not a façade.\n //\n // And only the polygons that a room is actually part of. Wall bodies are here to close the\n // gaps BETWEEN rooms; one standing away from the building — a garden wall, a retaining wall —\n // touches no room and would otherwise come back as a little footprint of its own, which then\n // reads as a façade with its own exterior finish.\n rings = merged\n .filter(pg => roomRings.some(r => r.some(q => pointInRing(q, openRing(pg[0])))))\n .map(pg => openRing(pg[0]));\n } catch {\n // Still degenerate. The rooms alone are a worse outline — they leave a gap at every wall —\n // but they are an outline, and no outline at all means no façade and no floor plate.\n try {\n const merged = polygonClipping.union(\n ...(rooms.map(o => [closeRing(o.rings![0])]) as [Ring[], ...Ring[][]]),\n ) as unknown as Ring[][];\n rings = merged.map(pg => openRing(pg[0]));\n } catch {\n rings = [];\n }\n }\n }\n }\n if (!rings.length)\n rings = project.objects.filter(o => o.kind === 'building' && o.rings).flatMap(o => o.rings!.map(openRing));\n cache.set(floorId, rings);\n return rings;\n}\n\n/** The axis a set of edges most runs along, weighted by length — edges within ALIGNED° of each other\n * count as one direction and are averaged, so a ragged outline still resolves to the single\n * direction a person would point at. Null when there is nothing long enough to be a direction. */\nfunction dominantAxis(edges: [Point, Point][]): number | null {\n const buckets: { seed: number; weight: number; drift: number }[] = [];\n for (const [a, b] of edges) {\n const length = distance(a, b);\n if (length < 0.5) continue; // vertex noise, not a direction\n const axis = axisOf(a, b);\n const bucket = buckets.find(x => Math.abs(axisDelta(x.seed, axis)) <= ALIGNED);\n if (!bucket) buckets.push({ seed: axis, weight: length, drift: 0 });\n else {\n // Drift accumulates *relative to the seed* so a bucket straddling the 0/180 wrap still averages.\n bucket.drift += length * axisDelta(bucket.seed, axis);\n bucket.weight += length;\n }\n }\n const best = buckets.sort((a, b) => b.weight - a.weight)[0];\n return best ? (((best.seed + best.drift / best.weight) % 180) + 180) % 180 : null;\n}\n\n/** The building's main axis on this floor: the direction its outline predominantly runs, in [0, 180).\n * Falls back to the walls already drawn, and finally to 0 — the site grid, which for a project whose\n * origin carries a bearing is already the building's street alignment. */\nexport function mainAxis(project: ProjectDocument, floorId: string | null): number {\n // Every pointer move asks for this, so memoise per document — edits replace the document object,\n // which retires the entry with it. Same bargain exteriorWalls strikes.\n let byFloor = axisCache.get(project);\n if (!byFloor) axisCache.set(project, (byFloor = new Map()));\n const key = floorId ?? '';\n const cached = byFloor.get(key);\n if (cached !== undefined) return cached;\n const axis =\n dominantAxis(floorOutline(project, floorId).flatMap(ringEdges)) ??\n dominantAxis(project.barriers.filter(b => b.floorId === floorId).map(b => barrierEnds(project, b))) ??\n 0;\n byFloor.set(key, axis);\n return axis;\n}\nconst axisCache = new WeakMap<ProjectDocument, Map<string, number>>();\n\nexport interface AxisReference {\n /** Degrees in [0, 180). New walls are held to this and its 15° multiples. */\n angle: number;\n source: 'wall' | 'exterior' | 'building';\n}\n/** What a wall drawn near `point` should line up with: whichever wall — interior or the building's own\n * exterior — it is being drawn beside, and failing that the building's main axis.\n *\n * Preferring the near edge over the building-wide average is what makes irregular plans workable. A\n * wing set askew, or a space with a raked end, has no one true direction; next to the raked end the\n * raked end *is* the direction, and squaring to the far-off main axis would look wrong there. Drawn\n * walls win ties against the exterior at equal distance, since an interior wall is the more\n * deliberate signal. */\nexport function referenceAxis(\n project: ProjectDocument,\n floorId: string | null,\n point: Point,\n reach: number,\n): AxisReference {\n let best = reach,\n found: AxisReference | null = null;\n const consider = (a: Point, b: Point, source: 'wall' | 'exterior') => {\n const d = segmentProjection(point, a, b).distance;\n if (d > best) return; // ties fall to the later candidate, and walls are considered last\n best = d;\n found = { angle: axisOf(a, b), source };\n };\n for (const ring of floorOutline(project, floorId)) for (const [a, b] of ringEdges(ring)) consider(a, b, 'exterior');\n for (const barrier of project.barriers)\n if (barrier.floorId === floorId) consider(...barrierEnds(project, barrier), 'wall');\n return found ?? { angle: mainAxis(project, floorId), source: 'building' };\n}\n\nconst cross = (px: number, py: number, qx: number, qy: number) => px * qy - py * qx;\n/** How far a ray from `from` along unit `dir` travels before crossing segment a→b; null if never. */\nfunction rayHit(from: Point, dir: Point, a: Point, b: Point): number | null {\n const ex = b[0] - a[0],\n ey = b[1] - a[1],\n denom = cross(dir[0], dir[1], ex, ey);\n if (Math.abs(denom) < 1e-9) return null; // parallel: a wall running the same way is never hit\n const rx = a[0] - from[0],\n ry = a[1] - from[1],\n t = cross(rx, ry, ex, ey) / denom,\n u = cross(rx, ry, dir[0], dir[1]) / denom;\n return t > 1e-6 && u >= 0 && u <= 1 ? t : null;\n}\n\nexport interface WallProposal {\n segment: [Point, Point];\n axis: number;\n reference: AxisReference;\n}\n/** The wall a space looks like it is missing. Hover anywhere inside one: this squares off against the\n * wall you are nearest (or the building, in a space that has none yet), runs a line both ways until\n * it meets something, and offers the whole span as one click.\n *\n * Null when there is nothing worth offering — no bound on one of the two sides, a span too short to\n * be a wall, or a wall already running this way within `clear` metres, which means the space is\n * divided here already. */\nexport function proposeWall(\n project: ProjectDocument,\n floorId: string | null,\n point: Point,\n clear = 1.5,\n): WallProposal | null {\n const reference = referenceAxis(project, floorId, point, Number.POSITIVE_INFINITY);\n const axis = (reference.angle + 90) % 180,\n rad = (axis * Math.PI) / 180,\n dir: Point = [Math.cos(rad), Math.sin(rad)];\n const walls = project.barriers.filter(b => b.floorId === floorId).map(b => barrierEnds(project, b));\n for (const [a, b] of walls)\n if (Math.abs(axisDelta(axisOf(a, b), axis)) <= ALIGNED && segmentProjection(point, a, b).distance < clear)\n return null;\n const graph = boundaryEdges(project)\n .filter(b => b.floorId === floorId)\n .map(b => barrierEnds(project, b));\n // A space's centreline boundaries outrank its derived inner-face outline. Stopping at a\n // cached wall face leaves a small disconnected stub when the wall there is now virtual.\n const edges = graph;\n const fallback = floorOutline(project, floorId).flatMap(ringEdges);\n const end = (sign: number): Point | null => {\n const d: Point = [dir[0] * sign, dir[1] * sign];\n let best = Number.POSITIVE_INFINITY;\n for (const [a, b] of edges) {\n const t = rayHit(point, d, a, b);\n if (t !== null && t < best) best = t;\n }\n if (!Number.isFinite(best))\n for (const [a, b] of fallback) {\n const t = rayHit(point, d, a, b);\n if (t !== null && t < best) best = t;\n }\n return Number.isFinite(best) ? [point[0] + d[0] * best, point[1] + d[1] * best] : null;\n };\n const start = end(1),\n finish = end(-1);\n if (!start || !finish) return null;\n const a = barrierJoinPoint(project, start, floorId),\n b = barrierJoinPoint(project, finish, floorId);\n if (barrierStrokeIssue(project, a, b, floorId)) return null;\n if (\n project.objects.some(o => o.floorId === floorId && o.geometry?.mode === 'boundaries') &&\n !transact(\n project,\n draft => {\n drawBarrier(draft, floorId, a, b);\n },\n { freeze: false },\n ).ok\n )\n return null;\n return distance(a, b) >= 1 ? { segment: [a, b], axis, reference } : null;\n}\n\n/** Where an opening would land if you placed it here. */\nexport interface OpeningFit {\n barrierId: string;\n offset: number;\n position: Point;\n /** The stretch of wall the leaf would occupy — what to draw so the placement is visible first. */\n span: [Point, Point];\n}\n/** Fit an opening to the nearest wall (or fence, for a gate).\n *\n * One rule, asked twice: the hover preview and the click that commits must agree, or the preview is\n * a lie. Returns null when nothing is close enough, which is what lets the tool say so before the\n * click rather than by throwing after it.\n *\n * The offset is clamped so the leaf stays on the wall — a door centred 10 cm from the end of its\n * wall would otherwise hang in the air. */\nexport function fitOpening(\n project: ProjectDocument,\n floorId: string | null,\n kind: 'door' | 'window' | 'gate' | 'turnstile',\n at: Point,\n width: number,\n reach: number,\n ignoreId?: string,\n): OpeningFit | null {\n const wanted = kind === 'gate' ? 'fence' : 'wall';\n let best: OpeningFit | null = null,\n bestDistance = reach;\n for (const barrier of project.barriers) {\n if (barrier.floorId !== floorId || barrier.kind !== wanted) continue;\n const [a, b] = barrierEnds(project, barrier);\n const hit = segmentProjection(at, a, b);\n // Match validation: the segment must be nondegenerate and hold the actual leaf width.\n if (hit.distance >= bestDistance || hit.length < Math.max(width, OPENING_MIN_SEGMENT)) continue;\n // Search the free stretches, not just the whole segment: a preview over an existing door\n // used to promise a placement which validation would then refuse. Sliding a leaf excludes\n // that leaf's old position from the occupied stretches.\n const occupied = project.objects\n .filter(o => o.barrierId === barrier.id && o.id !== ignoreId)\n .map(o => [Math.max(0, (o.offset ?? 0) - o.width / 2), Math.min(hit.length, (o.offset ?? 0) + o.width / 2)])\n .sort((a, b) => a[0] - b[0]);\n let cursor = 0;\n const offsets: number[] = [];\n for (const [start, end] of [...occupied, [hit.length, hit.length]]) {\n if (start - cursor >= width - 1e-9)\n offsets.push(Math.max(cursor + width / 2, Math.min(start - width / 2, hit.t * hit.length)));\n cursor = Math.max(cursor, end);\n }\n if (!offsets.length) continue;\n const offset = offsets.sort((a, b) => Math.abs(a - hit.t * hit.length) - Math.abs(b - hit.t * hit.length))[0];\n const ux = (b[0] - a[0]) / hit.length,\n uy = (b[1] - a[1]) / hit.length;\n const centre: Point = [a[0] + ux * offset, a[1] + uy * offset];\n const fitDistance = distance(at, centre);\n if (fitDistance >= bestDistance) continue;\n bestDistance = fitDistance;\n best = {\n barrierId: barrier.id,\n offset,\n position: centre,\n span: [\n [centre[0] - (ux * width) / 2, centre[1] - (uy * width) / 2],\n [centre[0] + (ux * width) / 2, centre[1] + (uy * width) / 2],\n ],\n };\n }\n return best;\n}\n"],"names":["axisOf","a","b","axisDelta","from","to","d","ALIGNED","ringEdges","ring","p","i","outlines","floorOutline","project","floorId","cache","hit","rings","o","openRing","rooms","_a","roomRings","closeRing","solids","r","c","barrierEnds","len","distance","angle","rectangle","snap","pg","q","clean","polygonClipping","pointInRing","dominantAxis","edges","buckets","length","axis","bucket","x","best","mainAxis","byFloor","axisCache","key","cached"],"mappings":"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"}
|