@pascal-app/core 1.0.0 → 1.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.
- package/dist/capture/index.d.ts +2 -2
- package/dist/capture/index.js +2 -2
- package/dist/capture/schema.d.ts.map +1 -1
- package/dist/capture/schema.js +2 -0
- package/dist/capture/source.d.ts +1 -1
- package/dist/capture/source.js +1 -1
- package/dist/events/bus.d.ts +6 -3
- package/dist/events/bus.d.ts.map +1 -1
- package/dist/hooks/spatial-grid/floor-placed-elevation.d.ts +4 -4
- package/dist/hooks/spatial-grid/floor-placed-elevation.d.ts.map +1 -1
- package/dist/hooks/spatial-grid/floor-placed-elevation.js +7 -12
- package/dist/hooks/spatial-grid/floor-placed-footprints.d.ts +4 -0
- package/dist/hooks/spatial-grid/floor-placed-footprints.d.ts.map +1 -0
- package/dist/hooks/spatial-grid/floor-placed-footprints.js +7 -0
- package/dist/hooks/spatial-grid/spatial-grid-manager.d.ts +5 -8
- package/dist/hooks/spatial-grid/spatial-grid-manager.d.ts.map +1 -1
- package/dist/hooks/spatial-grid/spatial-grid-manager.js +23 -106
- package/dist/hooks/spatial-grid/spatial-grid-sync.d.ts +221 -76
- package/dist/hooks/spatial-grid/spatial-grid-sync.d.ts.map +1 -1
- package/dist/hooks/spatial-grid/spatial-grid-sync.js +77 -13
- package/dist/hooks/spatial-grid/support-host-patch.d.ts +1 -1
- package/dist/hooks/spatial-grid/support-host-patch.js +6 -6
- package/dist/hooks/spatial-grid/use-spatial-query.d.ts +1 -1
- package/dist/hooks/spatial-grid/use-spatial-query.js +1 -1
- package/dist/index.d.ts +74 -72
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +71 -69
- package/dist/lib/door-operation.d.ts +1 -1
- package/dist/lib/item-polygon-overlap.d.ts +6 -0
- package/dist/lib/item-polygon-overlap.d.ts.map +1 -0
- package/dist/lib/item-polygon-overlap.js +67 -0
- package/dist/lib/level-name.d.ts +1 -1
- package/dist/lib/measurement-geometry.d.ts +4 -4
- package/dist/lib/plan-footprint.d.ts +41 -0
- package/dist/lib/plan-footprint.d.ts.map +1 -0
- package/dist/lib/plan-footprint.js +58 -0
- package/dist/lib/room-topology-index.d.ts +1 -1
- package/dist/lib/room-topology-index.js +1 -1
- package/dist/lib/selection-proxy.d.ts +1 -1
- package/dist/lib/slab-polygon.d.ts +2 -2
- package/dist/lib/slab-polygon.js +2 -2
- package/dist/lib/space-detection.d.ts +1 -1
- package/dist/lib/space-detection.d.ts.map +1 -1
- package/dist/lib/space-detection.js +16 -11
- package/dist/lib/terrain-brush.d.ts +1 -1
- package/dist/lib/terrain-brush.js +1 -1
- package/dist/lib/terrain-codec.d.ts +2 -2
- package/dist/lib/terrain-codec.js +1 -1
- package/dist/lib/terrain-raycast.d.ts +1 -1
- package/dist/lib/terrain-raycast.js +1 -1
- package/dist/lib/terrain-source.d.ts +3 -3
- package/dist/lib/terrain-source.js +3 -3
- package/dist/lib/terrain-support.d.ts +1 -1
- package/dist/lib/terrain-support.js +3 -3
- package/dist/lib/unit-containment.d.ts +19 -0
- package/dist/lib/unit-containment.d.ts.map +1 -0
- package/dist/lib/unit-containment.js +158 -0
- package/dist/lib/unit-report.d.ts +19 -0
- package/dist/lib/unit-report.d.ts.map +1 -0
- package/dist/lib/unit-report.js +36 -0
- package/dist/lib/wall-distance.d.ts +2 -2
- package/dist/lib/wall-distance.js +1 -1
- package/dist/lib/zone-quantities.d.ts +1 -1
- package/dist/lib/zone-quantities.js +7 -7
- package/dist/material-library.d.ts +1 -1
- package/dist/material-library.js +1 -1
- package/dist/procedural-items/fixtures.d.ts +261 -0
- package/dist/procedural-items/fixtures.d.ts.map +1 -0
- package/dist/procedural-items/fixtures.js +349 -0
- package/dist/procedural-items/index.d.ts +10 -0
- package/dist/procedural-items/index.d.ts.map +1 -0
- package/dist/procedural-items/index.js +9 -0
- package/dist/procedural-items/integration.d.ts +92 -0
- package/dist/procedural-items/integration.d.ts.map +1 -0
- package/dist/procedural-items/integration.js +19 -0
- package/dist/procedural-items/library-colors.d.ts +56 -0
- package/dist/procedural-items/library-colors.d.ts.map +1 -0
- package/dist/procedural-items/library-colors.js +84 -0
- package/dist/procedural-items/materials.d.ts +5 -0
- package/dist/procedural-items/materials.d.ts.map +1 -0
- package/dist/procedural-items/materials.js +47 -0
- package/dist/procedural-items/node.d.ts +280 -0
- package/dist/procedural-items/node.d.ts.map +1 -0
- package/dist/procedural-items/node.js +71 -0
- package/dist/procedural-items/placement.d.ts +7 -0
- package/dist/procedural-items/placement.d.ts.map +1 -0
- package/dist/procedural-items/placement.js +48 -0
- package/dist/procedural-items/query.d.ts +50 -0
- package/dist/procedural-items/query.d.ts.map +1 -0
- package/dist/procedural-items/query.js +298 -0
- package/dist/procedural-items/recipe.d.ts +144 -0
- package/dist/procedural-items/recipe.d.ts.map +1 -0
- package/dist/procedural-items/recipe.js +372 -0
- package/dist/procedural-items/spatial.d.ts +19 -0
- package/dist/procedural-items/spatial.d.ts.map +1 -0
- package/dist/procedural-items/spatial.js +39 -0
- package/dist/registry/__bench__/relations-resolver.bench.js +2 -2
- package/dist/registry/handles.d.ts +12 -4
- package/dist/registry/handles.d.ts.map +1 -1
- package/dist/registry/index.d.ts +6 -6
- package/dist/registry/index.js +4 -4
- package/dist/registry/registry.d.ts +1 -1
- package/dist/registry/relations-resolver.d.ts +2 -2
- package/dist/registry/relations-resolver.js +1 -1
- package/dist/registry/scene-api.d.ts +2 -2
- package/dist/registry/scene-api.js +2 -2
- package/dist/registry/subtree.d.ts +1 -1
- package/dist/registry/subtree.js +2 -2
- package/dist/registry/types.d.ts +21 -9
- package/dist/registry/types.d.ts.map +1 -1
- package/dist/registry/use-registry-version.js +1 -1
- package/dist/registry/validate-relations.d.ts +4 -0
- package/dist/registry/validate-relations.d.ts.map +1 -0
- package/dist/registry/validate-relations.js +22 -0
- package/dist/schema/__bench__/node-parsers.bench.js +3 -3
- package/dist/schema/__fixtures__/node-fixtures.d.ts +1 -1
- package/dist/schema/__fixtures__/node-fixtures.d.ts.map +1 -1
- package/dist/schema/__fixtures__/node-fixtures.js +4 -2
- package/dist/schema/base.js +1 -1
- package/dist/schema/collections.d.ts +1 -1
- package/dist/schema/collections.js +1 -1
- package/dist/schema/compiled-node-parsers.d.ts +1 -1
- package/dist/schema/compiled-node-parsers.js +1 -1
- package/dist/schema/index.d.ts +73 -70
- package/dist/schema/index.d.ts.map +1 -1
- package/dist/schema/index.js +64 -61
- package/dist/schema/material.d.ts +1 -1
- package/dist/schema/material.js +1 -1
- package/dist/schema/nodes/automatic-downspout.d.ts +4 -4
- package/dist/schema/nodes/automatic-downspout.js +5 -5
- package/dist/schema/nodes/block.d.ts +1 -1
- package/dist/schema/nodes/block.d.ts.map +1 -1
- package/dist/schema/nodes/block.js +3 -4
- package/dist/schema/nodes/box-vent.js +2 -2
- 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 +8 -5
- package/dist/schema/nodes/cabinet.d.ts +2 -2
- package/dist/schema/nodes/cabinet.js +4 -4
- package/dist/schema/nodes/ceiling.d.ts +1 -1
- package/dist/schema/nodes/ceiling.js +5 -5
- package/dist/schema/nodes/chimney.js +2 -2
- package/dist/schema/nodes/column.d.ts +1 -0
- package/dist/schema/nodes/column.d.ts.map +1 -1
- package/dist/schema/nodes/column.js +3 -2
- package/dist/schema/nodes/construction-dimension.d.ts +3 -3
- package/dist/schema/nodes/construction-dimension.js +2 -2
- package/dist/schema/nodes/cupola.d.ts +1 -1
- package/dist/schema/nodes/cupola.js +2 -2
- package/dist/schema/nodes/door.js +2 -2
- package/dist/schema/nodes/dormer.d.ts +3 -3
- package/dist/schema/nodes/dormer.js +4 -4
- package/dist/schema/nodes/downspout.js +2 -2
- package/dist/schema/nodes/duct-fitting.js +1 -1
- package/dist/schema/nodes/duct-segment.js +2 -2
- package/dist/schema/nodes/duct-terminal.js +1 -1
- package/dist/schema/nodes/elevator.js +2 -2
- package/dist/schema/nodes/eyebrow-vent.js +2 -2
- package/dist/schema/nodes/fence.js +2 -2
- package/dist/schema/nodes/guide.js +2 -2
- package/dist/schema/nodes/gutter.d.ts +1 -1
- package/dist/schema/nodes/gutter.js +4 -4
- package/dist/schema/nodes/hvac-equipment.js +1 -1
- package/dist/schema/nodes/imported-mesh.d.ts +40 -0
- package/dist/schema/nodes/imported-mesh.d.ts.map +1 -0
- package/dist/schema/nodes/imported-mesh.js +27 -0
- package/dist/schema/nodes/item.d.ts +3 -3
- package/dist/schema/nodes/item.js +3 -3
- package/dist/schema/nodes/lean-to-extension.js +4 -4
- 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 +1 -1
- package/dist/schema/nodes/lineset.js +1 -1
- package/dist/schema/nodes/liquid-line.js +1 -1
- package/dist/schema/nodes/measurement.js +2 -2
- package/dist/schema/nodes/pipe-fitting.d.ts +1 -1
- package/dist/schema/nodes/pipe-fitting.js +1 -1
- package/dist/schema/nodes/pipe-segment.d.ts +1 -1
- package/dist/schema/nodes/pipe-segment.js +2 -2
- package/dist/schema/nodes/pipe-trap.d.ts +1 -1
- package/dist/schema/nodes/pipe-trap.js +1 -1
- package/dist/schema/nodes/ridge-vent.d.ts +1 -1
- package/dist/schema/nodes/ridge-vent.js +3 -3
- package/dist/schema/nodes/roof-segment-shape.d.ts +1 -1
- package/dist/schema/nodes/roof-segment-shape.js +1 -1
- package/dist/schema/nodes/roof-segment-walls.d.ts +1 -1
- package/dist/schema/nodes/roof-segment-walls.js +1 -1
- package/dist/schema/nodes/roof-segment.d.ts +2 -2
- package/dist/schema/nodes/roof-segment.js +2 -2
- package/dist/schema/nodes/roof.d.ts +1 -1
- package/dist/schema/nodes/roof.js +3 -3
- package/dist/schema/nodes/scan.js +3 -3
- package/dist/schema/nodes/shelf.d.ts +1 -1
- package/dist/schema/nodes/shelf.d.ts.map +1 -1
- package/dist/schema/nodes/shelf.js +3 -4
- package/dist/schema/nodes/site.js +2 -2
- package/dist/schema/nodes/skylight.d.ts +4 -4
- package/dist/schema/nodes/skylight.js +2 -2
- package/dist/schema/nodes/slab.js +3 -3
- package/dist/schema/nodes/solar-panel.js +3 -3
- package/dist/schema/nodes/spawn.js +1 -1
- package/dist/schema/nodes/stair-segment.js +2 -2
- package/dist/schema/nodes/stair.d.ts +1 -1
- package/dist/schema/nodes/stair.js +3 -3
- package/dist/schema/nodes/structural-grid.js +1 -1
- package/dist/schema/nodes/turbine-vent.d.ts +1 -1
- package/dist/schema/nodes/turbine-vent.js +2 -2
- package/dist/schema/nodes/unit.d.ts +33 -0
- package/dist/schema/nodes/unit.d.ts.map +1 -0
- package/dist/schema/nodes/unit.js +13 -0
- package/dist/schema/nodes/wall.d.ts +2 -2
- package/dist/schema/nodes/wall.d.ts.map +1 -1
- package/dist/schema/nodes/wall.js +7 -6
- package/dist/schema/nodes/window.js +2 -2
- package/dist/schema/nodes/zone.js +1 -1
- package/dist/schema/scene-material.js +2 -2
- package/dist/schema/types.d.ts +341 -19
- package/dist/schema/types.d.ts.map +1 -1
- package/dist/schema/types.js +56 -49
- package/dist/services/alignment-anchors.d.ts +6 -11
- package/dist/services/alignment-anchors.d.ts.map +1 -1
- package/dist/services/alignment-anchors.js +7 -34
- package/dist/services/drag-session.d.ts +4 -4
- package/dist/services/drag-session.js +1 -1
- package/dist/services/hosting.d.ts +2 -8
- package/dist/services/hosting.d.ts.map +1 -1
- package/dist/services/hosting.js +2 -12
- package/dist/services/index.d.ts +16 -13
- package/dist/services/index.d.ts.map +1 -1
- package/dist/services/index.js +16 -13
- package/dist/services/level-height.d.ts +2 -2
- package/dist/services/level-height.js +3 -3
- package/dist/services/movement.d.ts +3 -3
- package/dist/services/movement.js +2 -2
- package/dist/services/port-connectivity.d.ts +1 -1
- package/dist/services/port-connectivity.js +1 -1
- package/dist/services/riser-diagram.d.ts +1 -1
- package/dist/services/shelf-board.d.ts +5 -0
- package/dist/services/shelf-board.d.ts.map +1 -0
- package/dist/services/shelf-board.js +15 -0
- package/dist/services/storey.d.ts +2 -2
- package/dist/services/storey.js +2 -2
- package/dist/services/surface-hosting-procedural-parity.fixtures.d.ts +13 -0
- package/dist/services/surface-hosting-procedural-parity.fixtures.d.ts.map +1 -0
- package/dist/services/surface-hosting-procedural-parity.fixtures.js +4691 -0
- package/dist/services/surface-hosting-shelf-parity.fixtures.d.ts +1060 -0
- package/dist/services/surface-hosting-shelf-parity.fixtures.d.ts.map +1 -0
- package/dist/services/surface-hosting-shelf-parity.fixtures.js +3127 -0
- package/dist/services/surface-hosting.d.ts +88 -0
- package/dist/services/surface-hosting.d.ts.map +1 -0
- package/dist/services/surface-hosting.js +268 -0
- package/dist/services/surface-region.d.ts +8 -0
- package/dist/services/surface-region.d.ts.map +1 -0
- package/dist/services/surface-region.js +44 -0
- package/dist/services/system-graph.d.ts +2 -2
- package/dist/services/system-graph.js +2 -2
- package/dist/services/validate-dwv.d.ts +1 -1
- package/dist/services/validate-dwv.js +1 -1
- package/dist/store/actions/node-actions.d.ts +2 -2
- package/dist/store/actions/node-actions.d.ts.map +1 -1
- package/dist/store/actions/node-actions.js +48 -9
- package/dist/store/history-control.d.ts +3 -3
- package/dist/store/history-invalidation.d.ts +1 -1
- package/dist/store/history-invalidation.js +1 -1
- package/dist/store/use-interactive.d.ts +2 -2
- package/dist/store/use-live-terrain.d.ts +1 -1
- package/dist/store/use-scene.d.ts +4 -4
- package/dist/store/use-scene.d.ts.map +1 -1
- package/dist/store/use-scene.js +37 -31
- package/dist/systems/elevator/elevator-dispatch.d.ts +2 -2
- package/dist/systems/elevator/elevator-dispatch.js +1 -1
- package/dist/systems/elevator/elevator-geometry.d.ts +1 -1
- package/dist/systems/elevator/elevator-opening-sync.d.ts +1 -1
- package/dist/systems/elevator/elevator-opening-sync.js +2 -2
- package/dist/systems/elevator/elevator-opening-system.js +3 -3
- package/dist/systems/elevator/elevator-runtime.d.ts +3 -3
- package/dist/systems/elevator/elevator-runtime.js +3 -3
- package/dist/systems/elevator/elevator-service.d.ts +1 -1
- package/dist/systems/elevator/elevator-service.js +1 -1
- package/dist/systems/fence/fence-centerline.d.ts +2 -2
- package/dist/systems/fence/fence-centerline.js +2 -2
- package/dist/systems/fence/fence-spline.d.ts +2 -2
- package/dist/systems/roof/roof-elevation-system.js +3 -3
- package/dist/systems/roof/roof-elevation.d.ts +2 -2
- package/dist/systems/roof/roof-elevation.js +6 -6
- package/dist/systems/roof/roof-footprint.d.ts +1 -1
- package/dist/systems/roof/roof-footprint.js +3 -3
- package/dist/systems/slab/slab-placement.d.ts +1 -1
- package/dist/systems/slab/slab-support.d.ts +1 -1
- package/dist/systems/slab/slab-support.js +4 -4
- package/dist/systems/stair/stair-flight.d.ts +2 -2
- package/dist/systems/stair/stair-flight.js +2 -2
- package/dist/systems/stair/stair-footprint.d.ts +1 -1
- package/dist/systems/stair/stair-opening-preview.d.ts +206 -79
- package/dist/systems/stair/stair-opening-preview.d.ts.map +1 -1
- package/dist/systems/stair/stair-opening-preview.js +2 -2
- package/dist/systems/stair/stair-opening-sync.d.ts +1 -1
- package/dist/systems/stair/stair-opening-sync.js +6 -6
- package/dist/systems/stair/stair-opening-system.js +8 -8
- package/dist/systems/stair/stair-rise.d.ts +1 -1
- package/dist/systems/stair/stair-rise.js +3 -3
- package/dist/systems/wall/wall-curve.d.ts +2 -2
- package/dist/systems/wall/wall-footprint.d.ts +3 -3
- package/dist/systems/wall/wall-footprint.js +3 -3
- package/dist/systems/wall/wall-mitering.d.ts +1 -1
- package/dist/systems/wall/wall-mitering.js +1 -1
- package/dist/systems/wall/wall-move.d.ts +1 -1
- package/dist/systems/wall/wall-top.d.ts +1 -1
- package/dist/systems/wall/wall-topology.d.ts +2 -2
- package/dist/systems/wall/wall-topology.js +4 -4
- package/dist/utils/clone-scene-graph.d.ts +3 -3
- package/dist/utils/clone-scene-graph.d.ts.map +1 -1
- package/dist/utils/clone-scene-graph.js +9 -3
- package/dist/utils/scene-migrations.d.ts +3 -3
- package/dist/utils/scene-migrations.js +3 -3
- package/dist/utils/vertical-scene-migration.js +5 -5
- package/dist/validation/validate-build-json.d.ts +5 -2
- package/dist/validation/validate-build-json.d.ts.map +1 -1
- package/dist/validation/validate-build-json.js +45 -4
- package/package.json +14 -3
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import type { SceneApi } from '../registry/types.js';
|
|
2
|
+
import type { AnyNode } from '../schema/types.js';
|
|
3
|
+
export type SurfaceId = string;
|
|
4
|
+
export interface SurfaceRegion {
|
|
5
|
+
kind: 'rect' | 'polygon';
|
|
6
|
+
/** Rectangle half-extents in the surface frame's XZ plane. */
|
|
7
|
+
size?: readonly [number, number];
|
|
8
|
+
center?: readonly [number, number];
|
|
9
|
+
/** Counterclockwise boundary in the surface frame's XZ plane. */
|
|
10
|
+
points?: readonly (readonly [number, number])[];
|
|
11
|
+
holes?: readonly (readonly (readonly [number, number])[])[];
|
|
12
|
+
}
|
|
13
|
+
interface SurfaceFrame {
|
|
14
|
+
label?: string;
|
|
15
|
+
position: readonly [number, number, number];
|
|
16
|
+
rotation?: readonly [number, number, number];
|
|
17
|
+
normal: readonly [number, number, number];
|
|
18
|
+
/** Defaults to true; the caller's snap function still controls whether the grid is active. */
|
|
19
|
+
gridSnap?: boolean;
|
|
20
|
+
}
|
|
21
|
+
export type DeclaredHostSurface = SurfaceFrame & {
|
|
22
|
+
id: SurfaceId;
|
|
23
|
+
region: SurfaceRegion;
|
|
24
|
+
};
|
|
25
|
+
export type HostSurface = DeclaredHostSurface | (SurfaceFrame & {
|
|
26
|
+
id: null;
|
|
27
|
+
region?: never;
|
|
28
|
+
});
|
|
29
|
+
export interface SurfaceHit {
|
|
30
|
+
point: readonly [number, number, number];
|
|
31
|
+
normalWorldY: number;
|
|
32
|
+
meshName?: string;
|
|
33
|
+
}
|
|
34
|
+
export interface SurfaceContext {
|
|
35
|
+
scene: Pick<SceneApi, 'get' | 'nodes'>;
|
|
36
|
+
}
|
|
37
|
+
export interface SurfaceProvider {
|
|
38
|
+
childFrame: 'host-local' | 'surface-local';
|
|
39
|
+
surfaces?(host: AnyNode, ctx: SurfaceContext): readonly DeclaredHostSurface[];
|
|
40
|
+
resolveHit(host: AnyNode, hit: SurfaceHit, ctx: SurfaceContext): HostSurface | null;
|
|
41
|
+
accepts?(host: AnyNode, childKind: string, surface: HostSurface, ctx: SurfaceContext): boolean;
|
|
42
|
+
}
|
|
43
|
+
export type SurfaceRejectReason = 'host-not-eligible' | 'invalid-hit' | 'no-surface' | 'child-not-accepted' | 'footprint-outside-surface' | 'footprint-exceeds-host' | 'surface-cutout';
|
|
44
|
+
export type SurfacePlacement = {
|
|
45
|
+
position: readonly [number, number, number];
|
|
46
|
+
rotationY: number;
|
|
47
|
+
surfaceId: SurfaceId | null;
|
|
48
|
+
/** Movers write surfaceLocal for surface-local providers, otherwise the host-local pose. */
|
|
49
|
+
childFrame: SurfaceProvider['childFrame'];
|
|
50
|
+
/** Null exactly for hit-derived surfaces. Full XYZ rotation is needed on tilted surfaces. */
|
|
51
|
+
surfaceLocal: {
|
|
52
|
+
position: readonly [number, number, number];
|
|
53
|
+
rotationY: number;
|
|
54
|
+
rotation: readonly [number, number, number];
|
|
55
|
+
} | null;
|
|
56
|
+
};
|
|
57
|
+
export declare const NON_PHYSICAL_HOST_KINDS: readonly string[];
|
|
58
|
+
export declare const hitDerivedSurfaceProvider: SurfaceProvider;
|
|
59
|
+
export declare const itemSurfaceProvider: SurfaceProvider;
|
|
60
|
+
export declare const shelfSurfaceProvider: SurfaceProvider;
|
|
61
|
+
export declare const proceduralItemSurfaceProvider: SurfaceProvider;
|
|
62
|
+
export declare function getSurfaceProvider(host: AnyNode): SurfaceProvider;
|
|
63
|
+
/** Hit and child rotation are host-local; bounds and dimensions are scaled child-local values. */
|
|
64
|
+
export declare function resolveSurfacePlacement(args: {
|
|
65
|
+
host: AnyNode;
|
|
66
|
+
childKind: string;
|
|
67
|
+
childFootprint: {
|
|
68
|
+
size: readonly [number, number, number];
|
|
69
|
+
rotationY: number;
|
|
70
|
+
/** Full host-local XYZ Euler rotation, when available; otherwise rotationY is used. */
|
|
71
|
+
rotation?: readonly [number, number, number];
|
|
72
|
+
localBounds?: {
|
|
73
|
+
min: readonly [number, number, number];
|
|
74
|
+
max: readonly [number, number, number];
|
|
75
|
+
};
|
|
76
|
+
};
|
|
77
|
+
hit: SurfaceHit;
|
|
78
|
+
/** Child origin after grab-offset correction; the hit still elects the actual support. */
|
|
79
|
+
origin?: readonly [number, number, number];
|
|
80
|
+
scene: SceneApi;
|
|
81
|
+
/** Pure child-centered grid function; omitted means snapping is off. */
|
|
82
|
+
snapScalar?: (position: number, dimension: number) => number;
|
|
83
|
+
/** Defaults to true; drag previews may skip fit while still enforcing host and child eligibility. */
|
|
84
|
+
checkFootprint?: boolean;
|
|
85
|
+
onReject?: (reason: SurfaceRejectReason) => void;
|
|
86
|
+
}): SurfacePlacement | null;
|
|
87
|
+
export {};
|
|
88
|
+
//# sourceMappingURL=surface-hosting.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"surface-hosting.d.ts","sourceRoot":"","sources":["../../src/services/surface-hosting.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
import { isProceduralItem } from '../procedural-items/query.js';
|
|
2
|
+
import { evaluateRecipe } from '../procedural-items/recipe.js';
|
|
3
|
+
import { boxCorners, frame, transformPoint } from '../procedural-items/spatial.js';
|
|
4
|
+
import { nodeRegistry } from '../registry/registry.js';
|
|
5
|
+
import { getScaledDimensions, isLowProfileItemSurface } from '../schema/nodes/item.js';
|
|
6
|
+
import { canHostOnTop } from './hosting.js';
|
|
7
|
+
import { shelfRowBoardDimensions } from './shelf-board.js';
|
|
8
|
+
import { surfaceRegionContainsFootprint, surfaceRegionContainsPoint } from './surface-region.js';
|
|
9
|
+
export const NON_PHYSICAL_HOST_KINDS = [
|
|
10
|
+
'guide',
|
|
11
|
+
'measurement',
|
|
12
|
+
'scan',
|
|
13
|
+
'construction-dimension',
|
|
14
|
+
'lineset',
|
|
15
|
+
'structural-grid',
|
|
16
|
+
'spawn',
|
|
17
|
+
'zone',
|
|
18
|
+
'unit',
|
|
19
|
+
'site',
|
|
20
|
+
'building',
|
|
21
|
+
'level',
|
|
22
|
+
];
|
|
23
|
+
const UPWARD_SURFACE_NORMAL_MIN_Y = 0.75;
|
|
24
|
+
export const hitDerivedSurfaceProvider = {
|
|
25
|
+
childFrame: 'host-local',
|
|
26
|
+
resolveHit(_host, hit) {
|
|
27
|
+
if (!(hit.normalWorldY >= UPWARD_SURFACE_NORMAL_MIN_Y) || !hit.point.every(Number.isFinite))
|
|
28
|
+
return null;
|
|
29
|
+
return { id: null, position: hit.point, normal: [0, 1, 0], gridSnap: true };
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
export const itemSurfaceProvider = {
|
|
33
|
+
childFrame: 'host-local',
|
|
34
|
+
resolveHit(host, hit) {
|
|
35
|
+
if (host.type !== 'item' || !canHostOnTop(host) || isLowProfileItemSurface(host))
|
|
36
|
+
return null;
|
|
37
|
+
if (!(hit.normalWorldY >= UPWARD_SURFACE_NORMAL_MIN_Y) || !hit.point.every(Number.isFinite))
|
|
38
|
+
return null;
|
|
39
|
+
const height = host.asset.surface ? host.asset.surface.height * host.scale[1] : hit.point[1];
|
|
40
|
+
return {
|
|
41
|
+
id: null,
|
|
42
|
+
position: [hit.point[0], height, hit.point[2]],
|
|
43
|
+
normal: [0, 1, 0],
|
|
44
|
+
gridSnap: true,
|
|
45
|
+
};
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
function nearestSurface(surfaces, localY) {
|
|
49
|
+
let best = surfaces[0] ?? null;
|
|
50
|
+
for (const surface of surfaces.slice(1)) {
|
|
51
|
+
if (best && Math.abs(surface.position[1] - localY) < Math.abs(best.position[1] - localY))
|
|
52
|
+
best = surface;
|
|
53
|
+
}
|
|
54
|
+
return best;
|
|
55
|
+
}
|
|
56
|
+
function shelfSurfaces(host) {
|
|
57
|
+
if (host.type !== 'shelf')
|
|
58
|
+
return [];
|
|
59
|
+
return (nodeRegistry.get(host.type)?.capabilities.surfaces?.custom?.(host) ?? []).map((surface, index) => {
|
|
60
|
+
const [width, , depth] = shelfRowBoardDimensions(host, surface.position[1]);
|
|
61
|
+
return {
|
|
62
|
+
...surface,
|
|
63
|
+
id: `row:${index}`,
|
|
64
|
+
gridSnap: true,
|
|
65
|
+
region: { kind: 'rect', size: [width / 2, depth / 2] },
|
|
66
|
+
};
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
export const shelfSurfaceProvider = {
|
|
70
|
+
childFrame: 'host-local',
|
|
71
|
+
surfaces: shelfSurfaces,
|
|
72
|
+
resolveHit(host, hit) {
|
|
73
|
+
if (!(hit.normalWorldY >= UPWARD_SURFACE_NORMAL_MIN_Y) || !hit.point.every(Number.isFinite))
|
|
74
|
+
return null;
|
|
75
|
+
return nearestSurface(shelfSurfaces(host), hit.point[1]);
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
const proceduralSurfaceCache = new WeakMap();
|
|
79
|
+
function proceduralSurfaces(host) {
|
|
80
|
+
if (!isProceduralItem(host))
|
|
81
|
+
return [];
|
|
82
|
+
// Include contents as well as identity so in-place parameter/recipe edits cannot leave stale surfaces.
|
|
83
|
+
const key = JSON.stringify([host.recipe, host.parameters]);
|
|
84
|
+
const cached = proceduralSurfaceCache.get(host.recipe);
|
|
85
|
+
if (cached?.key === key)
|
|
86
|
+
return cached.surfaces;
|
|
87
|
+
const surfaces = evaluateRecipe(host.recipe, host.parameters).surfaces.map((surface) => ({
|
|
88
|
+
id: surface.id,
|
|
89
|
+
label: surface.label,
|
|
90
|
+
position: surface.position,
|
|
91
|
+
rotation: surface.rotation,
|
|
92
|
+
normal: surface.normal,
|
|
93
|
+
gridSnap: true,
|
|
94
|
+
region: { kind: 'rect', size: [surface.size[0] / 2, surface.size[1] / 2] },
|
|
95
|
+
}));
|
|
96
|
+
proceduralSurfaceCache.set(host.recipe, { key, surfaces });
|
|
97
|
+
return surfaces;
|
|
98
|
+
}
|
|
99
|
+
function surfaceLocalPoint(surface, point) {
|
|
100
|
+
const delta = point.map((v, i) => v - surface.position[i]);
|
|
101
|
+
return frame([0, 0, 0], [...(surface.rotation ?? [0, 0, 0])]).axes.map((axis) => axis.reduce((sum, v, i) => sum + v * delta[i], 0));
|
|
102
|
+
}
|
|
103
|
+
function surfaceLocalRotation(surface, rotation) {
|
|
104
|
+
const surfaceRotation = surface.rotation ?? [0, 0, 0];
|
|
105
|
+
// Preserve unwrapped yaw exactly for the common horizontal, yaw-only case.
|
|
106
|
+
if (!surfaceRotation[0] && !surfaceRotation[2] && !rotation[0] && !rotation[2])
|
|
107
|
+
return [0, rotation[1] - surfaceRotation[1], 0];
|
|
108
|
+
const axes = frame([0, 0, 0], rotation).axes.map((axis) => surfaceLocalPoint({ ...surface, position: [0, 0, 0] }, axis));
|
|
109
|
+
const y = Math.asin(Math.max(-1, Math.min(1, axes[2][0])));
|
|
110
|
+
return Math.abs(axes[2][0]) < 0.9999999
|
|
111
|
+
? [Math.atan2(-axes[2][1], axes[2][2]), y, Math.atan2(-axes[1][0], axes[0][0])]
|
|
112
|
+
: [Math.atan2(axes[1][2], axes[1][1]), y, 0];
|
|
113
|
+
}
|
|
114
|
+
export const proceduralItemSurfaceProvider = {
|
|
115
|
+
childFrame: 'surface-local',
|
|
116
|
+
surfaces: proceduralSurfaces,
|
|
117
|
+
resolveHit(host, hit) {
|
|
118
|
+
if (!(hit.normalWorldY >= UPWARD_SURFACE_NORMAL_MIN_Y) || !hit.point.every(Number.isFinite))
|
|
119
|
+
return null;
|
|
120
|
+
const candidates = proceduralSurfaces(host).filter((surface) => {
|
|
121
|
+
const point = surfaceLocalPoint(surface, hit.point);
|
|
122
|
+
return (surface.normal[1] >= UPWARD_SURFACE_NORMAL_MIN_Y &&
|
|
123
|
+
surfaceRegionContainsPoint(surface.region, [point[0], point[2]]));
|
|
124
|
+
});
|
|
125
|
+
return nearestSurface(candidates, hit.point[1]);
|
|
126
|
+
},
|
|
127
|
+
};
|
|
128
|
+
const adapters = new Map([
|
|
129
|
+
['item', itemSurfaceProvider],
|
|
130
|
+
['shelf', shelfSurfaceProvider],
|
|
131
|
+
['procedural-item', proceduralItemSurfaceProvider],
|
|
132
|
+
]);
|
|
133
|
+
export function getSurfaceProvider(host) {
|
|
134
|
+
const declaration = nodeRegistry.get(host.type)?.capabilities.surfaces;
|
|
135
|
+
return declaration?.hosting ?? adapters.get(host.type) ?? hitDerivedSurfaceProvider;
|
|
136
|
+
}
|
|
137
|
+
function hostRegion(host, ctx) {
|
|
138
|
+
const capabilities = nodeRegistry.get(host.type)?.capabilities;
|
|
139
|
+
const evaluated = isProceduralItem(host) ? evaluateRecipe(host.recipe, host.parameters) : null;
|
|
140
|
+
const bounds = capabilities?.dragBounds?.(host, ctx.scene.nodes()) ??
|
|
141
|
+
(evaluated
|
|
142
|
+
? {
|
|
143
|
+
size: evaluated.dimensions,
|
|
144
|
+
center: evaluated.min.map((v, i) => (v + evaluated.max[i]) / 2),
|
|
145
|
+
}
|
|
146
|
+
: undefined);
|
|
147
|
+
const size = host.type === 'item'
|
|
148
|
+
? getScaledDimensions(host)
|
|
149
|
+
: (bounds?.size ??
|
|
150
|
+
capabilities?.floorPlaced?.footprint?.(host, { nodes: ctx.scene.nodes() }).dimensions);
|
|
151
|
+
if (!size)
|
|
152
|
+
return undefined;
|
|
153
|
+
return {
|
|
154
|
+
kind: 'rect',
|
|
155
|
+
size: [size[0] / 2, size[2] / 2],
|
|
156
|
+
center: bounds?.center ? [bounds.center[0], bounds.center[2]] : [0, 0],
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
/** Hit and child rotation are host-local; bounds and dimensions are scaled child-local values. */
|
|
160
|
+
export function resolveSurfacePlacement(args) {
|
|
161
|
+
const { host, hit, childKind, childFootprint } = args;
|
|
162
|
+
// Slabs support level children via supportSlabId. Defer to floor placement
|
|
163
|
+
// without a refusal, which would block the paired grid event's floor drop.
|
|
164
|
+
if (host.type === 'slab')
|
|
165
|
+
return null;
|
|
166
|
+
const reject = (reason) => {
|
|
167
|
+
args.onReject?.(reason);
|
|
168
|
+
return null;
|
|
169
|
+
};
|
|
170
|
+
if (!hit.point.every(Number.isFinite))
|
|
171
|
+
return reject('invalid-hit');
|
|
172
|
+
if (NON_PHYSICAL_HOST_KINDS.includes(host.type) || !canHostOnTop(host))
|
|
173
|
+
return reject('host-not-eligible');
|
|
174
|
+
const ctx = { scene: args.scene };
|
|
175
|
+
const provider = getSurfaceProvider(host);
|
|
176
|
+
const poseFor = (surface) => {
|
|
177
|
+
const declaredId = surface.id;
|
|
178
|
+
if (declaredId !== null && !surface.region) {
|
|
179
|
+
throw new Error(`Declared surface ${host.type}:${declaredId} must publish a region`);
|
|
180
|
+
}
|
|
181
|
+
const snap = (surface.gridSnap ?? true) ? args.snapScalar : undefined;
|
|
182
|
+
const origin = args.origin ?? hit.point;
|
|
183
|
+
const position = [
|
|
184
|
+
snap?.(origin[0], childFootprint.size[0]) ?? origin[0],
|
|
185
|
+
surface.position[1],
|
|
186
|
+
snap?.(origin[2], childFootprint.size[2]) ?? origin[2],
|
|
187
|
+
];
|
|
188
|
+
const rotation = surfaceLocalRotation(surface, [
|
|
189
|
+
...(childFootprint.rotation ?? [0, childFootprint.rotationY, 0]),
|
|
190
|
+
]);
|
|
191
|
+
const localPosition = surfaceLocalPoint(surface, position);
|
|
192
|
+
if (surface.id !== null) {
|
|
193
|
+
const bounds = childFootprint.localBounds ?? {
|
|
194
|
+
min: [-childFootprint.size[0] / 2, 0, -childFootprint.size[2] / 2],
|
|
195
|
+
max: [
|
|
196
|
+
childFootprint.size[0] / 2,
|
|
197
|
+
childFootprint.size[1],
|
|
198
|
+
childFootprint.size[2] / 2,
|
|
199
|
+
],
|
|
200
|
+
};
|
|
201
|
+
localPosition[1] = -Math.min(...boxCorners([...bounds.min], [...bounds.max]).map((p) => transformPoint(frame([0, 0, 0], rotation), p)[1]));
|
|
202
|
+
// Intersect the host-local vertical cursor line with the support plane, then lift the child's bottom.
|
|
203
|
+
const normal = frame([0, 0, 0], [...(surface.rotation ?? [0, 0, 0])]).axes[1];
|
|
204
|
+
position[1] =
|
|
205
|
+
surface.position[1] +
|
|
206
|
+
(localPosition[1] -
|
|
207
|
+
normal[0] * (position[0] - surface.position[0]) -
|
|
208
|
+
normal[2] * (position[2] - surface.position[2])) /
|
|
209
|
+
normal[1];
|
|
210
|
+
const projected = surfaceLocalPoint(surface, position);
|
|
211
|
+
localPosition[0] = projected[0];
|
|
212
|
+
localPosition[2] = projected[2];
|
|
213
|
+
}
|
|
214
|
+
return {
|
|
215
|
+
position,
|
|
216
|
+
rotationY: childFootprint.rotationY,
|
|
217
|
+
surfaceId: surface.id,
|
|
218
|
+
childFrame: surface.id === null ? 'host-local' : provider.childFrame,
|
|
219
|
+
surfaceLocal: surface.id === null ? null : { position: localPosition, rotationY: rotation[1], rotation },
|
|
220
|
+
};
|
|
221
|
+
};
|
|
222
|
+
let surface = provider.resolveHit(host, hit, ctx);
|
|
223
|
+
if (!surface && hit.normalWorldY >= UPWARD_SURFACE_NORMAL_MIN_Y) {
|
|
224
|
+
// Grab offsets and snapping can leave the pointer over a hole while the child's centre is supported.
|
|
225
|
+
surface = nearestSurface((provider.surfaces?.(host, ctx) ?? []).filter((candidate) => {
|
|
226
|
+
if (candidate.normal[1] < UPWARD_SURFACE_NORMAL_MIN_Y)
|
|
227
|
+
return false;
|
|
228
|
+
const proposal = poseFor(candidate).surfaceLocal;
|
|
229
|
+
return surfaceRegionContainsFootprint(candidate.region, proposal.position, childFootprint.size, proposal.rotation, childFootprint.localBounds);
|
|
230
|
+
}), hit.point[1]);
|
|
231
|
+
}
|
|
232
|
+
if (!surface && isProceduralItem(host)) {
|
|
233
|
+
const bounds = evaluateRecipe(host.recipe, host.parameters);
|
|
234
|
+
if (hit.point.every((value, axis) => value >= bounds.min[axis] - 1e-6 && value <= bounds.max[axis] + 1e-6))
|
|
235
|
+
surface = hitDerivedSurfaceProvider.resolveHit(host, hit, ctx);
|
|
236
|
+
}
|
|
237
|
+
if (!surface) {
|
|
238
|
+
const overCutout = hit.normalWorldY >= UPWARD_SURFACE_NORMAL_MIN_Y &&
|
|
239
|
+
provider.surfaces?.(host, ctx).some((candidate) => {
|
|
240
|
+
if (!candidate.region?.holes?.length)
|
|
241
|
+
return false;
|
|
242
|
+
const local = surfaceLocalPoint(candidate, hit.point);
|
|
243
|
+
const point = [local[0], local[2]];
|
|
244
|
+
return (surfaceRegionContainsPoint({ ...candidate.region, holes: [] }, point) &&
|
|
245
|
+
!surfaceRegionContainsPoint(candidate.region, point));
|
|
246
|
+
});
|
|
247
|
+
return reject(overCutout ? 'surface-cutout' : 'no-surface');
|
|
248
|
+
}
|
|
249
|
+
const pose = poseFor(surface);
|
|
250
|
+
const localPosition = pose.surfaceLocal?.position ?? [0, 0, 0];
|
|
251
|
+
const rotation = pose.surfaceLocal?.rotation ?? [0, childFootprint.rotationY, 0];
|
|
252
|
+
if (provider.accepts && !provider.accepts(host, childKind, surface, ctx)) {
|
|
253
|
+
return reject('child-not-accepted');
|
|
254
|
+
}
|
|
255
|
+
if (args.checkFootprint === false)
|
|
256
|
+
return pose;
|
|
257
|
+
if (surface.region) {
|
|
258
|
+
if (!surfaceRegionContainsFootprint(surface.region, localPosition, childFootprint.size, rotation, childFootprint.localBounds)) {
|
|
259
|
+
const insideOutline = surfaceRegionContainsFootprint({ ...surface.region, holes: [] }, localPosition, childFootprint.size, rotation, childFootprint.localBounds);
|
|
260
|
+
return reject(insideOutline ? 'surface-cutout' : 'footprint-outside-surface');
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
else {
|
|
264
|
+
if (!surfaceRegionContainsFootprint(hostRegion(host, ctx), pose.position, childFootprint.size, childFootprint.rotation ?? childFootprint.rotationY, childFootprint.localBounds))
|
|
265
|
+
return reject('footprint-exceeds-host');
|
|
266
|
+
}
|
|
267
|
+
return pose;
|
|
268
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { SurfaceRegion } from './surface-hosting.js';
|
|
2
|
+
export declare function surfaceRegionContainsPoint(region: SurfaceRegion | undefined, point: readonly [number, number]): boolean;
|
|
3
|
+
/** Position and XYZ rotation are surface-local; bounds and size are scaled child-local values. */
|
|
4
|
+
export declare function surfaceRegionContainsFootprint(region: SurfaceRegion | undefined, position: readonly [number, number, number], size: readonly [number, number, number], rotation: number | readonly [number, number, number], localBounds?: {
|
|
5
|
+
min: readonly [number, number, number];
|
|
6
|
+
max: readonly [number, number, number];
|
|
7
|
+
}): boolean;
|
|
8
|
+
//# sourceMappingURL=surface-region.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"surface-region.d.ts","sourceRoot":"","sources":["../../src/services/surface-region.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAiBtD,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,aAAa,GAAG,SAAS,EACjC,KAAK,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,OAAO,CAeT;AAED,kGAAkG;AAClG,wBAAgB,8BAA8B,CAC5C,MAAM,EAAE,aAAa,GAAG,SAAS,EACjC,QAAQ,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAC3C,IAAI,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EACvC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EACpD,WAAW,CAAC,EAAE;IAAE,GAAG,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,GAAG,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,GAC/F,OAAO,CAiBT"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { pointInPolygon } from '../lib/polygon-relations.js';
|
|
2
|
+
import { frame, transformPoint } from '../procedural-items/spatial.js';
|
|
3
|
+
const EPSILON = 1e-6;
|
|
4
|
+
function outline(region) {
|
|
5
|
+
if (region.kind === 'polygon')
|
|
6
|
+
return (region.points ?? []).map(([x, z]) => [x, z]);
|
|
7
|
+
if (!region.size)
|
|
8
|
+
return [];
|
|
9
|
+
const [x, z] = region.center ?? [0, 0];
|
|
10
|
+
const [w, d] = region.size;
|
|
11
|
+
return [
|
|
12
|
+
[x - w, z - d],
|
|
13
|
+
[x + w, z - d],
|
|
14
|
+
[x + w, z + d],
|
|
15
|
+
[x - w, z + d],
|
|
16
|
+
];
|
|
17
|
+
}
|
|
18
|
+
export function surfaceRegionContainsPoint(region, point) {
|
|
19
|
+
if (!region)
|
|
20
|
+
return true;
|
|
21
|
+
return ((region.kind === 'rect'
|
|
22
|
+
? !!region.size &&
|
|
23
|
+
Math.abs(point[0] - (region.center?.[0] ?? 0)) <= region.size[0] + EPSILON &&
|
|
24
|
+
Math.abs(point[1] - (region.center?.[1] ?? 0)) <= region.size[1] + EPSILON
|
|
25
|
+
: pointInPolygon([...point], outline(region))) &&
|
|
26
|
+
!(region.holes ?? []).some((hole) => pointInPolygon([...point], hole.map(([x, z]) => [x, z]))));
|
|
27
|
+
}
|
|
28
|
+
/** Position and XYZ rotation are surface-local; bounds and size are scaled child-local values. */
|
|
29
|
+
export function surfaceRegionContainsFootprint(region, position, size, rotation, localBounds) {
|
|
30
|
+
if (!region)
|
|
31
|
+
return true;
|
|
32
|
+
const bounds = localBounds ?? {
|
|
33
|
+
min: [-size[0] / 2, 0, -size[2] / 2],
|
|
34
|
+
max: [size[0] / 2, size[1], size[2] / 2],
|
|
35
|
+
};
|
|
36
|
+
const childFrame = frame([...position], typeof rotation === 'number' ? [0, rotation, 0] : [...rotation]);
|
|
37
|
+
// A rotated box and its XZ projection are centrally symmetric, so their centre is the transformed bounds midpoint.
|
|
38
|
+
const center = transformPoint(childFrame, [
|
|
39
|
+
(bounds.min[0] + bounds.max[0]) / 2,
|
|
40
|
+
(bounds.min[1] + bounds.max[1]) / 2,
|
|
41
|
+
(bounds.min[2] + bounds.max[2]) / 2,
|
|
42
|
+
]);
|
|
43
|
+
return surfaceRegionContainsPoint(region, [center[0], center[2]]);
|
|
44
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { type NodePort } from '../registry';
|
|
2
|
-
import type { AnyNode, AnyNodeId } from '../schema';
|
|
1
|
+
import { type NodePort } from '../registry/index.js';
|
|
2
|
+
import type { AnyNode, AnyNodeId } from '../schema/index.js';
|
|
3
3
|
export type SystemSummary = {
|
|
4
4
|
/** Every node in this connected component. */
|
|
5
5
|
nodeIds: AnyNodeId[];
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { nodeRegistry } from '../registry';
|
|
2
|
-
import { getLevelElevations } from './storey';
|
|
1
|
+
import { nodeRegistry } from '../registry/index.js';
|
|
2
|
+
import { getLevelElevations } from './storey.js';
|
|
3
3
|
/**
|
|
4
4
|
* The "System" primitive: connected components over the port graph.
|
|
5
5
|
*
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { buildPortComponents } from './system-graph';
|
|
1
|
+
import { buildPortComponents } from './system-graph.js';
|
|
2
2
|
/** IPC 704.1 minimum drainage slope (rise/run, dimensionless) by
|
|
3
3
|
* nominal pipe size: ¼"/ft (1:48) under 3", ⅛"/ft (1:96) for 3–6",
|
|
4
4
|
* 1/16"/ft (1:192) at 8"+. */
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { type AnyNode, type AnyNodeId } from '../../schema';
|
|
2
|
-
import type { SceneState } from '../use-scene';
|
|
1
|
+
import { type AnyNode, type AnyNodeId } from '../../schema/index.js';
|
|
2
|
+
import type { SceneState } from '../use-scene.js';
|
|
3
3
|
type NodeCreateOp = {
|
|
4
4
|
node: AnyNode;
|
|
5
5
|
parentId?: AnyNodeId;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"node-actions.d.ts","sourceRoot":"","sources":["../../../src/store/actions/node-actions.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"node-actions.d.ts","sourceRoot":"","sources":["../../../src/store/actions/node-actions.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { nodeRegistry } from '../../registry/registry';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
1
|
+
import { nodeRegistry } from '../../registry/registry.js';
|
|
2
|
+
import { validateNodeRelations } from '../../registry/validate-relations.js';
|
|
3
|
+
import { AnyNode as AnyNodeSchema, createDefaultGuttersForSegment, createDefaultRidgeVentsForSegment, DownspoutNode as DownspoutNodeSchema, defaultDownspoutMetadata, generateId, getDefaultGutterSide, getEffectiveWallSurfaceMaterial, getWallSurfaceMaterialSignature, isAutoGutterEnabled, isAutoRidgeVentEnabled, isDefaultDownspoutNode, isDefaultGutterNode, isDefaultRidgeVentNode, parseNode, planAutomaticDownspouts, resolveAutomaticDownspoutLength, } from '../../schema/index.js';
|
|
4
|
+
import { constrainWallCurveOffsetToAvoidIntersections } from '../../systems/wall/wall-curve.js';
|
|
5
|
+
import { activeSceneCommitNodeIds, addActiveSceneCommitNodeIds, runWithSceneCommitNodeIds, } from '../history-control.js';
|
|
5
6
|
const DEFAULT_RIDGE_VENT_REFRESH_FIELDS = new Set([
|
|
6
7
|
'roofType',
|
|
7
8
|
'width',
|
|
@@ -367,6 +368,10 @@ function warnSanitizedNodeMutation(mutation, nodeId, issues) {
|
|
|
367
368
|
}
|
|
368
369
|
function parseCreatedNode(node, parentId) {
|
|
369
370
|
const candidate = { ...node, parentId };
|
|
371
|
+
const registered = nodeRegistry.get(candidate.type)?.schema;
|
|
372
|
+
// Generated definitions must reject invalid geometry instead of retaining a failed parse.
|
|
373
|
+
if (registered?.meta?.()?.strictMutations === true)
|
|
374
|
+
return registered.parse(candidate);
|
|
370
375
|
const parsed = parseNode(candidate);
|
|
371
376
|
if (parsed.success)
|
|
372
377
|
return parsed.data;
|
|
@@ -393,9 +398,19 @@ function mergeNodeUpdate(currentNode, patch) {
|
|
|
393
398
|
}
|
|
394
399
|
function parseUpdatedNode(currentNode, data) {
|
|
395
400
|
const candidate = mergeNodeUpdate(currentNode, data);
|
|
401
|
+
// Graph links survive schemas that omit children; only an explicit patch may change them.
|
|
402
|
+
const preserveChildren = (updated) => !Object.hasOwn(data, 'children') &&
|
|
403
|
+
'children' in currentNode &&
|
|
404
|
+
Array.isArray(currentNode.children)
|
|
405
|
+
? { ...updated, children: currentNode.children }
|
|
406
|
+
: updated;
|
|
407
|
+
const registered = nodeRegistry.get(currentNode.type)?.schema;
|
|
408
|
+
// Generated definitions must reject invalid geometry instead of retaining a failed parse.
|
|
409
|
+
if (registered?.meta?.()?.strictMutations === true)
|
|
410
|
+
return preserveChildren(registered.parse(candidate));
|
|
396
411
|
const parsed = parseNode(candidate);
|
|
397
412
|
if (parsed.success)
|
|
398
|
-
return parsed.data;
|
|
413
|
+
return preserveChildren(parsed.data);
|
|
399
414
|
const schema = getNodeSchemaForType(candidate.type);
|
|
400
415
|
const sanitized = sanitizeNumericValue(schema, data, currentNode, []);
|
|
401
416
|
if (sanitized.issues.length === 0) {
|
|
@@ -1328,6 +1343,22 @@ const deleteNodesActionImpl = (set, get, ids) => {
|
|
|
1328
1343
|
}
|
|
1329
1344
|
}
|
|
1330
1345
|
}
|
|
1346
|
+
const deletedZoneIds = new Set();
|
|
1347
|
+
for (const id of allIds) {
|
|
1348
|
+
if (nextNodes[id]?.type === 'zone')
|
|
1349
|
+
deletedZoneIds.add(id);
|
|
1350
|
+
}
|
|
1351
|
+
if (deletedZoneIds.size > 0) {
|
|
1352
|
+
for (const node of Object.values(nextNodes)) {
|
|
1353
|
+
if (node.type !== 'unit' || allIds.has(node.id))
|
|
1354
|
+
continue;
|
|
1355
|
+
const members = node.members.filter((id) => !deletedZoneIds.has(id));
|
|
1356
|
+
if (members.length === node.members.length)
|
|
1357
|
+
continue;
|
|
1358
|
+
nextNodes[node.id] = { ...node, members };
|
|
1359
|
+
nodesToMarkDirty.add(node.id);
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1331
1362
|
for (const id of allIds) {
|
|
1332
1363
|
const node = nextNodes[id];
|
|
1333
1364
|
if (!node)
|
|
@@ -1382,10 +1413,18 @@ const deleteNodesActionImpl = (set, get, ids) => {
|
|
|
1382
1413
|
get().markDirty(id);
|
|
1383
1414
|
});
|
|
1384
1415
|
};
|
|
1416
|
+
function validatedSet(set) {
|
|
1417
|
+
return (change) => set((state) => {
|
|
1418
|
+
const patch = change(state);
|
|
1419
|
+
if (patch.nodes)
|
|
1420
|
+
validateNodeRelations(state.nodes, patch.nodes, activeSceneCommitNodeIds() ?? []);
|
|
1421
|
+
return patch;
|
|
1422
|
+
});
|
|
1423
|
+
}
|
|
1385
1424
|
export const createNodesAction = (set, get, ops) => runWithSceneCommitNodeIds(ops.flatMap(({ node, parentId }) => {
|
|
1386
1425
|
const effectiveParentId = parentId ?? node.parentId;
|
|
1387
1426
|
return effectiveParentId ? [node.id, effectiveParentId] : [node.id];
|
|
1388
|
-
}), () => createNodesActionImpl(set, get, ops));
|
|
1427
|
+
}), () => createNodesActionImpl(validatedSet(set), get, ops));
|
|
1389
1428
|
export const applyNodeChangesAction = (set, get, changes) => runWithSceneCommitNodeIds([
|
|
1390
1429
|
...(changes.create ?? []).flatMap(({ node, parentId }) => {
|
|
1391
1430
|
const effectiveParentId = parentId ?? node.parentId;
|
|
@@ -1393,6 +1432,6 @@ export const applyNodeChangesAction = (set, get, changes) => runWithSceneCommitN
|
|
|
1393
1432
|
}),
|
|
1394
1433
|
...(changes.update ?? []).map(({ id }) => id),
|
|
1395
1434
|
...(changes.delete ?? []),
|
|
1396
|
-
], () => applyNodeChangesActionImpl(set, get, changes));
|
|
1397
|
-
export const updateNodesAction = (set, get, updates) => runWithSceneCommitNodeIds(updates.map(({ id }) => id), () => updateNodesActionImpl(set, get, updates));
|
|
1398
|
-
export const deleteNodesAction = (set, get, ids) => runWithSceneCommitNodeIds(ids, () => deleteNodesActionImpl(set, get, ids));
|
|
1435
|
+
], () => applyNodeChangesActionImpl(validatedSet(set), get, changes));
|
|
1436
|
+
export const updateNodesAction = (set, get, updates) => runWithSceneCommitNodeIds(updates.map(({ id }) => id), () => updateNodesActionImpl(validatedSet(set), get, updates));
|
|
1437
|
+
export const deleteNodesAction = (set, get, ids) => runWithSceneCommitNodeIds(ids, () => deleteNodesActionImpl(validatedSet(set), get, ids));
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { Collection, CollectionId } from '../schema/collections';
|
|
2
|
-
import type { SceneMaterial, SceneMaterialId } from '../schema/scene-material';
|
|
3
|
-
import type { AnyNode, AnyNodeId } from '../schema/types';
|
|
1
|
+
import type { Collection, CollectionId } from '../schema/collections.js';
|
|
2
|
+
import type { SceneMaterial, SceneMaterialId } from '../schema/scene-material.js';
|
|
3
|
+
import type { AnyNode, AnyNodeId } from '../schema/types.js';
|
|
4
4
|
export type SceneSnapshot = {
|
|
5
5
|
nodes: Record<AnyNodeId, AnyNode>;
|
|
6
6
|
rootNodeIds: AnyNodeId[];
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import type { AnyNode, AnyNodeId } from '../schema';
|
|
1
|
+
import type { AnyNode, AnyNodeId } from '../schema/index.js';
|
|
2
2
|
export declare function getHistoryDirtyNodeIds(before: Record<string, AnyNode>, after: Record<string, AnyNode>): Set<AnyNodeId>;
|
|
3
3
|
//# sourceMappingURL=history-invalidation.d.ts.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { Interactive } from '../schema/nodes/item';
|
|
2
|
-
import type { AnyNodeId } from '../schema/types';
|
|
1
|
+
import type { Interactive } from '../schema/nodes/item.js';
|
|
2
|
+
import type { AnyNodeId } from '../schema/types.js';
|
|
3
3
|
export type ControlValue = boolean | number;
|
|
4
4
|
export type ItemInteractiveState = {
|
|
5
5
|
controlValues: ControlValue[];
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { TemporalState } from 'zundo';
|
|
2
2
|
import { type StoreApi, type UseBoundStore } from 'zustand';
|
|
3
|
-
import type { Collection, CollectionId } from '../schema/collections';
|
|
4
|
-
import { SceneMaterial, type SceneMaterialId } from '../schema/scene-material';
|
|
5
|
-
import { type AnyNode, type AnyNodeId } from '../schema/types';
|
|
6
|
-
import { type SceneCommitOrigin, type SceneSnapshot } from './history-control';
|
|
3
|
+
import type { Collection, CollectionId } from '../schema/collections.js';
|
|
4
|
+
import { SceneMaterial, type SceneMaterialId } from '../schema/scene-material.js';
|
|
5
|
+
import { type AnyNode, type AnyNodeId } from '../schema/types.js';
|
|
6
|
+
import { type SceneCommitOrigin, type SceneSnapshot } from './history-control.js';
|
|
7
7
|
export type SceneState = {
|
|
8
8
|
nodes: Record<AnyNodeId, AnyNode>;
|
|
9
9
|
rootNodeIds: AnyNodeId[];
|