@pascal-app/core 0.8.0 → 0.9.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/dist/events/bus.d.ts +39 -13
- package/dist/events/bus.d.ts.map +1 -1
- package/dist/hooks/scene-registry/scene-registry.d.ts +6 -22
- package/dist/hooks/scene-registry/scene-registry.d.ts.map +1 -1
- package/dist/hooks/scene-registry/scene-registry.js +33 -26
- package/dist/hooks/spatial-grid/floor-placed-elevation.d.ts +14 -0
- package/dist/hooks/spatial-grid/floor-placed-elevation.d.ts.map +1 -0
- package/dist/hooks/spatial-grid/floor-placed-elevation.js +51 -0
- package/dist/hooks/spatial-grid/floor-placed-elevation.test.d.ts +2 -0
- package/dist/hooks/spatial-grid/floor-placed-elevation.test.d.ts.map +1 -0
- package/dist/hooks/spatial-grid/floor-placed-elevation.test.js +273 -0
- package/dist/hooks/spatial-grid/spatial-grid-manager.d.ts +13 -2
- package/dist/hooks/spatial-grid/spatial-grid-manager.d.ts.map +1 -1
- package/dist/hooks/spatial-grid/spatial-grid-manager.js +95 -9
- package/dist/hooks/spatial-grid/spatial-grid-sync.d.ts +28 -1
- package/dist/hooks/spatial-grid/spatial-grid-sync.d.ts.map +1 -1
- package/dist/hooks/spatial-grid/spatial-grid-sync.js +91 -18
- package/dist/index.d.ts +22 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +19 -2
- package/dist/lib/polygon-relations.d.ts +10 -0
- package/dist/lib/polygon-relations.d.ts.map +1 -0
- package/dist/lib/polygon-relations.js +73 -0
- package/dist/lib/space-detection-pause.test.d.ts +2 -0
- package/dist/lib/space-detection-pause.test.d.ts.map +1 -0
- package/dist/lib/space-detection-pause.test.js +42 -0
- package/dist/lib/space-detection.d.ts +30 -1
- package/dist/lib/space-detection.d.ts.map +1 -1
- package/dist/lib/space-detection.js +215 -36
- package/dist/lib/space-detection.test.d.ts +2 -0
- package/dist/lib/space-detection.test.d.ts.map +1 -0
- package/dist/lib/space-detection.test.js +77 -0
- package/dist/material-library.d.ts +1 -1
- package/dist/material-library.d.ts.map +1 -1
- package/dist/material-library.js +1940 -143
- package/dist/registry/__bench__/relations-resolver.bench.d.ts +15 -0
- package/dist/registry/__bench__/relations-resolver.bench.d.ts.map +1 -0
- package/dist/registry/__bench__/relations-resolver.bench.js +170 -0
- package/dist/registry/handles.d.ts +321 -0
- package/dist/registry/handles.d.ts.map +1 -0
- package/dist/registry/handles.js +24 -0
- package/dist/registry/index.d.ts +7 -0
- package/dist/registry/index.d.ts.map +1 -0
- package/dist/registry/index.js +4 -0
- package/dist/registry/registry.d.ts +93 -0
- package/dist/registry/registry.d.ts.map +1 -0
- package/dist/registry/registry.js +201 -0
- package/dist/registry/registry.test.d.ts +2 -0
- package/dist/registry/registry.test.d.ts.map +1 -0
- package/dist/registry/registry.test.js +183 -0
- package/dist/registry/relations-resolver.d.ts +42 -0
- package/dist/registry/relations-resolver.d.ts.map +1 -0
- package/dist/registry/relations-resolver.js +77 -0
- package/dist/registry/relations-resolver.test.d.ts +2 -0
- package/dist/registry/relations-resolver.test.d.ts.map +1 -0
- package/dist/registry/relations-resolver.test.js +183 -0
- package/dist/registry/scene-api.d.ts +44 -0
- package/dist/registry/scene-api.d.ts.map +1 -0
- package/dist/registry/scene-api.js +111 -0
- package/dist/registry/scene-api.test.d.ts +2 -0
- package/dist/registry/scene-api.test.d.ts.map +1 -0
- package/dist/registry/scene-api.test.js +183 -0
- package/dist/registry/subtree.d.ts +67 -0
- package/dist/registry/subtree.d.ts.map +1 -0
- package/dist/registry/subtree.js +119 -0
- package/dist/registry/subtree.test.d.ts +2 -0
- package/dist/registry/subtree.test.d.ts.map +1 -0
- package/dist/registry/subtree.test.js +133 -0
- package/dist/registry/types.d.ts +1420 -0
- package/dist/registry/types.d.ts.map +1 -0
- package/dist/registry/types.js +1 -0
- package/dist/schema/index.d.ts +17 -2
- package/dist/schema/index.d.ts.map +1 -1
- package/dist/schema/index.js +17 -3
- package/dist/schema/material.d.ts +10 -0
- package/dist/schema/material.d.ts.map +1 -1
- package/dist/schema/material.js +10 -0
- package/dist/schema/nodes/box-vent.d.ts +74 -0
- package/dist/schema/nodes/box-vent.d.ts.map +1 -0
- package/dist/schema/nodes/box-vent.js +63 -0
- package/dist/schema/nodes/building.d.ts +1 -1
- package/dist/schema/nodes/building.d.ts.map +1 -1
- package/dist/schema/nodes/building.js +3 -2
- package/dist/schema/nodes/ceiling.d.ts +2 -0
- package/dist/schema/nodes/ceiling.d.ts.map +1 -1
- package/dist/schema/nodes/ceiling.js +1 -1
- package/dist/schema/nodes/chimney.d.ts +158 -0
- package/dist/schema/nodes/chimney.d.ts.map +1 -0
- package/dist/schema/nodes/chimney.js +67 -0
- package/dist/schema/nodes/column.d.ts +597 -5
- package/dist/schema/nodes/column.d.ts.map +1 -1
- package/dist/schema/nodes/column.js +586 -6
- package/dist/schema/nodes/cupola.d.ts +66 -0
- package/dist/schema/nodes/cupola.d.ts.map +1 -0
- package/dist/schema/nodes/cupola.js +28 -0
- package/dist/schema/nodes/dormer.d.ts +228 -0
- package/dist/schema/nodes/dormer.d.ts.map +1 -0
- package/dist/schema/nodes/dormer.js +119 -0
- package/dist/schema/nodes/downspout.d.ts +75 -0
- package/dist/schema/nodes/downspout.d.ts.map +1 -0
- package/dist/schema/nodes/downspout.js +63 -0
- package/dist/schema/nodes/elevator.d.ts +105 -0
- package/dist/schema/nodes/elevator.d.ts.map +1 -0
- package/dist/schema/nodes/elevator.js +54 -0
- package/dist/schema/nodes/eyebrow-vent.d.ts +68 -0
- package/dist/schema/nodes/eyebrow-vent.d.ts.map +1 -0
- package/dist/schema/nodes/eyebrow-vent.js +40 -0
- package/dist/schema/nodes/fence.d.ts +1 -0
- package/dist/schema/nodes/fence.d.ts.map +1 -1
- package/dist/schema/nodes/fence.js +2 -0
- package/dist/schema/nodes/gutter.d.ts +84 -0
- package/dist/schema/nodes/gutter.d.ts.map +1 -0
- package/dist/schema/nodes/gutter.js +76 -0
- package/dist/schema/nodes/item.d.ts +4 -0
- package/dist/schema/nodes/item.d.ts.map +1 -1
- package/dist/schema/nodes/item.js +9 -0
- package/dist/schema/nodes/level.d.ts +1 -1
- package/dist/schema/nodes/level.d.ts.map +1 -1
- package/dist/schema/nodes/level.js +2 -0
- package/dist/schema/nodes/ridge-vent.d.ts +67 -0
- package/dist/schema/nodes/ridge-vent.d.ts.map +1 -0
- package/dist/schema/nodes/ridge-vent.js +30 -0
- package/dist/schema/nodes/roof-segment.d.ts +195 -1
- package/dist/schema/nodes/roof-segment.d.ts.map +1 -1
- package/dist/schema/nodes/roof-segment.js +254 -3
- package/dist/schema/nodes/roof.d.ts.map +1 -1
- package/dist/schema/nodes/roof.js +4 -16
- package/dist/schema/nodes/shelf.d.ts +105 -0
- package/dist/schema/nodes/shelf.d.ts.map +1 -0
- package/dist/schema/nodes/shelf.js +84 -0
- package/dist/schema/nodes/site.d.ts +1 -122
- package/dist/schema/nodes/site.d.ts.map +1 -1
- package/dist/schema/nodes/site.js +2 -6
- package/dist/schema/nodes/skylight.d.ts +279 -0
- package/dist/schema/nodes/skylight.d.ts.map +1 -0
- package/dist/schema/nodes/skylight.js +152 -0
- package/dist/schema/nodes/slab.d.ts +2 -0
- package/dist/schema/nodes/slab.d.ts.map +1 -1
- package/dist/schema/nodes/slab.js +1 -1
- package/dist/schema/nodes/solar-panel.d.ts +117 -0
- package/dist/schema/nodes/solar-panel.d.ts.map +1 -0
- package/dist/schema/nodes/solar-panel.js +47 -0
- package/dist/schema/nodes/surface-hole-metadata.d.ts +2 -0
- package/dist/schema/nodes/surface-hole-metadata.d.ts.map +1 -1
- package/dist/schema/nodes/surface-hole-metadata.js +3 -1
- package/dist/schema/nodes/turbine-vent.d.ts +68 -0
- package/dist/schema/nodes/turbine-vent.d.ts.map +1 -0
- package/dist/schema/nodes/turbine-vent.js +41 -0
- package/dist/schema/types.d.ts +1325 -125
- package/dist/schema/types.d.ts.map +1 -1
- package/dist/schema/types.js +26 -0
- package/dist/services/alignment-anchors.d.ts +100 -0
- package/dist/services/alignment-anchors.d.ts.map +1 -0
- package/dist/services/alignment-anchors.js +283 -0
- package/dist/services/alignment-anchors.test.d.ts +2 -0
- package/dist/services/alignment-anchors.test.d.ts.map +1 -0
- package/dist/services/alignment-anchors.test.js +383 -0
- package/dist/services/alignment.d.ts +126 -0
- package/dist/services/alignment.d.ts.map +1 -0
- package/dist/services/alignment.js +169 -0
- package/dist/services/alignment.test.d.ts +2 -0
- package/dist/services/alignment.test.d.ts.map +1 -0
- package/dist/services/alignment.test.js +92 -0
- package/dist/services/drag-session.d.ts +47 -0
- package/dist/services/drag-session.d.ts.map +1 -0
- package/dist/services/drag-session.js +100 -0
- package/dist/services/drag-session.test.d.ts +2 -0
- package/dist/services/drag-session.test.d.ts.map +1 -0
- package/dist/services/drag-session.test.js +219 -0
- package/dist/services/hosting.d.ts +80 -0
- package/dist/services/hosting.d.ts.map +1 -0
- package/dist/services/hosting.js +122 -0
- package/dist/services/hosting.test.d.ts +2 -0
- package/dist/services/hosting.test.d.ts.map +1 -0
- package/dist/services/hosting.test.js +199 -0
- package/dist/services/index.d.ts +7 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/services/index.js +6 -0
- package/dist/services/movement.d.ts +48 -0
- package/dist/services/movement.d.ts.map +1 -0
- package/dist/services/movement.js +65 -0
- package/dist/services/movement.test.d.ts +2 -0
- package/dist/services/movement.test.d.ts.map +1 -0
- package/dist/services/movement.test.js +133 -0
- package/dist/services/single-undo-dance.test.d.ts +2 -0
- package/dist/services/single-undo-dance.test.d.ts.map +1 -0
- package/dist/services/single-undo-dance.test.js +210 -0
- package/dist/services/snap.d.ts +72 -0
- package/dist/services/snap.d.ts.map +1 -0
- package/dist/services/snap.js +120 -0
- package/dist/services/snap.test.d.ts +2 -0
- package/dist/services/snap.test.d.ts.map +1 -0
- package/dist/services/snap.test.js +95 -0
- package/dist/solar-panel-presets.d.ts +17 -0
- package/dist/solar-panel-presets.d.ts.map +1 -0
- package/dist/solar-panel-presets.js +43 -0
- package/dist/store/actions/node-actions.d.ts +14 -2
- package/dist/store/actions/node-actions.d.ts.map +1 -1
- package/dist/store/actions/node-actions.js +492 -15
- package/dist/store/actions/node-mutation-sanitize.test.d.ts +2 -0
- package/dist/store/actions/node-mutation-sanitize.test.d.ts.map +1 -0
- package/dist/store/actions/node-mutation-sanitize.test.js +147 -0
- package/dist/store/actions/reparent.test.d.ts +2 -0
- package/dist/store/actions/reparent.test.d.ts.map +1 -0
- package/dist/store/actions/reparent.test.js +212 -0
- package/dist/store/use-interactive.d.ts +39 -0
- package/dist/store/use-interactive.d.ts.map +1 -1
- package/dist/store/use-interactive.js +75 -0
- package/dist/store/use-live-node-overrides.d.ts +22 -0
- package/dist/store/use-live-node-overrides.d.ts.map +1 -0
- package/dist/store/use-live-node-overrides.js +59 -0
- package/dist/store/use-scene-elevator-migration.test.d.ts +2 -0
- package/dist/store/use-scene-elevator-migration.test.d.ts.map +1 -0
- package/dist/store/use-scene-elevator-migration.test.js +109 -0
- package/dist/store/use-scene.d.ts +11 -0
- package/dist/store/use-scene.d.ts.map +1 -1
- package/dist/store/use-scene.js +196 -6
- package/dist/systems/elevator/elevator-dispatch.d.ts +12 -0
- package/dist/systems/elevator/elevator-dispatch.d.ts.map +1 -0
- package/dist/systems/elevator/elevator-dispatch.js +57 -0
- package/dist/systems/elevator/elevator-geometry.d.ts +15 -0
- package/dist/systems/elevator/elevator-geometry.d.ts.map +1 -0
- package/dist/systems/elevator/elevator-geometry.js +50 -0
- package/dist/systems/elevator/elevator-opening-sync.d.ts +6 -0
- package/dist/systems/elevator/elevator-opening-sync.d.ts.map +1 -0
- package/dist/systems/elevator/elevator-opening-sync.js +218 -0
- package/dist/systems/elevator/elevator-opening-sync.test.d.ts +2 -0
- package/dist/systems/elevator/elevator-opening-sync.test.d.ts.map +1 -0
- package/dist/systems/elevator/elevator-opening-sync.test.js +146 -0
- package/dist/systems/elevator/elevator-opening-system.d.ts +2 -0
- package/dist/systems/elevator/elevator-opening-system.d.ts.map +1 -0
- package/dist/systems/elevator/elevator-opening-system.js +48 -0
- package/dist/systems/elevator/elevator-runtime-system.d.ts +2 -0
- package/dist/systems/elevator/elevator-runtime-system.d.ts.map +1 -0
- package/dist/systems/elevator/elevator-runtime-system.js +8 -0
- package/dist/systems/elevator/elevator-runtime.d.ts +18 -0
- package/dist/systems/elevator/elevator-runtime.d.ts.map +1 -0
- package/dist/systems/elevator/elevator-runtime.js +225 -0
- package/dist/systems/elevator/elevator-runtime.test.d.ts +2 -0
- package/dist/systems/elevator/elevator-runtime.test.d.ts.map +1 -0
- package/dist/systems/elevator/elevator-runtime.test.js +64 -0
- package/dist/systems/elevator/elevator-service.d.ts +19 -0
- package/dist/systems/elevator/elevator-service.d.ts.map +1 -0
- package/dist/systems/elevator/elevator-service.js +111 -0
- package/dist/systems/stair/stair-footprint.d.ts +44 -0
- package/dist/systems/stair/stair-footprint.d.ts.map +1 -0
- package/dist/systems/stair/stair-footprint.js +168 -0
- package/dist/systems/stair/stair-opening-preview.d.ts +1777 -0
- package/dist/systems/stair/stair-opening-preview.d.ts.map +1 -0
- package/dist/systems/stair/stair-opening-preview.js +93 -0
- package/dist/systems/stair/stair-opening-preview.test.d.ts +2 -0
- package/dist/systems/stair/stair-opening-preview.test.d.ts.map +1 -0
- package/dist/systems/stair/stair-opening-preview.test.js +80 -0
- package/dist/systems/stair/stair-opening-sync.d.ts.map +1 -1
- package/dist/systems/stair/stair-opening-sync.js +172 -147
- package/dist/systems/stair/stair-opening-sync.test.js +385 -2
- package/dist/systems/stair/stair-opening-system.d.ts +2 -0
- package/dist/systems/stair/stair-opening-system.d.ts.map +1 -0
- package/dist/systems/stair/stair-opening-system.js +104 -0
- package/dist/systems/wall/wall-move.d.ts +48 -0
- package/dist/systems/wall/wall-move.d.ts.map +1 -0
- package/dist/systems/wall/wall-move.js +193 -0
- package/dist/utils/clone-scene-graph.d.ts +6 -4
- package/dist/utils/clone-scene-graph.d.ts.map +1 -1
- package/dist/utils/clone-scene-graph.js +6 -4
- package/dist/utils/clone-scene-graph.test.d.ts +2 -0
- package/dist/utils/clone-scene-graph.test.d.ts.map +1 -0
- package/dist/utils/clone-scene-graph.test.js +59 -0
- package/dist/validation/validate-build-json.d.ts +43 -0
- package/dist/validation/validate-build-json.d.ts.map +1 -0
- package/dist/validation/validate-build-json.js +227 -0
- package/package.json +9 -2
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type WallNode } from '../schema';
|
|
1
|
+
import { type CeilingNode as CeilingNodeType, type SlabNode as SlabNodeType, type WallNode } from '../schema';
|
|
2
2
|
type Point2D = {
|
|
3
3
|
x: number;
|
|
4
4
|
y: number;
|
|
@@ -15,12 +15,41 @@ type WallSideUpdate = {
|
|
|
15
15
|
frontSide: 'interior' | 'exterior' | 'unknown';
|
|
16
16
|
backSide: 'interior' | 'exterior' | 'unknown';
|
|
17
17
|
};
|
|
18
|
+
export type AutoSlabSyncPlan = {
|
|
19
|
+
create: SlabNodeType[];
|
|
20
|
+
update: Array<{
|
|
21
|
+
id: SlabNodeType['id'];
|
|
22
|
+
data: Partial<SlabNodeType>;
|
|
23
|
+
}>;
|
|
24
|
+
delete: Array<SlabNodeType['id']>;
|
|
25
|
+
};
|
|
26
|
+
export type AutoCeilingSyncPlan = {
|
|
27
|
+
create: CeilingNodeType[];
|
|
28
|
+
update: Array<{
|
|
29
|
+
id: CeilingNodeType['id'];
|
|
30
|
+
data: Partial<CeilingNodeType>;
|
|
31
|
+
}>;
|
|
32
|
+
delete: Array<CeilingNodeType['id']>;
|
|
33
|
+
};
|
|
34
|
+
export type AutoCeilingPlanningContext = {
|
|
35
|
+
walls?: WallNode[];
|
|
36
|
+
slabs?: SlabNodeType[];
|
|
37
|
+
};
|
|
18
38
|
export declare function resolveWallSurfaceSides(wall: Pick<WallNode, 'start' | 'end' | 'thickness' | 'frontSide' | 'backSide'>, roomPolygons: Point2D[][]): Pick<WallSideUpdate, 'frontSide' | 'backSide'>;
|
|
39
|
+
export declare function planAutoSlabsForLevel(roomPolygons: Point2D[][], existingSlabs: SlabNodeType[]): AutoSlabSyncPlan;
|
|
40
|
+
export declare function projectAutoSlabsForPlan(existingSlabs: SlabNodeType[], plan: AutoSlabSyncPlan): SlabNodeType[];
|
|
41
|
+
export declare function planAutoCeilingsForLevel(roomPolygons: Point2D[][], existingCeilings: CeilingNodeType[], context?: AutoCeilingPlanningContext): AutoCeilingSyncPlan;
|
|
19
42
|
export declare function detectSpacesForLevel(levelId: string, walls: WallNode[]): {
|
|
20
43
|
roomPolygons: Point2D[][];
|
|
21
44
|
spaces: Space[];
|
|
22
45
|
wallUpdates: WallSideUpdate[];
|
|
23
46
|
};
|
|
47
|
+
/** Pause the wall-driven auto slab/ceiling sync. Refcounted — pair with `resumeSpaceDetection`. */
|
|
48
|
+
export declare function pauseSpaceDetection(): void;
|
|
49
|
+
/** Resume the wall-driven auto slab/ceiling sync. No-op if not currently paused. */
|
|
50
|
+
export declare function resumeSpaceDetection(): void;
|
|
51
|
+
/** True iff the wall-driven auto slab/ceiling sync is currently paused. */
|
|
52
|
+
export declare function isSpaceDetectionPaused(): boolean;
|
|
24
53
|
export declare function initSpaceDetectionSync(sceneStore: any, editorStore: any): () => void;
|
|
25
54
|
export declare function wallTouchesOthers(wall: WallNode, otherWalls: WallNode[]): boolean;
|
|
26
55
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"space-detection.d.ts","sourceRoot":"","sources":["../../src/lib/space-detection.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"space-detection.d.ts","sourceRoot":"","sources":["../../src/lib/space-detection.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,9 +4,11 @@ import { getClampedWallCurveOffset, getWallCurveFrameAt, isCurvedWall, } from '.
|
|
|
4
4
|
import { simplifyClosedPolygon } from './polygon-geometry';
|
|
5
5
|
const DEFAULT_AUTO_SLAB_ELEVATION = 0.05;
|
|
6
6
|
const DEFAULT_AUTO_CEILING_HEIGHT = 2.5;
|
|
7
|
+
const CEILING_HEIGHT_EPSILON = 1e-6;
|
|
7
8
|
const ROOM_CURVE_TOLERANCE = 0.04;
|
|
8
9
|
const MAX_CURVE_SUBDIVISION_DEPTH = 6;
|
|
9
10
|
const AUTO_SLAB_POLYGON_SIMPLIFY_TOLERANCE = 0.08;
|
|
11
|
+
const WALL_ROOM_BOUNDARY_TOLERANCE = 0.08;
|
|
10
12
|
function pointFromTuple(point) {
|
|
11
13
|
return { x: point[0], y: point[1] };
|
|
12
14
|
}
|
|
@@ -114,6 +116,80 @@ function bboxOverlapArea(a, b) {
|
|
|
114
116
|
const iy = Math.max(0, Math.min(a.maxY, b.maxY) - Math.max(a.minY, b.minY));
|
|
115
117
|
return ix * iy;
|
|
116
118
|
}
|
|
119
|
+
function pointDistanceToPolygonBoundary(point, polygon) {
|
|
120
|
+
let minDistance = Number.POSITIVE_INFINITY;
|
|
121
|
+
for (let index = 0; index < polygon.length; index += 1) {
|
|
122
|
+
const start = polygon[index];
|
|
123
|
+
const end = polygon[(index + 1) % polygon.length];
|
|
124
|
+
if (!(start && end))
|
|
125
|
+
continue;
|
|
126
|
+
minDistance = Math.min(minDistance, distanceToSegment(pointToTuple(point), pointToTuple(start), pointToTuple(end)));
|
|
127
|
+
}
|
|
128
|
+
return minDistance;
|
|
129
|
+
}
|
|
130
|
+
function wallBoundsRoom(wall, roomPolygon) {
|
|
131
|
+
const sampled = sampleWallPointsForRoomDetection(wall);
|
|
132
|
+
if (sampled.length === 0)
|
|
133
|
+
return false;
|
|
134
|
+
const candidates = sampled.length === 2
|
|
135
|
+
? [
|
|
136
|
+
sampled[0],
|
|
137
|
+
{
|
|
138
|
+
x: (sampled[0].x + sampled[1].x) / 2,
|
|
139
|
+
y: (sampled[0].y + sampled[1].y) / 2,
|
|
140
|
+
},
|
|
141
|
+
sampled[1],
|
|
142
|
+
]
|
|
143
|
+
: sampled;
|
|
144
|
+
const matchingPoints = candidates.filter((point) => pointDistanceToPolygonBoundary(point, roomPolygon) <= WALL_ROOM_BOUNDARY_TOLERANCE);
|
|
145
|
+
return matchingPoints.length >= 2;
|
|
146
|
+
}
|
|
147
|
+
function pointIsOnSlab(point, slab) {
|
|
148
|
+
if (slab.polygon.length < 3)
|
|
149
|
+
return false;
|
|
150
|
+
const slabPolygon = slab.polygon.map(pointFromTuple);
|
|
151
|
+
if (!pointInPolygon(point, slabPolygon))
|
|
152
|
+
return false;
|
|
153
|
+
for (const hole of slab.holes ?? []) {
|
|
154
|
+
if (hole.length >= 3 && pointInPolygon(point, hole.map(pointFromTuple))) {
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return true;
|
|
159
|
+
}
|
|
160
|
+
function slabSupportsRoom(roomPolygon, slab) {
|
|
161
|
+
if (slab.polygon.length < 3)
|
|
162
|
+
return false;
|
|
163
|
+
if (polygonSignature(slab.polygon.map(pointFromTuple)) === polygonSignature(roomPolygon)) {
|
|
164
|
+
return true;
|
|
165
|
+
}
|
|
166
|
+
return pointIsOnSlab(polygonCentroid(roomPolygon), slab);
|
|
167
|
+
}
|
|
168
|
+
function resolveRoomSlabElevation(roomPolygon, slabs = []) {
|
|
169
|
+
let maxElevation = 0;
|
|
170
|
+
for (const slab of slabs) {
|
|
171
|
+
if (!slabSupportsRoom(roomPolygon, slab))
|
|
172
|
+
continue;
|
|
173
|
+
maxElevation = Math.max(maxElevation, slab.elevation ?? DEFAULT_AUTO_SLAB_ELEVATION);
|
|
174
|
+
}
|
|
175
|
+
return maxElevation;
|
|
176
|
+
}
|
|
177
|
+
function resolveRoomWallHeight(roomPolygon, walls = []) {
|
|
178
|
+
let maxHeight = 0;
|
|
179
|
+
for (const wall of walls) {
|
|
180
|
+
if (!wallBoundsRoom(wall, roomPolygon))
|
|
181
|
+
continue;
|
|
182
|
+
const height = wall.height ?? DEFAULT_AUTO_CEILING_HEIGHT;
|
|
183
|
+
if (Number.isFinite(height)) {
|
|
184
|
+
maxHeight = Math.max(maxHeight, height);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return maxHeight > 0 ? maxHeight : DEFAULT_AUTO_CEILING_HEIGHT;
|
|
188
|
+
}
|
|
189
|
+
function resolveAutoCeilingHeight(roomPolygon, context = {}) {
|
|
190
|
+
return (resolveRoomSlabElevation(roomPolygon, context.slabs) +
|
|
191
|
+
resolveRoomWallHeight(roomPolygon, context.walls));
|
|
192
|
+
}
|
|
117
193
|
function getWallDirection(wall) {
|
|
118
194
|
const dx = wall.end[0] - wall.start[0];
|
|
119
195
|
const dy = wall.end[1] - wall.start[1];
|
|
@@ -340,12 +416,48 @@ function wallGeometrySignature(wall) {
|
|
|
340
416
|
wall.end[0].toFixed(4),
|
|
341
417
|
wall.end[1].toFixed(4),
|
|
342
418
|
(wall.thickness ?? 0.2).toFixed(4),
|
|
419
|
+
(wall.height ?? DEFAULT_AUTO_CEILING_HEIGHT).toFixed(4),
|
|
343
420
|
getClampedWallCurveOffset(wall).toFixed(4),
|
|
344
421
|
].join('|');
|
|
345
422
|
}
|
|
346
423
|
function levelWallSnapshot(walls) {
|
|
347
424
|
return walls.map(wallGeometrySignature).sort().join('||');
|
|
348
425
|
}
|
|
426
|
+
function slabGeometrySignature(slab) {
|
|
427
|
+
const polygon = slab.polygon
|
|
428
|
+
.map((point) => `${point[0].toFixed(4)},${point[1].toFixed(4)}`)
|
|
429
|
+
.join(';');
|
|
430
|
+
const holes = (slab.holes ?? [])
|
|
431
|
+
.map((hole) => hole.map((point) => `${point[0].toFixed(4)},${point[1].toFixed(4)}`).join(';'))
|
|
432
|
+
.join('/');
|
|
433
|
+
return [slab.id, (slab.elevation ?? DEFAULT_AUTO_SLAB_ELEVATION).toFixed(4), polygon, holes].join('|');
|
|
434
|
+
}
|
|
435
|
+
function levelSlabSnapshot(slabs) {
|
|
436
|
+
return slabs.map(slabGeometrySignature).sort().join('||');
|
|
437
|
+
}
|
|
438
|
+
function levelStructureSnapshots(nodes) {
|
|
439
|
+
const byLevel = new Map();
|
|
440
|
+
const getEntry = (levelId) => {
|
|
441
|
+
const entry = byLevel.get(levelId) ?? { walls: [], slabs: [] };
|
|
442
|
+
byLevel.set(levelId, entry);
|
|
443
|
+
return entry;
|
|
444
|
+
};
|
|
445
|
+
for (const node of Object.values(nodes)) {
|
|
446
|
+
if (!(node && typeof node === 'object' && 'parentId' in node && node.parentId))
|
|
447
|
+
continue;
|
|
448
|
+
if (node.type === 'wall') {
|
|
449
|
+
getEntry(node.parentId).walls.push(node);
|
|
450
|
+
}
|
|
451
|
+
else if (node.type === 'slab') {
|
|
452
|
+
getEntry(node.parentId).slabs.push(SlabNode.parse(node));
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
const snapshots = new Map();
|
|
456
|
+
for (const [levelId, entry] of byLevel.entries()) {
|
|
457
|
+
snapshots.set(levelId, `${levelWallSnapshot(entry.walls)}##${levelSlabSnapshot(entry.slabs)}`);
|
|
458
|
+
}
|
|
459
|
+
return snapshots;
|
|
460
|
+
}
|
|
349
461
|
function buildSpace(levelId, polygon) {
|
|
350
462
|
const signature = polygonSignature(polygon);
|
|
351
463
|
return {
|
|
@@ -356,7 +468,7 @@ function buildSpace(levelId, polygon) {
|
|
|
356
468
|
isExterior: false,
|
|
357
469
|
};
|
|
358
470
|
}
|
|
359
|
-
function
|
|
471
|
+
export function planAutoSlabsForLevel(roomPolygons, existingSlabs) {
|
|
360
472
|
const manualSlabs = existingSlabs.filter((slab) => !slab.autoFromWalls);
|
|
361
473
|
const manualSignatures = new Set(manualSlabs.map((slab) => polygonSignature(slab.polygon.map(pointFromTuple))));
|
|
362
474
|
const detected = roomPolygons
|
|
@@ -459,17 +571,42 @@ function syncAutoSlabsForLevel(levelId, roomPolygons, existingSlabs, sceneStore)
|
|
|
459
571
|
autoFromWalls: true,
|
|
460
572
|
}));
|
|
461
573
|
}
|
|
462
|
-
|
|
463
|
-
|
|
574
|
+
return {
|
|
575
|
+
create: slabsToCreate,
|
|
576
|
+
update: slabsToUpdate,
|
|
577
|
+
delete: slabsToDelete,
|
|
578
|
+
};
|
|
579
|
+
}
|
|
580
|
+
function syncAutoSlabsForLevel(levelId, roomPolygons, existingSlabs, sceneStore) {
|
|
581
|
+
const plan = planAutoSlabsForLevel(roomPolygons, existingSlabs);
|
|
582
|
+
if (plan.delete.length > 0) {
|
|
583
|
+
sceneStore.getState().deleteNodes(plan.delete);
|
|
584
|
+
}
|
|
585
|
+
if (plan.update.length > 0) {
|
|
586
|
+
sceneStore.getState().updateNodes(plan.update);
|
|
587
|
+
}
|
|
588
|
+
if (plan.create.length > 0) {
|
|
589
|
+
sceneStore.getState().createNodes(plan.create.map((node) => ({ node, parentId: levelId })));
|
|
464
590
|
}
|
|
465
|
-
|
|
466
|
-
|
|
591
|
+
return plan;
|
|
592
|
+
}
|
|
593
|
+
export function projectAutoSlabsForPlan(existingSlabs, plan) {
|
|
594
|
+
const slabsById = new Map(existingSlabs.map((slab) => [slab.id, slab]));
|
|
595
|
+
for (const id of plan.delete) {
|
|
596
|
+
slabsById.delete(id);
|
|
597
|
+
}
|
|
598
|
+
for (const update of plan.update) {
|
|
599
|
+
const slab = slabsById.get(update.id);
|
|
600
|
+
if (!slab)
|
|
601
|
+
continue;
|
|
602
|
+
slabsById.set(update.id, SlabNode.parse({ ...slab, ...update.data }));
|
|
467
603
|
}
|
|
468
|
-
|
|
469
|
-
|
|
604
|
+
for (const slab of plan.create) {
|
|
605
|
+
slabsById.set(slab.id, slab);
|
|
470
606
|
}
|
|
607
|
+
return [...slabsById.values()];
|
|
471
608
|
}
|
|
472
|
-
function
|
|
609
|
+
export function planAutoCeilingsForLevel(roomPolygons, existingCeilings, context = {}) {
|
|
473
610
|
const manualCeilings = existingCeilings.filter((ceiling) => !ceiling.autoFromWalls);
|
|
474
611
|
const manualSignatures = new Set(manualCeilings.map((ceiling) => polygonSignature(ceiling.polygon.map(pointFromTuple))));
|
|
475
612
|
const detected = roomPolygons
|
|
@@ -486,6 +623,7 @@ function syncAutoCeilingsForLevel(levelId, roomPolygons, existingCeilings, scene
|
|
|
486
623
|
centroid: polygonCentroid(room.poly),
|
|
487
624
|
area: Math.abs(polygonArea(room.poly)),
|
|
488
625
|
bbox: bboxOf(room.poly),
|
|
626
|
+
ceilingHeight: resolveAutoCeilingHeight(room.poly, context),
|
|
489
627
|
}))
|
|
490
628
|
.filter(({ sig }) => !manualSignatures.has(sig));
|
|
491
629
|
const existingAuto = existingCeilings.filter((ceiling) => ceiling.autoFromWalls);
|
|
@@ -512,7 +650,10 @@ function syncAutoCeilingsForLevel(levelId, roomPolygons, existingCeilings, scene
|
|
|
512
650
|
return;
|
|
513
651
|
matchedDetectedIdx.add(index);
|
|
514
652
|
matchedCeilingIds.add(existing.ceiling.id);
|
|
515
|
-
updatesById.set(existing.ceiling.id,
|
|
653
|
+
updatesById.set(existing.ceiling.id, {
|
|
654
|
+
polygon: room.poly.map(pointToTuple),
|
|
655
|
+
height: room.ceilingHeight,
|
|
656
|
+
});
|
|
516
657
|
});
|
|
517
658
|
const remainingDetected = detected
|
|
518
659
|
.map((room, index) => ({ room, index }))
|
|
@@ -541,7 +682,10 @@ function syncAutoCeilingsForLevel(levelId, roomPolygons, existingCeilings, scene
|
|
|
541
682
|
continue;
|
|
542
683
|
matchedDetectedIdx.add(index);
|
|
543
684
|
matchedCeilingIds.add(bestMatch.entry.ceiling.id);
|
|
544
|
-
updatesById.set(bestMatch.entry.ceiling.id,
|
|
685
|
+
updatesById.set(bestMatch.entry.ceiling.id, {
|
|
686
|
+
polygon: room.poly.map(pointToTuple),
|
|
687
|
+
height: room.ceilingHeight,
|
|
688
|
+
});
|
|
545
689
|
}
|
|
546
690
|
const ceilingsToDelete = existingAuto
|
|
547
691
|
.filter((ceiling) => !updatesById.has(ceiling.id))
|
|
@@ -549,12 +693,18 @@ function syncAutoCeilingsForLevel(levelId, roomPolygons, existingCeilings, scene
|
|
|
549
693
|
const ceilingsToUpdate = existingAuto
|
|
550
694
|
.filter((ceiling) => updatesById.has(ceiling.id))
|
|
551
695
|
.flatMap((ceiling) => {
|
|
552
|
-
const
|
|
553
|
-
if (!
|
|
696
|
+
const update = updatesById.get(ceiling.id);
|
|
697
|
+
if (!update)
|
|
554
698
|
return [];
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
699
|
+
const data = {};
|
|
700
|
+
if (!sameTuplePolygon(ceiling.polygon, update.polygon)) {
|
|
701
|
+
data.polygon = update.polygon;
|
|
702
|
+
}
|
|
703
|
+
if (Math.abs((ceiling.height ?? DEFAULT_AUTO_CEILING_HEIGHT) - update.height) >
|
|
704
|
+
CEILING_HEIGHT_EPSILON) {
|
|
705
|
+
data.height = update.height;
|
|
706
|
+
}
|
|
707
|
+
return Object.keys(data).length === 0 ? [] : [{ id: ceiling.id, data }];
|
|
558
708
|
});
|
|
559
709
|
const plannedCeilingsForNaming = [...existingCeilings];
|
|
560
710
|
const ceilingsToCreate = [];
|
|
@@ -570,18 +720,26 @@ function syncAutoCeilingsForLevel(levelId, roomPolygons, existingCeilings, scene
|
|
|
570
720
|
name,
|
|
571
721
|
polygon: room.poly.map(pointToTuple),
|
|
572
722
|
holes: [],
|
|
573
|
-
height:
|
|
723
|
+
height: room.ceilingHeight,
|
|
574
724
|
autoFromWalls: true,
|
|
575
725
|
}));
|
|
576
726
|
}
|
|
577
|
-
|
|
578
|
-
|
|
727
|
+
return {
|
|
728
|
+
create: ceilingsToCreate,
|
|
729
|
+
update: ceilingsToUpdate,
|
|
730
|
+
delete: ceilingsToDelete,
|
|
731
|
+
};
|
|
732
|
+
}
|
|
733
|
+
function syncAutoCeilingsForLevel(levelId, roomPolygons, existingCeilings, sceneStore, context = {}) {
|
|
734
|
+
const plan = planAutoCeilingsForLevel(roomPolygons, existingCeilings, context);
|
|
735
|
+
if (plan.delete.length > 0) {
|
|
736
|
+
sceneStore.getState().deleteNodes(plan.delete);
|
|
579
737
|
}
|
|
580
|
-
if (
|
|
581
|
-
sceneStore.getState().updateNodes(
|
|
738
|
+
if (plan.update.length > 0) {
|
|
739
|
+
sceneStore.getState().updateNodes(plan.update);
|
|
582
740
|
}
|
|
583
|
-
if (
|
|
584
|
-
sceneStore.getState().createNodes(
|
|
741
|
+
if (plan.create.length > 0) {
|
|
742
|
+
sceneStore.getState().createNodes(plan.create.map((node) => ({ node, parentId: levelId })));
|
|
585
743
|
}
|
|
586
744
|
}
|
|
587
745
|
function detectSpacesFromWalls(levelId, walls) {
|
|
@@ -626,14 +784,36 @@ function runSpaceDetection(levelIds, sceneStore, editorStore, nodes) {
|
|
|
626
784
|
},
|
|
627
785
|
})));
|
|
628
786
|
}
|
|
629
|
-
|
|
630
|
-
|
|
787
|
+
const parsedSlabs = slabs.map((slab) => SlabNode.parse(slab));
|
|
788
|
+
const slabPlan = syncAutoSlabsForLevel(levelId, roomPolygons, parsedSlabs, sceneStore);
|
|
789
|
+
const projectedSlabs = projectAutoSlabsForPlan(parsedSlabs, slabPlan);
|
|
790
|
+
syncAutoCeilingsForLevel(levelId, roomPolygons, ceilings.map((ceiling) => CeilingNode.parse(ceiling)), sceneStore, { walls, slabs: projectedSlabs });
|
|
631
791
|
for (const space of spaces) {
|
|
632
792
|
nextSpaces[space.id] = space;
|
|
633
793
|
}
|
|
634
794
|
}
|
|
635
795
|
editorStore.getState().setSpaces(nextSpaces);
|
|
636
796
|
}
|
|
797
|
+
// Refcount of outstanding pause requests, matching the pauseSceneHistory
|
|
798
|
+
// pattern. The community editor flips this off while the AI is actively
|
|
799
|
+
// mutating the scene so the wall-driven auto slab/ceiling sync doesn't race
|
|
800
|
+
// `create_room`'s explicit slabs/ceilings (see plan
|
|
801
|
+
// `ai-pause-space-detection`).
|
|
802
|
+
let spaceDetectionPauseDepth = 0;
|
|
803
|
+
/** Pause the wall-driven auto slab/ceiling sync. Refcounted — pair with `resumeSpaceDetection`. */
|
|
804
|
+
export function pauseSpaceDetection() {
|
|
805
|
+
spaceDetectionPauseDepth += 1;
|
|
806
|
+
}
|
|
807
|
+
/** Resume the wall-driven auto slab/ceiling sync. No-op if not currently paused. */
|
|
808
|
+
export function resumeSpaceDetection() {
|
|
809
|
+
if (spaceDetectionPauseDepth === 0)
|
|
810
|
+
return;
|
|
811
|
+
spaceDetectionPauseDepth -= 1;
|
|
812
|
+
}
|
|
813
|
+
/** True iff the wall-driven auto slab/ceiling sync is currently paused. */
|
|
814
|
+
export function isSpaceDetectionPaused() {
|
|
815
|
+
return spaceDetectionPauseDepth > 0;
|
|
816
|
+
}
|
|
637
817
|
export function initSpaceDetectionSync(sceneStore, editorStore) {
|
|
638
818
|
const previousSnapshots = new Map();
|
|
639
819
|
let isProcessing = false;
|
|
@@ -643,18 +823,16 @@ export function initSpaceDetectionSync(sceneStore, editorStore) {
|
|
|
643
823
|
if (getSceneHistoryPauseDepth() > 0)
|
|
644
824
|
return;
|
|
645
825
|
const nodes = state.nodes;
|
|
646
|
-
const
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
826
|
+
const currentSnapshots = levelStructureSnapshots(nodes);
|
|
827
|
+
// Paused: roll the snapshot forward so we don't backfill (and re-duplicate)
|
|
828
|
+
// every paused change once detection resumes. Whatever the AI built while
|
|
829
|
+
// paused becomes the new baseline; only future changes will reconcile.
|
|
830
|
+
if (spaceDetectionPauseDepth > 0) {
|
|
831
|
+
previousSnapshots.clear();
|
|
832
|
+
for (const [levelId, snapshot] of currentSnapshots.entries()) {
|
|
833
|
+
previousSnapshots.set(levelId, snapshot);
|
|
653
834
|
}
|
|
654
|
-
|
|
655
|
-
const currentSnapshots = new Map();
|
|
656
|
-
for (const [levelId, walls] of wallsByLevel.entries()) {
|
|
657
|
-
currentSnapshots.set(levelId, levelWallSnapshot(walls));
|
|
835
|
+
return;
|
|
658
836
|
}
|
|
659
837
|
const levelsToUpdate = new Set();
|
|
660
838
|
for (const levelId of new Set([...previousSnapshots.keys(), ...currentSnapshots.keys()])) {
|
|
@@ -677,7 +855,8 @@ export function initSpaceDetectionSync(sceneStore, editorStore) {
|
|
|
677
855
|
finally {
|
|
678
856
|
resumeSceneHistory(sceneStore);
|
|
679
857
|
previousSnapshots.clear();
|
|
680
|
-
|
|
858
|
+
const postRunSnapshots = levelStructureSnapshots(sceneStore.getState().nodes);
|
|
859
|
+
for (const [levelId, snapshot] of postRunSnapshots.entries()) {
|
|
681
860
|
previousSnapshots.set(levelId, snapshot);
|
|
682
861
|
}
|
|
683
862
|
isProcessing = false;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"space-detection.test.d.ts","sourceRoot":"","sources":["../../src/lib/space-detection.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { describe, expect, test } from 'bun:test';
|
|
2
|
+
import { CeilingNode, SlabNode, WallNode } from '../schema';
|
|
3
|
+
import { planAutoCeilingsForLevel } from './space-detection';
|
|
4
|
+
const square = [
|
|
5
|
+
[0, 0],
|
|
6
|
+
[4, 0],
|
|
7
|
+
[4, 3],
|
|
8
|
+
[0, 3],
|
|
9
|
+
];
|
|
10
|
+
function roomPolygon() {
|
|
11
|
+
return square.map(([x, y]) => ({ x, y }));
|
|
12
|
+
}
|
|
13
|
+
function squareWalls(height = 2.5) {
|
|
14
|
+
return [
|
|
15
|
+
WallNode.parse({ start: [0, 0], end: [4, 0], height }),
|
|
16
|
+
WallNode.parse({ start: [4, 0], end: [4, 3], height }),
|
|
17
|
+
WallNode.parse({ start: [4, 3], end: [0, 3], height }),
|
|
18
|
+
WallNode.parse({ start: [0, 3], end: [0, 0], height }),
|
|
19
|
+
];
|
|
20
|
+
}
|
|
21
|
+
function slab(elevation) {
|
|
22
|
+
return SlabNode.parse({
|
|
23
|
+
polygon: square,
|
|
24
|
+
elevation,
|
|
25
|
+
autoFromWalls: true,
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
describe('planAutoCeilingsForLevel', () => {
|
|
29
|
+
test('creates auto ceilings at the top of the room walls', () => {
|
|
30
|
+
const created = planAutoCeilingsForLevel([roomPolygon()], [], {
|
|
31
|
+
walls: squareWalls(),
|
|
32
|
+
slabs: [slab(0.05)],
|
|
33
|
+
}).create[0];
|
|
34
|
+
expect(created?.height).toBeCloseTo(2.55);
|
|
35
|
+
});
|
|
36
|
+
test('updates existing auto ceiling height when the slab elevation changes', () => {
|
|
37
|
+
const ceiling = CeilingNode.parse({
|
|
38
|
+
polygon: square,
|
|
39
|
+
height: 2.55,
|
|
40
|
+
autoFromWalls: true,
|
|
41
|
+
});
|
|
42
|
+
const plan = planAutoCeilingsForLevel([roomPolygon()], [ceiling], {
|
|
43
|
+
walls: squareWalls(),
|
|
44
|
+
slabs: [slab(0.4)],
|
|
45
|
+
});
|
|
46
|
+
expect(plan.update).toHaveLength(1);
|
|
47
|
+
expect(plan.update[0]?.id).toBe(ceiling.id);
|
|
48
|
+
expect(plan.update[0]?.data.polygon).toBeUndefined();
|
|
49
|
+
expect(plan.update[0]?.data.height).toBeCloseTo(2.9);
|
|
50
|
+
});
|
|
51
|
+
test('updates existing auto ceiling height when wall height changes', () => {
|
|
52
|
+
const ceiling = CeilingNode.parse({
|
|
53
|
+
polygon: square,
|
|
54
|
+
height: 2.55,
|
|
55
|
+
autoFromWalls: true,
|
|
56
|
+
});
|
|
57
|
+
const plan = planAutoCeilingsForLevel([roomPolygon()], [ceiling], {
|
|
58
|
+
walls: squareWalls(3),
|
|
59
|
+
slabs: [slab(0.05)],
|
|
60
|
+
});
|
|
61
|
+
expect(plan.update).toHaveLength(1);
|
|
62
|
+
expect(plan.update[0]?.data.height).toBeCloseTo(3.05);
|
|
63
|
+
});
|
|
64
|
+
test('does not replace a manual ceiling with an auto ceiling', () => {
|
|
65
|
+
const manualCeiling = CeilingNode.parse({
|
|
66
|
+
polygon: square,
|
|
67
|
+
height: 2.5,
|
|
68
|
+
autoFromWalls: false,
|
|
69
|
+
});
|
|
70
|
+
const plan = planAutoCeilingsForLevel([roomPolygon()], [manualCeiling], {
|
|
71
|
+
walls: squareWalls(),
|
|
72
|
+
slabs: [slab(0.4)],
|
|
73
|
+
});
|
|
74
|
+
expect(plan.create).toHaveLength(0);
|
|
75
|
+
expect(plan.update).toHaveLength(0);
|
|
76
|
+
});
|
|
77
|
+
});
|
|
@@ -8,7 +8,7 @@ export type MaterialCatalogItem = {
|
|
|
8
8
|
previewColor?: string;
|
|
9
9
|
preset: MaterialPresetPayload;
|
|
10
10
|
};
|
|
11
|
-
export declare const MATERIAL_CATEGORIES: readonly ["wood", "
|
|
11
|
+
export declare const MATERIAL_CATEGORIES: readonly ["wood", "flooring", "roof", "other"];
|
|
12
12
|
export type MaterialCategory = (typeof MATERIAL_CATEGORIES)[number];
|
|
13
13
|
export declare const MATERIAL_CATALOG: MaterialCatalogItem[];
|
|
14
14
|
export declare function getMaterialsForCategory(category: MaterialCategory): MaterialCatalogItem[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"material-library.d.ts","sourceRoot":"","sources":["../src/material-library.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,qBAAqB,EAG3B,MAAM,mBAAmB,CAAA;AAE1B,MAAM,MAAM,mBAAmB,GAAG;IAChC,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,gBAAgB,CAAA;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,MAAM,EAAE,qBAAqB,CAAA;CAC9B,CAAA;
|
|
1
|
+
{"version":3,"file":"material-library.d.ts","sourceRoot":"","sources":["../src/material-library.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,qBAAqB,EAG3B,MAAM,mBAAmB,CAAA;AAE1B,MAAM,MAAM,mBAAmB,GAAG;IAChC,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,gBAAgB,CAAA;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,MAAM,EAAE,qBAAqB,CAAA;CAC9B,CAAA;AAuCD,eAAO,MAAM,mBAAmB,gDAAiD,CAAA;AACjF,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,mBAAmB,CAAC,CAAC,MAAM,CAAC,CAAA;AAEnE,eAAO,MAAM,gBAAgB,EAAE,mBAAmB,EAqzEjD,CAAA;AAED,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,gBAAgB,GAAG,mBAAmB,EAAE,CAEzF;AAED,wBAAgB,sBAAsB,CAAC,EAAE,CAAC,EAAE,MAAM,GAAG,mBAAmB,GAAG,SAAS,CAGnF;AAED,eAAO,MAAM,2BAA2B,aAAa,CAAA;AAErD,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,MAAM,UAE9C;AAED,wBAAgB,2BAA2B,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,iBAItE;AAED,wBAAgB,sBAAsB,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,qBAAqB,GAAG,IAAI,CAIhG"}
|