@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.
Files changed (264) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +31 -2
  3. package/dist/adapters/basemap.js +11 -0
  4. package/dist/adapters/basemap.js.map +1 -0
  5. package/dist/adapters/persistence.js +121 -0
  6. package/dist/adapters/persistence.js.map +1 -0
  7. package/dist/adapters/status.js +18 -0
  8. package/dist/adapters/status.js.map +1 -0
  9. package/dist/components/ElevatorPanel.js +120 -0
  10. package/dist/components/ElevatorPanel.js.map +1 -0
  11. package/dist/components/Icons.js +57 -0
  12. package/dist/components/Icons.js.map +1 -0
  13. package/dist/components/Inspector.js +1184 -0
  14. package/dist/components/Inspector.js.map +1 -0
  15. package/dist/components/NavigatePanel.js +301 -0
  16. package/dist/components/NavigatePanel.js.map +1 -0
  17. package/dist/components/NavigationGraph.js +234 -0
  18. package/dist/components/NavigationGraph.js.map +1 -0
  19. package/dist/components/StructureExplorer.js +510 -0
  20. package/dist/components/StructureExplorer.js.map +1 -0
  21. package/dist/components/StructureView.js +286 -0
  22. package/dist/components/StructureView.js.map +1 -0
  23. package/dist/components/controls.js +170 -0
  24. package/dist/components/controls.js.map +1 -0
  25. package/dist/components/graphLayout.js +27 -0
  26. package/dist/components/graphLayout.js.map +1 -0
  27. package/dist/host.js +169 -0
  28. package/dist/host.js.map +1 -0
  29. package/dist/i18n.js +88 -0
  30. package/dist/i18n.js.map +1 -0
  31. package/dist/index.js +171 -0
  32. package/dist/index.js.map +1 -0
  33. package/dist/map/CabinMaterials.js +81 -0
  34. package/dist/map/CabinMaterials.js.map +1 -0
  35. package/dist/map/CabinMirror.js +43 -0
  36. package/dist/map/CabinMirror.js.map +1 -0
  37. package/dist/map/Doors.js +80 -0
  38. package/dist/map/Doors.js.map +1 -0
  39. package/dist/map/ElevatorCabin.js +93 -0
  40. package/dist/map/ElevatorCabin.js.map +1 -0
  41. package/dist/map/FixtureLights.js +117 -0
  42. package/dist/map/FixtureLights.js.map +1 -0
  43. package/dist/map/MapCanvas.js +2161 -0
  44. package/dist/map/MapCanvas.js.map +1 -0
  45. package/dist/map/Passenger.js +69 -0
  46. package/dist/map/Passenger.js.map +1 -0
  47. package/dist/map/SceneLayer.js +1534 -0
  48. package/dist/map/SceneLayer.js.map +1 -0
  49. package/dist/map/UndergroundContext.js +153 -0
  50. package/dist/map/UndergroundContext.js.map +1 -0
  51. package/dist/map/ambience.js +194 -0
  52. package/dist/map/ambience.js.map +1 -0
  53. package/dist/map/architecture.js +305 -0
  54. package/dist/map/architecture.js.map +1 -0
  55. package/dist/map/bathMusic.js +92 -0
  56. package/dist/map/bathMusic.js.map +1 -0
  57. package/dist/map/cameraAim.js +33 -0
  58. package/dist/map/cameraAim.js.map +1 -0
  59. package/dist/map/elevators.js +56 -0
  60. package/dist/map/elevators.js.map +1 -0
  61. package/dist/map/excavation.js +52 -0
  62. package/dist/map/excavation.js.map +1 -0
  63. package/dist/map/exteriors.js +25 -0
  64. package/dist/map/exteriors.js.map +1 -0
  65. package/dist/map/features.js +218 -0
  66. package/dist/map/features.js.map +1 -0
  67. package/dist/map/fences.js +29 -0
  68. package/dist/map/fences.js.map +1 -0
  69. package/dist/map/journey.js +112 -0
  70. package/dist/map/journey.js.map +1 -0
  71. package/dist/map/levels.js +6 -0
  72. package/dist/map/levels.js.map +1 -0
  73. package/dist/map/lighting.js +142 -0
  74. package/dist/map/lighting.js.map +1 -0
  75. package/dist/map/loadBasemap.js +26 -0
  76. package/dist/map/loadBasemap.js.map +1 -0
  77. package/dist/map/materials.js +183 -0
  78. package/dist/map/materials.js.map +1 -0
  79. package/dist/map/poolLighting.js +39 -0
  80. package/dist/map/poolLighting.js.map +1 -0
  81. package/dist/map/projection.js +24 -0
  82. package/dist/map/projection.js.map +1 -0
  83. package/dist/map/rampGeometry.js +38 -0
  84. package/dist/map/rampGeometry.js.map +1 -0
  85. package/dist/map/route.js +107 -0
  86. package/dist/map/route.js.map +1 -0
  87. package/dist/map/stairSurfaces.js +115 -0
  88. package/dist/map/stairSurfaces.js.map +1 -0
  89. package/dist/map/surfaces.js +136 -0
  90. package/dist/map/surfaces.js.map +1 -0
  91. package/dist/map/textures.js +95 -0
  92. package/dist/map/textures.js.map +1 -0
  93. package/dist/map/underground.js +34 -0
  94. package/dist/map/underground.js.map +1 -0
  95. package/dist/map/walk.js +433 -0
  96. package/dist/map/walk.js.map +1 -0
  97. package/dist/map/walkContext.js +46 -0
  98. package/dist/map/walkContext.js.map +1 -0
  99. package/dist/map/walkDimensions.js +7 -0
  100. package/dist/map/walkDimensions.js.map +1 -0
  101. package/dist/map/walkJourney.js +85 -0
  102. package/dist/map/walkJourney.js.map +1 -0
  103. package/dist/map/walkSurfaces.js +106 -0
  104. package/dist/map/walkSurfaces.js.map +1 -0
  105. package/dist/map/wallJoins.js +70 -0
  106. package/dist/map/wallJoins.js.map +1 -0
  107. package/dist/map/water.js +274 -0
  108. package/dist/map/water.js.map +1 -0
  109. package/dist/model/authoring.js +80 -0
  110. package/dist/model/authoring.js.map +1 -0
  111. package/dist/model/boundaries.js +548 -0
  112. package/dist/model/boundaries.js.map +1 -0
  113. package/dist/model/coverage.js +30 -0
  114. package/dist/model/coverage.js.map +1 -0
  115. package/dist/model/document.js +52 -0
  116. package/dist/model/document.js.map +1 -0
  117. package/dist/model/doors.js +47 -0
  118. package/dist/model/doors.js.map +1 -0
  119. package/dist/model/factory.js +56 -0
  120. package/dist/model/factory.js.map +1 -0
  121. package/dist/model/geometry.js +394 -0
  122. package/dist/model/geometry.js.map +1 -0
  123. package/dist/model/inference.js +182 -0
  124. package/dist/model/inference.js.map +1 -0
  125. package/dist/model/materials.js +11 -0
  126. package/dist/model/materials.js.map +1 -0
  127. package/dist/model/mutations.js +157 -0
  128. package/dist/model/mutations.js.map +1 -0
  129. package/dist/model/navigation.js +275 -0
  130. package/dist/model/navigation.js.map +1 -0
  131. package/dist/model/ontology.js +109 -0
  132. package/dist/model/ontology.js.map +1 -0
  133. package/dist/model/passages.js +5 -0
  134. package/dist/model/passages.js.map +1 -0
  135. package/dist/model/portals.js +42 -0
  136. package/dist/model/portals.js.map +1 -0
  137. package/dist/model/precision.js +11 -0
  138. package/dist/model/precision.js.map +1 -0
  139. package/dist/model/project.js +17 -0
  140. package/dist/model/project.js.map +1 -0
  141. package/dist/model/ramps.js +10 -0
  142. package/dist/model/ramps.js.map +1 -0
  143. package/dist/model/roomNavigation.js +87 -0
  144. package/dist/model/roomNavigation.js.map +1 -0
  145. package/dist/model/routeClearance.js +72 -0
  146. package/dist/model/routeClearance.js.map +1 -0
  147. package/dist/model/spaces.js +104 -0
  148. package/dist/model/spaces.js.map +1 -0
  149. package/dist/model/stairGeometry.js +40 -0
  150. package/dist/model/stairGeometry.js.map +1 -0
  151. package/dist/model/stairTraversal.js +48 -0
  152. package/dist/model/stairTraversal.js.map +1 -0
  153. package/dist/model/topology.js +63 -0
  154. package/dist/model/topology.js.map +1 -0
  155. package/dist/model/types.js +35 -0
  156. package/dist/model/types.js.map +1 -0
  157. package/dist/model/validate.js +240 -0
  158. package/dist/model/validate.js.map +1 -0
  159. package/dist/model/vertical.js +129 -0
  160. package/dist/model/vertical.js.map +1 -0
  161. package/dist/model/walls.js +71 -0
  162. package/dist/model/walls.js.map +1 -0
  163. package/dist/styles.css +4931 -0
  164. package/dist/theme/index.js +34 -0
  165. package/dist/theme/index.js.map +1 -0
  166. package/dist/types/adapters/basemap.d.ts +2 -0
  167. package/dist/types/adapters/persistence.d.ts +29 -0
  168. package/dist/types/adapters/status.d.ts +4 -0
  169. package/dist/types/components/ElevatorPanel.d.ts +18 -0
  170. package/dist/types/components/Icons.d.ts +8 -0
  171. package/dist/types/components/Inspector.d.ts +38 -0
  172. package/dist/types/components/NavigatePanel.d.ts +27 -0
  173. package/dist/types/components/NavigationGraph.d.ts +10 -0
  174. package/dist/types/components/StructureExplorer.d.ts +8 -0
  175. package/dist/types/components/StructureView.d.ts +22 -0
  176. package/dist/types/components/controls.d.ts +52 -0
  177. package/dist/types/components/graphLayout.d.ts +9 -0
  178. package/dist/types/i18n.d.ts +85 -0
  179. package/dist/types/import/analysisPreview.d.ts +27 -0
  180. package/dist/types/import/brief.d.ts +22 -0
  181. package/dist/types/import/checkpoint.d.ts +49 -0
  182. package/dist/types/import/instructions.d.ts +7 -0
  183. package/dist/types/import/usage.d.ts +10 -0
  184. package/dist/types/map/CabinMaterials.d.ts +18 -0
  185. package/dist/types/map/CabinMirror.d.ts +13 -0
  186. package/dist/types/map/Doors.d.ts +11 -0
  187. package/dist/types/map/ElevatorCabin.d.ts +20 -0
  188. package/dist/types/map/FixtureLights.d.ts +35 -0
  189. package/dist/types/map/MapCanvas.d.ts +87 -0
  190. package/dist/types/map/Passenger.d.ts +18 -0
  191. package/dist/types/map/SceneLayer.d.ts +306 -0
  192. package/dist/types/map/UndergroundContext.d.ts +18 -0
  193. package/dist/types/map/ambience.d.ts +29 -0
  194. package/dist/types/map/architecture.d.ts +8 -0
  195. package/dist/types/map/bathMusic.d.ts +14 -0
  196. package/dist/types/map/cameraAim.d.ts +20 -0
  197. package/dist/types/map/elevators.d.ts +16 -0
  198. package/dist/types/map/excavation.d.ts +9 -0
  199. package/dist/types/map/exteriors.d.ts +3 -0
  200. package/dist/types/map/features.d.ts +33 -0
  201. package/dist/types/map/fences.d.ts +4 -0
  202. package/dist/types/map/journey.d.ts +36 -0
  203. package/dist/types/map/levels.d.ts +4 -0
  204. package/dist/types/map/lighting.d.ts +103 -0
  205. package/dist/types/map/loadBasemap.d.ts +4 -0
  206. package/dist/types/map/materials.d.ts +38 -0
  207. package/dist/types/map/poolLighting.d.ts +11 -0
  208. package/dist/types/map/projection.d.ts +9 -0
  209. package/dist/types/map/rampGeometry.d.ts +8 -0
  210. package/dist/types/map/route.d.ts +59 -0
  211. package/dist/types/map/stairSurfaces.d.ts +29 -0
  212. package/dist/types/map/surfaces.d.ts +36 -0
  213. package/dist/types/map/textures.d.ts +8 -0
  214. package/dist/types/map/underground.d.ts +30 -0
  215. package/dist/types/map/walk.d.ts +232 -0
  216. package/dist/types/map/walkContext.d.ts +11 -0
  217. package/dist/types/map/walkDimensions.d.ts +5 -0
  218. package/dist/types/map/walkJourney.d.ts +33 -0
  219. package/dist/types/map/walkSurfaces.d.ts +27 -0
  220. package/dist/types/map/wallJoins.d.ts +6 -0
  221. package/dist/types/map/water.d.ts +12 -0
  222. package/dist/types/model/authoring.d.ts +38 -0
  223. package/dist/types/model/boundaries.d.ts +41 -0
  224. package/dist/types/model/coverage.d.ts +8 -0
  225. package/dist/types/model/document.d.ts +9 -0
  226. package/dist/types/model/doors.d.ts +19 -0
  227. package/dist/types/model/factory.d.ts +5 -0
  228. package/dist/types/model/geometry.d.ts +99 -0
  229. package/dist/types/model/host.d.ts +154 -0
  230. package/dist/types/model/inference.d.ts +46 -0
  231. package/dist/types/model/live.d.ts +60 -0
  232. package/dist/types/model/materials.d.ts +28 -0
  233. package/dist/types/model/mutations.d.ts +178 -0
  234. package/dist/types/model/navigation.d.ts +70 -0
  235. package/dist/types/model/ontology.d.ts +43 -0
  236. package/dist/types/model/passages.d.ts +2 -0
  237. package/dist/types/model/portals.d.ts +9 -0
  238. package/dist/types/model/precision.d.ts +11 -0
  239. package/dist/types/model/project.d.ts +8 -0
  240. package/dist/types/model/ramps.d.ts +7 -0
  241. package/dist/types/model/roomNavigation.d.ts +15 -0
  242. package/dist/types/model/routeClearance.d.ts +11 -0
  243. package/dist/types/model/spaces.d.ts +46 -0
  244. package/dist/types/model/stairGeometry.d.ts +27 -0
  245. package/dist/types/model/stairTraversal.d.ts +8 -0
  246. package/dist/types/model/topology.d.ts +14 -0
  247. package/dist/types/model/types.d.ts +369 -0
  248. package/dist/types/model/validate.d.ts +38 -0
  249. package/dist/types/model/vertical.d.ts +145 -0
  250. package/dist/types/model/walls.d.ts +71 -0
  251. package/dist/types/schema/index.d.ts +25 -0
  252. package/dist/types/theme/index.d.ts +55 -0
  253. package/dist/types/viewer/FloorViewer.d.ts +76 -0
  254. package/dist/types/viewer/ViewerWorkspace.d.ts +2 -0
  255. package/dist/types/viewer/host.d.ts +11 -0
  256. package/dist/types/viewer/index.d.ts +2 -0
  257. package/dist/types/viewer/panels.d.ts +12 -0
  258. package/dist/viewer/FloorViewer.js +104 -0
  259. package/dist/viewer/FloorViewer.js.map +1 -0
  260. package/dist/viewer/ViewerWorkspace.js +370 -0
  261. package/dist/viewer/ViewerWorkspace.js.map +1 -0
  262. package/dist/viewer/panels.js +35 -0
  263. package/dist/viewer/panels.js.map +1 -0
  264. package/package.json +52 -4
