@pascal-app/core 1.0.0-beta.5 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +23 -0
- package/dist/capture/index.d.ts +3 -0
- package/dist/capture/index.d.ts.map +1 -0
- package/dist/capture/index.js +2 -0
- package/dist/capture/schema.d.ts +417 -0
- package/dist/capture/schema.d.ts.map +1 -0
- package/dist/capture/schema.js +348 -0
- package/dist/capture/source.d.ts +60 -0
- package/dist/capture/source.d.ts.map +1 -0
- package/dist/capture/source.js +254 -0
- package/dist/events/bus.d.ts +55 -7
- package/dist/events/bus.d.ts.map +1 -1
- package/dist/events/hidden-wall-pointer-hold.d.ts +28 -0
- package/dist/events/hidden-wall-pointer-hold.d.ts.map +1 -0
- package/dist/events/hidden-wall-pointer-hold.js +37 -0
- package/dist/hooks/spatial-grid/spatial-grid-sync.d.ts +2785 -1
- package/dist/hooks/spatial-grid/spatial-grid-sync.d.ts.map +1 -1
- package/dist/hooks/spatial-grid/spatial-grid-sync.js +132 -19
- package/dist/hooks/spatial-grid/support-host-patch.d.ts +45 -1
- package/dist/hooks/spatial-grid/support-host-patch.d.ts.map +1 -1
- package/dist/hooks/spatial-grid/support-host-patch.js +171 -6
- package/dist/index.d.ts +14 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +16 -5
- package/dist/lib/asset-storage.d.ts.map +1 -1
- package/dist/lib/asset-storage.js +4 -1
- package/dist/lib/polygon-union.d.ts +4 -0
- package/dist/lib/polygon-union.d.ts.map +1 -0
- package/dist/lib/polygon-union.js +306 -0
- package/dist/lib/roof-overlap.d.ts +32 -0
- package/dist/lib/roof-overlap.d.ts.map +1 -0
- package/dist/lib/roof-overlap.js +72 -0
- package/dist/lib/slab-polygon.d.ts +35 -2
- package/dist/lib/slab-polygon.d.ts.map +1 -1
- package/dist/lib/slab-polygon.js +133 -27
- package/dist/lib/space-detection.d.ts +11 -0
- package/dist/lib/space-detection.d.ts.map +1 -1
- package/dist/lib/space-detection.js +42 -14
- package/dist/registry/handles.d.ts +24 -3
- package/dist/registry/handles.d.ts.map +1 -1
- package/dist/registry/index.d.ts +3 -4
- package/dist/registry/index.d.ts.map +1 -1
- package/dist/registry/index.js +1 -2
- package/dist/registry/registry.d.ts +13 -6
- package/dist/registry/registry.d.ts.map +1 -1
- package/dist/registry/registry.js +37 -4
- package/dist/registry/scene-api.d.ts +16 -0
- package/dist/registry/scene-api.d.ts.map +1 -1
- package/dist/registry/scene-api.js +49 -1
- package/dist/registry/types.d.ts +226 -13
- package/dist/registry/types.d.ts.map +1 -1
- package/dist/schema/__bench__/node-parsers.bench.d.ts +24 -0
- package/dist/schema/__bench__/node-parsers.bench.d.ts.map +1 -0
- package/dist/schema/__bench__/node-parsers.bench.js +314 -0
- package/dist/schema/__fixtures__/node-fixtures.d.ts +28 -0
- package/dist/schema/__fixtures__/node-fixtures.d.ts.map +1 -0
- package/dist/schema/__fixtures__/node-fixtures.js +125 -0
- package/dist/schema/base.d.ts +1 -1
- package/dist/schema/base.d.ts.map +1 -1
- package/dist/schema/base.js +8 -1
- package/dist/schema/compiled-node-parsers.d.ts +40 -0
- package/dist/schema/compiled-node-parsers.d.ts.map +1 -0
- package/dist/schema/compiled-node-parsers.js +95 -0
- package/dist/schema/index.d.ts +19 -15
- package/dist/schema/index.d.ts.map +1 -1
- package/dist/schema/index.js +17 -12
- package/dist/schema/material.d.ts +2 -1
- package/dist/schema/material.d.ts.map +1 -1
- package/dist/schema/material.js +1 -0
- package/dist/schema/nodes/automatic-downspout.d.ts +17 -0
- package/dist/schema/nodes/automatic-downspout.d.ts.map +1 -0
- package/dist/schema/nodes/automatic-downspout.js +237 -0
- package/dist/schema/nodes/block.d.ts +107 -0
- package/dist/schema/nodes/block.d.ts.map +1 -0
- package/dist/schema/nodes/block.js +207 -0
- package/dist/schema/nodes/box-vent.d.ts +8 -2
- package/dist/schema/nodes/box-vent.d.ts.map +1 -1
- package/dist/schema/nodes/box-vent.js +2 -0
- package/dist/schema/nodes/building.d.ts +1 -1
- package/dist/schema/nodes/cabinet.d.ts +25 -3
- package/dist/schema/nodes/cabinet.d.ts.map +1 -1
- package/dist/schema/nodes/cabinet.js +26 -5
- package/dist/schema/nodes/ceiling.d.ts +1 -1
- package/dist/schema/nodes/chimney.d.ts +2 -2
- package/dist/schema/nodes/column.d.ts +6 -6
- package/dist/schema/nodes/construction-dimension.d.ts +4 -4
- package/dist/schema/nodes/cupola.d.ts +9 -1
- package/dist/schema/nodes/cupola.d.ts.map +1 -1
- package/dist/schema/nodes/cupola.js +2 -0
- package/dist/schema/nodes/door.d.ts +1 -1
- package/dist/schema/nodes/dormer.d.ts +44 -1
- package/dist/schema/nodes/dormer.d.ts.map +1 -1
- package/dist/schema/nodes/dormer.js +131 -7
- package/dist/schema/nodes/downspout.d.ts +11 -1
- package/dist/schema/nodes/downspout.d.ts.map +1 -1
- package/dist/schema/nodes/downspout.js +24 -0
- package/dist/schema/nodes/duct-fitting.d.ts +18 -1
- package/dist/schema/nodes/duct-fitting.d.ts.map +1 -1
- package/dist/schema/nodes/duct-fitting.js +25 -4
- package/dist/schema/nodes/duct-segment.d.ts +23 -1
- package/dist/schema/nodes/duct-segment.d.ts.map +1 -1
- package/dist/schema/nodes/duct-segment.js +20 -0
- package/dist/schema/nodes/duct-terminal.d.ts +1 -1
- package/dist/schema/nodes/elevator.d.ts +1 -1
- package/dist/schema/nodes/eyebrow-vent.d.ts +8 -2
- package/dist/schema/nodes/eyebrow-vent.d.ts.map +1 -1
- package/dist/schema/nodes/eyebrow-vent.js +2 -0
- package/dist/schema/nodes/fence.d.ts +1 -1
- package/dist/schema/nodes/guide.d.ts +1 -1
- package/dist/schema/nodes/gutter.d.ts +36 -2
- package/dist/schema/nodes/gutter.d.ts.map +1 -1
- package/dist/schema/nodes/gutter.js +308 -1
- package/dist/schema/nodes/hanger-overrides.d.ts +7 -0
- package/dist/schema/nodes/hanger-overrides.d.ts.map +1 -0
- package/dist/schema/nodes/hanger-overrides.js +6 -0
- package/dist/schema/nodes/hvac-equipment.d.ts +1 -1
- package/dist/schema/nodes/item.d.ts +4 -3
- package/dist/schema/nodes/item.d.ts.map +1 -1
- package/dist/schema/nodes/item.js +5 -0
- package/dist/schema/nodes/lean-to-extension.d.ts +212 -0
- package/dist/schema/nodes/lean-to-extension.d.ts.map +1 -0
- package/dist/schema/nodes/lean-to-extension.js +114 -0
- package/dist/schema/nodes/level.d.ts +2 -2
- package/dist/schema/nodes/level.d.ts.map +1 -1
- package/dist/schema/nodes/lineset.d.ts +1 -1
- package/dist/schema/nodes/liquid-line.d.ts +1 -1
- package/dist/schema/nodes/measurement.d.ts +1 -1
- package/dist/schema/nodes/pipe-fitting.d.ts +9 -1
- package/dist/schema/nodes/pipe-fitting.d.ts.map +1 -1
- package/dist/schema/nodes/pipe-fitting.js +6 -2
- package/dist/schema/nodes/pipe-segment.d.ts +23 -1
- package/dist/schema/nodes/pipe-segment.d.ts.map +1 -1
- package/dist/schema/nodes/pipe-segment.js +20 -0
- package/dist/schema/nodes/pipe-trap.d.ts +1 -1
- package/dist/schema/nodes/ridge-vent.d.ts +1 -1
- package/dist/schema/nodes/ridge-vent.d.ts.map +1 -1
- package/dist/schema/nodes/ridge-vent.js +4 -1
- package/dist/schema/nodes/roof-segment-shape.d.ts +4 -0
- package/dist/schema/nodes/roof-segment-shape.d.ts.map +1 -1
- package/dist/schema/nodes/roof-segment-shape.js +64 -1
- package/dist/schema/nodes/roof-segment-walls.d.ts +3 -1
- package/dist/schema/nodes/roof-segment-walls.d.ts.map +1 -1
- package/dist/schema/nodes/roof-segment-walls.js +1 -1
- package/dist/schema/nodes/roof-segment.d.ts +41 -1
- package/dist/schema/nodes/roof-segment.d.ts.map +1 -1
- package/dist/schema/nodes/roof-segment.js +79 -2
- package/dist/schema/nodes/roof.d.ts +22 -1
- package/dist/schema/nodes/roof.d.ts.map +1 -1
- package/dist/schema/nodes/roof.js +14 -0
- package/dist/schema/nodes/scan.d.ts +19 -2
- package/dist/schema/nodes/scan.d.ts.map +1 -1
- package/dist/schema/nodes/scan.js +11 -1
- package/dist/schema/nodes/shelf.d.ts +2 -2
- package/dist/schema/nodes/site.d.ts +1 -1
- package/dist/schema/nodes/skylight.d.ts +1 -1
- package/dist/schema/nodes/slab.d.ts +1 -1
- package/dist/schema/nodes/solar-panel.d.ts +1 -1
- package/dist/schema/nodes/spawn.d.ts +1 -1
- package/dist/schema/nodes/stair-segment.d.ts +1 -1
- package/dist/schema/nodes/stair.d.ts +1 -1
- package/dist/schema/nodes/structural-grid.d.ts +1 -1
- package/dist/schema/nodes/turbine-vent.d.ts +7 -1
- package/dist/schema/nodes/turbine-vent.d.ts.map +1 -1
- package/dist/schema/nodes/turbine-vent.js +2 -0
- package/dist/schema/nodes/wall.d.ts +4 -4
- package/dist/schema/nodes/wall.d.ts.map +1 -1
- package/dist/schema/nodes/wall.js +7 -1
- package/dist/schema/nodes/window.d.ts +10 -3
- package/dist/schema/nodes/window.d.ts.map +1 -1
- package/dist/schema/nodes/window.js +4 -0
- package/dist/schema/nodes/zone.d.ts +2 -2
- package/dist/schema/nodes/zone.js +1 -1
- package/dist/schema/types.d.ts +484 -109
- package/dist/schema/types.d.ts.map +1 -1
- package/dist/schema/types.js +27 -1
- package/dist/services/hosting.d.ts +2 -2
- package/dist/services/hosting.d.ts.map +1 -1
- package/dist/services/hosting.js +4 -4
- package/dist/services/index.d.ts +1 -1
- package/dist/services/index.d.ts.map +1 -1
- package/dist/services/index.js +1 -1
- package/dist/services/system-graph.d.ts +11 -0
- package/dist/services/system-graph.d.ts.map +1 -1
- package/dist/services/system-graph.js +35 -5
- package/dist/store/actions/node-actions.d.ts +7 -0
- package/dist/store/actions/node-actions.d.ts.map +1 -1
- package/dist/store/actions/node-actions.js +429 -17
- package/dist/store/history-invalidation.d.ts +3 -0
- package/dist/store/history-invalidation.d.ts.map +1 -0
- package/dist/store/history-invalidation.js +72 -0
- package/dist/store/scene-hydration.d.ts +5 -0
- package/dist/store/scene-hydration.d.ts.map +1 -0
- package/dist/store/scene-hydration.js +57 -0
- package/dist/store/use-scene.d.ts +3 -0
- package/dist/store/use-scene.d.ts.map +1 -1
- package/dist/store/use-scene.js +332 -64
- package/dist/systems/roof/roof-elevation-system.d.ts +3 -0
- package/dist/systems/roof/roof-elevation-system.d.ts.map +1 -0
- package/dist/systems/roof/roof-elevation-system.js +94 -0
- package/dist/systems/roof/roof-elevation.d.ts +4 -0
- package/dist/systems/roof/roof-elevation.d.ts.map +1 -0
- package/dist/systems/roof/roof-elevation.js +95 -0
- package/dist/systems/roof/roof-footprint.d.ts +17 -0
- package/dist/systems/roof/roof-footprint.d.ts.map +1 -0
- package/dist/systems/roof/roof-footprint.js +83 -0
- package/dist/systems/stair/stair-flight.d.ts +17 -0
- package/dist/systems/stair/stair-flight.d.ts.map +1 -0
- package/dist/systems/stair/stair-flight.js +27 -0
- package/dist/systems/stair/stair-opening-preview.d.ts +1555 -1328
- package/dist/systems/stair/stair-opening-preview.d.ts.map +1 -1
- package/dist/systems/stair/stair-opening-system.d.ts +1 -0
- package/dist/systems/stair/stair-opening-system.d.ts.map +1 -1
- package/dist/systems/stair/stair-opening-system.js +93 -96
- package/dist/systems/stair/stair-rise.d.ts.map +1 -1
- package/dist/systems/stair/stair-rise.js +20 -21
- package/dist/systems/wall/wall-curve.d.ts +1 -0
- package/dist/systems/wall/wall-curve.d.ts.map +1 -1
- package/dist/systems/wall/wall-curve.js +73 -0
- package/dist/systems/wall/wall-footprint.d.ts +1 -0
- package/dist/systems/wall/wall-footprint.d.ts.map +1 -1
- package/dist/systems/wall/wall-footprint.js +1 -0
- package/dist/utils/clone-scene-graph.d.ts.map +1 -1
- package/dist/utils/clone-scene-graph.js +26 -0
- package/dist/validation/validate-build-json.d.ts +3 -0
- package/dist/validation/validate-build-json.d.ts.map +1 -1
- package/dist/validation/validate-build-json.js +49 -2
- package/package.json +10 -3
- package/dist/systems/elevator/elevator-runtime-system.d.ts +0 -2
- package/dist/systems/elevator/elevator-runtime-system.d.ts.map +0 -1
- package/dist/systems/elevator/elevator-runtime-system.js +0 -8
package/dist/index.js
CHANGED
|
@@ -1,18 +1,21 @@
|
|
|
1
1
|
export { emitter, eventSuffixes } from './events/bus';
|
|
2
|
+
export { hiddenWallPointerEventsHeld, holdHiddenWallPointerEvents, } from './events/hidden-wall-pointer-hold';
|
|
2
3
|
export { itemClipRegistry } from './hooks/scene-registry/item-clip-registry';
|
|
3
4
|
export { sceneRegistry, useRegistry, } from './hooks/scene-registry/scene-registry';
|
|
4
5
|
export { GROUND_SUPPORT_ID, getFloorPlacedElevation, getFloorPlacedFootprints, getFloorStackedPosition, } from './hooks/spatial-grid/floor-placed-elevation';
|
|
5
6
|
export { getWallBaseElevationForNodes, getWallEffectiveHeightForNodes, pointInPolygon, SUPPORT_ELEVATION_EPSILON, spatialGridManager, } from './hooks/spatial-grid/spatial-grid-manager';
|
|
6
7
|
export { findLevelAncestorId, initSpatialGridSync, markSlabChangeDependents, resolveBuildingForLevel, resolveLevelId, } from './hooks/spatial-grid/spatial-grid-sync';
|
|
7
|
-
export { resolveFenceSupportSlabPatch, resolveMovedWallSupportSlabPatch, resolveSupportSlabPatch, resolveWallSupportSlabPatch, } from './hooks/spatial-grid/support-host-patch';
|
|
8
|
+
export { resolveFenceConstructionSupport, resolveFenceSupportSlabPatch, resolveFrozenFloorPlacementPatch, resolveMovedWallSupportSlabPatch, resolveSupportSlabPatch, resolveTerrainWallConstructionOptions, resolveWallConstruction, resolveWallSupportSlabPatch, } from './hooks/spatial-grid/support-host-patch';
|
|
8
9
|
export { useSpatialQuery } from './hooks/spatial-grid/use-spatial-query';
|
|
9
10
|
export { loadAssetUrl, saveAsset } from './lib/asset-storage';
|
|
10
11
|
export { clampDoorOperationState, getDoorRenderOpenAmount, getGarageVisibleOpeningRatio, isOperationDoorType, SECTIONAL_GARAGE_RENDER_OPEN_SCALE, } from './lib/door-operation';
|
|
11
12
|
export { getDefaultLevelName, getLevelDisplayName } from './lib/level-name';
|
|
12
13
|
export { areMeasurementPointsCoplanar, closestMeasurementFeatureBinding, MEASUREMENT_PLANAR_TOLERANCE, measurementAnchorFallback, measurementAnchorReferenceNodeIds, measurementAngle, measurementArea, measurementAreaVector, measurementCentroid, measurementDistance, measurementFeatureLength, measurementNormal, measurementPerimeter, measurementPrismVolume, measurementReferenceNodeIds, remapMeasurementAnchors, remapMeasurementReferences, } from './lib/measurement-geometry';
|
|
13
14
|
export { pointInPolygon as pointInPolygon2D, pointOnSegment, polygonContainsPolygon, polygonsIntersect, polygonsOverlap, segmentsIntersect, } from './lib/polygon-relations';
|
|
15
|
+
export { subtractPolygonsFromPolygon, unionPolygons, } from './lib/polygon-union';
|
|
16
|
+
export { compareRoofOverlapIdentity, getRoofPlanBounds, roofOverlapEntryOwns, roofPlanBoundsOverlap, roofPlanOverlapEntryOwns, } from './lib/roof-overlap';
|
|
14
17
|
export { resolveSelectionProxyId, selectionProxyIdFromMetadata } from './lib/selection-proxy';
|
|
15
|
-
export { getRenderableSlabPolygon, slabPolygonContextFromGeometry, snapSlabEdgeToWallBand, } from './lib/slab-polygon';
|
|
18
|
+
export { getRenderableSlabPolygon, prepareSlabPolygonContext, scopeSlabPolygonContext, slabPolygonContextChanges, slabPolygonContextForLevel, slabPolygonContextFromGeometry, snapSlabEdgeToWallBand, } from './lib/slab-polygon';
|
|
16
19
|
export { deriveSlotId, isSlotMaterialName, SLOT_MATERIAL_PREFIX, slotLabelFromId, } from './lib/slots';
|
|
17
20
|
export { detectSpacesForLevel, initSpaceDetectionSync, isSpaceDetectionPaused, pauseSpaceDetection, planAutoCeilingsForLevel, planAutoSlabsForLevel, planAutoZonesForLevel, resolveAutoZonePolygon, resumeSpaceDetection, wallClosesRoom, wallTouchesOthers, } from './lib/space-detection';
|
|
18
21
|
export { advanceStroke, beginStroke, brushHeightAt, DEFAULT_BRUSH_SETTINGS, detachStrokeAnchor, highestOver, MIN_BRUSH_RADIUS_IN_SPACINGS, maxCoverage, minBrushRadius, RAISE_METRES_PER_STROKE, sampleTarget, weightAt, } from './lib/terrain-brush';
|
|
@@ -25,10 +28,15 @@ export { closestOnSegment, collectLevelWallSegments, nearestWallSegment, WALL_SN
|
|
|
25
28
|
export { deriveZoneQuantityReport, } from './lib/zone-quantities';
|
|
26
29
|
export { getCatalogMaterialById, getDynamicLibraryMaterials, getLibraryMaterialIdFromRef, getLibraryMaterialsVersion, getMaterialPresetByRef, getMaterialsForCategory, getSceneMaterialIdFromRef, LIBRARY_MATERIAL_REF_PREFIX, MATERIAL_CATALOG, MATERIAL_CATEGORIES, MATERIAL_SURFACES, parseMaterialRef, registerLibraryMaterials, SCENE_MATERIAL_REF_PREFIX, subscribeLibraryMaterials, toLibraryMaterialRef, toSceneMaterialRef, unregisterLibraryMaterials, } from './material-library';
|
|
27
30
|
export * from './registry';
|
|
31
|
+
// Exported here rather than from the registry barrel: that barrel is
|
|
32
|
+
// reachable from server-safe graphs (schema → spatial grid → registry)
|
|
33
|
+
// and must stay free of React imports.
|
|
34
|
+
export { useRegistryVersion } from './registry/use-registry-version';
|
|
28
35
|
export * from './schema';
|
|
29
36
|
export * from './services';
|
|
30
37
|
export { isMovable, movePlanToward, moveToward, resolveMovable } from './services/movement';
|
|
31
|
-
export { acquireSceneHistoryPause, getSceneHistoryPauseDepth, pauseSceneHistory, resetSceneHistoryPauseDepth, resumeSceneHistory, runAsSingleSceneHistoryStep, subscribeSceneCommits, } from './store/history-control';
|
|
38
|
+
export { acquireSceneHistoryPause, activeSceneCommitNodeIds, getSceneHistoryPauseDepth, pauseSceneHistory, resetSceneHistoryPauseDepth, resumeSceneHistory, runAsSingleSceneHistoryStep, subscribeSceneCommits, } from './store/history-control';
|
|
39
|
+
export { getHistoryDirtyNodeIds } from './store/history-invalidation';
|
|
32
40
|
export { useInteractive, } from './store/use-interactive';
|
|
33
41
|
export { default as useLiveNodeOverrides, getEffectiveNode, } from './store/use-live-node-overrides';
|
|
34
42
|
export { default as useLiveTerrain, } from './store/use-live-terrain';
|
|
@@ -39,18 +47,21 @@ export { getElevatorCabCenterZ, getElevatorCabDepth, getElevatorCabWidth, getEle
|
|
|
39
47
|
export { syncAutoElevatorOpenings } from './systems/elevator/elevator-opening-sync';
|
|
40
48
|
export { ElevatorOpeningSystem } from './systems/elevator/elevator-opening-system';
|
|
41
49
|
export { createElevatorInteractiveState, openElevatorDoor, openElevatorDoorState, queueElevatorRequest, requestElevatorLevel, stepElevatorRuntimeState, stepElevatorRuntimes, } from './systems/elevator/elevator-runtime';
|
|
42
|
-
export { ElevatorRuntimeSystem } from './systems/elevator/elevator-runtime-system';
|
|
43
50
|
export { resolveElevatorBuildingLevels, resolveElevatorLevels, resolveElevatorServiceLevelIds, resolveElevatorServiceLevels, } from './systems/elevator/elevator-service';
|
|
44
51
|
export { getFenceCenterlineFrameAt, getFenceCenterlineLength, sampleFenceCenterline, } from './systems/fence/fence-centerline';
|
|
45
52
|
export { getFenceControlHandle, getFenceSplineFrameAt, getFenceSplineLength, getTwoPointFenceCurveTangents, isSplineFence, sampleFenceSpline, } from './systems/fence/fence-spline';
|
|
53
|
+
export { resolveRoofElevation, resolveRoofWallTopElevation } from './systems/roof/roof-elevation';
|
|
54
|
+
export { RoofElevationSystem } from './systems/roof/roof-elevation-system';
|
|
55
|
+
export { fitRoofFootprint, resolveRoomRoofFootprint, resolveRoomRoofFootprintOnLevel, } from './systems/roof/roof-footprint';
|
|
46
56
|
export { resolveSlabPlacementElevation } from './systems/slab/slab-placement';
|
|
47
57
|
export { clampSlabElevationForWalls, getSlabElevationUpperBound, } from './systems/slab/slab-support';
|
|
58
|
+
export { createDefaultStairSegment, createStairFlightFromStair, } from './systems/stair/stair-flight';
|
|
48
59
|
export { stairFootprintAABB } from './systems/stair/stair-footprint';
|
|
49
60
|
export { createSurfaceOpeningPreviewController } from './systems/stair/stair-opening-preview';
|
|
50
61
|
export { syncAutoStairOpenings } from './systems/stair/stair-opening-sync';
|
|
51
62
|
export { StairOpeningSystem } from './systems/stair/stair-opening-system';
|
|
52
63
|
export { resolveStairTotalRise, syncStairRises } from './systems/stair/stair-rise';
|
|
53
|
-
export { getClampedWallCurveOffset, getMaxWallCurveOffset, getWallArcData, getWallChordFrame, getWallCurveFrameAt, getWallCurveLength, getWallMidpointHandlePoint, getWallStraightSnapOffset, getWallSurfacePolygon, isCurvedWall, normalizeWallCurveOffset, sampleWallCenterline, } from './systems/wall/wall-curve';
|
|
64
|
+
export { constrainWallCurveOffsetToAvoidIntersections, getClampedWallCurveOffset, getMaxWallCurveOffset, getWallArcData, getWallChordFrame, getWallCurveFrameAt, getWallCurveLength, getWallMidpointHandlePoint, getWallStraightSnapOffset, getWallSurfacePolygon, isCurvedWall, normalizeWallCurveOffset, sampleWallCenterline, } from './systems/wall/wall-curve';
|
|
54
65
|
export { DEFAULT_WALL_HEIGHT, DEFAULT_WALL_THICKNESS, getWallPlanFootprint, getWallThickness, } from './systems/wall/wall-footprint';
|
|
55
66
|
export { calculateLevelMiters, getAdjacentWallIds, getWallMiterBoundaryPoints, pointToKey, } from './systems/wall/wall-mitering';
|
|
56
67
|
export { constrainWallMoveDeltaToAxis, getLinkedWallUpdates, getPerpendicularWallMoveAxis, getPlannedLinkedWallUpdates, planWallMoveJunctions, } from './systems/wall/wall-move';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"asset-storage.d.ts","sourceRoot":"","sources":["../../src/lib/asset-storage.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"asset-storage.d.ts","sourceRoot":"","sources":["../../src/lib/asset-storage.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,YAAY,gBAAgB,CAAA;AAQzC;;GAEG;AACH,wBAAsB,SAAS,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAI3D;AAED;;;GAGG;AACH,wBAAsB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAkCtE"}
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import { get, set } from 'idb-keyval';
|
|
2
|
+
import { customAlphabet } from 'nanoid';
|
|
2
3
|
export const ASSET_PREFIX = 'asset_data:';
|
|
3
4
|
// Cache for active object URLs to prevent leaks and flickering
|
|
4
5
|
const urlCache = new Map();
|
|
6
|
+
// Unlike crypto.randomUUID(), nanoid works outside secure contexts.
|
|
7
|
+
const nanoAssetId = customAlphabet('0123456789abcdefghijklmnopqrstuvwxyz', 16);
|
|
5
8
|
/**
|
|
6
9
|
* Save a file to IndexedDB and return a custom protocol URL
|
|
7
10
|
*/
|
|
8
11
|
export async function saveAsset(file) {
|
|
9
|
-
const id =
|
|
12
|
+
const id = nanoAssetId();
|
|
10
13
|
await set(`${ASSET_PREFIX}${id}`, file);
|
|
11
14
|
return `asset://${id}`;
|
|
12
15
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"polygon-union.d.ts","sourceRoot":"","sources":["../../src/lib/polygon-union.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,OAAO,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;AA6TtC,wBAAgB,aAAa,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,GAAG,OAAO,EAAE,EAAE,CAShE;AA4CD,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,GAAG,OAAO,EAAE,EAAE,CAkBjG"}
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
const EPSILON = 1e-7;
|
|
2
|
+
const KEY_SCALE = 1e6;
|
|
3
|
+
function pointsEqual(a, b, tolerance = EPSILON) {
|
|
4
|
+
return Math.hypot(a[0] - b[0], a[1] - b[1]) <= tolerance;
|
|
5
|
+
}
|
|
6
|
+
function pointKey(point) {
|
|
7
|
+
return `${Math.round(point[0] * KEY_SCALE)}:${Math.round(point[1] * KEY_SCALE)}`;
|
|
8
|
+
}
|
|
9
|
+
function interpolate(a, b, t) {
|
|
10
|
+
return [a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t];
|
|
11
|
+
}
|
|
12
|
+
function cross(ax, ay, bx, by) {
|
|
13
|
+
return ax * by - ay * bx;
|
|
14
|
+
}
|
|
15
|
+
function polygonArea(points) {
|
|
16
|
+
let area = 0;
|
|
17
|
+
for (let i = 0; i < points.length; i++) {
|
|
18
|
+
const current = points[i];
|
|
19
|
+
const next = points[(i + 1) % points.length];
|
|
20
|
+
area += current[0] * next[1] - next[0] * current[1];
|
|
21
|
+
}
|
|
22
|
+
return area / 2;
|
|
23
|
+
}
|
|
24
|
+
function pointOnSegment(point, start, end) {
|
|
25
|
+
const dx = end[0] - start[0];
|
|
26
|
+
const dz = end[1] - start[1];
|
|
27
|
+
const crossValue = cross(point[0] - start[0], point[1] - start[1], dx, dz);
|
|
28
|
+
if (Math.abs(crossValue) > EPSILON)
|
|
29
|
+
return false;
|
|
30
|
+
const dot = (point[0] - start[0]) * (point[0] - end[0]) + (point[1] - start[1]) * (point[1] - end[1]);
|
|
31
|
+
return dot <= EPSILON;
|
|
32
|
+
}
|
|
33
|
+
function pointInPolygonOrOnBoundary(point, polygon) {
|
|
34
|
+
if (polygon.some((start, index) => pointOnSegment(point, start, polygon[(index + 1) % polygon.length]))) {
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
return pointInPolygon(point, polygon);
|
|
38
|
+
}
|
|
39
|
+
function pointInPolygon(point, polygon) {
|
|
40
|
+
let inside = false;
|
|
41
|
+
for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
|
|
42
|
+
const pi = polygon[i];
|
|
43
|
+
const pj = polygon[j];
|
|
44
|
+
if (pointOnSegment(point, pj, pi))
|
|
45
|
+
return false;
|
|
46
|
+
const intersects = pi[1] > point[1] !== pj[1] > point[1] &&
|
|
47
|
+
point[0] < ((pj[0] - pi[0]) * (point[1] - pi[1])) / (pj[1] - pi[1]) + pi[0];
|
|
48
|
+
if (intersects)
|
|
49
|
+
inside = !inside;
|
|
50
|
+
}
|
|
51
|
+
return inside;
|
|
52
|
+
}
|
|
53
|
+
function normalizeRing(ring) {
|
|
54
|
+
const normalized = [];
|
|
55
|
+
for (const [x, z] of ring) {
|
|
56
|
+
if (!Number.isFinite(x) || !Number.isFinite(z))
|
|
57
|
+
continue;
|
|
58
|
+
const point = [x, z];
|
|
59
|
+
const previous = normalized[normalized.length - 1];
|
|
60
|
+
if (!previous || !pointsEqual(previous, point)) {
|
|
61
|
+
normalized.push(point);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
const first = normalized[0];
|
|
65
|
+
const last = normalized[normalized.length - 1];
|
|
66
|
+
if (first && last && pointsEqual(first, last)) {
|
|
67
|
+
normalized.pop();
|
|
68
|
+
}
|
|
69
|
+
if (normalized.length < 3 || Math.abs(polygonArea(normalized)) <= EPSILON)
|
|
70
|
+
return [];
|
|
71
|
+
return polygonArea(normalized) < 0 ? [...normalized].reverse() : normalized;
|
|
72
|
+
}
|
|
73
|
+
function addSplit(edge, t) {
|
|
74
|
+
if (t < -EPSILON || t > 1 + EPSILON)
|
|
75
|
+
return;
|
|
76
|
+
const clamped = Math.max(0, Math.min(1, t));
|
|
77
|
+
if (edge.splits.some((split) => Math.abs(split - clamped) <= EPSILON))
|
|
78
|
+
return;
|
|
79
|
+
edge.splits.push(clamped);
|
|
80
|
+
}
|
|
81
|
+
function parameterOnEdge(point, edge) {
|
|
82
|
+
const dx = edge.end[0] - edge.start[0];
|
|
83
|
+
const dz = edge.end[1] - edge.start[1];
|
|
84
|
+
const lengthSquared = dx * dx + dz * dz;
|
|
85
|
+
if (lengthSquared <= EPSILON)
|
|
86
|
+
return 0;
|
|
87
|
+
return ((point[0] - edge.start[0]) * dx + (point[1] - edge.start[1]) * dz) / lengthSquared;
|
|
88
|
+
}
|
|
89
|
+
function addIntersectionSplits(left, right) {
|
|
90
|
+
const rx = left.end[0] - left.start[0];
|
|
91
|
+
const rz = left.end[1] - left.start[1];
|
|
92
|
+
const sx = right.end[0] - right.start[0];
|
|
93
|
+
const sz = right.end[1] - right.start[1];
|
|
94
|
+
const qpx = right.start[0] - left.start[0];
|
|
95
|
+
const qpz = right.start[1] - left.start[1];
|
|
96
|
+
const denominator = cross(rx, rz, sx, sz);
|
|
97
|
+
const numerator = cross(qpx, qpz, rx, rz);
|
|
98
|
+
if (Math.abs(denominator) <= EPSILON) {
|
|
99
|
+
if (Math.abs(numerator) > EPSILON)
|
|
100
|
+
return;
|
|
101
|
+
for (const point of [left.start, left.end, right.start, right.end]) {
|
|
102
|
+
if (pointOnSegment(point, left.start, left.end) &&
|
|
103
|
+
pointOnSegment(point, right.start, right.end)) {
|
|
104
|
+
addSplit(left, parameterOnEdge(point, left));
|
|
105
|
+
addSplit(right, parameterOnEdge(point, right));
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
const t = cross(qpx, qpz, sx, sz) / denominator;
|
|
111
|
+
const u = cross(qpx, qpz, rx, rz) / denominator;
|
|
112
|
+
if (t < -EPSILON || t > 1 + EPSILON || u < -EPSILON || u > 1 + EPSILON)
|
|
113
|
+
return;
|
|
114
|
+
addSplit(left, t);
|
|
115
|
+
addSplit(right, u);
|
|
116
|
+
}
|
|
117
|
+
function buildEdges(polygons) {
|
|
118
|
+
const edges = [];
|
|
119
|
+
polygons.forEach((polygon, polygonIndex) => {
|
|
120
|
+
for (let i = 0; i < polygon.length; i++) {
|
|
121
|
+
edges.push({
|
|
122
|
+
start: polygon[i],
|
|
123
|
+
end: polygon[(i + 1) % polygon.length],
|
|
124
|
+
polygonIndex,
|
|
125
|
+
splits: [0, 1],
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
for (let i = 0; i < edges.length; i++) {
|
|
130
|
+
for (let j = i + 1; j < edges.length; j++) {
|
|
131
|
+
const left = edges[i];
|
|
132
|
+
const right = edges[j];
|
|
133
|
+
if (left.polygonIndex === right.polygonIndex)
|
|
134
|
+
continue;
|
|
135
|
+
addIntersectionSplits(left, right);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return edges;
|
|
139
|
+
}
|
|
140
|
+
function buildBoundarySegments(edges, polygons) {
|
|
141
|
+
const segments = [];
|
|
142
|
+
for (const edge of edges) {
|
|
143
|
+
const splits = [...edge.splits].sort((a, b) => a - b);
|
|
144
|
+
for (let i = 0; i < splits.length - 1; i++) {
|
|
145
|
+
const startT = splits[i];
|
|
146
|
+
const endT = splits[i + 1];
|
|
147
|
+
if (endT - startT <= EPSILON)
|
|
148
|
+
continue;
|
|
149
|
+
const start = interpolate(edge.start, edge.end, startT);
|
|
150
|
+
const end = interpolate(edge.start, edge.end, endT);
|
|
151
|
+
const mid = interpolate(edge.start, edge.end, (startT + endT) / 2);
|
|
152
|
+
const insideAnother = polygons.some((polygon, index) => index !== edge.polygonIndex && pointInPolygon(mid, polygon));
|
|
153
|
+
if (!insideAnother) {
|
|
154
|
+
segments.push({ start, end, used: false });
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return removeDuplicateInteriorSegments(segments);
|
|
159
|
+
}
|
|
160
|
+
function segmentKey(segment) {
|
|
161
|
+
const start = pointKey(segment.start);
|
|
162
|
+
const end = pointKey(segment.end);
|
|
163
|
+
return start < end ? `${start}|${end}` : `${end}|${start}`;
|
|
164
|
+
}
|
|
165
|
+
function removeDuplicateInteriorSegments(segments) {
|
|
166
|
+
const groups = new Map();
|
|
167
|
+
for (const segment of segments) {
|
|
168
|
+
const key = segmentKey(segment);
|
|
169
|
+
const group = groups.get(key);
|
|
170
|
+
if (group) {
|
|
171
|
+
group.push(segment);
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
groups.set(key, [segment]);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
const result = [];
|
|
178
|
+
for (const group of groups.values()) {
|
|
179
|
+
if (group.length === 1) {
|
|
180
|
+
result.push(group[0]);
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
const firstStart = pointKey(group[0].start);
|
|
184
|
+
const firstEnd = pointKey(group[0].end);
|
|
185
|
+
const hasOppositeDirection = group.some((segment) => pointKey(segment.start) === firstEnd && pointKey(segment.end) === firstStart);
|
|
186
|
+
if (!hasOppositeDirection) {
|
|
187
|
+
result.push(group[0]);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return result;
|
|
191
|
+
}
|
|
192
|
+
function assembleRings(segments) {
|
|
193
|
+
const byStart = new Map();
|
|
194
|
+
for (const segment of segments) {
|
|
195
|
+
const key = pointKey(segment.start);
|
|
196
|
+
const group = byStart.get(key);
|
|
197
|
+
if (group) {
|
|
198
|
+
group.push(segment);
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
byStart.set(key, [segment]);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
const rings = [];
|
|
205
|
+
const nextBoundarySegment = (ring, candidates) => {
|
|
206
|
+
const previous = ring[ring.length - 2];
|
|
207
|
+
const current = ring[ring.length - 1];
|
|
208
|
+
const incomingX = current[0] - previous[0];
|
|
209
|
+
const incomingZ = current[1] - previous[1];
|
|
210
|
+
const reverseIncomingAngle = Math.atan2(-incomingZ, -incomingX);
|
|
211
|
+
const clockwiseTurn = (segment) => {
|
|
212
|
+
const outgoingAngle = Math.atan2(segment.end[1] - current[1], segment.end[0] - current[0]);
|
|
213
|
+
const turn = reverseIncomingAngle - outgoingAngle;
|
|
214
|
+
return ((turn % (Math.PI * 2)) + Math.PI * 2) % (Math.PI * 2);
|
|
215
|
+
};
|
|
216
|
+
return [...candidates].sort((left, right) => {
|
|
217
|
+
return (clockwiseTurn(left) - clockwiseTurn(right) ||
|
|
218
|
+
pointKey(left.end).localeCompare(pointKey(right.end)));
|
|
219
|
+
})[0];
|
|
220
|
+
};
|
|
221
|
+
for (const firstSegment of segments) {
|
|
222
|
+
if (firstSegment.used)
|
|
223
|
+
continue;
|
|
224
|
+
firstSegment.used = true;
|
|
225
|
+
const ring = [firstSegment.start, firstSegment.end];
|
|
226
|
+
const startKey = pointKey(firstSegment.start);
|
|
227
|
+
let currentKey = pointKey(firstSegment.end);
|
|
228
|
+
while (currentKey !== startKey) {
|
|
229
|
+
const candidates = byStart.get(currentKey)?.filter((segment) => !segment.used) ?? [];
|
|
230
|
+
const next = nextBoundarySegment(ring, candidates);
|
|
231
|
+
if (!next)
|
|
232
|
+
break;
|
|
233
|
+
next.used = true;
|
|
234
|
+
ring.push(next.end);
|
|
235
|
+
currentKey = pointKey(next.end);
|
|
236
|
+
}
|
|
237
|
+
if (currentKey !== startKey)
|
|
238
|
+
continue;
|
|
239
|
+
const last = ring[ring.length - 1];
|
|
240
|
+
if (last && pointsEqual(ring[0], last)) {
|
|
241
|
+
ring.pop();
|
|
242
|
+
}
|
|
243
|
+
const normalized = normalizeRing(ring);
|
|
244
|
+
if (normalized.length >= 3) {
|
|
245
|
+
rings.push(normalized);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
return rings;
|
|
249
|
+
}
|
|
250
|
+
export function unionPolygons(polygons) {
|
|
251
|
+
const validPolygons = polygons.map(normalizeRing).filter((polygon) => polygon.length >= 3);
|
|
252
|
+
if (validPolygons.length <= 1)
|
|
253
|
+
return validPolygons;
|
|
254
|
+
const edges = buildEdges(validPolygons);
|
|
255
|
+
const segments = buildBoundarySegments(edges, validPolygons);
|
|
256
|
+
const rings = assembleRings(segments);
|
|
257
|
+
return rings.length > 0 ? rings : validPolygons;
|
|
258
|
+
}
|
|
259
|
+
function buildDifferenceBoundarySegments(edges, polygons) {
|
|
260
|
+
const subject = polygons[0];
|
|
261
|
+
const cutters = polygons.slice(1);
|
|
262
|
+
if (!subject)
|
|
263
|
+
return [];
|
|
264
|
+
const segments = [];
|
|
265
|
+
for (const edge of edges) {
|
|
266
|
+
const splits = [...edge.splits].sort((a, b) => a - b);
|
|
267
|
+
for (let i = 0; i < splits.length - 1; i++) {
|
|
268
|
+
const startT = splits[i];
|
|
269
|
+
const endT = splits[i + 1];
|
|
270
|
+
if (endT - startT <= EPSILON)
|
|
271
|
+
continue;
|
|
272
|
+
const start = interpolate(edge.start, edge.end, startT);
|
|
273
|
+
const end = interpolate(edge.start, edge.end, endT);
|
|
274
|
+
const mid = interpolate(edge.start, edge.end, (startT + endT) / 2);
|
|
275
|
+
if (edge.polygonIndex === 0) {
|
|
276
|
+
const insideCutter = cutters.some((cutter) => pointInPolygonOrOnBoundary(mid, cutter));
|
|
277
|
+
if (!insideCutter) {
|
|
278
|
+
segments.push({ start, end, used: false });
|
|
279
|
+
}
|
|
280
|
+
continue;
|
|
281
|
+
}
|
|
282
|
+
const insideSubject = pointInPolygon(mid, subject);
|
|
283
|
+
const insideAnotherCutter = cutters.some((cutter, cutterIndex) => cutterIndex !== edge.polygonIndex - 1 && pointInPolygonOrOnBoundary(mid, cutter));
|
|
284
|
+
if (insideSubject && !insideAnotherCutter) {
|
|
285
|
+
segments.push({ start: end, end: start, used: false });
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
return removeDuplicateInteriorSegments(segments);
|
|
290
|
+
}
|
|
291
|
+
export function subtractPolygonsFromPolygon(subject, cutters) {
|
|
292
|
+
const validSubject = normalizeRing(subject);
|
|
293
|
+
if (validSubject.length < 3)
|
|
294
|
+
return [];
|
|
295
|
+
const validCutters = cutters.map(normalizeRing).filter((polygon) => polygon.length >= 3);
|
|
296
|
+
if (validCutters.length === 0)
|
|
297
|
+
return [validSubject];
|
|
298
|
+
const polygons = [validSubject, ...validCutters];
|
|
299
|
+
const edges = buildEdges(polygons);
|
|
300
|
+
const segments = buildDifferenceBoundarySegments(edges, polygons);
|
|
301
|
+
const rings = assembleRings(segments);
|
|
302
|
+
if (rings.length > 0)
|
|
303
|
+
return rings;
|
|
304
|
+
const fullyCovered = validSubject.every((point) => validCutters.some((cutter) => pointInPolygonOrOnBoundary(point, cutter)));
|
|
305
|
+
return fullyCovered ? [] : [validSubject];
|
|
306
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export type RoofOverlapEntry = {
|
|
2
|
+
roofId: string;
|
|
3
|
+
segmentId: string;
|
|
4
|
+
supportRoofId?: string;
|
|
5
|
+
supportRoofSegmentId?: string;
|
|
6
|
+
roofType?: string;
|
|
7
|
+
width: number;
|
|
8
|
+
depth: number;
|
|
9
|
+
};
|
|
10
|
+
export type RoofPlanBounds = {
|
|
11
|
+
minX: number;
|
|
12
|
+
minZ: number;
|
|
13
|
+
maxX: number;
|
|
14
|
+
maxZ: number;
|
|
15
|
+
};
|
|
16
|
+
export type RoofPlanSegment = {
|
|
17
|
+
position: readonly [number, number, number];
|
|
18
|
+
rotation?: number;
|
|
19
|
+
width: number;
|
|
20
|
+
depth: number;
|
|
21
|
+
};
|
|
22
|
+
export type RoofPlan = {
|
|
23
|
+
position: readonly [number, number, number];
|
|
24
|
+
rotation?: number;
|
|
25
|
+
segments: readonly RoofPlanSegment[];
|
|
26
|
+
};
|
|
27
|
+
export declare function compareRoofOverlapIdentity(a: RoofOverlapEntry, b: RoofOverlapEntry): number;
|
|
28
|
+
export declare function roofOverlapEntryOwns(candidate: RoofOverlapEntry, current: RoofOverlapEntry, epsilon?: number): boolean;
|
|
29
|
+
export declare function roofPlanOverlapEntryOwns(candidate: RoofOverlapEntry, current: RoofOverlapEntry, epsilon?: number): boolean;
|
|
30
|
+
export declare function getRoofPlanBounds(roof: RoofPlan): RoofPlanBounds | null;
|
|
31
|
+
export declare function roofPlanBoundsOverlap(a: RoofPlanBounds, b: RoofPlanBounds, epsilon?: number): boolean;
|
|
32
|
+
//# sourceMappingURL=roof-overlap.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"roof-overlap.d.ts","sourceRoot":"","sources":["../../src/lib/roof-overlap.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,gBAAgB,GAAG;IAC7B,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,MAAM,CAAA;IACjB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,oBAAoB,CAAC,EAAE,MAAM,CAAA;IAC7B,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;CACd,CAAA;AAED,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;CACb,CAAA;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;IAC3C,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;CACd,CAAA;AAED,MAAM,MAAM,QAAQ,GAAG;IACrB,QAAQ,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;IAC3C,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,SAAS,eAAe,EAAE,CAAA;CACrC,CAAA;AAED,wBAAgB,0BAA0B,CAAC,CAAC,EAAE,gBAAgB,EAAE,CAAC,EAAE,gBAAgB,GAAG,MAAM,CAG3F;AAED,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,gBAAgB,EAC3B,OAAO,EAAE,gBAAgB,EACzB,OAAO,SAAO,GACb,OAAO,CAiBT;AAED,wBAAgB,wBAAwB,CACtC,SAAS,EAAE,gBAAgB,EAC3B,OAAO,EAAE,gBAAgB,EACzB,OAAO,SAAO,GACb,OAAO,CAYT;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,QAAQ,GAAG,cAAc,GAAG,IAAI,CAmCvE;AAED,wBAAgB,qBAAqB,CACnC,CAAC,EAAE,cAAc,EACjB,CAAC,EAAE,cAAc,EACjB,OAAO,SAAO,GACb,OAAO,CAOT"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
export function compareRoofOverlapIdentity(a, b) {
|
|
2
|
+
const roofOrder = a.roofId.localeCompare(b.roofId);
|
|
3
|
+
return roofOrder !== 0 ? roofOrder : a.segmentId.localeCompare(b.segmentId);
|
|
4
|
+
}
|
|
5
|
+
export function roofOverlapEntryOwns(candidate, current, epsilon = 1e-6) {
|
|
6
|
+
const candidateIsMountedOnCurrent = candidate.supportRoofId === current.roofId ||
|
|
7
|
+
candidate.supportRoofSegmentId === current.segmentId;
|
|
8
|
+
if (candidateIsMountedOnCurrent)
|
|
9
|
+
return false;
|
|
10
|
+
const currentIsMountedOnCandidate = current.supportRoofId === candidate.roofId ||
|
|
11
|
+
current.supportRoofSegmentId === candidate.segmentId;
|
|
12
|
+
if (currentIsMountedOnCandidate)
|
|
13
|
+
return true;
|
|
14
|
+
const candidateArea = candidate.width * candidate.depth;
|
|
15
|
+
const currentArea = current.width * current.depth;
|
|
16
|
+
return (candidateArea > currentArea + epsilon ||
|
|
17
|
+
(Math.abs(candidateArea - currentArea) <= epsilon &&
|
|
18
|
+
compareRoofOverlapIdentity(candidate, current) < 0));
|
|
19
|
+
}
|
|
20
|
+
export function roofPlanOverlapEntryOwns(candidate, current, epsilon = 1e-6) {
|
|
21
|
+
const candidateIsMountedOnCurrent = candidate.supportRoofId === current.roofId ||
|
|
22
|
+
candidate.supportRoofSegmentId === current.segmentId;
|
|
23
|
+
if (candidateIsMountedOnCurrent)
|
|
24
|
+
return true;
|
|
25
|
+
const currentIsMountedOnCandidate = current.supportRoofId === candidate.roofId ||
|
|
26
|
+
current.supportRoofSegmentId === candidate.segmentId;
|
|
27
|
+
if (currentIsMountedOnCandidate)
|
|
28
|
+
return false;
|
|
29
|
+
return roofOverlapEntryOwns(candidate, current, epsilon);
|
|
30
|
+
}
|
|
31
|
+
export function getRoofPlanBounds(roof) {
|
|
32
|
+
if (roof.segments.length === 0)
|
|
33
|
+
return null;
|
|
34
|
+
const roofRotation = roof.rotation ?? 0;
|
|
35
|
+
const roofCos = Math.cos(roofRotation);
|
|
36
|
+
const roofSin = Math.sin(roofRotation);
|
|
37
|
+
const bounds = {
|
|
38
|
+
minX: Number.POSITIVE_INFINITY,
|
|
39
|
+
minZ: Number.POSITIVE_INFINITY,
|
|
40
|
+
maxX: Number.NEGATIVE_INFINITY,
|
|
41
|
+
maxZ: Number.NEGATIVE_INFINITY,
|
|
42
|
+
};
|
|
43
|
+
for (const segment of roof.segments) {
|
|
44
|
+
const segmentRotation = segment.rotation ?? 0;
|
|
45
|
+
const segmentCos = Math.cos(segmentRotation);
|
|
46
|
+
const segmentSin = Math.sin(segmentRotation);
|
|
47
|
+
const halfWidth = Math.max(0, segment.width) / 2;
|
|
48
|
+
const halfDepth = Math.max(0, segment.depth) / 2;
|
|
49
|
+
for (const [x, z] of [
|
|
50
|
+
[-halfWidth, -halfDepth],
|
|
51
|
+
[halfWidth, -halfDepth],
|
|
52
|
+
[halfWidth, halfDepth],
|
|
53
|
+
[-halfWidth, halfDepth],
|
|
54
|
+
]) {
|
|
55
|
+
const roofX = segment.position[0] + x * segmentCos + z * segmentSin;
|
|
56
|
+
const roofZ = segment.position[2] - x * segmentSin + z * segmentCos;
|
|
57
|
+
const worldX = roof.position[0] + roofX * roofCos + roofZ * roofSin;
|
|
58
|
+
const worldZ = roof.position[2] - roofX * roofSin + roofZ * roofCos;
|
|
59
|
+
bounds.minX = Math.min(bounds.minX, worldX);
|
|
60
|
+
bounds.minZ = Math.min(bounds.minZ, worldZ);
|
|
61
|
+
bounds.maxX = Math.max(bounds.maxX, worldX);
|
|
62
|
+
bounds.maxZ = Math.max(bounds.maxZ, worldZ);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return bounds;
|
|
66
|
+
}
|
|
67
|
+
export function roofPlanBoundsOverlap(a, b, epsilon = 1e-6) {
|
|
68
|
+
return !(a.maxX < b.minX - epsilon ||
|
|
69
|
+
b.maxX < a.minX - epsilon ||
|
|
70
|
+
a.maxZ < b.minZ - epsilon ||
|
|
71
|
+
b.maxZ < a.minZ - epsilon);
|
|
72
|
+
}
|
|
@@ -1,18 +1,36 @@
|
|
|
1
1
|
import type { GeometryContext } from '../registry/types';
|
|
2
|
-
import type { SlabNode, WallNode } from '../schema';
|
|
2
|
+
import type { AnyNode, AnyNodeId, SlabNode, WallNode } from '../schema';
|
|
3
3
|
export type SlabPolygonContext = {
|
|
4
4
|
/** Walls on the slab's level. */
|
|
5
5
|
walls: WallNode[];
|
|
6
6
|
/** Other slabs on the same level — the slab itself must be excluded. */
|
|
7
7
|
siblingSlabs: SlabNode[];
|
|
8
8
|
};
|
|
9
|
+
/** [ax, az, bx, bz] segment in plan space. */
|
|
10
|
+
type Segment = [number, number, number, number];
|
|
11
|
+
/** A sibling slab edge and the direction of its polygon interior. */
|
|
12
|
+
type NeighborSegment = {
|
|
13
|
+
slab: SlabNode;
|
|
14
|
+
segment: Segment;
|
|
15
|
+
bounds: Bounds;
|
|
16
|
+
elevation: number;
|
|
17
|
+
/** Unit normal pointing into the sibling polygon at this edge. */
|
|
18
|
+
inwardX: number;
|
|
19
|
+
inwardZ: number;
|
|
20
|
+
};
|
|
9
21
|
/**
|
|
10
22
|
* Derive a {@link SlabPolygonContext} from a registry `GeometryContext`:
|
|
11
23
|
* sibling slabs come from `ctx.siblings` (same kind, same parent, self
|
|
12
24
|
* excluded) and walls from the parent level's children.
|
|
13
25
|
*/
|
|
14
26
|
export declare function slabPolygonContextFromGeometry(ctx: GeometryContext | undefined): SlabPolygonContext;
|
|
15
|
-
export declare function
|
|
27
|
+
export declare function slabPolygonContextForLevel(parent: AnyNode | null, resolve: (id: AnyNodeId) => AnyNode | undefined, fallbackSlabs?: SlabNode[]): SlabPolygonContext;
|
|
28
|
+
export declare function getRenderableSlabPolygon(slabNode: SlabNode, context: SlabPolygonContext | PreparedSlabPolygonContext): Array<[number, number]>;
|
|
29
|
+
type WallCandidate = {
|
|
30
|
+
wall: WallNode;
|
|
31
|
+
segments: Segment[];
|
|
32
|
+
halfThickness: number;
|
|
33
|
+
};
|
|
16
34
|
export type SlabEdgeWallBandSnap = {
|
|
17
35
|
wallId: WallNode['id'];
|
|
18
36
|
/** The candidate edge translated perpendicular onto the wall centerline. */
|
|
@@ -29,4 +47,19 @@ export type SlabEdgeWallBandSnap = {
|
|
|
29
47
|
export declare function snapSlabEdgeToWallBand(a: [number, number], b: [number, number], walls: readonly WallNode[], options?: {
|
|
30
48
|
maxLateral?: number;
|
|
31
49
|
}): SlabEdgeWallBandSnap | null;
|
|
50
|
+
type Bounds = [number, number, number, number];
|
|
51
|
+
export declare function scopeSlabPolygonContext(slab: SlabNode, context: PreparedSlabPolygonContext): PreparedSlabPolygonContext;
|
|
52
|
+
export declare function slabPolygonContextChanges(before: PreparedSlabPolygonContext, after: PreparedSlabPolygonContext): (slab: SlabNode) => boolean;
|
|
53
|
+
type PreparedWallCandidate = WallCandidate & {
|
|
54
|
+
bounds: Bounds;
|
|
55
|
+
};
|
|
56
|
+
type PreparedSlabPolygonContext = SlabPolygonContext & {
|
|
57
|
+
wallCache: WeakMap<WallNode, PreparedWallCandidate>;
|
|
58
|
+
neighborCache: WeakMap<SlabNode, NeighborSegment[]>;
|
|
59
|
+
wallCandidates: PreparedWallCandidate[];
|
|
60
|
+
neighborSegments: NeighborSegment[];
|
|
61
|
+
siblingBreakTolerance: number;
|
|
62
|
+
};
|
|
63
|
+
export declare function prepareSlabPolygonContext(context: SlabPolygonContext, previous?: PreparedSlabPolygonContext): PreparedSlabPolygonContext;
|
|
64
|
+
export {};
|
|
32
65
|
//# sourceMappingURL=slab-polygon.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slab-polygon.d.ts","sourceRoot":"","sources":["../../src/lib/slab-polygon.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AACxD,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"slab-polygon.d.ts","sourceRoot":"","sources":["../../src/lib/slab-polygon.ts"],"names":[],"mappings":"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"}
|