@kerros/editor 0.0.0-stage → 0.1.0-rc.2
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 +35 -2
- package/dist/SitePlanner.js +1189 -0
- package/dist/SitePlanner.js.map +1 -0
- package/dist/adapters/basemap.js +11 -0
- package/dist/adapters/basemap.js.map +1 -0
- package/dist/adapters/persistence.js +121 -0
- package/dist/adapters/persistence.js.map +1 -0
- package/dist/adapters/status.js +11 -0
- package/dist/adapters/status.js.map +1 -0
- package/dist/adapters/useProjectPersistence.js +56 -0
- package/dist/adapters/useProjectPersistence.js.map +1 -0
- package/dist/components/AiImportPanel.js +635 -0
- package/dist/components/AiImportPanel.js.map +1 -0
- package/dist/components/AnalysisPreview.js +179 -0
- package/dist/components/AnalysisPreview.js.map +1 -0
- package/dist/components/ElevatorPanel.js +120 -0
- package/dist/components/ElevatorPanel.js.map +1 -0
- package/dist/components/Icons.js +57 -0
- package/dist/components/Icons.js.map +1 -0
- package/dist/components/ImportDialog.js +467 -0
- package/dist/components/ImportDialog.js.map +1 -0
- package/dist/components/Inspector.js +1184 -0
- package/dist/components/Inspector.js.map +1 -0
- package/dist/components/NavigatePanel.js +301 -0
- package/dist/components/NavigatePanel.js.map +1 -0
- package/dist/components/NavigationGraph.js +234 -0
- package/dist/components/NavigationGraph.js.map +1 -0
- package/dist/components/StructureExplorer.js +510 -0
- package/dist/components/StructureExplorer.js.map +1 -0
- package/dist/components/StructureView.js +286 -0
- package/dist/components/StructureView.js.map +1 -0
- package/dist/components/controls.js +209 -0
- package/dist/components/controls.js.map +1 -0
- package/dist/components/graphLayout.js +27 -0
- package/dist/components/graphLayout.js.map +1 -0
- package/dist/host.js +175 -0
- package/dist/host.js.map +1 -0
- package/dist/i18n.js +88 -0
- package/dist/i18n.js.map +1 -0
- package/dist/import/analysisPreview.js +41 -0
- package/dist/import/analysisPreview.js.map +1 -0
- package/dist/import/brief.js +29 -0
- package/dist/import/brief.js.map +1 -0
- package/dist/import/checkpoint.js +50 -0
- package/dist/import/checkpoint.js.map +1 -0
- package/dist/import/detect.js +120 -0
- package/dist/import/detect.js.map +1 -0
- package/dist/import/documentSvg.js +49 -0
- package/dist/import/documentSvg.js.map +1 -0
- package/dist/import/instructions.js +20 -0
- package/dist/import/instructions.js.map +1 -0
- package/dist/import/openings.js +61 -0
- package/dist/import/openings.js.map +1 -0
- package/dist/import/planImport.js +233 -0
- package/dist/import/planImport.js.map +1 -0
- package/dist/import/register.js +47 -0
- package/dist/import/register.js.map +1 -0
- package/dist/import/runs.js +67 -0
- package/dist/import/runs.js.map +1 -0
- package/dist/import/session.js +23 -0
- package/dist/import/session.js.map +1 -0
- package/dist/import/types.js +13 -0
- package/dist/import/types.js.map +1 -0
- package/dist/import/usage.js +29 -0
- package/dist/import/usage.js.map +1 -0
- package/dist/import/walls.js +112 -0
- package/dist/import/walls.js.map +1 -0
- package/dist/index.js +178 -0
- package/dist/index.js.map +1 -0
- package/dist/map/CabinMaterials.js +81 -0
- package/dist/map/CabinMaterials.js.map +1 -0
- package/dist/map/CabinMirror.js +43 -0
- package/dist/map/CabinMirror.js.map +1 -0
- package/dist/map/Doors.js +80 -0
- package/dist/map/Doors.js.map +1 -0
- package/dist/map/ElevatorCabin.js +93 -0
- package/dist/map/ElevatorCabin.js.map +1 -0
- package/dist/map/FixtureLights.js +117 -0
- package/dist/map/FixtureLights.js.map +1 -0
- package/dist/map/MapCanvas.js +2161 -0
- package/dist/map/MapCanvas.js.map +1 -0
- package/dist/map/Passenger.js +69 -0
- package/dist/map/Passenger.js.map +1 -0
- package/dist/map/SceneLayer.js +1534 -0
- package/dist/map/SceneLayer.js.map +1 -0
- package/dist/map/UndergroundContext.js +153 -0
- package/dist/map/UndergroundContext.js.map +1 -0
- package/dist/map/ambience.js +194 -0
- package/dist/map/ambience.js.map +1 -0
- package/dist/map/architecture.js +305 -0
- package/dist/map/architecture.js.map +1 -0
- package/dist/map/bathMusic.js +92 -0
- package/dist/map/bathMusic.js.map +1 -0
- package/dist/map/cameraAim.js +33 -0
- package/dist/map/cameraAim.js.map +1 -0
- package/dist/map/elevators.js +56 -0
- package/dist/map/elevators.js.map +1 -0
- package/dist/map/excavation.js +52 -0
- package/dist/map/excavation.js.map +1 -0
- package/dist/map/exteriors.js +25 -0
- package/dist/map/exteriors.js.map +1 -0
- package/dist/map/features.js +218 -0
- package/dist/map/features.js.map +1 -0
- package/dist/map/fences.js +29 -0
- package/dist/map/fences.js.map +1 -0
- package/dist/map/journey.js +112 -0
- package/dist/map/journey.js.map +1 -0
- package/dist/map/levels.js +6 -0
- package/dist/map/levels.js.map +1 -0
- package/dist/map/lighting.js +143 -0
- package/dist/map/lighting.js.map +1 -0
- package/dist/map/loadBasemap.js +26 -0
- package/dist/map/loadBasemap.js.map +1 -0
- package/dist/map/materials.js +183 -0
- package/dist/map/materials.js.map +1 -0
- package/dist/map/poolLighting.js +39 -0
- package/dist/map/poolLighting.js.map +1 -0
- package/dist/map/projection.js +24 -0
- package/dist/map/projection.js.map +1 -0
- package/dist/map/rampGeometry.js +38 -0
- package/dist/map/rampGeometry.js.map +1 -0
- package/dist/map/route.js +107 -0
- package/dist/map/route.js.map +1 -0
- package/dist/map/stairSurfaces.js +115 -0
- package/dist/map/stairSurfaces.js.map +1 -0
- package/dist/map/surfaces.js +136 -0
- package/dist/map/surfaces.js.map +1 -0
- package/dist/map/textures.js +95 -0
- package/dist/map/textures.js.map +1 -0
- package/dist/map/underground.js +34 -0
- package/dist/map/underground.js.map +1 -0
- package/dist/map/walk.js +433 -0
- package/dist/map/walk.js.map +1 -0
- package/dist/map/walkContext.js +46 -0
- package/dist/map/walkContext.js.map +1 -0
- package/dist/map/walkDimensions.js +7 -0
- package/dist/map/walkDimensions.js.map +1 -0
- package/dist/map/walkJourney.js +85 -0
- package/dist/map/walkJourney.js.map +1 -0
- package/dist/map/walkSurfaces.js +106 -0
- package/dist/map/walkSurfaces.js.map +1 -0
- package/dist/map/wallJoins.js +70 -0
- package/dist/map/wallJoins.js.map +1 -0
- package/dist/map/water.js +274 -0
- package/dist/map/water.js.map +1 -0
- package/dist/model/authoring.js +99 -0
- package/dist/model/authoring.js.map +1 -0
- package/dist/model/boundaries.js +548 -0
- package/dist/model/boundaries.js.map +1 -0
- package/dist/model/coverage.js +30 -0
- package/dist/model/coverage.js.map +1 -0
- package/dist/model/document.js +52 -0
- package/dist/model/document.js.map +1 -0
- package/dist/model/doors.js +47 -0
- package/dist/model/doors.js.map +1 -0
- package/dist/model/factory.js +56 -0
- package/dist/model/factory.js.map +1 -0
- package/dist/model/geometry.js +454 -0
- package/dist/model/geometry.js.map +1 -0
- package/dist/model/history.js +29 -0
- package/dist/model/history.js.map +1 -0
- package/dist/model/imports.js +45 -0
- package/dist/model/imports.js.map +1 -0
- package/dist/model/inference.js +182 -0
- package/dist/model/inference.js.map +1 -0
- package/dist/model/materials.js +11 -0
- package/dist/model/materials.js.map +1 -0
- package/dist/model/mutations.js +157 -0
- package/dist/model/mutations.js.map +1 -0
- package/dist/model/navigation.js +275 -0
- package/dist/model/navigation.js.map +1 -0
- package/dist/model/ontology.js +109 -0
- package/dist/model/ontology.js.map +1 -0
- package/dist/model/passages.js +5 -0
- package/dist/model/passages.js.map +1 -0
- package/dist/model/portals.js +42 -0
- package/dist/model/portals.js.map +1 -0
- package/dist/model/precision.js +11 -0
- package/dist/model/precision.js.map +1 -0
- package/dist/model/project.js +31 -0
- package/dist/model/project.js.map +1 -0
- package/dist/model/ramps.js +10 -0
- package/dist/model/ramps.js.map +1 -0
- package/dist/model/roomNavigation.js +87 -0
- package/dist/model/roomNavigation.js.map +1 -0
- package/dist/model/routeClearance.js +72 -0
- package/dist/model/routeClearance.js.map +1 -0
- package/dist/model/spaces.js +104 -0
- package/dist/model/spaces.js.map +1 -0
- package/dist/model/stairGeometry.js +40 -0
- package/dist/model/stairGeometry.js.map +1 -0
- package/dist/model/stairTraversal.js +48 -0
- package/dist/model/stairTraversal.js.map +1 -0
- package/dist/model/topology.js +63 -0
- package/dist/model/topology.js.map +1 -0
- package/dist/model/types.js +35 -0
- package/dist/model/types.js.map +1 -0
- package/dist/model/validate.js +240 -0
- package/dist/model/validate.js.map +1 -0
- package/dist/model/vertical.js +129 -0
- package/dist/model/vertical.js.map +1 -0
- package/dist/model/walls.js +261 -0
- package/dist/model/walls.js.map +1 -0
- package/dist/planner/DrawingDock.js +231 -0
- package/dist/planner/DrawingDock.js.map +1 -0
- package/dist/planner/FloorControls.js +93 -0
- package/dist/planner/FloorControls.js.map +1 -0
- package/dist/planner/MapSettings.js +143 -0
- package/dist/planner/MapSettings.js.map +1 -0
- package/dist/planner/PlannerHelp.js +57 -0
- package/dist/planner/PlannerHelp.js.map +1 -0
- package/dist/planner/ProjectSidebar.js +246 -0
- package/dist/planner/ProjectSidebar.js.map +1 -0
- package/dist/planner/floors.js +18 -0
- package/dist/planner/floors.js.map +1 -0
- package/dist/planner/useDrawingTools.js +341 -0
- package/dist/planner/useDrawingTools.js.map +1 -0
- package/dist/planner/useMapDisplay.js +51 -0
- package/dist/planner/useMapDisplay.js.map +1 -0
- package/dist/planner/usePlannerKeyboard.js +138 -0
- package/dist/planner/usePlannerKeyboard.js.map +1 -0
- package/dist/planner/useReferenceAlignment.js +50 -0
- package/dist/planner/useReferenceAlignment.js.map +1 -0
- package/dist/styles.css +4931 -0
- package/dist/theme/index.js +34 -0
- package/dist/theme/index.js.map +1 -0
- package/dist/types/SitePlanner.d.ts +5 -0
- package/dist/types/adapters/basemap.d.ts +2 -0
- package/dist/types/adapters/persistence.d.ts +29 -0
- package/dist/types/adapters/status.d.ts +4 -0
- package/dist/types/adapters/useProjectPersistence.d.ts +13 -0
- package/dist/types/components/AiImportPanel.d.ts +9 -0
- package/dist/types/components/AnalysisPreview.d.ts +4 -0
- package/dist/types/components/ElevatorPanel.d.ts +18 -0
- package/dist/types/components/Icons.d.ts +8 -0
- package/dist/types/components/ImportDialog.d.ts +42 -0
- package/dist/types/components/Inspector.d.ts +38 -0
- package/dist/types/components/NavigatePanel.d.ts +27 -0
- package/dist/types/components/NavigationGraph.d.ts +10 -0
- package/dist/types/components/StructureExplorer.d.ts +8 -0
- package/dist/types/components/StructureView.d.ts +22 -0
- package/dist/types/components/controls.d.ts +52 -0
- package/dist/types/components/graphLayout.d.ts +9 -0
- package/dist/types/editor/host.d.ts +9 -0
- package/dist/types/editor/index.d.ts +2 -0
- package/dist/types/i18n.d.ts +85 -0
- package/dist/types/import/analysisPreview.d.ts +27 -0
- package/dist/types/import/brief.d.ts +22 -0
- package/dist/types/import/checkpoint.d.ts +49 -0
- package/dist/types/import/detect.d.ts +34 -0
- package/dist/types/import/documentSvg.d.ts +2 -0
- package/dist/types/import/index.d.ts +10 -0
- package/dist/types/import/instructions.d.ts +7 -0
- package/dist/types/import/openings.d.ts +22 -0
- package/dist/types/import/planImport.d.ts +22 -0
- package/dist/types/import/register.d.ts +25 -0
- package/dist/types/import/runs.d.ts +31 -0
- package/dist/types/import/session.d.ts +33 -0
- package/dist/types/import/types.d.ts +56 -0
- package/dist/types/import/usage.d.ts +10 -0
- package/dist/types/import/walls.d.ts +37 -0
- package/dist/types/map/CabinMaterials.d.ts +18 -0
- package/dist/types/map/CabinMirror.d.ts +13 -0
- package/dist/types/map/Doors.d.ts +11 -0
- package/dist/types/map/ElevatorCabin.d.ts +20 -0
- package/dist/types/map/FixtureLights.d.ts +35 -0
- package/dist/types/map/MapCanvas.d.ts +87 -0
- package/dist/types/map/Passenger.d.ts +18 -0
- package/dist/types/map/SceneLayer.d.ts +306 -0
- package/dist/types/map/UndergroundContext.d.ts +18 -0
- package/dist/types/map/ambience.d.ts +29 -0
- package/dist/types/map/architecture.d.ts +8 -0
- package/dist/types/map/bathMusic.d.ts +14 -0
- package/dist/types/map/cameraAim.d.ts +20 -0
- package/dist/types/map/elevators.d.ts +16 -0
- package/dist/types/map/excavation.d.ts +9 -0
- package/dist/types/map/exteriors.d.ts +3 -0
- package/dist/types/map/features.d.ts +33 -0
- package/dist/types/map/fences.d.ts +4 -0
- package/dist/types/map/journey.d.ts +36 -0
- package/dist/types/map/levels.d.ts +4 -0
- package/dist/types/map/lighting.d.ts +103 -0
- package/dist/types/map/loadBasemap.d.ts +4 -0
- package/dist/types/map/materials.d.ts +38 -0
- package/dist/types/map/poolLighting.d.ts +11 -0
- package/dist/types/map/projection.d.ts +9 -0
- package/dist/types/map/rampGeometry.d.ts +8 -0
- package/dist/types/map/route.d.ts +59 -0
- package/dist/types/map/stairSurfaces.d.ts +29 -0
- package/dist/types/map/surfaces.d.ts +36 -0
- package/dist/types/map/textures.d.ts +8 -0
- package/dist/types/map/underground.d.ts +30 -0
- package/dist/types/map/walk.d.ts +232 -0
- package/dist/types/map/walkContext.d.ts +11 -0
- package/dist/types/map/walkDimensions.d.ts +5 -0
- package/dist/types/map/walkJourney.d.ts +33 -0
- package/dist/types/map/walkSurfaces.d.ts +27 -0
- package/dist/types/map/wallJoins.d.ts +6 -0
- package/dist/types/map/water.d.ts +12 -0
- package/dist/types/model/authoring.d.ts +38 -0
- package/dist/types/model/boundaries.d.ts +41 -0
- package/dist/types/model/coverage.d.ts +8 -0
- package/dist/types/model/document.d.ts +9 -0
- package/dist/types/model/doors.d.ts +19 -0
- package/dist/types/model/factory.d.ts +5 -0
- package/dist/types/model/geometry.d.ts +99 -0
- package/dist/types/model/history.d.ts +10 -0
- package/dist/types/model/host.d.ts +154 -0
- package/dist/types/model/imports.d.ts +7 -0
- package/dist/types/model/inference.d.ts +46 -0
- package/dist/types/model/live.d.ts +60 -0
- package/dist/types/model/materials.d.ts +28 -0
- package/dist/types/model/mutations.d.ts +178 -0
- package/dist/types/model/navigation.d.ts +70 -0
- package/dist/types/model/ontology.d.ts +43 -0
- package/dist/types/model/passages.d.ts +2 -0
- package/dist/types/model/portals.d.ts +9 -0
- package/dist/types/model/precision.d.ts +11 -0
- package/dist/types/model/project.d.ts +8 -0
- package/dist/types/model/ramps.d.ts +7 -0
- package/dist/types/model/roomNavigation.d.ts +15 -0
- package/dist/types/model/routeClearance.d.ts +11 -0
- package/dist/types/model/spaces.d.ts +46 -0
- package/dist/types/model/stairGeometry.d.ts +27 -0
- package/dist/types/model/stairTraversal.d.ts +8 -0
- package/dist/types/model/topology.d.ts +14 -0
- package/dist/types/model/types.d.ts +369 -0
- package/dist/types/model/validate.d.ts +38 -0
- package/dist/types/model/vertical.d.ts +145 -0
- package/dist/types/model/walls.d.ts +71 -0
- package/dist/types/planner/DrawingDock.d.ts +16 -0
- package/dist/types/planner/FloorControls.d.ts +17 -0
- package/dist/types/planner/MapSettings.d.ts +16 -0
- package/dist/types/planner/PlannerHelp.d.ts +3 -0
- package/dist/types/planner/ProjectSidebar.d.ts +32 -0
- package/dist/types/planner/floors.d.ts +3 -0
- package/dist/types/planner/types.d.ts +3 -0
- package/dist/types/planner/useDrawingTools.d.ts +52 -0
- package/dist/types/planner/useMapDisplay.d.ts +29 -0
- package/dist/types/planner/usePlannerKeyboard.d.ts +18 -0
- package/dist/types/planner/useReferenceAlignment.d.ts +30 -0
- package/dist/types/schema/index.d.ts +25 -0
- package/dist/types/theme/index.d.ts +55 -0
- package/package.json +58 -4
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"walkContext.js","sources":["../../../../src/map/walkContext.ts"],"sourcesContent":["import { centroid, pointInRing, ringArea } from '../model/geometry';\nimport type { Floor, ProjectDocument, Ring, SiteObject } from '../model/types';\nimport { flights, primaryShafts } from '../model/vertical';\nimport { rampFloors } from '../model/ramps';\n\n/** Build the structural surroundings of both landings before a passenger starts climbing.\n * The selected floor remains the walking datum; these levels only supply visible context. */\nexport function walkConnectionContext(project: ProjectDocument, active: Floor, primary = primaryShafts(project)) {\n const levels = new Map<string, Floor>();\n for (const o of project.objects) {\n const pairs =\n o.kind === 'stairs' && primary.has(o.id)\n ? flights(project, o).map(f => [f.from, f.to])\n : o.slope\n ? [Object.values(rampFloors(project, o))]\n : [];\n for (const pair of pairs) {\n if (!pair.some(f => f?.id === active.id)) continue;\n for (const f of pair) if (f && f.id !== active.id) levels.set(f.id, f);\n }\n }\n return [...levels.values()];\n}\n\n/** Structural storeys seen through an authored atrium. Stops at a plate that closes the opening;\n * room fit-out is deliberately left on its own floor. A room nested in a zone adds no new void. */\nexport function walkVoidContext(project: ProjectDocument, active: Floor, ceilingFloor: Floor) {\n const levels = project.floors\n .filter(f => f.buildingId === active.buildingId)\n .sort((a, b) => a.elevation - b.elevation);\n const areas = new Map<string, SiteObject[]>();\n for (const floor of levels) {\n const own = project.objects.filter(o => o.floorId === floor.id && o.rings?.length && !o.slope);\n const zones = own.filter(o => o.kind === 'zone' && !o.parentId);\n areas.set(floor.id, zones.length ? zones : own.filter(o => o.kind === 'room'));\n }\n const covers = (floor: Floor, ring: Ring) => {\n const at = centroid(ring);\n return (areas.get(floor.id) ?? []).some(\n o => pointInRing(at, o.rings![0]) && !o.rings!.slice(1).some(h => pointInRing(at, h)),\n );\n };\n const holes = (floor: Floor) =>\n (areas.get(floor.id) ?? []).flatMap(o => o.rings!.slice(1)).filter(r => ringArea(r) > 0.01 && !covers(floor, r));\n const above = levels.filter(f => f.elevation >= ceilingFloor.elevation + ceilingFloor.height - 0.01);\n const ceilingOpenings = above.length ? holes(above[0]) : [];\n const ownOpenings = holes(active),\n upper = [...ownOpenings, ...ceilingOpenings];\n const context: Floor[] = [];\n let pending = upper;\n for (const floor of above) {\n if (!pending.length) break;\n context.push(floor);\n pending = pending.filter(r => !covers(floor, r));\n }\n const roof = pending.length ? context.at(-1) : undefined;\n pending = ownOpenings;\n for (const floor of levels.filter(f => f.elevation < active.elevation).reverse()) {\n if (!pending.length) break;\n context.push(floor);\n pending = pending.filter(r => !covers(floor, r));\n }\n return { levels: context, roof, ceilingOpenings };\n}\n"],"names":["walkConnectionContext","project","active","primary","primaryShafts","levels","o","pairs","flights","f","rampFloors","pair","walkVoidContext","ceilingFloor","a","b","areas","floor","own","_a","zones","covers","ring","at","centroid","pointInRing","holes","r","ringArea","above","ceilingOpenings","ownOpenings","upper","context","pending","roof"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"walkDimensions.js","sources":["../../../../src/map/walkDimensions.ts"],"sourcesContent":["/** Eye height and shoulder radius in scene metres. Shared by stair rails and planar collision. */\nexport const EYE = 1.7;\nexport const BODY = 0.28;\n/** Walls starting above this band are lintels the walker can pass beneath. */\nexport const HEAD_ROOM = 1.25;\n"],"names":["EYE","BODY","HEAD_ROOM"],"mappings":"AACO,MAAMA,IAAM,KACNC,IAAO,MAEPC,IAAY;"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
var j = Object.defineProperty;
|
|
2
|
+
var u = (n, o, t) => o in n ? j(n, o, { enumerable: !0, configurable: !0, writable: !0, value: t }) : n[o] = t;
|
|
3
|
+
var g = (n, o, t) => u(n, typeof o != "symbol" ? o + "" : o, t);
|
|
4
|
+
import { distance as I } from "../model/geometry.js";
|
|
5
|
+
import { stairTraversal as v } from "../model/stairTraversal.js";
|
|
6
|
+
import { elevatorLanding as y } from "./elevators.js";
|
|
7
|
+
import { legStepIndices as b } from "./route.js";
|
|
8
|
+
class R {
|
|
9
|
+
constructor(o) {
|
|
10
|
+
g(this, "stopped", !1);
|
|
11
|
+
g(this, "timer");
|
|
12
|
+
g(this, "rejectWait");
|
|
13
|
+
this.host = o;
|
|
14
|
+
}
|
|
15
|
+
stop() {
|
|
16
|
+
var o;
|
|
17
|
+
this.stopped = !0, clearTimeout(this.timer), (o = this.rejectWait) == null || o.call(this, new Error("Route playback stopped."));
|
|
18
|
+
}
|
|
19
|
+
async waitUntil(o) {
|
|
20
|
+
const t = Date.now();
|
|
21
|
+
for (; !o(); ) {
|
|
22
|
+
if (this.stopped) throw new Error("Route playback stopped.");
|
|
23
|
+
if (Date.now() - t > 15e3)
|
|
24
|
+
throw new Error("The elevator has not opened. Route playback paused at the landing.");
|
|
25
|
+
await new Promise((l, d) => {
|
|
26
|
+
this.rejectWait = d, this.timer = setTimeout(l, 40);
|
|
27
|
+
}), this.rejectWait = void 0;
|
|
28
|
+
}
|
|
29
|
+
if (this.stopped) throw new Error("Route playback stopped.");
|
|
30
|
+
}
|
|
31
|
+
object(o) {
|
|
32
|
+
return this.host.project.objects.find((t) => t.id === o.edge.objectId && t.kind === o.edge.kind);
|
|
33
|
+
}
|
|
34
|
+
entry(o) {
|
|
35
|
+
var l;
|
|
36
|
+
const t = this.object(o);
|
|
37
|
+
if (!t) throw new Error("This route needs a connection to its physical lift or stair.");
|
|
38
|
+
return t.kind === "elevator" ? y(t) : ((l = v(this.host.project, t, o.from.floorId, o.to.floorId)) == null ? void 0 : l.points[0]) ?? o.from.position;
|
|
39
|
+
}
|
|
40
|
+
async play(o) {
|
|
41
|
+
var k;
|
|
42
|
+
const t = this.host, l = b(o);
|
|
43
|
+
o.nodes[0] && t.floor() !== o.nodes[0].floorId && await t.changeFloor(o.nodes[0].floorId);
|
|
44
|
+
let d;
|
|
45
|
+
for (let a = 0; a < o.legs.length && !this.stopped; a++) {
|
|
46
|
+
const r = o.legs[a];
|
|
47
|
+
if (t.step(l[a]), r.edge.kind === "elevator") {
|
|
48
|
+
const e = this.object(r), i = t.controls();
|
|
49
|
+
if (!(e != null && e.feedId) || !i) throw new Error("Connect elevator controls to play this lift journey.");
|
|
50
|
+
const s = e.feedId, w = r.from.floorId, p = r.to.floorId, f = y(e);
|
|
51
|
+
I(t.position(), f) > 0.01 && await t.walk([t.position(), f]), t.message(`Calling ${e.name}…`), await this.waitUntil(() => {
|
|
52
|
+
var h;
|
|
53
|
+
return !((h = t.status(s)) != null && h.moving);
|
|
54
|
+
});
|
|
55
|
+
const m = (h) => {
|
|
56
|
+
const c = t.status(s);
|
|
57
|
+
return !!(c != null && c.open) && !c.moving && !c.targetFloorId && c.carFloorId === h;
|
|
58
|
+
};
|
|
59
|
+
m(w) || (i.call(s, w), await this.waitUntil(() => m(w))), t.message(`Entering ${e.name}`), await t.walk([f, e.position], { physical: !0 }), t.ride(e.id), t.message(`Taking ${e.name} to ${((k = t.project.floors.find((h) => h.id === p)) == null ? void 0 : k.name) ?? p}`), i.call(s, p), await this.waitUntil(() => m(p)), t.floor() !== p && await t.changeFloor(p), t.message(`Exiting ${e.name}`), await t.walk([e.position, f], { physical: !0 }), t.ride(null), d = f;
|
|
60
|
+
} else if (r.edge.kind === "stairs") {
|
|
61
|
+
const e = this.object(r), i = e && v(t.project, e, r.from.floorId, r.to.floorId);
|
|
62
|
+
if (!e || !i) throw new Error("This stair route has no matching physical flight.");
|
|
63
|
+
const s = e.feedId ? t.status(e.feedId) : void 0;
|
|
64
|
+
if (i.escalator && (s == null ? void 0 : s.running) !== !1 && ((s == null ? void 0 : s.travel) ?? e.travel ?? "up") === "up" !== i.up)
|
|
65
|
+
throw new Error("The escalator is running the other way. Choose another route.");
|
|
66
|
+
I(t.position(), i.points[0]) > 0.01 && await t.walk([t.position(), i.points[0]]), t.message(`${i.escalator ? "Riding" : "Walking"} ${e.name}`), await t.walk(i.points, {
|
|
67
|
+
physical: !0,
|
|
68
|
+
speed: i.escalator && (s == null ? void 0 : s.running) !== !1 ? i.speed : void 0
|
|
69
|
+
}), t.floor() !== r.to.floorId && await t.changeFloor(r.to.floorId), d = i.points.at(-1);
|
|
70
|
+
} else {
|
|
71
|
+
t.message("");
|
|
72
|
+
const e = [d ?? r.from.position, r.to.position];
|
|
73
|
+
for (d = void 0; a + 1 < o.legs.length && o.legs[a + 1].edge.kind !== "elevator" && o.legs[a + 1].edge.kind !== "stairs" && o.legs[a + 1].from.floorId === r.from.floorId; )
|
|
74
|
+
a++, e.push(o.legs[a].to.position);
|
|
75
|
+
const i = o.legs[a + 1];
|
|
76
|
+
i && (i.edge.kind === "stairs" || i.edge.kind === "elevator") && (e[e.length - 1] = this.entry(i)), await t.walk(e);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
this.stopped || (t.message("Arrived"), t.step(o.steps.length - 1));
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
export {
|
|
83
|
+
R as WalkJourney
|
|
84
|
+
};
|
|
85
|
+
//# sourceMappingURL=walkJourney.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"walkJourney.js","sources":["../../../../src/map/walkJourney.ts"],"sourcesContent":["import type { ElevatorControls } from '../model/host';\nimport type { StatusReading } from '../model/live';\nimport type { Route, RouteLeg } from '../model/navigation';\nimport type { Point, ProjectDocument } from '../model/types';\nimport { distance } from '../model/geometry';\nimport { stairTraversal } from '../model/stairTraversal';\nimport { elevatorLanding } from './elevators';\nimport { legStepIndices } from './route';\n\nexport interface WalkJourneyHost {\n project: ProjectDocument;\n floor(): string | null;\n changeFloor(id: string | null): Promise<void>;\n position(): Point;\n walk(points: Point[], options?: { speed?: number; physical?: boolean }): Promise<void>;\n controls(): ElevatorControls | undefined;\n status(feedId: string): StatusReading | undefined;\n ride(id: string | null): void;\n message(text: string): void;\n step(index: number): void;\n}\n\n/** Stateful playback uses the same lift commands/readings as a passenger. Vertical legs must not\n * disappear when a route is split into floor runs: that used to teleport straight past the ride. */\nexport class WalkJourney {\n private stopped = false;\n private timer?: ReturnType<typeof setTimeout>;\n private rejectWait?: (error: Error) => void;\n constructor(private host: WalkJourneyHost) {}\n stop() {\n this.stopped = true;\n clearTimeout(this.timer);\n this.rejectWait?.(new Error('Route playback stopped.'));\n }\n private async waitUntil(test: () => boolean) {\n const started = Date.now();\n while (!test()) {\n if (this.stopped) throw new Error('Route playback stopped.');\n if (Date.now() - started > 15_000)\n throw new Error('The elevator has not opened. Route playback paused at the landing.');\n await new Promise<void>((resolve, reject) => {\n this.rejectWait = reject;\n this.timer = setTimeout(resolve, 40);\n });\n this.rejectWait = undefined;\n }\n if (this.stopped) throw new Error('Route playback stopped.');\n }\n private object(leg: RouteLeg) {\n return this.host.project.objects.find(o => o.id === leg.edge.objectId && o.kind === leg.edge.kind);\n }\n private entry(leg: RouteLeg): Point {\n const o = this.object(leg);\n if (!o) throw new Error('This route needs a connection to its physical lift or stair.');\n if (o.kind === 'elevator') return elevatorLanding(o);\n return stairTraversal(this.host.project, o, leg.from.floorId!, leg.to.floorId!)?.points[0] ?? leg.from.position;\n }\n async play(route: Route) {\n const h = this.host,\n steps = legStepIndices(route);\n if (route.nodes[0] && h.floor() !== route.nodes[0].floorId) await h.changeFloor(route.nodes[0].floorId);\n let arrival: Point | undefined;\n for (let i = 0; i < route.legs.length && !this.stopped; i++) {\n const leg = route.legs[i];\n h.step(steps[i]);\n if (leg.edge.kind === 'elevator') {\n const o = this.object(leg),\n controls = h.controls();\n if (!o?.feedId || !controls) throw new Error('Connect elevator controls to play this lift journey.');\n const feed = o.feedId,\n from = leg.from.floorId!,\n to = leg.to.floorId!,\n landing = elevatorLanding(o);\n if (distance(h.position(), landing) > 0.01) await h.walk([h.position(), landing]);\n h.message(`Calling ${o.name}…`);\n await this.waitUntil(() => !h.status(feed)?.moving);\n const openHere = (floor: string) => {\n const s = h.status(feed);\n return !!s?.open && !s.moving && !s.targetFloorId && s.carFloorId === floor;\n };\n if (!openHere(from)) {\n controls.call(feed, from);\n await this.waitUntil(() => openHere(from));\n }\n h.message(`Entering ${o.name}`);\n await h.walk([landing, o.position], { physical: true });\n h.ride(o.id);\n h.message(`Taking ${o.name} to ${h.project.floors.find(f => f.id === to)?.name ?? to}`);\n controls.call(feed, to);\n await this.waitUntil(() => openHere(to));\n if (h.floor() !== to) await h.changeFloor(to);\n h.message(`Exiting ${o.name}`);\n await h.walk([o.position, landing], { physical: true });\n h.ride(null);\n arrival = landing;\n } else if (leg.edge.kind === 'stairs') {\n const o = this.object(leg);\n const path = o && stairTraversal(h.project, o, leg.from.floorId!, leg.to.floorId!);\n if (!o || !path) throw new Error('This stair route has no matching physical flight.');\n const status = o.feedId ? h.status(o.feedId) : undefined;\n if (path.escalator && status?.running !== false && ((status?.travel ?? o.travel ?? 'up') === 'up') !== path.up)\n throw new Error('The escalator is running the other way. Choose another route.');\n if (distance(h.position(), path.points[0]) > 0.01) await h.walk([h.position(), path.points[0]]);\n h.message(`${path.escalator ? 'Riding' : 'Walking'} ${o.name}`);\n await h.walk(path.points, {\n physical: true,\n speed: path.escalator && status?.running !== false ? path.speed : undefined,\n });\n if (h.floor() !== leg.to.floorId) await h.changeFloor(leg.to.floorId);\n arrival = path.points.at(-1)!;\n } else {\n h.message('');\n const points = [arrival ?? leg.from.position, leg.to.position];\n arrival = undefined;\n while (\n i + 1 < route.legs.length &&\n route.legs[i + 1].edge.kind !== 'elevator' &&\n route.legs[i + 1].edge.kind !== 'stairs' &&\n route.legs[i + 1].from.floorId === leg.from.floorId\n ) {\n i++;\n points.push(route.legs[i].to.position);\n }\n const next = route.legs[i + 1];\n if (next && (next.edge.kind === 'stairs' || next.edge.kind === 'elevator'))\n points[points.length - 1] = this.entry(next);\n await h.walk(points);\n }\n }\n if (!this.stopped) {\n h.message('Arrived');\n h.step(route.steps.length - 1);\n }\n }\n}\n"],"names":["WalkJourney","host","__publicField","_a","test","started","resolve","reject","leg","o","elevatorLanding","stairTraversal","route","h","steps","legStepIndices","arrival","i","controls","feed","from","to","landing","distance","openHere","floor","s","f","path","status","points","next"],"mappings":"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"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { objectArea as w, pointInRing as m, slopeElevation as M } from "../model/geometry.js";
|
|
2
|
+
import { rampFloors as k } from "../model/ramps.js";
|
|
3
|
+
import { spacePoint as H } from "../model/spaces.js";
|
|
4
|
+
import { wallPieces as A } from "./features.js";
|
|
5
|
+
import { wallFootprints as R } from "./wallJoins.js";
|
|
6
|
+
import { HEAD_ROOM as S } from "./walkDimensions.js";
|
|
7
|
+
const I = (o, l) => {
|
|
8
|
+
var i;
|
|
9
|
+
return !!((i = o.rings) != null && i.length) && m(l, o.rings[0]) && !o.rings.slice(1).some((g) => m(l, g));
|
|
10
|
+
};
|
|
11
|
+
function C(o, l) {
|
|
12
|
+
const i = o.floors.find((e) => e.id === l), g = new Map(o.floors.map((e) => [e.id, e])), a = (i == null ? void 0 : i.elevation) ?? 0;
|
|
13
|
+
return A(o, null, !0).filter((e) => {
|
|
14
|
+
const s = g.get(e.floorId ?? "");
|
|
15
|
+
return (i ? (s == null ? void 0 : s.buildingId) === i.buildingId || e.floorId === null && i.elevation >= 0 : e.floorId === null) && e.base < a + S && e.height > a + 0.01;
|
|
16
|
+
}).map((e) => e.ring);
|
|
17
|
+
}
|
|
18
|
+
function D(o) {
|
|
19
|
+
var b;
|
|
20
|
+
const l = new Map(o.floors.map((n) => [n.id, n])), i = /* @__PURE__ */ new Map(), g = o.objects.filter((n) => {
|
|
21
|
+
var t;
|
|
22
|
+
return n.slope && ((t = n.rings) == null ? void 0 : t.length);
|
|
23
|
+
}), a = new Map(
|
|
24
|
+
o.floors.map((n) => [n.id, g.filter((t) => {
|
|
25
|
+
var r;
|
|
26
|
+
return t.floorId === n.id && ((r = k(o, t).high) == null ? void 0 : r.id) === n.id;
|
|
27
|
+
})])
|
|
28
|
+
);
|
|
29
|
+
for (const n of o.objects) {
|
|
30
|
+
if (n.kind !== "room" && n.kind !== "zone" || !((b = n.rings) != null && b.length)) continue;
|
|
31
|
+
const t = i.get(n.floorId) ?? [];
|
|
32
|
+
t.push(n), i.set(n.floorId, t);
|
|
33
|
+
}
|
|
34
|
+
const e = /* @__PURE__ */ new Map(), s = R(o);
|
|
35
|
+
for (const n of o.barriers) {
|
|
36
|
+
const t = s.get(n.id);
|
|
37
|
+
if (!t) continue;
|
|
38
|
+
const r = e.get(n.floorId) ?? [];
|
|
39
|
+
r.push(t), e.set(n.floorId, r);
|
|
40
|
+
}
|
|
41
|
+
const d = (n, t) => (i.get(t) ?? []).some((r) => I(r, n)) || (e.get(t) ?? []).some((r) => m(n, r)), f = (n, t) => (i.get(t) ?? []).some((r) => !r.slope && r.rings.slice(1).some((c) => m(n, c))), u = new Map(
|
|
42
|
+
o.floors.map((n) => [
|
|
43
|
+
n.id,
|
|
44
|
+
o.floors.filter((t) => t.buildingId === n.buildingId && t.elevation < n.elevation - 0.01).sort((t, r) => r.elevation - t.elevation)
|
|
45
|
+
])
|
|
46
|
+
), v = (n, t) => {
|
|
47
|
+
var p;
|
|
48
|
+
const r = l.get(t ?? "");
|
|
49
|
+
if (!r || d(n, t) || !f(n, t)) return;
|
|
50
|
+
const c = (p = u.get(r.id)) == null ? void 0 : p.find((h) => d(n, h.id));
|
|
51
|
+
return c ? { floorId: c.id, distance: r.elevation - c.elevation } : void 0;
|
|
52
|
+
};
|
|
53
|
+
return { supports: (n, t) => {
|
|
54
|
+
var c, p;
|
|
55
|
+
const r = l.get(t ?? "");
|
|
56
|
+
return r && ((c = a.get(r.id)) != null && c.some((h) => m(n, h.rings[0]) && M(h.slope, n) < r.elevation - 0.05)) ? !1 : !r || !((p = i.get(t)) != null && p.length) || d(n, t) ? !0 : f(n, t) ? !!v(n, t) : Math.abs(r.elevation) < 0.01;
|
|
57
|
+
}, dropAt: v };
|
|
58
|
+
}
|
|
59
|
+
function N(o, l, i) {
|
|
60
|
+
if (i(l)) return l;
|
|
61
|
+
if (!i(o)) return o;
|
|
62
|
+
let g = 0, a = 1;
|
|
63
|
+
for (let e = 0; e < 16; e++) {
|
|
64
|
+
const s = (g + a) / 2, d = [o[0] + (l[0] - o[0]) * s, o[1] + (l[1] - o[1]) * s];
|
|
65
|
+
i(d) ? g = s : a = s;
|
|
66
|
+
}
|
|
67
|
+
return [o[0] + (l[0] - o[0]) * g, o[1] + (l[1] - o[1]) * g];
|
|
68
|
+
}
|
|
69
|
+
function W(o, l, i) {
|
|
70
|
+
var d;
|
|
71
|
+
if (!l) return i;
|
|
72
|
+
const g = o.objects.filter((f) => f.floorId === l && (f.kind === "room" || f.kind === "zone"));
|
|
73
|
+
if (g.some((f) => I(f, i))) return i;
|
|
74
|
+
const a = o.objects.filter((f) => f.floorId === l && f.water), e = (f) => g.some((u) => I(u, f)) && !a.some((u) => I(u, f)), s = (d = o.navNodes) == null ? void 0 : d.find((f) => f.floorId === l && e(f.position));
|
|
75
|
+
if (s) return s.position;
|
|
76
|
+
for (const f of g.sort((u, v) => w(v) - w(u))) {
|
|
77
|
+
const u = H(o, f);
|
|
78
|
+
if (e(u)) return u;
|
|
79
|
+
}
|
|
80
|
+
return i;
|
|
81
|
+
}
|
|
82
|
+
function _(o, l) {
|
|
83
|
+
const i = o.floors.find((e) => e.id === l);
|
|
84
|
+
if (!i) return [];
|
|
85
|
+
const g = new Map(o.floors.map((e) => [e.id, e])), a = o.objects.filter((e) => {
|
|
86
|
+
var d;
|
|
87
|
+
const s = g.get(e.floorId ?? "");
|
|
88
|
+
return (s == null ? void 0 : s.buildingId) === i.buildingId && ((d = e.rings) == null ? void 0 : d.length) && e.ceilingHeight && e.ceilingHeight > s.height + 0.01 && s.elevation <= i.elevation && s.elevation + e.ceilingHeight > i.elevation;
|
|
89
|
+
});
|
|
90
|
+
return o.objects.filter((e) => {
|
|
91
|
+
if (e.floorId === l) return !1;
|
|
92
|
+
const s = g.get(e.floorId ?? "");
|
|
93
|
+
return (s == null ? void 0 : s.buildingId) === i.buildingId && a.some((d) => {
|
|
94
|
+
const f = g.get(d.floorId).elevation;
|
|
95
|
+
return s.elevation >= f && s.elevation < f + d.ceilingHeight && m(e.position, d.rings[0]);
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
export {
|
|
100
|
+
W as floorArrival,
|
|
101
|
+
D as floorSupport,
|
|
102
|
+
N as supportedStep,
|
|
103
|
+
_ as tallSpaceContext,
|
|
104
|
+
C as walkingWallRings
|
|
105
|
+
};
|
|
106
|
+
//# sourceMappingURL=walkSurfaces.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"walkSurfaces.js","sources":["../../../../src/map/walkSurfaces.ts"],"sourcesContent":["import type { Point, ProjectDocument, Ring, SiteObject } from '../model/types';\nimport { objectArea, pointInRing, slopeElevation } from '../model/geometry';\nimport { rampFloors } from '../model/ramps';\nimport { spacePoint } from '../model/spaces';\nimport { wallPieces } from './features';\nimport { wallFootprints } from './wallJoins';\nimport { HEAD_ROOM } from './walkDimensions';\n\nconst contains = (o: SiteObject, at: Point) =>\n !!o.rings?.length && pointInRing(at, o.rings[0]) && !o.rings.slice(1).some(hole => pointInRing(at, hole));\n\n/** Evaluate wall solids in absolute building heights. A host wall may continue through a\n * mezzanine, while a low partition below it and a lintel overhead do not obstruct the walker. */\nexport function walkingWallRings(project: ProjectDocument, floorId: string | null): Ring[] {\n const active = project.floors.find(f => f.id === floorId);\n const floors = new Map(project.floors.map(f => [f.id, f]));\n const foot = active?.elevation ?? 0;\n return wallPieces(project, null, true)\n .filter(piece => {\n const floor = floors.get(piece.floorId ?? '');\n const relevant = active\n ? floor?.buildingId === active.buildingId || (piece.floorId === null && active.elevation >= 0)\n : piece.floorId === null;\n return relevant && piece.base < foot + HEAD_ROOM && piece.height > foot + 0.01;\n })\n .map(piece => piece.ring);\n}\n\n/** Build the support lookup when geometry changes, not for every walking frame. Open-air grade\n * and floors without authored plates stay traversable. Higher and buried plates have real edges. */\nexport function floorSupport(project: ProjectDocument) {\n const floors = new Map(project.floors.map(f => [f.id, f]));\n const areas = new Map<string | null, SiteObject[]>();\n const ramps = project.objects.filter(o => o.slope && o.rings?.length);\n const openings = new Map(\n project.floors.map(f => [f.id, ramps.filter(o => o.floorId === f.id && rampFloors(project, o).high?.id === f.id)]),\n );\n for (const o of project.objects) {\n if ((o.kind !== 'room' && o.kind !== 'zone') || !o.rings?.length) continue;\n const own = areas.get(o.floorId) ?? [];\n own.push(o);\n areas.set(o.floorId, own);\n }\n // Rooms may end at the inner wall face. The slab beneath a wall also supports its doorway;\n // otherwise a valid room-to-room threshold would look like a narrow unsupported gap.\n const thresholds = new Map<string | null, Ring[]>();\n const footprints = wallFootprints(project);\n for (const wall of project.barriers) {\n const ring = footprints.get(wall.id);\n if (!ring) continue;\n const own = thresholds.get(wall.floorId) ?? [];\n own.push(ring);\n thresholds.set(wall.floorId, own);\n }\n const solid = (at: Point, id: string | null) =>\n (areas.get(id) ?? []).some(o => contains(o, at)) || (thresholds.get(id) ?? []).some(ring => pointInRing(at, ring));\n const hole = (at: Point, id: string | null) =>\n (areas.get(id) ?? []).some(o => !o.slope && o.rings!.slice(1).some(ring => pointInRing(at, ring)));\n const lower = new Map(\n project.floors.map(f => [\n f.id,\n project.floors\n .filter(b => b.buildingId === f.buildingId && b.elevation < f.elevation - 0.01)\n .sort((a, b) => b.elevation - a.elevation),\n ]),\n );\n const dropAt = (at: Point, id: string | null) => {\n const floor = floors.get(id ?? '');\n if (!floor || solid(at, id) || !hole(at, id)) return;\n const below = lower.get(floor.id)?.find(f => solid(at, f.id));\n return below ? { floorId: below.id, distance: floor.elevation - below.elevation } : undefined;\n };\n const supports = (at: Point, id: string | null): boolean => {\n const floor = floors.get(id ?? '');\n // The ramp walker supplies support on the incline. The deck must not supply an invisible\n // horizontal floor above it when someone tries to enter the opening from the side.\n if (\n floor &&\n openings\n .get(floor.id)\n ?.some(o => pointInRing(at, o.rings![0]) && slopeElevation(o.slope!, at) < floor.elevation - 0.05)\n )\n return false;\n if (!floor || !areas.get(id)?.length || solid(at, id)) return true;\n if (hole(at, id)) return !!dropAt(at, id);\n // An exposed gallery edge is not an authored opening. At grade, the surrounding ground\n // supports an exit from the building; in the air or an excavation, keep the last supported point.\n return Math.abs(floor.elevation) < 0.01;\n };\n return { supports, dropAt };\n}\n\n/** Clip a small walking step at the last supported point; never snap it onto another island. */\nexport function supportedStep(from: Point, to: Point, supports: (at: Point) => boolean): Point {\n if (supports(to)) return to;\n if (!supports(from)) return from;\n let low = 0,\n high = 1;\n for (let i = 0; i < 16; i++) {\n const t = (low + high) / 2;\n const at: Point = [from[0] + (to[0] - from[0]) * t, from[1] + (to[1] - from[1]) * t];\n if (supports(at)) low = t;\n else high = t;\n }\n return [from[0] + (to[0] - from[0]) * low, from[1] + (to[1] - from[1]) * low];\n}\n\n/** Keep a valid position across storeys, including a fall's landing. A floor picker can instead\n * select a smaller footprint: enter it at a dry navigation point, not in the void outside it. */\nexport function floorArrival(project: ProjectDocument, floorId: string | null, at: Point): Point {\n if (!floorId) return at;\n const rooms = project.objects.filter(o => o.floorId === floorId && (o.kind === 'room' || o.kind === 'zone'));\n if (rooms.some(o => contains(o, at))) return at;\n const pools = project.objects.filter(o => o.floorId === floorId && o.water);\n const dry = (p: Point) => rooms.some(o => contains(o, p)) && !pools.some(o => contains(o, p));\n const node = project.navNodes?.find(n => n.floorId === floorId && dry(n.position));\n if (node) return node.position;\n for (const room of rooms.sort((a, b) => objectArea(b) - objectArea(a))) {\n const point = spacePoint(project, room);\n if (dry(point)) return point;\n }\n return at;\n}\n\n/** Only authored openings trigger a drop. Gaps outside the building, an undrawn floor, and\n * overlapping areas that still provide a slab are not floor holes. */\nexport function floorDropAt(project: ProjectDocument, floorId: string | null, at: Point) {\n return floorSupport(project).dropAt(at, floorId);\n}\n\n/** Rooms which span the active storey bring the geometry inside their footprint along with them.\n * The same context supplies the lower plate seen through an opening and the gallery seen from below. */\nexport function tallSpaceContext(project: ProjectDocument, floorId: string | null): SiteObject[] {\n const active = project.floors.find(f => f.id === floorId);\n if (!active) return [];\n const floors = new Map(project.floors.map(f => [f.id, f]));\n const halls = project.objects.filter(o => {\n const floor = floors.get(o.floorId ?? '');\n return (\n floor?.buildingId === active.buildingId &&\n o.rings?.length &&\n o.ceilingHeight &&\n o.ceilingHeight > floor.height + 0.01 &&\n floor.elevation <= active.elevation &&\n floor.elevation + o.ceilingHeight > active.elevation\n );\n });\n return project.objects.filter(o => {\n if (o.floorId === floorId) return false;\n const floor = floors.get(o.floorId ?? '');\n return (\n floor?.buildingId === active.buildingId &&\n halls.some(hall => {\n const base = floors.get(hall.floorId!)!.elevation;\n return (\n floor.elevation >= base &&\n floor.elevation < base + hall.ceilingHeight! &&\n pointInRing(o.position, hall.rings![0])\n );\n })\n );\n });\n}\n"],"names":["contains","at","_a","pointInRing","hole","walkingWallRings","project","floorId","active","f","floors","foot","wallPieces","piece","floor","HEAD_ROOM","floorSupport","areas","ramps","o","openings","rampFloors","own","thresholds","footprints","wallFootprints","wall","ring","solid","id","lower","b","a","dropAt","below","slopeElevation","_b","supportedStep","from","to","supports","low","high","i","t","floorArrival","rooms","pools","dry","p","node","n","room","objectArea","point","spacePoint","tallSpaceContext","halls","hall","base"],"mappings":"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"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { distance as k } from "../model/geometry.js";
|
|
2
|
+
const I = (s, c) => s[0] * c[1] - s[1] * c[0], p = (s, c) => [
|
|
3
|
+
-s.direction[1] * s.wall.thickness * c / 2,
|
|
4
|
+
s.direction[0] * s.wall.thickness * c / 2
|
|
5
|
+
];
|
|
6
|
+
function F(s) {
|
|
7
|
+
const c = new Map(s.junctions.map((n) => [n.id, n.position])), d = /* @__PURE__ */ new Map();
|
|
8
|
+
for (const n of s.barriers)
|
|
9
|
+
for (const [o, a] of [
|
|
10
|
+
[n.startId, n.endId],
|
|
11
|
+
[n.endId, n.startId]
|
|
12
|
+
]) {
|
|
13
|
+
const i = c.get(o), f = c.get(a);
|
|
14
|
+
if (!i || !f) continue;
|
|
15
|
+
const t = k(i, f);
|
|
16
|
+
if (t < 1e-9) continue;
|
|
17
|
+
const e = [(f[0] - i[0]) / t, (f[1] - i[1]) / t], l = d.get(o) ?? [];
|
|
18
|
+
l.push({ wall: n, direction: e, length: t, angle: Math.atan2(e[1], e[0]) }), d.set(o, l);
|
|
19
|
+
}
|
|
20
|
+
const g = /* @__PURE__ */ new Map();
|
|
21
|
+
for (const [n, o] of d) {
|
|
22
|
+
o.sort((t, e) => t.angle - e.angle);
|
|
23
|
+
const a = c.get(n), i = (t) => [a[0] + t[0], a[1] + t[1]], f = o.map((t, e) => {
|
|
24
|
+
const l = o[(e + 1) % o.length], r = p(t, 1), h = p(l, -1), u = I(t.direction, l.direction), m = [(r[0] + h[0]) / 2, (r[1] + h[1]) / 2];
|
|
25
|
+
if (Math.abs(u) < 1e-6) return i(m);
|
|
26
|
+
const M = I([h[0] - r[0], h[1] - r[1]], l.direction) / u, b = [r[0] + t.direction[0] * M, r[1] + t.direction[1] * M], $ = Math.min(
|
|
27
|
+
4 * Math.max(t.wall.thickness, l.wall.thickness),
|
|
28
|
+
Math.min(t.length, l.length) * 0.45
|
|
29
|
+
);
|
|
30
|
+
return i(Math.hypot(...b) <= $ ? b : m);
|
|
31
|
+
});
|
|
32
|
+
for (let t = 0; t < o.length; t++) {
|
|
33
|
+
const e = o[t];
|
|
34
|
+
g.set(
|
|
35
|
+
`${n}:${e.wall.id}`,
|
|
36
|
+
o.length === 1 ? { left: i(p(e, 1)), right: i(p(e, -1)) } : { left: f[t], right: f[(t + o.length - 1) % o.length] }
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
const w = /* @__PURE__ */ new Map();
|
|
41
|
+
for (const n of s.barriers) {
|
|
42
|
+
const o = g.get(`${n.startId}:${n.id}`), a = g.get(`${n.endId}:${n.id}`);
|
|
43
|
+
o && a && w.set(n.id, [o.right, a.left, a.right, o.left]);
|
|
44
|
+
}
|
|
45
|
+
return w;
|
|
46
|
+
}
|
|
47
|
+
function S(s, c, d, g, w) {
|
|
48
|
+
let n = s;
|
|
49
|
+
for (const [o, a] of [
|
|
50
|
+
[g, 1],
|
|
51
|
+
[w, -1]
|
|
52
|
+
]) {
|
|
53
|
+
if (o === null) continue;
|
|
54
|
+
const i = [], f = (t) => ((t[0] - c[0]) * d[0] + (t[1] - c[1]) * d[1] - o) * a;
|
|
55
|
+
for (let t = 0; t < n.length; t++) {
|
|
56
|
+
const e = n[t], l = n[(t + 1) % n.length], r = f(e), h = f(l);
|
|
57
|
+
if (r >= 0 && i.push(e), r >= 0 != h >= 0) {
|
|
58
|
+
const u = r / (r - h);
|
|
59
|
+
i.push([e[0] + (l[0] - e[0]) * u, e[1] + (l[1] - e[1]) * u]);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
n = i;
|
|
63
|
+
}
|
|
64
|
+
return n;
|
|
65
|
+
}
|
|
66
|
+
export {
|
|
67
|
+
F as wallFootprints,
|
|
68
|
+
S as wallSpan
|
|
69
|
+
};
|
|
70
|
+
//# sourceMappingURL=wallJoins.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wallJoins.js","sources":["../../../../src/map/wallJoins.ts"],"sourcesContent":["import { distance } from '../model/geometry';\nimport type { Barrier, Point, ProjectDocument, Ring } from '../model/types';\n\ntype Ray = { wall: Barrier; direction: Point; length: number; angle: number };\nconst cross = (a: Point, b: Point) => a[0] * b[1] - a[1] * b[0];\nconst offset = (ray: Ray, side: number): Point => [\n (-ray.direction[1] * ray.wall.thickness * side) / 2,\n (ray.direction[0] * ray.wall.thickness * side) / 2,\n];\n\n/** Adjacent wall faces intersect once at each junction. Both walls use that same corner, so\n * oblique L, T and four-way joins close without rectangular end caps protruding through them. */\nexport function wallFootprints(project: ProjectDocument): Map<string, Ring> {\n const positions = new Map(project.junctions.map(j => [j.id, j.position]));\n const meeting = new Map<string, Ray[]>();\n for (const wall of project.barriers) {\n for (const [from, to] of [\n [wall.startId, wall.endId],\n [wall.endId, wall.startId],\n ]) {\n const a = positions.get(from),\n b = positions.get(to);\n if (!a || !b) continue;\n const length = distance(a, b);\n if (length < 1e-9) continue;\n const direction: Point = [(b[0] - a[0]) / length, (b[1] - a[1]) / length];\n const rays = meeting.get(from) ?? [];\n rays.push({ wall, direction, length, angle: Math.atan2(direction[1], direction[0]) });\n meeting.set(from, rays);\n }\n }\n const ends = new Map<string, { left: Point; right: Point }>();\n for (const [id, rays] of meeting) {\n rays.sort((a, b) => a.angle - b.angle);\n const origin = positions.get(id)!;\n const absolute = (p: Point): Point => [origin[0] + p[0], origin[1] + p[1]];\n // Each wedge has the left face of one ray and the right face of the next ray.\n const corners = rays.map((ray, i): Point => {\n const next = rays[(i + 1) % rays.length];\n const a = offset(ray, 1),\n b = offset(next, -1);\n const denominator = cross(ray.direction, next.direction);\n const halfway: Point = [(a[0] + b[0]) / 2, (a[1] + b[1]) / 2];\n if (Math.abs(denominator) < 1e-6) return absolute(halfway);\n const t = cross([b[0] - a[0], b[1] - a[1]], next.direction) / denominator;\n const at: Point = [a[0] + ray.direction[0] * t, a[1] + ray.direction[1] * t];\n // Bound mitres at nearly parallel branches and on tiny returns. A shared bevel point\n // prevents spikes and inverted pieces while preserving one join for both participants.\n const limit = Math.min(\n 4 * Math.max(ray.wall.thickness, next.wall.thickness),\n Math.min(ray.length, next.length) * 0.45,\n );\n return absolute(Math.hypot(...at) <= limit ? at : halfway);\n });\n for (let i = 0; i < rays.length; i++) {\n const ray = rays[i];\n ends.set(\n `${id}:${ray.wall.id}`,\n rays.length === 1\n ? { left: absolute(offset(ray, 1)), right: absolute(offset(ray, -1)) }\n : { left: corners[i], right: corners[(i + rays.length - 1) % rays.length] },\n );\n }\n }\n const result = new Map<string, Ring>();\n for (const wall of project.barriers) {\n const a = ends.get(`${wall.startId}:${wall.id}`),\n b = ends.get(`${wall.endId}:${wall.id}`);\n if (a && b) result.set(wall.id, [a.right, b.left, b.right, a.left]);\n }\n return result;\n}\n\n/** Cut an opening across the joined footprint, retaining the mitres on the wall's outside ends. */\nexport function wallSpan(ring: Ring, origin: Point, direction: Point, start: number | null, end: number | null): Ring {\n let result = ring;\n for (const [bound, sign] of [\n [start, 1],\n [end, -1],\n ] as const) {\n if (bound === null) continue;\n const next: Ring = [];\n const signed = (p: Point) => ((p[0] - origin[0]) * direction[0] + (p[1] - origin[1]) * direction[1] - bound) * sign;\n for (let i = 0; i < result.length; i++) {\n const a = result[i],\n b = result[(i + 1) % result.length],\n da = signed(a),\n db = signed(b);\n if (da >= 0) next.push(a);\n if (da >= 0 !== db >= 0) {\n const t = da / (da - db);\n next.push([a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t]);\n }\n }\n result = next;\n }\n return result;\n}\n"],"names":["cross","a","b","offset","ray","side","wallFootprints","project","positions","j","meeting","wall","from","to","length","distance","direction","rays","ends","id","origin","absolute","p","corners","i","next","denominator","halfway","t","at","limit","result","wallSpan","ring","start","end","bound","sign","signed","da","db"],"mappings":"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"}
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
import * as e from "three";
|
|
2
|
+
import { closeRing as E, rotate as _ } from "../model/geometry.js";
|
|
3
|
+
import { metricUVs as T } from "./surfaces.js";
|
|
4
|
+
import { poolBounces as B } from "./poolLighting.js";
|
|
5
|
+
const A = `
|
|
6
|
+
uniform float poolTime;
|
|
7
|
+
uniform float poolRipple;
|
|
8
|
+
varying vec2 poolXY;
|
|
9
|
+
vec3 poolWave(vec2 p) {
|
|
10
|
+
vec3 wave = vec3(0.0);
|
|
11
|
+
for (int i = 0; i < 4; i++) {
|
|
12
|
+
float n = float(i);
|
|
13
|
+
float angle = n * 2.399 + 0.35;
|
|
14
|
+
vec2 direction = vec2(cos(angle), sin(angle));
|
|
15
|
+
float k = 2.6 + n * 2.3;
|
|
16
|
+
float amplitude = poolRipple / (1.0 + n * 1.6);
|
|
17
|
+
float phase = k * dot(direction, p) - sqrt(9.81 * k) * poolTime * 0.45;
|
|
18
|
+
wave.x += amplitude * sin(phase);
|
|
19
|
+
wave.yz += amplitude * k * cos(phase) * direction;
|
|
20
|
+
}
|
|
21
|
+
return wave;
|
|
22
|
+
}
|
|
23
|
+
`;
|
|
24
|
+
function D(d) {
|
|
25
|
+
return `
|
|
26
|
+
uniform vec4 poolEdges[${d}];
|
|
27
|
+
uniform float poolRefractionGuard;
|
|
28
|
+
float poolClearance(vec2 p) {
|
|
29
|
+
bool inside = false;
|
|
30
|
+
float clearance = 1e6;
|
|
31
|
+
for (int i = 0; i < ${d}; i++) {
|
|
32
|
+
vec2 a = poolEdges[i].xy, b = poolEdges[i].zw, edge = b-a;
|
|
33
|
+
float t = clamp(dot(p-a, edge) / max(dot(edge, edge), 1e-8), 0.0, 1.0);
|
|
34
|
+
clearance = min(clearance, length(p-a-edge*t));
|
|
35
|
+
if ((a.y > p.y) != (b.y > p.y)) {
|
|
36
|
+
float x = a.x + (p.y-a.y) * edge.x / edge.y;
|
|
37
|
+
if (p.x < x) inside = !inside;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return inside ? clearance : -clearance;
|
|
41
|
+
}
|
|
42
|
+
`;
|
|
43
|
+
}
|
|
44
|
+
function F(d, y) {
|
|
45
|
+
const h = new e.ShapeGeometry(d).toNonIndexed(), w = h.getAttribute("position"), g = [], b = (s, n, r, o) => {
|
|
46
|
+
if (o < 6 && Math.max(s.distanceToSquared(n), n.distanceToSquared(r), r.distanceToSquared(s)) > 2.25) {
|
|
47
|
+
const l = s.clone().add(n).multiplyScalar(0.5), a = n.clone().add(r).multiplyScalar(0.5), f = r.clone().add(s).multiplyScalar(0.5);
|
|
48
|
+
b(s, l, f, o + 1), b(l, n, a, o + 1), b(f, a, r, o + 1), b(l, a, f, o + 1);
|
|
49
|
+
} else for (const l of [s, n, r]) g.push(l.x, l.y, y);
|
|
50
|
+
};
|
|
51
|
+
for (let s = 0; s < w.count; s += 3)
|
|
52
|
+
b(
|
|
53
|
+
new e.Vector3().fromBufferAttribute(w, s),
|
|
54
|
+
new e.Vector3().fromBufferAttribute(w, s + 1),
|
|
55
|
+
new e.Vector3().fromBufferAttribute(w, s + 2),
|
|
56
|
+
0
|
|
57
|
+
);
|
|
58
|
+
h.dispose();
|
|
59
|
+
const c = new e.BufferGeometry();
|
|
60
|
+
return c.setAttribute("position", new e.Float32BufferAttribute(g, 3)), c.computeVertexNormals(), T(c), c;
|
|
61
|
+
}
|
|
62
|
+
function X(d, y, h, w, g, b = !0) {
|
|
63
|
+
const c = new e.Group(), s = d.rings, n = new e.Shape(s[0].map((t) => new e.Vector2(...y(t))));
|
|
64
|
+
n.holes = s.slice(1).map((t) => new e.Path(t.map((i) => new e.Vector2(...y(i)))));
|
|
65
|
+
const r = d.water.depth, o = (t, i) => {
|
|
66
|
+
const m = new e.Mesh(t, i);
|
|
67
|
+
return m.userData.entityId = d.id, m.receiveShadow = !i.transparent, c.add(m), m;
|
|
68
|
+
}, l = w.get("tile", "#f2f3ef"), a = new e.ShapeGeometry(n);
|
|
69
|
+
a.translate(0, 0, h - r), T(a), o(a, l);
|
|
70
|
+
for (const t of s)
|
|
71
|
+
for (let i = 1; i < t.length; i++) {
|
|
72
|
+
const m = y(t[i - 1]), P = y(t[i]), W = Math.hypot(P[0] - m[0], P[1] - m[1]), G = new e.BoxGeometry(W, 0.15, r + 0.08);
|
|
73
|
+
G.rotateZ(Math.atan2(P[1] - m[1], P[0] - m[0])), G.translate((m[0] + P[0]) / 2, (m[1] + P[1]) / 2, h - r / 2 + 0.04), T(G), o(G, l);
|
|
74
|
+
}
|
|
75
|
+
if (!b) {
|
|
76
|
+
const t = new e.ShapeGeometry(n);
|
|
77
|
+
t.translate(0, 0, h - 0.08);
|
|
78
|
+
const i = o(
|
|
79
|
+
t,
|
|
80
|
+
new e.MeshBasicMaterial({
|
|
81
|
+
color: "#72bcc9",
|
|
82
|
+
transparent: !0,
|
|
83
|
+
opacity: 0.65,
|
|
84
|
+
depthWrite: !1,
|
|
85
|
+
side: e.DoubleSide
|
|
86
|
+
})
|
|
87
|
+
);
|
|
88
|
+
return i.userData.water = !0, i.renderOrder = 2, c;
|
|
89
|
+
}
|
|
90
|
+
const f = new e.MeshPhysicalMaterial({
|
|
91
|
+
color: "#d4f3ef",
|
|
92
|
+
roughness: 0.12,
|
|
93
|
+
metalness: 0,
|
|
94
|
+
transmission: 0.94,
|
|
95
|
+
thickness: r,
|
|
96
|
+
ior: 1.333,
|
|
97
|
+
attenuationColor: "#63bec5",
|
|
98
|
+
attenuationDistance: 12,
|
|
99
|
+
transparent: !0,
|
|
100
|
+
depthWrite: !1,
|
|
101
|
+
// Water is an open surface viewed from above, not a solid glass volume. DoubleSide makes
|
|
102
|
+
// Three render its back face into the transmission target and resolve mipmaps a second time.
|
|
103
|
+
side: e.FrontSide,
|
|
104
|
+
forceSinglePass: !0,
|
|
105
|
+
envMapIntensity: 0.8
|
|
106
|
+
}), x = s.map(E).flatMap((t) => t.slice(0, -1).map((i, m) => new e.Vector4(...y(i), ...y(t[m + 1])))), R = { value: 1 };
|
|
107
|
+
f.userData.refractionGuard = R, f.onBeforeCompile = (t) => {
|
|
108
|
+
t.uniforms.poolTime = g, t.uniforms.poolRipple = { value: d.water.ripple ?? 0.015 }, t.uniforms.poolEdges = { value: x }, t.uniforms.poolRefractionGuard = R, t.vertexShader = A + t.vertexShader.replace(
|
|
109
|
+
"#include <begin_vertex>",
|
|
110
|
+
`#include <begin_vertex>
|
|
111
|
+
poolXY = position.xy; transformed.z += poolWave(position.xy).x;`
|
|
112
|
+
), t.fragmentShader = A + D(x.length) + t.fragmentShader.replace(
|
|
113
|
+
"#include <normal_fragment_begin>",
|
|
114
|
+
`#include <normal_fragment_begin>
|
|
115
|
+
vec3 wave = poolWave(poolXY);
|
|
116
|
+
normal = normalize(mat3(viewMatrix) * vec3(-wave.y, -wave.z, 1.0)) * faceDirection;`
|
|
117
|
+
).replace(
|
|
118
|
+
"#include <transmission_pars_fragment>",
|
|
119
|
+
e.ShaderChunk.transmission_pars_fragment.replaceAll(
|
|
120
|
+
"vec4 ndcPos = projMatrix * viewMatrix * vec4( refractedRayExit, 1.0 );",
|
|
121
|
+
`
|
|
122
|
+
vec3 sight = refractedRayExit - cameraPosition;
|
|
123
|
+
// Test at the rim, slightly above the waterline, to include the retaining face.
|
|
124
|
+
float crossing = (position.z + 0.12 - cameraPosition.z) / min(sight.z, -0.00001);
|
|
125
|
+
vec2 entry = cameraPosition.xy + sight.xy * crossing;
|
|
126
|
+
float safeRefraction = smoothstep(0.12, 0.42, poolClearance(entry));
|
|
127
|
+
refractedRayExit = mix(position, refractedRayExit, mix(1.0, safeRefraction, poolRefractionGuard));
|
|
128
|
+
vec4 ndcPos = projMatrix * viewMatrix * vec4(refractedRayExit, 1.0);
|
|
129
|
+
`
|
|
130
|
+
)
|
|
131
|
+
);
|
|
132
|
+
}, f.customProgramCacheKey = () => `pool-water-v2-${x.length}`;
|
|
133
|
+
const V = o(F(n, h - 0.08), f);
|
|
134
|
+
V.userData.water = !0, V.renderOrder = 2;
|
|
135
|
+
const S = y(s[0][0]), p = y(s[0][1]), u = new e.Vector2(p[0] - S[0], p[1] - S[1]).normalize(), z = new e.Vector2(-u.y, u.x), M = new e.Vector2(S[0], S[1]).lerp(new e.Vector2(p[0], p[1]), 0.22), v = (t, i, m) => new e.Vector3(
|
|
136
|
+
M.x + u.x * t + z.x * i,
|
|
137
|
+
M.y + u.y * t + z.y * i,
|
|
138
|
+
h + m
|
|
139
|
+
), C = w.metal("#bfcfce");
|
|
140
|
+
for (const t of [-0.3, 0.3]) {
|
|
141
|
+
const i = new e.CatmullRomCurve3([
|
|
142
|
+
v(t, -0.5, 0.02),
|
|
143
|
+
v(t, -0.5, 0.7),
|
|
144
|
+
v(t, 0, 0.88),
|
|
145
|
+
v(t, 0.4, 0.6),
|
|
146
|
+
v(t, 0.4, -r + 0.18)
|
|
147
|
+
]);
|
|
148
|
+
o(new e.TubeGeometry(i, 32, 0.035, 8, !1), C);
|
|
149
|
+
}
|
|
150
|
+
for (let t = -0.15; t > -r + 0.2; t -= 0.28) {
|
|
151
|
+
const i = v(-0.3, 0.4, t), m = v(0.3, 0.4, t), P = new e.CylinderGeometry(0.027, 0.027, i.distanceTo(m), 8);
|
|
152
|
+
P.applyQuaternion(
|
|
153
|
+
new e.Quaternion().setFromUnitVectors(new e.Vector3(0, 1, 0), m.clone().sub(i).normalize())
|
|
154
|
+
), P.translate((i.x + m.x) / 2, (i.y + m.y) / 2, i.z), o(P, C);
|
|
155
|
+
}
|
|
156
|
+
return c;
|
|
157
|
+
}
|
|
158
|
+
function q(d, y, h, w) {
|
|
159
|
+
const g = d.slide, b = g.path.map(([p, u, z]) => {
|
|
160
|
+
const M = _([p, u], d.rotation), v = y([d.position[0] + M[0], d.position[1] + M[1]]);
|
|
161
|
+
return new e.Vector3(v[0], v[1], h + z + g.radius);
|
|
162
|
+
}), c = new e.CatmullRomCurve3(b, !1, "centripetal"), s = [], n = [], r = 96, o = 16;
|
|
163
|
+
for (let p = 0; p <= r; p++) {
|
|
164
|
+
const u = c.getPointAt(p / r), z = c.getTangentAt(p / r), M = new e.Vector3(-z.y, z.x, 0).normalize();
|
|
165
|
+
for (let v = 0; v <= o; v++) {
|
|
166
|
+
const C = Math.PI + Math.PI * v / o;
|
|
167
|
+
if (s.push(
|
|
168
|
+
u.x + M.x * g.radius * Math.cos(C),
|
|
169
|
+
u.y + M.y * g.radius * Math.cos(C),
|
|
170
|
+
u.z + g.radius * Math.sin(C)
|
|
171
|
+
), p < r && v < o) {
|
|
172
|
+
const t = p * (o + 1) + v, i = t + o + 1;
|
|
173
|
+
n.push(t, i, t + 1, i, i + 1, t + 1);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
const l = new e.BufferGeometry();
|
|
178
|
+
l.setAttribute("position", new e.Float32BufferAttribute(s, 3)), l.setIndex(n), l.computeVertexNormals(), T(l);
|
|
179
|
+
const a = new e.Group(), f = new e.Mesh(l, w.polished(w.get("tile", d.color ?? "#f2f3ef"), 0.22));
|
|
180
|
+
f.userData.entityId = d.id, f.castShadow = f.receiveShadow = !0, a.add(f);
|
|
181
|
+
const x = w.metal("#c6d5d6");
|
|
182
|
+
for (const p of [0, 0.25, 0.5, 0.75]) {
|
|
183
|
+
const u = c.getPointAt(p), z = u.z - h - g.radius;
|
|
184
|
+
if (z < 0.2) continue;
|
|
185
|
+
const M = new e.Mesh(new e.CylinderGeometry(0.1, 0.1, z, 8).rotateX(Math.PI / 2), x);
|
|
186
|
+
M.position.set(u.x, u.y, h + z / 2), a.add(M);
|
|
187
|
+
}
|
|
188
|
+
const R = b[0], V = R.z - h - g.radius, S = Math.ceil(V / 0.2);
|
|
189
|
+
for (let p = 1; p <= S; p++) {
|
|
190
|
+
const u = new e.Mesh(new e.BoxGeometry(1.1, 0.27, 0.12), w.get("tile", "#f2f3ef"));
|
|
191
|
+
u.position.set(R.x - g.radius - 0.7, R.y - (S - p) * 0.25, h + V * p / S), a.add(u);
|
|
192
|
+
}
|
|
193
|
+
return a;
|
|
194
|
+
}
|
|
195
|
+
function K(d, y, h, w, g, b) {
|
|
196
|
+
const c = B(y, h).map(({ room: n, pool: r, strength: o, height: l }) => {
|
|
197
|
+
const a = w(n.position), f = w([n.position[0] + 1, n.position[1]]), x = Math.hypot(f[0] - a[0], f[1] - a[1]);
|
|
198
|
+
return {
|
|
199
|
+
frame: new e.Vector4(a[0], a[1], (f[0] - a[0]) / x, (f[1] - a[1]) / x),
|
|
200
|
+
room: new e.Vector4(n.width / 2 + 0.25, n.depth / 2 + 0.25, g + l, o),
|
|
201
|
+
pool: r ? new e.Vector4(
|
|
202
|
+
r.position[0] - n.position[0],
|
|
203
|
+
r.position[1] - n.position[1],
|
|
204
|
+
r.width / 2,
|
|
205
|
+
r.depth / 2
|
|
206
|
+
) : new e.Vector4(0, 0, -1, -1)
|
|
207
|
+
};
|
|
208
|
+
});
|
|
209
|
+
if (!c.length) return;
|
|
210
|
+
const s = /* @__PURE__ */ new Map();
|
|
211
|
+
d.traverse((n) => {
|
|
212
|
+
if (!(n instanceof e.Mesh)) return;
|
|
213
|
+
const r = (o) => {
|
|
214
|
+
if (!(o instanceof e.MeshStandardMaterial) || o.userData.finish !== "tile") return o;
|
|
215
|
+
if (s.has(o)) return s.get(o);
|
|
216
|
+
const l = o.clone();
|
|
217
|
+
return l.userData = { ...o.userData, shared: !1, poolCaustics: !0 }, l.onBeforeCompile = (a, f) => {
|
|
218
|
+
o.onBeforeCompile(a, f), a.uniforms.bathTime = b, a.uniforms.bathFrames = { value: c.map((x) => x.frame) }, a.uniforms.bathRooms = { value: c.map((x) => x.room) }, a.uniforms.bathPools = { value: c.map((x) => x.pool) }, a.vertexShader = `varying vec3 bathWorld;
|
|
219
|
+
` + a.vertexShader.replace(
|
|
220
|
+
"#include <worldpos_vertex>",
|
|
221
|
+
`#include <worldpos_vertex>
|
|
222
|
+
bathWorld = (modelMatrix * vec4(transformed, 1.0)).xyz;`
|
|
223
|
+
), a.fragmentShader = `varying vec3 bathWorld; uniform float bathTime;
|
|
224
|
+
uniform vec4 bathFrames[${c.length}]; uniform vec4 bathRooms[${c.length}];
|
|
225
|
+
uniform vec4 bathPools[${c.length}];
|
|
226
|
+
` + a.fragmentShader.replace(
|
|
227
|
+
"#include <emissivemap_fragment>",
|
|
228
|
+
`
|
|
229
|
+
#include <emissivemap_fragment>
|
|
230
|
+
#ifdef USE_MAP
|
|
231
|
+
// Keep 15 cm tile joints legible at room scale. A 3 mm seam in a mipmapped image
|
|
232
|
+
// otherwise averages away several metres from the wall. Widen subpixel seams only
|
|
233
|
+
// while individual tiles remain resolvable, then fade to avoid distant moiré.
|
|
234
|
+
vec2 tileCell = vMapUv * vec2(4.0, 6.0);
|
|
235
|
+
vec2 tilePixel = max(fwidth(tileCell), vec2(0.0001));
|
|
236
|
+
vec2 tileEdge = min(fract(tileCell), 1.0 - fract(tileCell));
|
|
237
|
+
vec2 tileWidth = max(vec2(0.012), tilePixel * 0.35);
|
|
238
|
+
vec2 tileLine = 1.0 - smoothstep(tileWidth - tilePixel * 0.5, tileWidth + tilePixel * 0.5, tileEdge);
|
|
239
|
+
float grout = max(tileLine.x, tileLine.y) * (1.0 - smoothstep(0.35, 0.7, max(tilePixel.x, tilePixel.y)));
|
|
240
|
+
diffuseColor.rgb *= 1.0 - grout * 0.62;
|
|
241
|
+
#endif
|
|
242
|
+
float bathGlow = 0.0;
|
|
243
|
+
for (int i = 0; i < ${c.length}; i++) {
|
|
244
|
+
vec2 delta = bathWorld.xy - bathFrames[i].xy;
|
|
245
|
+
vec2 local = vec2(dot(delta, bathFrames[i].zw), dot(delta, vec2(-bathFrames[i].w, bathFrames[i].z)));
|
|
246
|
+
if (abs(local.x) > bathRooms[i].x || abs(local.y) > bathRooms[i].y) continue;
|
|
247
|
+
bool reflected = bathPools[i].z < 0.0;
|
|
248
|
+
vec2 outside = reflected ? vec2(0.0) : max(abs(local - bathPools[i].xy) - bathPools[i].zw, vec2(0.0));
|
|
249
|
+
float dz = bathWorld.z - bathRooms[i].z;
|
|
250
|
+
float falloff = 1.0 / (1.0 + 0.012 * (dot(outside, outside) + dz * dz));
|
|
251
|
+
// Two independent phases on vertical faces as well as horizontal ones: a flat XY
|
|
252
|
+
// projection stretches the caustics into neon stripes up every wall.
|
|
253
|
+
vec3 phase = vec3(local, bathWorld.z) * 7.0;
|
|
254
|
+
float a = sin(phase.x + phase.z * 0.91 + sin(phase.y * 0.81 + phase.z * 0.73 + bathTime * 0.6));
|
|
255
|
+
float b = sin(phase.y - phase.z * 1.13 - sin(phase.x * 0.93 + phase.z * 1.31 - bathTime * 0.47));
|
|
256
|
+
float focus = reflected ? 0.0 : pow(max(0.0, 1.0 - abs(a + b) * 0.75), 10.0);
|
|
257
|
+
// Diffuse reflected pool light carries the tiles; focused ripples modulate it.
|
|
258
|
+
float spread = 0.65 + 0.35 * sin(local.x * 0.27 + sin(local.y * 0.23) + bathTime * 0.18);
|
|
259
|
+
bathGlow += (0.65 + focus * 0.14 * spread / (1.0 + abs(dz) * 0.05)) * falloff * bathRooms[i].w;
|
|
260
|
+
}
|
|
261
|
+
totalEmissiveRadiance += vec3(0.40, 0.66, 1.0) * min(bathGlow, 1.8) * diffuseColor.rgb;
|
|
262
|
+
`
|
|
263
|
+
);
|
|
264
|
+
}, l.customProgramCacheKey = () => `${o.customProgramCacheKey()}/bath-caustics-${c.length}`, s.set(o, l), l;
|
|
265
|
+
};
|
|
266
|
+
n.material = Array.isArray(n.material) ? n.material.map(r) : r(n.material);
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
export {
|
|
270
|
+
K as applyPoolCaustics,
|
|
271
|
+
X as makePool,
|
|
272
|
+
q as makeWaterSlide
|
|
273
|
+
};
|
|
274
|
+
//# sourceMappingURL=water.js.map
|