@ubuilder-ai/workspace 0.0.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 (74) hide show
  1. package/README.md +7 -0
  2. package/dist/floorplan-annotation-obstacles.d.ts +4 -0
  3. package/dist/floorplan-annotation-obstacles.d.ts.map +1 -0
  4. package/dist/floorplan-annotation-obstacles.js +25 -0
  5. package/dist/floorplan-camera-sync.d.ts +24 -0
  6. package/dist/floorplan-camera-sync.d.ts.map +1 -0
  7. package/dist/floorplan-camera-sync.js +187 -0
  8. package/dist/floorplan-coordinates.d.ts +10 -0
  9. package/dist/floorplan-coordinates.d.ts.map +1 -0
  10. package/dist/floorplan-coordinates.js +25 -0
  11. package/dist/floorplan-dimension-renderer.d.ts +42 -0
  12. package/dist/floorplan-dimension-renderer.d.ts.map +1 -0
  13. package/dist/floorplan-dimension-renderer.js +245 -0
  14. package/dist/floorplan-geometry-renderer.d.ts +56 -0
  15. package/dist/floorplan-geometry-renderer.d.ts.map +1 -0
  16. package/dist/floorplan-geometry-renderer.js +319 -0
  17. package/dist/floorplan-initial-view.d.ts +29 -0
  18. package/dist/floorplan-initial-view.d.ts.map +1 -0
  19. package/dist/floorplan-initial-view.js +30 -0
  20. package/dist/floorplan-label-angle.d.ts +24 -0
  21. package/dist/floorplan-label-angle.d.ts.map +1 -0
  22. package/dist/floorplan-label-angle.js +60 -0
  23. package/dist/floorplan-navigation-presentation.d.ts +57 -0
  24. package/dist/floorplan-navigation-presentation.d.ts.map +1 -0
  25. package/dist/floorplan-navigation-presentation.js +81 -0
  26. package/dist/floorplan-navigation-shell.d.ts +87 -0
  27. package/dist/floorplan-navigation-shell.d.ts.map +1 -0
  28. package/dist/floorplan-navigation-shell.js +1212 -0
  29. package/dist/floorplan-navigation-types.d.ts +10 -0
  30. package/dist/floorplan-navigation-types.d.ts.map +1 -0
  31. package/dist/floorplan-navigation-types.js +1 -0
  32. package/dist/floorplan-registry-projection.d.ts +84 -0
  33. package/dist/floorplan-registry-projection.d.ts.map +1 -0
  34. package/dist/floorplan-registry-projection.js +260 -0
  35. package/dist/floorplan-render-context.d.ts +56 -0
  36. package/dist/floorplan-render-context.d.ts.map +1 -0
  37. package/dist/floorplan-render-context.js +75 -0
  38. package/dist/index.d.ts +21 -0
  39. package/dist/index.d.ts.map +1 -0
  40. package/dist/index.js +19 -0
  41. package/dist/marquee-geometry.d.ts +22 -0
  42. package/dist/marquee-geometry.d.ts.map +1 -0
  43. package/dist/marquee-geometry.js +151 -0
  44. package/dist/spatial-workspace-camera-action-control.d.ts +18 -0
  45. package/dist/spatial-workspace-camera-action-control.d.ts.map +1 -0
  46. package/dist/spatial-workspace-camera-action-control.js +13 -0
  47. package/dist/spatial-workspace-display-control.d.ts +64 -0
  48. package/dist/spatial-workspace-display-control.d.ts.map +1 -0
  49. package/dist/spatial-workspace-display-control.js +81 -0
  50. package/dist/spatial-workspace-gis-control.d.ts +58 -0
  51. package/dist/spatial-workspace-gis-control.d.ts.map +1 -0
  52. package/dist/spatial-workspace-gis-control.js +38 -0
  53. package/dist/spatial-workspace-instance.d.ts +2 -0
  54. package/dist/spatial-workspace-instance.d.ts.map +1 -0
  55. package/dist/spatial-workspace-instance.js +14 -0
  56. package/dist/spatial-workspace-level-selector.d.ts +25 -0
  57. package/dist/spatial-workspace-level-selector.d.ts.map +1 -0
  58. package/dist/spatial-workspace-level-selector.js +18 -0
  59. package/dist/spatial-workspace-level-wall-control.d.ts +38 -0
  60. package/dist/spatial-workspace-level-wall-control.d.ts.map +1 -0
  61. package/dist/spatial-workspace-level-wall-control.js +43 -0
  62. package/dist/spatial-workspace-mode.d.ts +11 -0
  63. package/dist/spatial-workspace-mode.d.ts.map +1 -0
  64. package/dist/spatial-workspace-mode.js +29 -0
  65. package/dist/spatial-workspace-styles.d.ts +3 -0
  66. package/dist/spatial-workspace-styles.d.ts.map +1 -0
  67. package/dist/spatial-workspace-styles.js +72 -0
  68. package/dist/spatial-workspace-view-mode-control.d.ts +19 -0
  69. package/dist/spatial-workspace-view-mode-control.d.ts.map +1 -0
  70. package/dist/spatial-workspace-view-mode-control.js +14 -0
  71. package/dist/spatial-workspace-viewport.d.ts +25 -0
  72. package/dist/spatial-workspace-viewport.d.ts.map +1 -0
  73. package/dist/spatial-workspace-viewport.js +106 -0
  74. package/package.json +47 -0