@@ -0,0 +1,369 @@
1
+ import type { ExteriorPreset } from './materials';
2
+ export type Point = [number, number];
3
+ /** Geographic anchor of the local tangent frame — [lng, lat] in WGS84 degrees; an optional third
4
+ * element rotates the site clockwise from true north (degrees). */
5
+ export type Origin = [number, number] | [number, number, number];
6
+ export type Ring = Point[];
7
+ /** The built-in taxonomy of drawable things, spanning the toolkit's use-cases (spaces, openings,
8
+ * devices, logistics, safety). It is intentionally broad rather than tied to one domain; model
9
+ * host-specific subtypes (e.g. a turnstile variety, a sensor class) as `category`/`metadata` on the
10
+ * object rather than by extending this union. Runtime tuple so the type and the document validator
11
+ * share one source of truth. */
12
+ export declare const OBJECT_KINDS: readonly ["zone", "room", "parcel", "building", "office", "container", "storage", "door", "window", "gate", "turnstile", "reader", "camera", "elevator", "stairs", "poi", "fixture", "light", "landscape", "sensor", "alarm", "equipment", "evacuation"];
13
+ export type ObjectKind = (typeof OBJECT_KINDS)[number];
14
+ export type MaterialKind = 'brick' | 'stone' | 'plaster' | 'timber' | 'oak' | 'tile' | 'terrazzo' | 'grass' | 'paving' | 'carpet' | 'wallpaper';
15
+ export type ModelKind = 'bed' | 'sofa' | 'dining' | 'cabinet' | 'counter' | 'toilet' | 'basin' | 'shower' | 'sauna' | 'tree' | 'shrub' | 'lamp' | 'car' | 'chimney' | 'vent' | 'post';
16
+ export interface RoofSection {
17
+ footprint: Ring;
18
+ ridge: [Point, Point];
19
+ eave: number;
20
+ peak: number;
21
+ }
22
+ export interface BuildingRoof {
23
+ floorId: string;
24
+ color: string;
25
+ sections: RoofSection[];
26
+ }
27
+ /** mezzanine marks an intermediate (entresol) level: the plan draws it in context of the levels around it. */
28
+ export interface Floor {
29
+ id: string;
30
+ buildingId: string;
31
+ name: string;
32
+ elevation: number;
33
+ height: number;
34
+ mezzanine?: boolean;
35
+ code?: string;
36
+ /** The artificial light this level burns — colour temperature and how brightly. Absent means a
37
+ * fluorescent ceiling at working brightness, which is what is actually overhead in most of the
38
+ * buildings anyone draws. A building's own lighting does not follow the sun, so this is what
39
+ * keeps a storey legible in a plan opened after dark. */
40
+ light?: InteriorLight;
41
+ /** What this level sounds like from inside it — the default for every space on it that does not
42
+ * say otherwise. Absent is silence. */
43
+ ambience?: Ambience;
44
+ }
45
+ /** The sound of a space, heard at eye level: a named soundscape or musical preset,
46
+ * and how loud, 0 to 1 (absent is 1). Kept in
47
+ * the document rather than the renderer for the same reason light is: what a room sounds like
48
+ * describes the room. */
49
+ export interface Ambience {
50
+ preset: AmbiencePreset;
51
+ level?: number;
52
+ }
53
+ export type AmbiencePreset = 'silent' | 'office' | 'backrooms' | 'plant' | 'baths' | 'elevator';
54
+ export declare const AMBIENCE_PRESETS: AmbiencePreset[];
55
+ /** Colour temperature in kelvin (2700 tungsten, 4000 fluorescent, 6500 daylight) and how brightly,
56
+ * 0 to 1. Kept here rather than in the renderer: what a space is lit by describes the space. */
57
+ export interface InteriorLight {
58
+ kelvin: number;
59
+ level: number;
60
+ /** Optional colour cast from the fittings or reflected surroundings. */
61
+ tint?: string;
62
+ }
63
+ /** A ceiling luminaire. Object height is the mounting height above the floor, intensity is candela,
64
+ * range is metres, and flicker is the optional 0..1 depth of intermittent ballast dimming. */
65
+ export interface LightFixture {
66
+ /** Optional mounting elevation above the floor; negative values place a lamp inside a pool. */
67
+ mountHeight?: number;
68
+ kelvin: number;
69
+ intensity: number;
70
+ range: number;
71
+ flicker?: number;
72
+ }
73
+ export interface Building {
74
+ id: string;
75
+ name: string;
76
+ roof?: BuildingRoof;
77
+ /** Provenance id of the basemap footprint a building was adopted from — the value of the host's
78
+ * configured footprints.idField (a string or number, depending on the tile source); absent for
79
+ * hand-drawn buildings. */
80
+ sourceId?: string | number;
81
+ exteriorPreset?: ExteriorPreset;
82
+ }
83
+ export interface Junction {
84
+ id: string;
85
+ floorId: string | null;
86
+ position: Point;
87
+ }
88
+ /** An unwalled space boundary. Physical boundaries use Barrier IDs and endpoints directly. */
89
+ export interface VirtualBoundary {
90
+ id: string;
91
+ floorId: string | null;
92
+ startId: string;
93
+ endId: string;
94
+ }
95
+ export interface BoundaryUse {
96
+ edgeId: string;
97
+ /** Traverse from endId to startId. Absent means startId to endId. */
98
+ reversed?: boolean;
99
+ }
100
+ export type SpaceGeometry = {
101
+ mode: 'independent';
102
+ } | {
103
+ mode: 'boundaries';
104
+ loops: BoundaryUse[][];
105
+ };
106
+ export interface Barrier {
107
+ id: string;
108
+ floorId: string | null;
109
+ startId: string;
110
+ endId: string;
111
+ kind: 'wall' | 'fence';
112
+ thickness: number;
113
+ height: number;
114
+ name: string;
115
+ color?: string;
116
+ material?: MaterialKind;
117
+ category?: string;
118
+ metadata?: Record<string, unknown>;
119
+ }
120
+ /** A sloped area: the footprint falls linearly from `high` at `axis[0]` to `low` at `axis[1]`, and
121
+ * stays flat beyond either end — so a footprint longer than its axis keeps level aprons where it
122
+ * meets the decks it joins. Elevations are absolute metres in the same frame as Floor.elevation, so a
123
+ * ramp can span floors (a garage ramp, a loading incline, a sloped plaza). Any area object may carry
124
+ * one; without it an area is flat. The axis always runs downhill: `high` >= `low`. */
125
+ export interface Slope {
126
+ axis: [Point, Point];
127
+ high: number;
128
+ low: number;
129
+ }
130
+ export interface SiteObject {
131
+ id: string;
132
+ kind: ObjectKind;
133
+ floorId: string | null;
134
+ name: string;
135
+ position: Point;
136
+ rotation: number;
137
+ rings?: Ring[];
138
+ /** Boundary-driven spaces keep rings and dimensions as a validated, generated cache.
139
+ * Absent is an independent outline, preserving existing/imported projects without guessing. */
140
+ geometry?: SpaceGeometry;
141
+ /** Makes this area a sloped plane rather than a flat plate; see Slope. */
142
+ slope?: Slope;
143
+ /** Clear ceiling height above this floor's datum; omitted uses the storey height.
144
+ * A room spanning storeys uses this with holes in the plates above it. */
145
+ ceilingHeight?: number;
146
+ /** A basin below this area's walking surface, filled with clear water. */
147
+ water?: {
148
+ depth: number;
149
+ ripple?: number;
150
+ };
151
+ /** Open water-slide centreline, in metres relative to the object's position and rotation.
152
+ * The third coordinate is height above its floor. */
153
+ slide?: {
154
+ path: [number, number, number][];
155
+ radius: number;
156
+ };
157
+ /** Bottom of a raised fixture, metres above the floor surface (e.g. a doorway lintel). */
158
+ baseHeight?: number;
159
+ width: number;
160
+ depth: number;
161
+ height: number;
162
+ parentId?: string;
163
+ barrierId?: string;
164
+ offset?: number;
165
+ /** Door hinge at the wall start (left, default) or end (right). */
166
+ doorHinge?: 'left' | 'right';
167
+ /** Door mechanism. Omitted means a conventional single hinged leaf. */
168
+ doorType?: 'hinged' | 'sliding' | 'double';
169
+ /** Door opens to the left (+1, default) or right (-1) of the directed wall. */
170
+ doorSwing?: 1 | -1;
171
+ feedId?: string;
172
+ symbol?: 'personnel' | 'service' | 'driveway' | 'parking' | 'assembly' | 'info' | 'aed' | 'extinguisher' | 'firstaid' | 'exit' | 'firealarm';
173
+ servedFloorIds?: string[];
174
+ /** Stairs only: which geometry this climb has.
175
+ *
176
+ * `straight` is one run. `switchback` turns back on itself around a half-landing — the half-turn
177
+ * stair that fits a storey into a short core box, and what most building stairs actually are.
178
+ * `dogleg` turns a quarter at its landing, for a stair in the corner of a plan. `spiral` winds
179
+ * around a pole and can land on every level it passes. `escalator` is a machine: a fixed 30° pitch
180
+ * and a flat comb plate at each end.
181
+ *
182
+ * All of them connect the same floors and route identically, which is why this is one field rather
183
+ * than five kinds — the difference is what it looks like and how much run it needs. Absent picks
184
+ * the shape that fits: a straight run where the footprint allows a civil pitch, a switchback where
185
+ * it does not, since a plan that gives a 4.5 m box to a 4.4 m storey means a stair that turns, not
186
+ * a ladder. */
187
+ stairModel?: 'straight' | 'switchback' | 'dogleg' | 'spiral' | 'escalator';
188
+ /** Stairs with the same group on a level share one rectangular floor/ceiling opening. */
189
+ wellGroup?: string;
190
+ /** Escalators only: which way the machine carries you.
191
+ *
192
+ * It is a property of the machine, not of the drawing and not of its name: an escalator runs one
193
+ * way, a bank of them runs alternate ways, and which way this one runs is the first thing anyone
194
+ * asks of it. The same vocabulary as `Zone.connects`, so the described layer and the object never
195
+ * need translating between them. Absent means up, which is what a lone escalator nearly always
196
+ * does. A stair that is not an escalator ignores it — a stair carries you both ways.
197
+ *
198
+ * An imported escalator arrives once per storey it is drawn on; the twin standing lowest is the
199
+ * one the model draws and routes from, so that is the one to author. */
200
+ travel?: 'up' | 'down';
201
+ /** Elevators only: which faces of the car open, relative to the object's own rotation. A through-car
202
+ * opens front and back; a corner lobby car opens front and one side. Absent means the front alone,
203
+ * which is what almost every lift does. Which FLOORS it opens on is `servedFloorIds` — a level the
204
+ * shaft passes without serving has no doors on any side. */
205
+ doorSides?: ('front' | 'back' | 'left' | 'right')[];
206
+ coverageAngle?: number;
207
+ coverageRange?: number;
208
+ color?: string;
209
+ /** Camera: ids of the doors, rooms and elevators this camera observes (event footage lookup). */
210
+ watchedIds?: string[];
211
+ material?: MaterialKind;
212
+ model?: ModelKind;
213
+ light?: LightFixture;
214
+ /** A space's own sound, overriding its floor's. See Floor.ambience. */
215
+ ambience?: Ambience;
216
+ /** Host-defined subtype/category (e.g. 'meeting-room', 'speedgate', 'cold-store') for styling, filtering and event handlers. */
217
+ category?: string;
218
+ /** Arbitrary host metadata; ignored by the core, available to style hooks and event handlers. */
219
+ metadata?: Record<string, unknown>;
220
+ }
221
+ export interface Drawing {
222
+ id: string;
223
+ floorId: string | null;
224
+ assetId: string;
225
+ name: string;
226
+ width: number;
227
+ height: number;
228
+ origin: Point;
229
+ scale: number;
230
+ rotation: number;
231
+ opacity: number;
232
+ visible: boolean;
233
+ locked: boolean;
234
+ }
235
+ /** A named set of spaces — the area objects listed in `spaceIds`.
236
+ *
237
+ * A zone is a *membership*, not a shape: it has no geometry of its own, which is exactly what lets it
238
+ * span floors, skip buildings, and overlap other zones freely. (Contrast `SiteObject.parentId`, which
239
+ * is the strict single-parent, same-floor, geometrically-contained tree.) A lift shaft is a zone of
240
+ * its per-floor lift spaces; a security area is a zone of the rooms inside it. */
241
+ export interface Zone {
242
+ id: string;
243
+ name: string;
244
+ spaceIds: string[];
245
+ /** Zones may contain zones: a DAG, not a tree. Cycles are rejected; several parents are fine.
246
+ * Membership does NOT imply access inheritance — that is policy, and policy is the host's. */
247
+ childZoneIds?: string[];
248
+ /** How the spaces within this zone reach each other, when they are not joined by portals.
249
+ * 'all' — every space mutually reachable: a lift serving all of its landings.
250
+ * 'adjacent' — reachable in elevation order, both ways: a stair, passing each level in turn.
251
+ * 'up'/'down'— adjacent but one-way: an escalator, which carries you in a single direction.
252
+ * omitted — no implied connectivity; the zone is a grouping only. */
253
+ connects?: 'all' | 'adjacent' | 'up' | 'down';
254
+ /** Host vocabulary ('security', 'evacuation', 'hvac', …). The core never interprets it. */
255
+ purpose?: string;
256
+ metadata?: Record<string, unknown>;
257
+ }
258
+ /** A traversable connection between exactly two spaces.
259
+ *
260
+ * Direction is deliberately not baked in: a door's entry and exit sides are relative to whichever
261
+ * area you are asking about, so the two sides are stored symmetrically and direction is derived
262
+ * (see `entryInto`). Applications that attach a rule per direction of travel split a two-way door into
263
+ * two objects; that is the right shape for an application layer and the wrong shape for a topology one. */
264
+ export interface Portal {
265
+ id: string;
266
+ /** The opening object (door/gate/turnstile) crossed here; absent for a virtual portal — an open
267
+ * boundary between two spaces with no physical separator, which malls and open-plan floors need.
268
+ * Kept as a reference rather than folded in, so two portals can be known to share one door leaf. */
269
+ openingId?: string;
270
+ /** The two sides. Order is storage, not meaning — ask `entryInto` rather than reading it. */
271
+ a: string;
272
+ b: string;
273
+ /** Which way passage is possible at all. Default 'both'. */
274
+ passage?: 'both' | 'a-to-b' | 'b-to-a' | 'none';
275
+ /** How far a crossing here can be trusted to have actually happened.
276
+ * 'confirmed' — a sensor saw it (a door monitor opening and closing).
277
+ * 'assumed' — inferred from a grant; nobody watched. The default for a plain door.
278
+ * 'none' — the crossing is a *request*, not an event: a lift floor button, where the
279
+ * passenger may go anywhere. Anything counting crossings must ignore these. */
280
+ attests?: 'confirmed' | 'assumed' | 'none';
281
+ name?: string;
282
+ metadata?: Record<string, unknown>;
283
+ }
284
+ /** An explicit set of portals, for sets that are not a zone boundary — "all loading-bay shutters",
285
+ * "every door with a reader". Where a set *is* a zone boundary, derive it instead: see `perimeter`. */
286
+ export interface PortalGroup {
287
+ id: string;
288
+ name: string;
289
+ portalIds: string[];
290
+ metadata?: Record<string, unknown>;
291
+ }
292
+ /** Authored route-graph node for indoor navigation. objectId binds it to a SiteObject (door, elevator, stairs, room, poi) so routing can anchor at that object and instructions can name it. */
293
+ export interface NavNode {
294
+ id: string;
295
+ floorId: string | null;
296
+ position: Point;
297
+ objectId?: string;
298
+ }
299
+ export type NavEdgeKind = 'walk' | 'door' | 'stairs' | 'elevator';
300
+ /** Undirected route-graph edge. walk stays on one floor; door stays on one floor or steps outside (one end floorId null); stairs/elevator join different floors and bind the serving object via objectId. weight overrides the derived metres-equivalent cost. */
301
+ export interface NavEdge {
302
+ id: string;
303
+ kind: NavEdgeKind;
304
+ aId: string;
305
+ bId: string;
306
+ /** Passable from `aId` to `bId` only. Escalators and one-way doors; absent means both ways. */
307
+ directed?: boolean;
308
+ objectId?: string;
309
+ weight?: number;
310
+ }
311
+ export interface ProjectDocument {
312
+ schemaVersion: 1;
313
+ id: string;
314
+ name: string;
315
+ description: string;
316
+ updatedAt: string;
317
+ origin: Origin;
318
+ datum: string;
319
+ buildings: Building[];
320
+ floors: Floor[];
321
+ junctions: Junction[];
322
+ barriers: Barrier[];
323
+ virtualBoundaries?: VirtualBoundary[];
324
+ objects: SiteObject[];
325
+ drawings: Drawing[];
326
+ referenceNote?: string;
327
+ /** The floor a viewer should open on — the document's own front door, so a project that is really
328
+ * about its offices does not always land on the street. `null` means the outdoor site; omit it
329
+ * entirely to let the viewer pick (ground floor, else the lowest). Hosts can still override per
330
+ * session via ViewState.floor / FloorViewer's `floorId`. */
331
+ initialFloorId?: string | null;
332
+ /** The spatial ontology layered over the geometry: which spaces group together, and what connects
333
+ * to what. Spaces themselves are the ringed area objects in `objects` — nothing is duplicated. */
334
+ zones?: Zone[];
335
+ portals?: Portal[];
336
+ portalGroups?: PortalGroup[];
337
+ /** Optional authored navigation graph. When absent, the graph is derived from spaces and portals
338
+ * (see model/topology.ts) — the plan is then the single source of truth and cannot drift out of
339
+ * step with a hand-kept copy of itself. */
340
+ navNodes?: NavNode[];
341
+ navEdges?: NavEdge[];
342
+ }
343
+ export interface ProjectSummary {
344
+ id: string;
345
+ name: string;
346
+ updatedAt: string;
347
+ }
348
+ export interface ProjectRepository {
349
+ list(): Promise<ProjectSummary[]>;
350
+ load(id: string): Promise<ProjectDocument | null>;
351
+ save(project: ProjectDocument): Promise<void>;
352
+ delete(id: string): Promise<void>;
353
+ }
354
+ export interface AssetRepository {
355
+ get(id: string): Promise<Blob | undefined>;
356
+ put(id: string, asset: Blob): Promise<void>;
357
+ delete(id: string): Promise<void>;
358
+ }
359
+ export type Tool = 'select' | 'pan' | 'wall' | 'boundary' | 'partition' | 'fence' | 'zone' | 'room' | 'rectangle' | 'enclose' | 'hole' | 'door' | 'window' | 'gate' | 'turnstile' | 'reader' | 'camera' | 'elevator' | 'stairs' | 'office' | 'container' | 'storage' | 'poi' | 'measure' | 'adopt' | 'split' | 'route' | 'sensor' | 'light' | 'alarm' | 'equipment' | 'evacuation';
360
+ /** A fresh entity id. A bare UUIDv4 (no type prefix) so ids drop straight into a database `uuid`
361
+ * column and validate there; the entity's type is already given by which collection it lives in. */
362
+ export declare const uid: () => `${string}-${string}-${string}-${string}-${string}`;
363
+ export declare const isArea: (kind: ObjectKind) => boolean;
364
+ export declare const isDevice: (kind: ObjectKind) => boolean;
365
+ export declare const isOpening: (kind: ObjectKind) => kind is "door" | "window" | "gate";
366
+ /** A *space* is somewhere you can stand — the unit the ontology groups and connects. Areas qualify,
367
+ * and so do lifts and stairs: the inside of a car is as much a place as the lobby it opens onto, and
368
+ * the through-car case depends on being able to say so. */
369
+ export declare const isSpace: (kind: ObjectKind) => boolean;
@@ -0,0 +1,38 @@
1
+ import type { ProjectDocument, Ring } from './types';
2
+ /** How far from the site origin a coordinate may sit, in metres. A guard against corrupt data, not a
3
+ * modelling limit: no site is 100 km across, but a NaN that became 1e15 through arithmetic would
4
+ * otherwise sail through every finite() check and break projection math much later and much worse. */
5
+ export declare const COORD_LIMIT = 100000;
6
+ /** Geometric sanity of an area's rings: a real outer boundary, no self-crossings, holes inside and
7
+ * disjoint. Returns an error message or null. */
8
+ export declare function validateRings(rings: Ring[], minEdge?: number, minArea?: number): string | null;
9
+ /** Spatial relationships between entities: ring sanity per object, parent containment without
10
+ * cycles, and openings that actually fit the barriers they sit in. */
11
+ export declare function validateRelationships(project: ProjectDocument): string | null;
12
+ /** Semantic validation of the navigation graph — authored or derived; structural checks (ids,
13
+ * uniqueness) live in validateProject. Returns an error message or null. */
14
+ export declare function validateNavigation(p: ProjectDocument): string | null;
15
+ /** Parse and validate an unknown value into a ProjectDocument; throws with a reason on any failure.
16
+ * Every rule the schema has lives behind this one entry point. */
17
+ export declare function validateProject(value: unknown): ProjectDocument;
18
+ /** Deep-freeze a document in place and return it. Writes to a frozen document throw (modules run in
19
+ * strict mode), so an out-of-band mutation — one that dodged `transact` — fails at the assignment
20
+ * that would have corrupted the document, not three renders later. */
21
+ export declare function freezeProject(project: ProjectDocument): ProjectDocument;
22
+ export type TransactResult = {
23
+ ok: true;
24
+ project: ProjectDocument;
25
+ } | {
26
+ ok: false;
27
+ error: string;
28
+ };
29
+ /** Apply a change atomically: work on a clone, run every rule, and only then hand the result back —
30
+ * frozen, so nothing can edit it except the next transaction. If the change throws, or the outcome
31
+ * breaks any rule, the original is untouched and the reason comes back as `error`; there is no
32
+ * state in which a caller holds a partly-applied or invalid document.
33
+ *
34
+ * `updatedAt` is deliberately not stamped here — that belongs to whoever owns persistence and
35
+ * history (see commitHistory), which also stamps undo and redo. */
36
+ export declare function transact(project: ProjectDocument, change: (draft: ProjectDocument) => void, options?: {
37
+ freeze?: boolean;
38
+ }): TransactResult;
@@ -0,0 +1,145 @@
1
+ import type { Floor, Point, ProjectDocument, Ring, SiteObject } from './types';
2
+ /** True for the kinds that connect levels. */
3
+ export declare const isVertical: (kind: string) => kind is "elevator" | "stairs";
4
+ /** Which shaft this object is part of.
5
+ *
6
+ * A shaft is one thing standing in one place reaching several levels, and that is how the document
7
+ * can describe it: one object, one position, one `servedFloorIds`. Older documents — the demo among
8
+ * them — instead repeat the object on every storey it serves, because that is what you had to do
9
+ * before anything read the served list. Both must draw as one lift, so a shaft is identified by what
10
+ * makes it the same shaft: its kind, its name, and where it stands. */
11
+ export declare const shaftKey: (object: SiteObject) => string;
12
+ /** Every level this shaft reaches, lowest first.
13
+ *
14
+ * `servedFloorIds` when it is authored — that is the document's own statement and outranks any
15
+ * inference. A shaft that declares nothing serves the floor it stands on and no other: better to
16
+ * draw a stair going nowhere, which is visibly wrong and easy to fix, than to invent a connection
17
+ * that routing would then happily send someone through. */
18
+ export declare function servedFloors(project: ProjectDocument, object: SiteObject): Floor[];
19
+ /** One climb between two levels the shaft serves. */
20
+ export interface Flight {
21
+ from: Floor;
22
+ to: Floor;
23
+ /** Metres climbed, always positive. */
24
+ rise: number;
25
+ }
26
+ /** The climbs this shaft makes, bottom first.
27
+ *
28
+ * Consecutive served levels, so a stair serving 0-1-2 makes two flights and a lift serving the same
29
+ * three makes two hops of shaft. A level in between that the shaft passes without stopping is simply
30
+ * not in the list, and the flight spanning it is the taller for it — which is the right picture: an
31
+ * express lift's shaft does pass those floors, and a spiral stair's helix does wind past a mezzanine
32
+ * it has no landing on. */
33
+ export declare function flights(project: ProjectDocument, object: SiteObject): Flight[];
34
+ /** Does this shaft reach the given level? Includes the floor it is filed under. */
35
+ export declare const reaches: (project: ProjectDocument, object: SiteObject, floorId: string | null) => boolean;
36
+ /** The flight arriving at a level from below, and the one leaving it upward — what you would see
37
+ * standing on that level. Either may be absent at the ends of the run. */
38
+ export declare function flightsAt(project: ProjectDocument, object: SiteObject, floorId: string | null): {
39
+ up?: Flight;
40
+ down?: Flight;
41
+ };
42
+ /** How far a shaft's body runs, as absolute elevations: the lowest level it serves to the highest.
43
+ * Null when it serves fewer than two levels and so has no body to speak of. */
44
+ export declare function span(project: ProjectDocument, object: SiteObject): {
45
+ bottom: number;
46
+ top: number;
47
+ } | null;
48
+ /** The angle a flight of this kind wants to climb at, in degrees from horizontal.
49
+ *
50
+ * An escalator is a machine built to one geometry: 30°, the world over. A stair is free-er but a
51
+ * comfortable flight is around 32°, and anything past about 42° is a ladder. These are what the
52
+ * plan should be *checked against* — the footprint stays the truth of where the thing is — so a run
53
+ * too short for its rise reads as the steep slope it would really be. */
54
+ export declare const PITCH: {
55
+ readonly escalator: 30;
56
+ readonly stairs: 32;
57
+ };
58
+ /** The run a flight of this rise needs to climb at its natural pitch. Use it to size a shaft the
59
+ * drawing has not sized, and to tell whether one it has sized is plausible. */
60
+ export declare const runFor: (rise: number, pitch?: number) => number;
61
+ /** The pitch a flight actually climbs at, given the run the plan gives it. */
62
+ export declare const pitchOf: (rise: number, run: number) => number;
63
+ /** The deepest tread that is still a tread. Past about a third of a metre a flight stops being a
64
+ * stair: the going grows with the box while the rise per step does not, and what you get is a ramp
65
+ * with slabs laid on it. A box longer than the climb needs keeps its length — the surplus is
66
+ * landing, which is what that spare floor really is. */
67
+ export declare const MAX_GOING = 0.32;
68
+ /** The treads one flight of this rise gets, and how deep each is in a run of this length. Capped
69
+ * both ways: a handful of boxes reads as a stair, two hundred reads as a stair and costs a level. */
70
+ export declare function treads(rise: number, run: number): {
71
+ steps: number;
72
+ going: number;
73
+ };
74
+ export type StairModel = NonNullable<SiteObject['stairModel']>;
75
+ /** How a shaft is built — as the document says, or as its own footprint implies.
76
+ *
77
+ * A footprint that cannot take the rise at a civil pitch is describing a stair that turns, which is
78
+ * what a short wide core box always is. Both the renderer and the plate it cuts its hole in have to
79
+ * agree about that, so the reading lives here rather than in either of them. */
80
+ export declare function stairModel(project: ProjectDocument, object: SiteObject): StairModel;
81
+ /** A bank of escalators is stacked criss-cross: every other flight runs the other way, so you step
82
+ * off one and turn to step onto the next. Which way round a given flight is decides which end of
83
+ * the footprint it arrives at, and so which end of it the floor above has to be open. */
84
+ export declare const flightReversed: (model: StairModel, index: number) => boolean;
85
+ /** Where one flight's sloping part actually lies inside the box the plan drew.
86
+ *
87
+ * The plan gives a footprint; the climb is what has to fit in it. An escalator keeps a metre of flat
88
+ * comb plate at each end and then climbs at the machine's own 30°, so a box with run to spare gets a
89
+ * longer landing at its foot instead of a shallower ramp — a 1.6 m rise in a 12 m box is a stair with
90
+ * a lot of floor round it, not a slide. A stair's treads are capped at MAX_GOING for the same reason.
91
+ * A box too short for its rise is left alone: it really is that steep and the picture should say so.
92
+ *
93
+ * `t` runs along the object's depth axis from its centre. The foot of a flight is at +depth/2 and its
94
+ * head at -depth/2, which is how the renderer has always laid one out; the surplus goes to whichever
95
+ * end that leaves free — the foot of an escalator, whose comb plates must stay at the ends the plan
96
+ * drew them at, and the head of a stair, which has none. */
97
+ export interface FlightRun {
98
+ /** The rotation this flight is drawn at — see flightReversed. */
99
+ rotation: number;
100
+ /** A point t metres along the run from the object's centre. */
101
+ at: (t: number) => Point;
102
+ /** How long the box is along that axis, and half of it. */
103
+ length: number;
104
+ half: number;
105
+ /** The flat plate at each end of an escalator; zero for a stair. */
106
+ pad: number;
107
+ /** The sloping part: how long it is, and where it starts and ends along the run. */
108
+ incline: number;
109
+ topT: number;
110
+ footT: number;
111
+ }
112
+ export declare function flightRun(project: ProjectDocument, object: SiteObject, rise: number, model: StairModel, index?: number): FlightRun;
113
+ /** The one twin that stands for the shaft: the lowest, so the flights count upward from the bottom.
114
+ * A shaft described the modern way — one object — is trivially its own primary. */
115
+ export declare function primaryShafts(project: ProjectDocument): Set<string>;
116
+ /** Does something come up through this level's floor? The shaft starts below it and reaches it or
117
+ * goes past — so a flight arrives here and the plate it arrives through has to be open.
118
+ *
119
+ * This is one half of what `passesThrough` used to answer for both. Requiring a served level above
120
+ * as well meant the flight arriving at the top of a run was drawn under an uncut slab: on Stockmann's
121
+ * ninth floor all twelve shafts arrive and not one of them cut a hole, so the last flight of every
122
+ * stair in the building was invisible from the storey it lands on. */
123
+ export declare function arrivesAt(project: ProjectDocument, object: SiteObject, floorId: string): boolean;
124
+ /** The other half: does something climb out through this level's ceiling? The shaft stands on this
125
+ * level or lower and carries on above it, so the lid a walked storey gets has to be open over it —
126
+ * which the bottom of a run needs just as much as the middle of one. */
127
+ export declare function departsFrom(project: ProjectDocument, object: SiteObject, floorId: string): boolean;
128
+ /** Does this shaft pass THROUGH a level — arriving from below and carrying on above? Both halves at
129
+ * once, which is the question a level in the middle of a run answers yes to and no end of one does. */
130
+ export declare const passesThrough: (project: ProjectDocument, object: SiteObject, floorId: string) => boolean;
131
+ export interface VoidOptions {
132
+ /** `floor` (the default) cuts what arrives from below; `ceiling` cuts what departs upward. */
133
+ through?: 'floor' | 'ceiling';
134
+ /** Ignore flights that set off below this elevation. An above-ground view draws nothing under
135
+ * grade, and a hole with nothing beneath it is worse than no hole at all. */
136
+ lowest?: number;
137
+ }
138
+ /** What to cut out of a level's floor plate, or out of the lid over a walked storey.
139
+ *
140
+ * A stairwell is a hole. Drawing the slab whole and the stair beneath it puts a lid over the flight,
141
+ * so from the landing above you see a plate where the opening should be and the stair appears to
142
+ * climb into the ceiling. The hole is only as big as the flight needs, though: a lift or a turning
143
+ * core is a shaft and takes its whole footprint, but a straight flight or an escalator lies under
144
+ * the floor for most of its length and wants opening only where it comes up — see headroomBand. */
145
+ export declare function shaftVoids(project: ProjectDocument, floorId: string, primary?: Set<string>, options?: VoidOptions): Ring[];
@@ -0,0 +1,71 @@
1
+ import type { Point, ProjectDocument, Ring } from './types';
2
+ /** A drag snaps in the geometry's frame. Rounding x/y independently moves an imported wall's
3
+ * junction off its centreline, even when the pointer follows that centreline exactly. */
4
+ export declare function snapDragPoint(project: ProjectDocument, floorId: string | null, kind: 'junction' | 'barrier' | 'ring', id: string, raw: Point, snapping: boolean, tolerance: number, ringIndex?: number, vertexIndex?: number): Point;
5
+ /** A segment's direction as an *axis* in [0, 180). A wall and the same wall drawn backwards describe
6
+ * one line, so 10° and 190° have to compare equal or half of every alignment test misses. */
7
+ export declare const axisOf: (a: Point, b: Point) => number;
8
+ /** The smallest rotation carrying axis `from` onto axis `to`, in (-90, 90]. */
9
+ export declare const axisDelta: (from: number, to: number) => number;
10
+ /** Two directions count as the same when they are within this many degrees. Wide enough to absorb a
11
+ * footprint traced by hand or imported from a cadastral outline, narrow enough that a wall set at a
12
+ * deliberate angle still reads as its own direction. */
13
+ export declare const ALIGNED = 4;
14
+ /** The rings describing a floor's outline: its own top-level zones when it has them, else the union
15
+ * of the rooms drawn on it, else the site's building footprints. Shared with `exteriorWalls` so
16
+ * "the outline" means one thing across the app.
17
+ *
18
+ * The room union is what a floor without a zone over it actually has. An imported plan is exactly
19
+ * that — rooms traced off a drawing, no covering zone and no site footprint — and skipping straight
20
+ * to the building objects returned nothing for it, so not one of its walls counted as exterior:
21
+ * no exterior material, no facade preset, and nothing for a stacked view to draw a storey's edge
22
+ * from. Cached per document, which is immutable between edits, because the union is not free. */
23
+ export declare function floorOutline(project: ProjectDocument, floorId: string | null): Ring[];
24
+ /** The building's main axis on this floor: the direction its outline predominantly runs, in [0, 180).
25
+ * Falls back to the walls already drawn, and finally to 0 — the site grid, which for a project whose
26
+ * origin carries a bearing is already the building's street alignment. */
27
+ export declare function mainAxis(project: ProjectDocument, floorId: string | null): number;
28
+ export interface AxisReference {
29
+ /** Degrees in [0, 180). New walls are held to this and its 15° multiples. */
30
+ angle: number;
31
+ source: 'wall' | 'exterior' | 'building';
32
+ }
33
+ /** What a wall drawn near `point` should line up with: whichever wall — interior or the building's own
34
+ * exterior — it is being drawn beside, and failing that the building's main axis.
35
+ *
36
+ * Preferring the near edge over the building-wide average is what makes irregular plans workable. A
37
+ * wing set askew, or a space with a raked end, has no one true direction; next to the raked end the
38
+ * raked end *is* the direction, and squaring to the far-off main axis would look wrong there. Drawn
39
+ * walls win ties against the exterior at equal distance, since an interior wall is the more
40
+ * deliberate signal. */
41
+ export declare function referenceAxis(project: ProjectDocument, floorId: string | null, point: Point, reach: number): AxisReference;
42
+ export interface WallProposal {
43
+ segment: [Point, Point];
44
+ axis: number;
45
+ reference: AxisReference;
46
+ }
47
+ /** The wall a space looks like it is missing. Hover anywhere inside one: this squares off against the
48
+ * wall you are nearest (or the building, in a space that has none yet), runs a line both ways until
49
+ * it meets something, and offers the whole span as one click.
50
+ *
51
+ * Null when there is nothing worth offering — no bound on one of the two sides, a span too short to
52
+ * be a wall, or a wall already running this way within `clear` metres, which means the space is
53
+ * divided here already. */
54
+ export declare function proposeWall(project: ProjectDocument, floorId: string | null, point: Point, clear?: number): WallProposal | null;
55
+ /** Where an opening would land if you placed it here. */
56
+ export interface OpeningFit {
57
+ barrierId: string;
58
+ offset: number;
59
+ position: Point;
60
+ /** The stretch of wall the leaf would occupy — what to draw so the placement is visible first. */
61
+ span: [Point, Point];
62
+ }
63
+ /** Fit an opening to the nearest wall (or fence, for a gate).
64
+ *
65
+ * One rule, asked twice: the hover preview and the click that commits must agree, or the preview is
66
+ * a lie. Returns null when nothing is close enough, which is what lets the tool say so before the
67
+ * click rather than by throwing after it.
68
+ *
69
+ * The offset is clamped so the leaf stays on the wall — a door centred 10 cm from the end of its
70
+ * wall would otherwise hang in the air. */
71
+ export declare function fitOpening(project: ProjectDocument, floorId: string | null, kind: 'door' | 'window' | 'gate' | 'turnstile', at: Point, width: number, reach: number, ignoreId?: string): OpeningFit | null;
@@ -0,0 +1,25 @@
1
+ export type { ProjectDocument, ProjectSummary, Building, BuildingRoof, RoofSection, Floor, InteriorLight, LightFixture, Junction, VirtualBoundary, BoundaryUse, SpaceGeometry, Barrier, SiteObject, Drawing, Origin, Point, Ring, ObjectKind, Slope, MaterialKind, ModelKind, AssetRepository, NavNode, NavEdge, NavEdgeKind, Zone, Portal, PortalGroup, } from '../model/types';
2
+ export { spaces, spaceAt, spacePoint, inSpace } from '../model/spaces';
3
+ export { entryInto, perimeter, captive, zoneSpaces, addZone, removeZone, setZoneMembers, nestZone, addPortalGroup, removePortalGroup, setPortalGroupMembers, pruneOntology, } from '../model/ontology';
4
+ export { inferPortals, inferOpenBoundaries, refreshPortals, divideSpaces, spacesDividedBy, spacesRejoinedBy, mergeSpaces, } from '../model/inference';
5
+ export { derivedGraph } from '../model/topology';
6
+ export { sharedBoundaryPortals, effectivePortals } from '../model/portals';
7
+ export { isArea, isDevice, isOpening, isSpace, uid, OBJECT_KINDS } from '../model/types';
8
+ export { createObject, emptyProject } from '../model/factory';
9
+ export { enclosedRegion, enclosedRegions, refitEnclosedRooms } from '../model/spaces';
10
+ export { boundaryEdges, boundaryRegions, boundaryRegionAt, boundaryRings, derivedSpaceRings, bindSpaceToRegion, followsBoundaries, connectSpace, disconnectSpace, addVirtualBoundary, drawVirtualBoundary, addBoundaryHole, normalizeBoundaries, refreshBoundarySpaces, } from '../model/boundaries';
11
+ export type { BoundaryEdge, BoundaryRegion } from '../model/boundaries';
12
+ export { coverageOf, DEFAULT_RANGE, DEFAULT_FOV } from '../model/coverage';
13
+ export { copyProject, openingFloorId } from '../model/project';
14
+ export { MIN_SEGMENT, OPENING_MIN_SEGMENT, geoOrigin, moveOrigin, segmentsCross, toLngLat, toLocal, addBarrier, duplicateFloor, removeFloor, rectangle, rotate, centroid, distance, footprint, closeRing, openRing, ringArea, objectArea, pointInRing, objectPosition, objectRotation, barrierEnds, segmentProjection, slopeElevation, } from '../model/geometry';
15
+ export { exportProject, importProject, parseExport, blobDataUrl } from '../model/document';
16
+ export { validateProject, validateRelationships, validateRings, validateNavigation, transact, freezeProject, COORD_LIMIT, } from '../model/validate';
17
+ export type { TransactResult } from '../model/validate';
18
+ export { applyMutation, applyMutations } from '../model/mutations';
19
+ export type { Mutation, MutationOutcome, MutationResult } from '../model/mutations';
20
+ export { findRoute, navNodes, navEdges, HERE, routeSteps, routeAnchors, floorPhrase, addNavNode, addNavEdge, navPath, chainVertical, edgeCost, edgeLength, ELEVATOR_BASE, ELEVATOR_PER_METRE, STAIR_CLIMB_FACTOR, DOOR_COST, NAV_WELD, } from '../model/navigation';
21
+ export type { Route, RouteStep, RouteLeg, RouteOptions, RouteEnd, RoutePlace } from '../model/navigation';
22
+ export { EXTERIOR_PRESETS, type ExteriorPreset } from '../model/materials';
23
+ export { flights, flightsAt, isVertical, passesThrough, PITCH, pitchOf, primaryShafts, reaches, runFor, servedFloors, shaftKey, shaftVoids, span, type Flight, } from '../model/vertical';
24
+ export { buildRoomNavigation } from '../model/roomNavigation';
25
+ export type { RoomAccess, RoomNavigationGraph } from '../model/roomNavigation';