@@ -0,0 +1,10 @@
1
+ export type FloorplanNavigationSource = '2d' | '3d';
2
+ export type FloorplanNavigationPose = {
3
+ source: FloorplanNavigationSource;
4
+ revision: number;
5
+ target: [number, number, number];
6
+ azimuth: number;
7
+ viewWidth: number;
8
+ };
9
+ export type FloorplanNavigationPoseInput = Omit<FloorplanNavigationPose, 'revision'>;
10
+ //# sourceMappingURL=floorplan-navigation-types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"floorplan-navigation-types.d.ts","sourceRoot":"","sources":["../src/floorplan-navigation-types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,yBAAyB,GAAG,IAAI,GAAG,IAAI,CAAA;AAEnD,MAAM,MAAM,uBAAuB,GAAG;IACpC,MAAM,EAAE,yBAAyB,CAAA;IACjC,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;IAChC,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;CAClB,CAAA;AAED,MAAM,MAAM,4BAA4B,GAAG,IAAI,CAAC,uBAAuB,EAAE,UAAU,CAAC,CAAA"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,84 @@
1
+ import type { AnyNode, AnyNodeDefinition, AnyNodeId, FloorplanGeometry, FloorplanPalette, GeometryContext } from '@ubuilder-ai/core';
2
+ import { type FloorplanMetricNotation, type FloorplanRenderPurpose, type FloorplanWallDimensionReference } from '@ubuilder-ai/floorplan-contract';
3
+ export type FloorplanDefinitionResolver = (kind: string) => AnyNodeDefinition | undefined;
4
+ export type FloorplanProjectionContext = {
5
+ children: AnyNode[];
6
+ siblings: AnyNode[];
7
+ parent: AnyNode | null;
8
+ };
9
+ export type FloorplanRegistryProjectionEntry = {
10
+ id: AnyNodeId;
11
+ node: AnyNode;
12
+ context?: FloorplanProjectionContext;
13
+ };
14
+ export type FloorplanRegistryProjection = {
15
+ entries: readonly FloorplanRegistryProjectionEntry[];
16
+ levelNodeIdsByType: ReadonlyMap<string, readonly AnyNodeId[]>;
17
+ };
18
+ export type ProjectFloorplanRegistryOptions = {
19
+ nodes: Readonly<Record<string, AnyNode>>;
20
+ activeLevelId: AnyNodeId;
21
+ getDefinition: FloorplanDefinitionResolver;
22
+ isKindEnabled: (kind: string) => boolean;
23
+ includeBuildingScoped: boolean;
24
+ };
25
+ /**
26
+ * Stable z-order sort. SVG renders in document order — later siblings
27
+ * paint on top of earlier ones — so anything that should sit under other
28
+ * floor-plan geometry has to declare a lower `floorplanOrder`. Within a
29
+ * layer bucket the original projection order remains stable.
30
+ */
31
+ export declare function orderFloorplanProjectionEntries<T extends {
32
+ node: AnyNode;
33
+ }>(entries: readonly T[], getDefinition: FloorplanDefinitionResolver): T[];
34
+ export declare function projectFloorplanRegistry({ nodes, activeLevelId, getDefinition, isKindEnabled, includeBuildingScoped, }: ProjectFloorplanRegistryOptions): FloorplanRegistryProjection;
35
+ export type FloorplanGeometryContextViewState = {
36
+ selected: boolean;
37
+ unit: 'metric' | 'imperial';
38
+ metricNotation: FloorplanMetricNotation;
39
+ purpose?: FloorplanRenderPurpose;
40
+ wallDimensionReference?: FloorplanWallDimensionReference;
41
+ highlighted: boolean;
42
+ hovered: boolean;
43
+ moving: boolean;
44
+ palette: FloorplanPalette | undefined;
45
+ };
46
+ export type CreateFloorplanGeometryContextOptions = {
47
+ node: AnyNode;
48
+ nodes: Readonly<Record<string, AnyNode>>;
49
+ viewState: FloorplanGeometryContextViewState;
50
+ context?: FloorplanProjectionContext;
51
+ levelData?: unknown;
52
+ runtimeState?: unknown;
53
+ resolve?: GeometryContext['resolve'];
54
+ };
55
+ export declare function createFloorplanGeometryContext({ node, nodes, viewState, context, levelData, runtimeState, resolve: resolveOverride, }: CreateFloorplanGeometryContextOptions): GeometryContext;
56
+ export declare function computeFloorplanLevelData({ definition, nodeIds, nodes, }: {
57
+ definition: AnyNodeDefinition | undefined;
58
+ nodeIds: readonly AnyNodeId[] | undefined;
59
+ nodes: Readonly<Record<string, AnyNode>>;
60
+ }): unknown;
61
+ /**
62
+ * Walk a `FloorplanGeometry` tree and split it into two trees: one with
63
+ * only "base" primitives (polygons, paths, fills, strokes) and one with
64
+ * only "overlay" primitives (handles, labels — see `OVERLAY_KINDS`).
65
+ *
66
+ * Groups recurse: a `kind: 'group'` is split into a base group and an
67
+ * overlay group, both carrying the same `transform` so nested rotations
68
+ * / translations apply in both passes. Empty groups collapse to `null`
69
+ * so the caller can skip emitting an `<g>` when there's nothing to draw.
70
+ */
71
+ export declare function splitFloorplanOverlay(geometry: FloorplanGeometry): {
72
+ base: FloorplanGeometry | null;
73
+ overlay: FloorplanGeometry | null;
74
+ };
75
+ export declare function buildFloorplanGeometryPartition({ definition, node, context, transformGeometry, }: {
76
+ definition: AnyNodeDefinition | undefined;
77
+ node: AnyNode;
78
+ context: GeometryContext;
79
+ transformGeometry?: (geometry: FloorplanGeometry) => FloorplanGeometry | null;
80
+ }): {
81
+ base: FloorplanGeometry | null;
82
+ overlay: FloorplanGeometry | null;
83
+ };
84
+ //# sourceMappingURL=floorplan-registry-projection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"floorplan-registry-projection.d.ts","sourceRoot":"","sources":["../src/floorplan-registry-projection.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,260 @@
1
+ import { createFloorplanContextExtensions, } from '@ubuilder-ai/floorplan-contract';
2
+ import { isFloorplanAnnotationObstacleGeometry } from './floorplan-annotation-obstacles';
3
+ /**
4
+ * Stable z-order sort. SVG renders in document order — later siblings
5
+ * paint on top of earlier ones — so anything that should sit under other
6
+ * floor-plan geometry has to declare a lower `floorplanOrder`. Within a
7
+ * layer bucket the original projection order remains stable.
8
+ */
9
+ export function orderFloorplanProjectionEntries(entries, getDefinition) {
10
+ return entries
11
+ .map((entry, index) => ({ entry, index }))
12
+ .sort((left, right) => {
13
+ const leftOrder = getDefinition(left.entry.node.type)?.floorplanOrder ?? 2;
14
+ const rightOrder = getDefinition(right.entry.node.type)?.floorplanOrder ?? 2;
15
+ return leftOrder - rightOrder || left.index - right.index;
16
+ })
17
+ .map(({ entry }) => entry);
18
+ }
19
+ function childIdsOf(node) {
20
+ const children = node.children;
21
+ return Array.isArray(children) ? children : [];
22
+ }
23
+ function resolveLevelContainerId(activeLevelId, nodes, getDefinition) {
24
+ const level = nodes[activeLevelId];
25
+ if (!level)
26
+ return null;
27
+ const parentId = level.parentId;
28
+ if (parentId) {
29
+ const parent = nodes[parentId];
30
+ if (parent && getDefinition(parent.type)?.capabilities.floorplanLevelContainer) {
31
+ return parentId;
32
+ }
33
+ }
34
+ for (const candidate of Object.values(nodes)) {
35
+ if (!getDefinition(candidate.type)?.capabilities.floorplanLevelContainer)
36
+ continue;
37
+ if (childIdsOf(candidate).includes(activeLevelId))
38
+ return candidate.id;
39
+ }
40
+ return null;
41
+ }
42
+ export function projectFloorplanRegistry({ nodes, activeLevelId, getDefinition, isKindEnabled, includeBuildingScoped, }) {
43
+ if (!nodes[activeLevelId]) {
44
+ return { entries: [], levelNodeIdsByType: new Map() };
45
+ }
46
+ const levelNodeIdsByType = new Map();
47
+ const levelDataVisited = new Set();
48
+ const collectLevelDataIds = (id) => {
49
+ if (levelDataVisited.has(id))
50
+ return;
51
+ levelDataVisited.add(id);
52
+ const node = nodes[id];
53
+ if (!node || !isKindEnabled(node.type))
54
+ return;
55
+ const definition = getDefinition(node.type);
56
+ if (definition?.computeFloorplanLevelData) {
57
+ const ids = levelNodeIdsByType.get(node.type);
58
+ if (ids)
59
+ ids.push(id);
60
+ else
61
+ levelNodeIdsByType.set(node.type, [id]);
62
+ }
63
+ for (const childId of childIdsOf(node))
64
+ collectLevelDataIds(childId);
65
+ };
66
+ collectLevelDataIds(activeLevelId);
67
+ const entries = [];
68
+ const pushEntry = (id, node, context) => {
69
+ if (!isKindEnabled(node.type))
70
+ return;
71
+ entries.push(context ? { id, node, context } : { id, node });
72
+ };
73
+ const entryVisited = new Set();
74
+ const visit = (id) => {
75
+ if (entryVisited.has(id))
76
+ return;
77
+ entryVisited.add(id);
78
+ const node = nodes[id];
79
+ if (!node)
80
+ return;
81
+ pushEntry(id, node);
82
+ for (const childId of childIdsOf(node))
83
+ visit(childId);
84
+ };
85
+ visit(activeLevelId);
86
+ const activeLevel = nodes[activeLevelId];
87
+ const collectedIds = new Set(entries.map((entry) => entry.id));
88
+ for (const node of Object.values(nodes)) {
89
+ const id = node.id;
90
+ if (collectedIds.has(id) || !isKindEnabled(node.type))
91
+ continue;
92
+ const definition = getDefinition(node.type);
93
+ const linkedLevelIds = definition?.floorplanLinkedLevelIds;
94
+ if (!definition?.floorplan || !linkedLevelIds?.(node).includes(activeLevelId))
95
+ continue;
96
+ pushEntry(id, node, {
97
+ children: childIdsOf(node)
98
+ .map((childId) => nodes[childId])
99
+ .filter((child) => child !== undefined),
100
+ siblings: [],
101
+ parent: activeLevel,
102
+ });
103
+ collectedIds.add(id);
104
+ }
105
+ if (includeBuildingScoped) {
106
+ const levelContainerId = resolveLevelContainerId(activeLevelId, nodes, getDefinition);
107
+ if (levelContainerId) {
108
+ for (const node of Object.values(nodes)) {
109
+ const id = node.id;
110
+ if (collectedIds.has(id) || !isKindEnabled(node.type))
111
+ continue;
112
+ const definition = getDefinition(node.type);
113
+ if (definition?.floorplanScope !== 'building')
114
+ continue;
115
+ if (node.parentId !== levelContainerId)
116
+ continue;
117
+ pushEntry(id, node, { children: [], siblings: [], parent: activeLevel });
118
+ collectedIds.add(id);
119
+ }
120
+ }
121
+ }
122
+ return {
123
+ entries,
124
+ levelNodeIdsByType,
125
+ };
126
+ }
127
+ export function createFloorplanGeometryContext({ node, nodes, viewState, context, levelData, runtimeState, resolve: resolveOverride, }) {
128
+ const resolve = resolveOverride ?? ((id) => nodes[id]);
129
+ let children;
130
+ let siblings;
131
+ let parent;
132
+ if (context) {
133
+ children = context.children;
134
+ siblings = context.siblings;
135
+ parent = context.parent;
136
+ }
137
+ else {
138
+ children = childIdsOf(node)
139
+ .map((childId) => nodes[childId])
140
+ .filter((child) => child !== undefined);
141
+ const parentId = node.parentId;
142
+ parent = parentId ? (nodes[parentId] ?? null) : null;
143
+ siblings = [];
144
+ if (parent) {
145
+ const parentChildren = parent.children;
146
+ if (Array.isArray(parentChildren)) {
147
+ const parentChildIds = parentChildren;
148
+ for (const siblingId of parentChildIds) {
149
+ if (siblingId === node.id)
150
+ continue;
151
+ const sibling = nodes[siblingId];
152
+ if (sibling?.type === node.type)
153
+ siblings.push(sibling);
154
+ }
155
+ }
156
+ else {
157
+ siblings = Object.values(nodes).filter((sibling) => sibling !== node && sibling.type === node.type && sibling.parentId === parentId);
158
+ }
159
+ }
160
+ }
161
+ return {
162
+ resolve,
163
+ children,
164
+ siblings,
165
+ parent,
166
+ levelData,
167
+ extensions: createFloorplanContextExtensions({
168
+ metricNotation: viewState.metricNotation,
169
+ purpose: viewState.purpose ?? 'edit',
170
+ runtimeState,
171
+ wallDimensionReference: viewState.wallDimensionReference,
172
+ }),
173
+ viewState: viewState.palette
174
+ ? {
175
+ selected: viewState.selected,
176
+ unit: viewState.unit,
177
+ highlighted: viewState.highlighted,
178
+ hovered: viewState.hovered,
179
+ moving: viewState.moving,
180
+ palette: viewState.palette,
181
+ }
182
+ : undefined,
183
+ };
184
+ }
185
+ export function computeFloorplanLevelData({ definition, nodeIds, nodes, }) {
186
+ const compute = definition?.computeFloorplanLevelData;
187
+ const kind = definition?.kind;
188
+ if (!compute || !kind || !nodeIds?.length)
189
+ return undefined;
190
+ const siblings = nodeIds
191
+ .map((id) => nodes[id])
192
+ .filter((node) => node?.type === kind);
193
+ return compute({ siblings, nodes: nodes });
194
+ }
195
+ /**
196
+ * Geometry kinds that always render in the overlay pass — interactive
197
+ * handles and node labels. These need to sit above every kind's base
198
+ * geometry regardless of the owning node's z-bucket so that:
199
+ * - polygon edit handles on a selected slab don't get hidden by the
200
+ * walls / items resting on top of the slab,
201
+ * - a zone's name stays legible above the slab covering the zone, and
202
+ * - measurement labels never get clipped by structural fills.
203
+ */
204
+ const OVERLAY_KINDS = new Set([
205
+ 'text',
206
+ 'endpoint-handle',
207
+ 'midpoint-handle',
208
+ 'edge-handle',
209
+ 'move-handle',
210
+ 'move-arrow',
211
+ 'rotate-arrow',
212
+ 'dimension',
213
+ 'dimension-string',
214
+ 'dimension-label',
215
+ 'equal-spacing-badge',
216
+ ]);
217
+ /**
218
+ * Walk a `FloorplanGeometry` tree and split it into two trees: one with
219
+ * only "base" primitives (polygons, paths, fills, strokes) and one with
220
+ * only "overlay" primitives (handles, labels — see `OVERLAY_KINDS`).
221
+ *
222
+ * Groups recurse: a `kind: 'group'` is split into a base group and an
223
+ * overlay group, both carrying the same `transform` so nested rotations
224
+ * / translations apply in both passes. Empty groups collapse to `null`
225
+ * so the caller can skip emitting an `<g>` when there's nothing to draw.
226
+ */
227
+ export function splitFloorplanOverlay(geometry) {
228
+ if (isFloorplanAnnotationObstacleGeometry(geometry)) {
229
+ return { base: null, overlay: geometry };
230
+ }
231
+ if (OVERLAY_KINDS.has(geometry.kind)) {
232
+ return { base: null, overlay: geometry };
233
+ }
234
+ if (geometry.kind === 'group') {
235
+ const baseChildren = [];
236
+ const overlayChildren = [];
237
+ for (const child of geometry.children) {
238
+ const split = splitFloorplanOverlay(child);
239
+ if (split.base)
240
+ baseChildren.push(split.base);
241
+ if (split.overlay)
242
+ overlayChildren.push(split.overlay);
243
+ }
244
+ return {
245
+ base: baseChildren.length > 0
246
+ ? { kind: 'group', children: baseChildren, transform: geometry.transform }
247
+ : null,
248
+ overlay: overlayChildren.length > 0
249
+ ? { kind: 'group', children: overlayChildren, transform: geometry.transform }
250
+ : null,
251
+ };
252
+ }
253
+ return { base: geometry, overlay: null };
254
+ }
255
+ export function buildFloorplanGeometryPartition({ definition, node, context, transformGeometry, }) {
256
+ const builder = definition?.floorplan;
257
+ const geometry = builder?.(node, context) ?? null;
258
+ const transformed = geometry && transformGeometry ? transformGeometry(geometry) : geometry;
259
+ return transformed ? splitFloorplanOverlay(transformed) : { base: null, overlay: null };
260
+ }
@@ -0,0 +1,56 @@
1
+ import type { FloorplanPalette } from '@ubuilder-ai/core';
2
+ import { type ReactNode } from 'react';
3
+ /**
4
+ * Per-frame render context shared between a host floorplan surface
5
+ * and its registry-driven scene renderer.
6
+ *
7
+ * The host is the authoritative owner of the floor-plan SVG —
8
+ * it computes `unitsPerPixel` from the viewBox / surface size, mounts the
9
+ * pan/zoom `<g>`, and knows the active theme. The scene renderer is mounted
10
+ * inside the same `<g>`, so anything it draws shares the same coordinate
11
+ * system; this context plumbs through the bits it can't recompute on its
12
+ * own without re-implementing the legacy's resize / theme logic.
13
+ *
14
+ */
15
+ export type FloorplanRenderContextValue = {
16
+ /** SVG units per screen pixel — used to keep handle radii consistent at any zoom. */
17
+ unitsPerPixel: number;
18
+ /** Themed palette mirroring the host `FloorplanPalette` accent slots. */
19
+ palette: FloorplanPalette;
20
+ /** SVG `<pattern>` id mounted in `<defs>` by the host for hatch fills. */
21
+ hatchPatternId: string;
22
+ /**
23
+ * Rotation (degrees) applied to the registry layer's parent `<g>` by the
24
+ * host surface — 90° by default, adjusted by building rotation. Renderers
25
+ * that emit text labels use this to keep their final on-screen orientation
26
+ * readable instead of mirroring whatever the parent rotation is.
27
+ */
28
+ sceneRotationDeg: number;
29
+ };
30
+ export type FloorplanStaticRenderContextValue = Omit<FloorplanRenderContextValue, 'sceneRotationDeg' | 'unitsPerPixel'> & {
31
+ getSceneRotationDeg: () => number;
32
+ getUnitsPerPixel: () => number;
33
+ subscribeUnitsPerPixel: (listener: () => void) => () => void;
34
+ };
35
+ export type FloorplanRenderScaleReference = {
36
+ read: () => number;
37
+ subscribe: (listener: () => void) => () => void;
38
+ update: (unitsPerPixel: number) => void;
39
+ };
40
+ export declare function createFloorplanRenderScaleReference(initialUnitsPerPixel: number): FloorplanRenderScaleReference;
41
+ export declare function FloorplanRenderProvider({ children, unitsPerPixel, palette, hatchPatternId, sceneRotationDeg, getSceneRotationDeg, }: FloorplanRenderContextValue & {
42
+ children: ReactNode;
43
+ getSceneRotationDeg: () => number;
44
+ }): import("react").JSX.Element;
45
+ /**
46
+ * Read the active render context. Returns `null` when called outside a
47
+ * provider — consumers treat this as "render statically, skip
48
+ * theme-aware chrome and interactive handles". This makes the layer
49
+ * usable in isolation tests + future editor shells without bringing the
50
+ * whole host surface along.
51
+ */
52
+ export declare function useFloorplanRender(): FloorplanRenderContextValue | null;
53
+ export declare function useFloorplanStaticRender(): FloorplanStaticRenderContextValue | null;
54
+ export declare function useFloorplanStaticUnitsPerPixel(): number;
55
+ export declare function useFloorplanSceneRotation(): number;
56
+ //# sourceMappingURL=floorplan-render-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"floorplan-render-context.d.ts","sourceRoot":"","sources":["../src/floorplan-render-context.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AACzD,OAAO,EAEL,KAAK,SAAS,EAMf,MAAM,OAAO,CAAA;AAEd;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,2BAA2B,GAAG;IACxC,qFAAqF;IACrF,aAAa,EAAE,MAAM,CAAA;IACrB,yEAAyE;IACzE,OAAO,EAAE,gBAAgB,CAAA;IACzB,0EAA0E;IAC1E,cAAc,EAAE,MAAM,CAAA;IACtB;;;;;OAKG;IACH,gBAAgB,EAAE,MAAM,CAAA;CACzB,CAAA;AAED,MAAM,MAAM,iCAAiC,GAAG,IAAI,CAClD,2BAA2B,EAC3B,kBAAkB,GAAG,eAAe,CACrC,GAAG;IACF,mBAAmB,EAAE,MAAM,MAAM,CAAA;IACjC,gBAAgB,EAAE,MAAM,MAAM,CAAA;IAC9B,sBAAsB,EAAE,CAAC,QAAQ,EAAE,MAAM,IAAI,KAAK,MAAM,IAAI,CAAA;CAC7D,CAAA;AAMD,MAAM,MAAM,6BAA6B,GAAG;IAC1C,IAAI,EAAE,MAAM,MAAM,CAAA;IAClB,SAAS,EAAE,CAAC,QAAQ,EAAE,MAAM,IAAI,KAAK,MAAM,IAAI,CAAA;IAC/C,MAAM,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,IAAI,CAAA;CACxC,CAAA;AAED,wBAAgB,mCAAmC,CACjD,oBAAoB,EAAE,MAAM,GAC3B,6BAA6B,CAe/B;AAED,wBAAgB,uBAAuB,CAAC,EACtC,QAAQ,EACR,aAAa,EACb,OAAO,EACP,cAAc,EACd,gBAAgB,EAChB,mBAAmB,GACpB,EAAE,2BAA2B,GAAG;IAC/B,QAAQ,EAAE,SAAS,CAAA;IACnB,mBAAmB,EAAE,MAAM,MAAM,CAAA;CAClC,+BA6BA;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,IAAI,2BAA2B,GAAG,IAAI,CAgBvE;AAED,wBAAgB,wBAAwB,IAAI,iCAAiC,GAAG,IAAI,CAEnF;AAKD,wBAAgB,+BAA+B,IAAI,MAAM,CAOxD;AAED,wBAAgB,yBAAyB,IAAI,MAAM,CAElD"}
@@ -0,0 +1,75 @@
1
+ 'use client';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { createContext, useContext, useLayoutEffect, useMemo, useRef, useSyncExternalStore, } from 'react';
4
+ const FloorplanStaticRenderContext = createContext(null);
5
+ const FloorplanSceneRotationContext = createContext(0);
6
+ const FloorplanUnitsPerPixelContext = createContext(1);
7
+ export function createFloorplanRenderScaleReference(initialUnitsPerPixel) {
8
+ let unitsPerPixel = initialUnitsPerPixel;
9
+ const listeners = new Set();
10
+ return {
11
+ read: () => unitsPerPixel,
12
+ subscribe: (listener) => {
13
+ listeners.add(listener);
14
+ return () => listeners.delete(listener);
15
+ },
16
+ update: (nextUnitsPerPixel) => {
17
+ if (Object.is(unitsPerPixel, nextUnitsPerPixel))
18
+ return;
19
+ unitsPerPixel = nextUnitsPerPixel;
20
+ for (const listener of listeners)
21
+ listener();
22
+ },
23
+ };
24
+ }
25
+ export function FloorplanRenderProvider({ children, unitsPerPixel, palette, hatchPatternId, sceneRotationDeg, getSceneRotationDeg, }) {
26
+ const renderScaleReference = useRef(null);
27
+ if (!renderScaleReference.current) {
28
+ renderScaleReference.current = createFloorplanRenderScaleReference(unitsPerPixel);
29
+ }
30
+ const getUnitsPerPixel = renderScaleReference.current.read;
31
+ const subscribeUnitsPerPixel = renderScaleReference.current.subscribe;
32
+ useLayoutEffect(() => {
33
+ renderScaleReference.current?.update(unitsPerPixel);
34
+ }, [unitsPerPixel]);
35
+ const staticValue = useMemo(() => ({
36
+ palette,
37
+ hatchPatternId,
38
+ getSceneRotationDeg,
39
+ getUnitsPerPixel,
40
+ subscribeUnitsPerPixel,
41
+ }), [palette, hatchPatternId, getSceneRotationDeg, getUnitsPerPixel, subscribeUnitsPerPixel]);
42
+ return (_jsx(FloorplanStaticRenderContext.Provider, { value: staticValue, children: _jsx(FloorplanUnitsPerPixelContext.Provider, { value: unitsPerPixel, children: _jsx(FloorplanSceneRotationContext.Provider, { value: sceneRotationDeg, children: children }) }) }));
43
+ }
44
+ /**
45
+ * Read the active render context. Returns `null` when called outside a
46
+ * provider — consumers treat this as "render statically, skip
47
+ * theme-aware chrome and interactive handles". This makes the layer
48
+ * usable in isolation tests + future editor shells without bringing the
49
+ * whole host surface along.
50
+ */
51
+ export function useFloorplanRender() {
52
+ const staticValue = useContext(FloorplanStaticRenderContext);
53
+ const sceneRotationDeg = useContext(FloorplanSceneRotationContext);
54
+ const unitsPerPixel = useContext(FloorplanUnitsPerPixelContext);
55
+ return useMemo(() => staticValue
56
+ ? {
57
+ unitsPerPixel,
58
+ palette: staticValue.palette,
59
+ hatchPatternId: staticValue.hatchPatternId,
60
+ sceneRotationDeg,
61
+ }
62
+ : null, [sceneRotationDeg, staticValue, unitsPerPixel]);
63
+ }
64
+ export function useFloorplanStaticRender() {
65
+ return useContext(FloorplanStaticRenderContext);
66
+ }
67
+ const subscribeToNoFloorplanScale = () => () => { };
68
+ const readDefaultFloorplanScale = () => 1;
69
+ export function useFloorplanStaticUnitsPerPixel() {
70
+ const staticValue = useContext(FloorplanStaticRenderContext);
71
+ return useSyncExternalStore(staticValue?.subscribeUnitsPerPixel ?? subscribeToNoFloorplanScale, staticValue?.getUnitsPerPixel ?? readDefaultFloorplanScale, staticValue?.getUnitsPerPixel ?? readDefaultFloorplanScale);
72
+ }
73
+ export function useFloorplanSceneRotation() {
74
+ return useContext(FloorplanSceneRotationContext);
75
+ }
@@ -0,0 +1,21 @@
1
+ export * from './floorplan-annotation-obstacles';
2
+ export { cameraPoseToFloorplanNavigationPose, createFloorplanCameraNavigationChannel, createFloorplanCameraSyncBridge, type FloorplanCameraNavigationChannel, type FloorplanCameraSyncBridge, floorplanNavigationPoseToCameraPose, floorplanNavigationPoseToInspectionCameraPose, } from './floorplan-camera-sync';
3
+ export { type FloorplanPoint, floorplanLocalToWorldPoint, worldToFloorplanLocalPoint, } from './floorplan-coordinates';
4
+ export * from './floorplan-dimension-renderer';
5
+ export * from './floorplan-geometry-renderer';
6
+ export * from './floorplan-label-angle';
7
+ export { type FloorplanNavigationController, FloorplanNavigationShell, type FloorplanNavigationShellProps, type FloorplanViewport, rotateFloorplanPoint, type UseFloorplanNavigationOptions, useFloorplanNavigation, } from './floorplan-navigation-shell';
8
+ export type { FloorplanNavigationPose, FloorplanNavigationPoseInput, FloorplanNavigationSource, } from './floorplan-navigation-types';
9
+ export * from './floorplan-registry-projection';
10
+ export * from './floorplan-render-context';
11
+ export * from './marquee-geometry';
12
+ export * from './spatial-workspace-camera-action-control';
13
+ export * from './spatial-workspace-display-control';
14
+ export * from './spatial-workspace-gis-control';
15
+ export * from './spatial-workspace-level-selector';
16
+ export * from './spatial-workspace-level-wall-control';
17
+ export * from './spatial-workspace-mode';
18
+ export * from './spatial-workspace-styles';
19
+ export * from './spatial-workspace-view-mode-control';
20
+ export { type SpatialWorkspaceRenderContext, type SpatialWorkspaceViewMode, SpatialWorkspaceViewport, type SpatialWorkspaceViewportProps, } from './spatial-workspace-viewport';
21
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,kCAAkC,CAAA;AAChD,OAAO,EACL,mCAAmC,EACnC,sCAAsC,EACtC,+BAA+B,EAC/B,KAAK,gCAAgC,EACrC,KAAK,yBAAyB,EAC9B,mCAAmC,EACnC,6CAA6C,GAC9C,MAAM,yBAAyB,CAAA;AAChC,OAAO,EACL,KAAK,cAAc,EACnB,0BAA0B,EAC1B,0BAA0B,GAC3B,MAAM,yBAAyB,CAAA;AAChC,cAAc,gCAAgC,CAAA;AAC9C,cAAc,+BAA+B,CAAA;AAC7C,cAAc,yBAAyB,CAAA;AACvC,OAAO,EACL,KAAK,6BAA6B,EAClC,wBAAwB,EACxB,KAAK,6BAA6B,EAClC,KAAK,iBAAiB,EACtB,oBAAoB,EACpB,KAAK,6BAA6B,EAClC,sBAAsB,GACvB,MAAM,8BAA8B,CAAA;AACrC,YAAY,EACV,uBAAuB,EACvB,4BAA4B,EAC5B,yBAAyB,GAC1B,MAAM,8BAA8B,CAAA;AACrC,cAAc,iCAAiC,CAAA;AAC/C,cAAc,4BAA4B,CAAA;AAC1C,cAAc,oBAAoB,CAAA;AAClC,cAAc,2CAA2C,CAAA;AACzD,cAAc,qCAAqC,CAAA;AACnD,cAAc,iCAAiC,CAAA;AAC/C,cAAc,oCAAoC,CAAA;AAClD,cAAc,wCAAwC,CAAA;AACtD,cAAc,0BAA0B,CAAA;AACxC,cAAc,4BAA4B,CAAA;AAC1C,cAAc,uCAAuC,CAAA;AACrD,OAAO,EACL,KAAK,6BAA6B,EAClC,KAAK,wBAAwB,EAC7B,wBAAwB,EACxB,KAAK,6BAA6B,GACnC,MAAM,8BAA8B,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,19 @@
1
+ export * from './floorplan-annotation-obstacles';
2
+ export { cameraPoseToFloorplanNavigationPose, createFloorplanCameraNavigationChannel, createFloorplanCameraSyncBridge, floorplanNavigationPoseToCameraPose, floorplanNavigationPoseToInspectionCameraPose, } from './floorplan-camera-sync';
3
+ export { floorplanLocalToWorldPoint, worldToFloorplanLocalPoint, } from './floorplan-coordinates';
4
+ export * from './floorplan-dimension-renderer';
5
+ export * from './floorplan-geometry-renderer';
6
+ export * from './floorplan-label-angle';
7
+ export { FloorplanNavigationShell, rotateFloorplanPoint, useFloorplanNavigation, } from './floorplan-navigation-shell';
8
+ export * from './floorplan-registry-projection';
9
+ export * from './floorplan-render-context';
10
+ export * from './marquee-geometry';
11
+ export * from './spatial-workspace-camera-action-control';
12
+ export * from './spatial-workspace-display-control';
13
+ export * from './spatial-workspace-gis-control';
14
+ export * from './spatial-workspace-level-selector';
15
+ export * from './spatial-workspace-level-wall-control';
16
+ export * from './spatial-workspace-mode';
17
+ export * from './spatial-workspace-styles';
18
+ export * from './spatial-workspace-view-mode-control';
19
+ export { SpatialWorkspaceViewport, } from './spatial-workspace-viewport';
@@ -0,0 +1,22 @@
1
+ export type ScreenRect = {
2
+ minX: number;
3
+ minY: number;
4
+ maxX: number;
5
+ maxY: number;
6
+ };
7
+ export type Point2 = [number, number];
8
+ /** Andrew monotone-chain convex hull. Returns CCW hull without the closing
9
+ * point; degenerate inputs (0–2 points, collinear sets) pass through. */
10
+ export declare function convexHull2D(points: readonly Point2[]): Point2[];
11
+ /** Segment vs polygon (general, possibly concave): endpoint containment or
12
+ * any edge crossing. */
13
+ export declare function segmentIntersectsPolygon(a: Point2, b: Point2, polygon: readonly Point2[]): boolean;
14
+ /** Polygon vs polygon (general): containment either way or any edge crossing. */
15
+ export declare function polygonsIntersect(a: readonly Point2[], b: readonly Point2[]): boolean;
16
+ /**
17
+ * Does the axis-aligned marquee rect intersect the (convex) hull polygon?
18
+ * Covers all cases: hull vertex inside the rect, rect fully inside the hull,
19
+ * and pure edge crossings.
20
+ */
21
+ export declare function rectIntersectsHull(rect: ScreenRect, hull: readonly Point2[]): boolean;
22
+ //# sourceMappingURL=marquee-geometry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"marquee-geometry.d.ts","sourceRoot":"","sources":["../src/marquee-geometry.ts"],"names":[],"mappings":"AAMA,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;CACb,CAAA;AAED,MAAM,MAAM,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;AAMrC;0EAC0E;AAC1E,wBAAgB,YAAY,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,CAsBhE;AAgDD;yBACyB;AACzB,wBAAgB,wBAAwB,CACtC,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,OAAO,EAAE,SAAS,MAAM,EAAE,GACzB,OAAO,CAQT;AAED,iFAAiF;AACjF,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,SAAS,MAAM,EAAE,EAAE,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAYrF;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CA2BrF"}