@kerros/editor 0.0.0-stage → 0.1.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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 +52 -4
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"water.js","sources":["../../../../src/map/water.ts"],"sourcesContent":["import * as THREE from 'three';\nimport type { Point, SiteObject } from '../model/types';\nimport { closeRing, rotate } from '../model/geometry';\nimport type { MaterialLibrary } from './materials';\nimport { metricUVs } from './surfaces';\nimport { poolBounces } from './poolLighting';\n\n/** Original small-wave implementation informed by https://reakt.io/ocean.html:\n * sum directional Gerstner terms and differentiate them for normals. Pool wavelengths and\n * amplitudes are metres and centimetres; physical transmission supplies clear water and Fresnel. */\nconst waves = `\nuniform float poolTime;\nuniform float poolRipple;\nvarying vec2 poolXY;\nvec3 poolWave(vec2 p) {\n vec3 wave = vec3(0.0);\n for (int i = 0; i < 4; i++) {\n float n = float(i);\n float angle = n * 2.399 + 0.35;\n vec2 direction = vec2(cos(angle), sin(angle));\n float k = 2.6 + n * 2.3;\n float amplitude = poolRipple / (1.0 + n * 1.6);\n float phase = k * dot(direction, p) - sqrt(9.81 * k) * poolTime * 0.45;\n wave.x += amplitude * sin(phase);\n wave.yz += amplitude * k * cos(phase) * direction;\n }\n return wave;\n}\n`;\n\n/** Screen-space refraction has no knowledge of occluders: its exit pixel may actually show the\n * deck in front of the pool. Fade to the undistorted sample when that sight line crosses outside\n * the basin, including holes, instead of importing a camera-dependent patch of foreground tiles. */\nfunction refractionGuard(edgeCount: number) {\n return `\nuniform vec4 poolEdges[${edgeCount}];\nuniform float poolRefractionGuard;\nfloat poolClearance(vec2 p) {\n bool inside = false;\n float clearance = 1e6;\n for (int i = 0; i < ${edgeCount}; i++) {\n vec2 a = poolEdges[i].xy, b = poolEdges[i].zw, edge = b-a;\n float t = clamp(dot(p-a, edge) / max(dot(edge, edge), 1e-8), 0.0, 1.0);\n clearance = min(clearance, length(p-a-edge*t));\n if ((a.y > p.y) != (b.y > p.y)) {\n float x = a.x + (p.y-a.y) * edge.x / edge.y;\n if (p.x < x) inside = !inside;\n }\n }\n return inside ? clearance : -clearance;\n}\n`;\n}\n\n/** Tessellate the authored shape without moving its boundary or closing its holes. */\nfunction waveGeometry(shape: THREE.Shape, base: number): THREE.BufferGeometry {\n const flat = new THREE.ShapeGeometry(shape).toNonIndexed();\n const source = flat.getAttribute('position');\n const vertices: number[] = [];\n const split = (a: THREE.Vector3, b: THREE.Vector3, c: THREE.Vector3, depth: number) => {\n if (depth < 6 && Math.max(a.distanceToSquared(b), b.distanceToSquared(c), c.distanceToSquared(a)) > 2.25) {\n const ab = a.clone().add(b).multiplyScalar(0.5),\n bc = b.clone().add(c).multiplyScalar(0.5),\n ca = c.clone().add(a).multiplyScalar(0.5);\n split(a, ab, ca, depth + 1);\n split(ab, b, bc, depth + 1);\n split(ca, bc, c, depth + 1);\n split(ab, bc, ca, depth + 1);\n } else for (const p of [a, b, c]) vertices.push(p.x, p.y, base);\n };\n for (let i = 0; i < source.count; i += 3)\n split(\n new THREE.Vector3().fromBufferAttribute(source, i),\n new THREE.Vector3().fromBufferAttribute(source, i + 1),\n new THREE.Vector3().fromBufferAttribute(source, i + 2),\n 0,\n );\n flat.dispose();\n const geometry = new THREE.BufferGeometry();\n geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3));\n geometry.computeVertexNormals();\n metricUVs(geometry);\n return geometry;\n}\n\nexport function makePool(\n o: SiteObject,\n xy: (p: Point) => Point,\n base: number,\n materials: MaterialLibrary,\n time: { value: number },\n detailed = true,\n): THREE.Group {\n const group = new THREE.Group();\n const rings = o.rings!;\n const shape = new THREE.Shape(rings[0].map(p => new THREE.Vector2(...xy(p))));\n shape.holes = rings.slice(1).map(r => new THREE.Path(r.map(p => new THREE.Vector2(...xy(p)))));\n const depth = o.water!.depth;\n const add = (geometry: THREE.BufferGeometry, material: THREE.Material) => {\n const mesh = new THREE.Mesh(geometry, material);\n mesh.userData.entityId = o.id;\n mesh.receiveShadow = !material.transparent;\n group.add(mesh);\n return mesh;\n };\n const tile = materials.get('tile', '#f2f3ef');\n const bottom = new THREE.ShapeGeometry(shape);\n bottom.translate(0, 0, base - depth);\n metricUVs(bottom);\n add(bottom, tile);\n // Four or more tiled retaining faces follow the same outline as the water, including islands.\n for (const ring of rings)\n for (let i = 1; i < ring.length; i++) {\n const a = xy(ring[i - 1]),\n b = xy(ring[i]);\n const length = Math.hypot(b[0] - a[0], b[1] - a[1]);\n const wall = new THREE.BoxGeometry(length, 0.15, depth + 0.08);\n wall.rotateZ(Math.atan2(b[1] - a[1], b[0] - a[0]));\n wall.translate((a[0] + b[0]) / 2, (a[1] + b[1]) / 2, base - depth / 2 + 0.04);\n metricUVs(wall);\n add(wall, tile);\n }\n // Overview maps need the pool outline and depth, without an extra full-scene transmission pass,\n // subdivided waves or ladder draw calls. Walking rebuilds this group with the detailed surface.\n if (!detailed) {\n const flat = new THREE.ShapeGeometry(shape);\n flat.translate(0, 0, base - 0.08);\n const surface = add(\n flat,\n new THREE.MeshBasicMaterial({\n color: '#72bcc9',\n transparent: true,\n opacity: 0.65,\n depthWrite: false,\n side: THREE.DoubleSide,\n }),\n );\n surface.userData.water = true;\n surface.renderOrder = 2;\n return group;\n }\n const water = new THREE.MeshPhysicalMaterial({\n color: '#d4f3ef',\n roughness: 0.12,\n metalness: 0,\n transmission: 0.94,\n thickness: depth,\n ior: 1.333,\n attenuationColor: '#63bec5',\n attenuationDistance: 12,\n transparent: true,\n depthWrite: false,\n // Water is an open surface viewed from above, not a solid glass volume. DoubleSide makes\n // Three render its back face into the transmission target and resolve mipmaps a second time.\n side: THREE.FrontSide,\n forceSinglePass: true,\n envMapIntensity: 0.8,\n });\n const edges = rings\n .map(closeRing)\n .flatMap(ring => ring.slice(0, -1).map((a, i) => new THREE.Vector4(...xy(a), ...xy(ring[i + 1]))));\n const guard = { value: 1 };\n water.userData.refractionGuard = guard;\n water.onBeforeCompile = shader => {\n shader.uniforms.poolTime = time;\n shader.uniforms.poolRipple = { value: o.water!.ripple ?? 0.015 };\n shader.uniforms.poolEdges = { value: edges };\n shader.uniforms.poolRefractionGuard = guard;\n shader.vertexShader =\n waves +\n shader.vertexShader.replace(\n '#include <begin_vertex>',\n '#include <begin_vertex>\\npoolXY = position.xy; transformed.z += poolWave(position.xy).x;',\n );\n shader.fragmentShader =\n waves +\n refractionGuard(edges.length) +\n shader.fragmentShader\n .replace(\n '#include <normal_fragment_begin>',\n `#include <normal_fragment_begin>\n vec3 wave = poolWave(poolXY);\n normal = normalize(mat3(viewMatrix) * vec3(-wave.y, -wave.z, 1.0)) * faceDirection;`,\n )\n .replace(\n '#include <transmission_pars_fragment>',\n THREE.ShaderChunk.transmission_pars_fragment.replaceAll(\n 'vec4 ndcPos = projMatrix * viewMatrix * vec4( refractedRayExit, 1.0 );',\n `\n vec3 sight = refractedRayExit - cameraPosition;\n // Test at the rim, slightly above the waterline, to include the retaining face.\n float crossing = (position.z + 0.12 - cameraPosition.z) / min(sight.z, -0.00001);\n vec2 entry = cameraPosition.xy + sight.xy * crossing;\n float safeRefraction = smoothstep(0.12, 0.42, poolClearance(entry));\n refractedRayExit = mix(position, refractedRayExit, mix(1.0, safeRefraction, poolRefractionGuard));\n vec4 ndcPos = projMatrix * viewMatrix * vec4(refractedRayExit, 1.0);\n `,\n ),\n );\n };\n water.customProgramCacheKey = () => `pool-water-v2-${edges.length}`;\n const surface = add(waveGeometry(shape, base - 0.08), water);\n surface.userData.water = true;\n surface.renderOrder = 2;\n // Stainless rails curl over the edge; the rungs remain visible through the clear water.\n const a = xy(rings[0][0]),\n b = xy(rings[0][1]);\n const edge = new THREE.Vector2(b[0] - a[0], b[1] - a[1]).normalize();\n const inward = new THREE.Vector2(-edge.y, edge.x);\n const center = new THREE.Vector2(a[0], a[1]).lerp(new THREE.Vector2(b[0], b[1]), 0.22);\n const point = (side: number, inset: number, z: number) =>\n new THREE.Vector3(\n center.x + edge.x * side + inward.x * inset,\n center.y + edge.y * side + inward.y * inset,\n base + z,\n );\n const steel = materials.metal('#bfcfce');\n for (const side of [-0.3, 0.3]) {\n const curve = new THREE.CatmullRomCurve3([\n point(side, -0.5, 0.02),\n point(side, -0.5, 0.7),\n point(side, 0, 0.88),\n point(side, 0.4, 0.6),\n point(side, 0.4, -depth + 0.18),\n ]);\n add(new THREE.TubeGeometry(curve, 32, 0.035, 8, false), steel);\n }\n for (let z = -0.15; z > -depth + 0.2; z -= 0.28) {\n const left = point(-0.3, 0.4, z),\n right = point(0.3, 0.4, z);\n const rung = new THREE.CylinderGeometry(0.027, 0.027, left.distanceTo(right), 8);\n rung.applyQuaternion(\n new THREE.Quaternion().setFromUnitVectors(new THREE.Vector3(0, 1, 0), right.clone().sub(left).normalize()),\n );\n rung.translate((left.x + right.x) / 2, (left.y + right.y) / 2, left.z);\n add(rung, steel);\n }\n return group;\n}\n\nexport function makeWaterSlide(o: SiteObject, xy: (p: Point) => Point, base: number, materials: MaterialLibrary) {\n const slide = o.slide!;\n const points = slide.path.map(([x, y, z]) => {\n const p = rotate([x, y], o.rotation),\n at = xy([o.position[0] + p[0], o.position[1] + p[1]]);\n return new THREE.Vector3(at[0], at[1], base + z + slide.radius);\n });\n const curve = new THREE.CatmullRomCurve3(points, false, 'centripetal');\n const vertices: number[] = [],\n indices: number[] = [];\n const steps = 96,\n sides = 16;\n for (let i = 0; i <= steps; i++) {\n const p = curve.getPointAt(i / steps),\n tangent = curve.getTangentAt(i / steps);\n const side = new THREE.Vector3(-tangent.y, tangent.x, 0).normalize();\n for (let j = 0; j <= sides; j++) {\n const angle = Math.PI + (Math.PI * j) / sides;\n vertices.push(\n p.x + side.x * slide.radius * Math.cos(angle),\n p.y + side.y * slide.radius * Math.cos(angle),\n p.z + slide.radius * Math.sin(angle),\n );\n if (i < steps && j < sides) {\n const a = i * (sides + 1) + j,\n b = a + sides + 1;\n indices.push(a, b, a + 1, b, b + 1, a + 1);\n }\n }\n }\n const geometry = new THREE.BufferGeometry();\n geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3));\n geometry.setIndex(indices);\n geometry.computeVertexNormals();\n metricUVs(geometry);\n const group = new THREE.Group();\n const mesh = new THREE.Mesh(geometry, materials.polished(materials.get('tile', o.color ?? '#f2f3ef'), 0.22));\n mesh.userData.entityId = o.id;\n mesh.castShadow = mesh.receiveShadow = true;\n group.add(mesh);\n const support = materials.metal('#c6d5d6');\n for (const t of [0, 0.25, 0.5, 0.75]) {\n const p = curve.getPointAt(t),\n height = p.z - base - slide.radius;\n if (height < 0.2) continue;\n const post = new THREE.Mesh(new THREE.CylinderGeometry(0.1, 0.1, height, 8).rotateX(Math.PI / 2), support);\n post.position.set(p.x, p.y, base + height / 2);\n group.add(post);\n }\n // A compact access stair alongside the launch platform.\n const start = points[0],\n rise = start.z - base - slide.radius,\n count = Math.ceil(rise / 0.2);\n for (let i = 1; i <= count; i++) {\n const step = new THREE.Mesh(new THREE.BoxGeometry(1.1, 0.27, 0.12), materials.get('tile', '#f2f3ef'));\n step.position.set(start.x - slide.radius - 0.7, start.y - (count - i) * 0.25, base + (rise * i) / count);\n group.add(step);\n }\n return group;\n}\n\n/** Moving blue caustics from lit pools, restricted to their containing tiled chambers. This\n * inexpensive projection approximates the focused light from small ripples; it is not ray tracing. */\nexport function applyPoolCaustics(\n scene: THREE.Scene,\n project: import('../model/types').ProjectDocument,\n floorId: string,\n xy: (point: Point) => Point,\n base: number,\n time: { value: number },\n) {\n const sources = poolBounces(project, floorId).map(({ room, pool, strength, height }) => {\n const center = xy(room.position),\n end = xy([room.position[0] + 1, room.position[1]]);\n const length = Math.hypot(end[0] - center[0], end[1] - center[1]);\n return {\n frame: new THREE.Vector4(center[0], center[1], (end[0] - center[0]) / length, (end[1] - center[1]) / length),\n room: new THREE.Vector4(room.width / 2 + 0.25, room.depth / 2 + 0.25, base + height, strength),\n pool: pool\n ? new THREE.Vector4(\n pool.position[0] - room.position[0],\n pool.position[1] - room.position[1],\n pool.width / 2,\n pool.depth / 2,\n )\n : new THREE.Vector4(0, 0, -1, -1),\n };\n });\n if (!sources.length) return;\n const replacements = new Map<THREE.Material, THREE.MeshStandardMaterial>();\n scene.traverse(object => {\n if (!(object instanceof THREE.Mesh)) return;\n const replace = (source: THREE.Material) => {\n if (!(source instanceof THREE.MeshStandardMaterial) || source.userData.finish !== 'tile') return source;\n if (replacements.has(source)) return replacements.get(source)!;\n const material = source.clone();\n material.userData = { ...source.userData, shared: false, poolCaustics: true };\n material.onBeforeCompile = (shader, renderer) => {\n source.onBeforeCompile(shader, renderer);\n shader.uniforms.bathTime = time;\n shader.uniforms.bathFrames = { value: sources.map(s => s.frame) };\n shader.uniforms.bathRooms = { value: sources.map(s => s.room) };\n shader.uniforms.bathPools = { value: sources.map(s => s.pool) };\n shader.vertexShader =\n 'varying vec3 bathWorld;\\n' +\n shader.vertexShader.replace(\n '#include <worldpos_vertex>',\n '#include <worldpos_vertex>\\nbathWorld = (modelMatrix * vec4(transformed, 1.0)).xyz;',\n );\n shader.fragmentShader =\n `varying vec3 bathWorld; uniform float bathTime;\n uniform vec4 bathFrames[${sources.length}]; uniform vec4 bathRooms[${sources.length}];\n uniform vec4 bathPools[${sources.length}];\\n` +\n shader.fragmentShader.replace(\n '#include <emissivemap_fragment>',\n `\n #include <emissivemap_fragment>\n #ifdef USE_MAP\n // Keep 15 cm tile joints legible at room scale. A 3 mm seam in a mipmapped image\n // otherwise averages away several metres from the wall. Widen subpixel seams only\n // while individual tiles remain resolvable, then fade to avoid distant moiré.\n vec2 tileCell = vMapUv * vec2(4.0, 6.0);\n vec2 tilePixel = max(fwidth(tileCell), vec2(0.0001));\n vec2 tileEdge = min(fract(tileCell), 1.0 - fract(tileCell));\n vec2 tileWidth = max(vec2(0.012), tilePixel * 0.35);\n vec2 tileLine = 1.0 - smoothstep(tileWidth - tilePixel * 0.5, tileWidth + tilePixel * 0.5, tileEdge);\n float grout = max(tileLine.x, tileLine.y) * (1.0 - smoothstep(0.35, 0.7, max(tilePixel.x, tilePixel.y)));\n diffuseColor.rgb *= 1.0 - grout * 0.62;\n #endif\n float bathGlow = 0.0;\n for (int i = 0; i < ${sources.length}; i++) {\n vec2 delta = bathWorld.xy - bathFrames[i].xy;\n vec2 local = vec2(dot(delta, bathFrames[i].zw), dot(delta, vec2(-bathFrames[i].w, bathFrames[i].z)));\n if (abs(local.x) > bathRooms[i].x || abs(local.y) > bathRooms[i].y) continue;\n bool reflected = bathPools[i].z < 0.0;\n vec2 outside = reflected ? vec2(0.0) : max(abs(local - bathPools[i].xy) - bathPools[i].zw, vec2(0.0));\n float dz = bathWorld.z - bathRooms[i].z;\n float falloff = 1.0 / (1.0 + 0.012 * (dot(outside, outside) + dz * dz));\n // Two independent phases on vertical faces as well as horizontal ones: a flat XY\n // projection stretches the caustics into neon stripes up every wall.\n vec3 phase = vec3(local, bathWorld.z) * 7.0;\n float a = sin(phase.x + phase.z * 0.91 + sin(phase.y * 0.81 + phase.z * 0.73 + bathTime * 0.6));\n float b = sin(phase.y - phase.z * 1.13 - sin(phase.x * 0.93 + phase.z * 1.31 - bathTime * 0.47));\n float focus = reflected ? 0.0 : pow(max(0.0, 1.0 - abs(a + b) * 0.75), 10.0);\n // Diffuse reflected pool light carries the tiles; focused ripples modulate it.\n float spread = 0.65 + 0.35 * sin(local.x * 0.27 + sin(local.y * 0.23) + bathTime * 0.18);\n bathGlow += (0.65 + focus * 0.14 * spread / (1.0 + abs(dz) * 0.05)) * falloff * bathRooms[i].w;\n }\n totalEmissiveRadiance += vec3(0.40, 0.66, 1.0) * min(bathGlow, 1.8) * diffuseColor.rgb;\n `,\n );\n };\n material.customProgramCacheKey = () => `${source.customProgramCacheKey()}/bath-caustics-${sources.length}`;\n replacements.set(source, material);\n return material;\n };\n object.material = Array.isArray(object.material) ? object.material.map(replace) : replace(object.material);\n });\n}\n"],"names":["waves","refractionGuard","edgeCount","waveGeometry","shape","base","flat","THREE","source","vertices","split","a","b","c","depth","ab","bc","ca","p","i","geometry","metricUVs","makePool","o","xy","materials","time","detailed","group","rings","r","add","material","mesh","tile","bottom","ring","length","wall","surface","water","edges","closeRing","guard","shader","edge","inward","center","point","side","inset","z","steel","curve","left","right","rung","makeWaterSlide","slide","points","x","y","rotate","at","indices","steps","sides","tangent","j","angle","support","t","height","post","start","rise","count","step","applyPoolCaustics","scene","project","floorId","sources","poolBounces","room","pool","strength","end","replacements","object","replace","renderer","s"],"mappings":"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"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { objectArea as y, centroid as I, distance as g, MIN_SEGMENT as R, barrierStrokeIssue as M, addBarrier as G, barrierEnds as b, barrierJoinPoint as w, segmentProjection as N, intersects as P, openRing as x, closeRing as _ } from "./geometry.js";
|
|
2
|
+
import { inSpace as E } from "./spaces.js";
|
|
3
|
+
import { createObject as A } from "./factory.js";
|
|
4
|
+
import { divideSpaces as B } from "./inference.js";
|
|
5
|
+
import { normalizeBoundaries as O, boundaryRegionAt as T, derivedSpaceRings as D, bindSpaceToRegion as J, boundaryEdges as k } from "./boundaries.js";
|
|
6
|
+
import { GEOMETRY_EPS as S, MIN_RING_EDGE as $, JOIN_EPS as z } from "./precision.js";
|
|
7
|
+
function U(i, n) {
|
|
8
|
+
if (!i.length) return { points: [n] };
|
|
9
|
+
if (g(i.at(-1), n) < 1e-3) return { points: i };
|
|
10
|
+
for (let t = 0; t + 2 < i.length; t++)
|
|
11
|
+
if (P(i.at(-1), n, i[t], i[t + 1]))
|
|
12
|
+
return { points: i, error: "That edge crosses the outline. Choose another point, or undo the last point." };
|
|
13
|
+
return { points: [...i, n] };
|
|
14
|
+
}
|
|
15
|
+
function V(i, n, t, r, c = "wall") {
|
|
16
|
+
if (g(t, r) < R - 1e-6) return;
|
|
17
|
+
const f = M(i, t, r, n);
|
|
18
|
+
if (f) throw new Error(f);
|
|
19
|
+
const u = G(i, t, r, n, c);
|
|
20
|
+
return u && c === "wall" && B(i, n, ...b(i, u)), u;
|
|
21
|
+
}
|
|
22
|
+
function W(i, n, t, r, c = "wall") {
|
|
23
|
+
const f = c !== "boundary", u = f ? w(i, t, n) : t, s = f ? w(i, r, n) : r;
|
|
24
|
+
return g(u, s) < (f ? R - S : $) ? !1 : k(i).some((o) => {
|
|
25
|
+
if (o.floorId !== n) return !1;
|
|
26
|
+
const d = f && "kind" in o ? z : S;
|
|
27
|
+
return N(s, ...b(i, o)).distance <= d;
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
function X(i, n, t) {
|
|
31
|
+
O(i, n);
|
|
32
|
+
const r = T(i, n, t);
|
|
33
|
+
if (!r) return null;
|
|
34
|
+
const c = r.rings[0], f = i.objects.filter((e) => e.floorId === n && e.kind === "room" && E(e, t)).sort((e, a) => y(e) - y(a))[0], u = f ?? A("room", I(c), n, "Room"), s = { ...u, geometry: { mode: "boundaries", loops: r.loops } };
|
|
35
|
+
if (s.rings = D(i, s), !E(s, t)) return null;
|
|
36
|
+
const o = (e) => e.flat().map((a) => `${a.edgeId}:${!!a.reversed}`).sort().join("|"), d = i.objects.find(
|
|
37
|
+
(e) => {
|
|
38
|
+
var a;
|
|
39
|
+
return ((a = e.geometry) == null ? void 0 : a.mode) === "boundaries" && o(e.geometry.loops) === o(r.loops);
|
|
40
|
+
}
|
|
41
|
+
);
|
|
42
|
+
return d ? { room: d, existing: !0 } : (J(i, u, r), f || i.objects.push(u), { room: u, existing: !!f });
|
|
43
|
+
}
|
|
44
|
+
function C(i, n) {
|
|
45
|
+
for (const t of i.barriers) {
|
|
46
|
+
if (!n.has(t.startId) && !n.has(t.endId)) continue;
|
|
47
|
+
const r = g(...b(i, t)), c = i.objects.filter((s) => s.barrierId === t.id).sort((s, o) => (s.offset ?? 0) - (o.offset ?? 0));
|
|
48
|
+
let f = r;
|
|
49
|
+
for (const s of [...c].reverse())
|
|
50
|
+
s.offset = Math.min(s.offset ?? 0, f - s.width / 2), f = s.offset - s.width / 2;
|
|
51
|
+
let u = 0;
|
|
52
|
+
for (const s of c)
|
|
53
|
+
s.offset = Math.max(s.offset ?? 0, u + s.width / 2), u = s.offset + s.width / 2;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function Y(i, n) {
|
|
57
|
+
var c, f, u, s;
|
|
58
|
+
const t = /* @__PURE__ */ new Map();
|
|
59
|
+
let r;
|
|
60
|
+
if (n.kind === "junction") {
|
|
61
|
+
const o = i.junctions.find((d) => d.id === n.id);
|
|
62
|
+
if (!o) return;
|
|
63
|
+
t.set(o.id, [n.point[0] - o.position[0], n.point[1] - o.position[1]]);
|
|
64
|
+
} else if (n.kind === "barrier") {
|
|
65
|
+
const o = k(i).find((p) => p.id === n.id);
|
|
66
|
+
if (!o) return;
|
|
67
|
+
const [d, e] = b(i, o), a = g(d, e), l = -(e[1] - d[1]) / a, h = (e[0] - d[0]) / a, m = (n.point[0] - (d[0] + e[0]) / 2) * l + (n.point[1] - (d[1] + e[1]) / 2) * h;
|
|
68
|
+
for (const p of [o.startId, o.endId]) t.set(p, [l * m, h * m]);
|
|
69
|
+
} else {
|
|
70
|
+
const o = (f = (c = i.objects.find((d) => d.id === n.id)) == null ? void 0 : c.rings) == null ? void 0 : f[n.ringIndex];
|
|
71
|
+
if (((s = (u = i.objects.find((d) => d.id === n.id)) == null ? void 0 : u.geometry) == null ? void 0 : s.mode) === "boundaries" || (r = o && x(o)[n.index], !r)) return;
|
|
72
|
+
}
|
|
73
|
+
return (o, d) => {
|
|
74
|
+
for (const e of o.junctions) {
|
|
75
|
+
const a = t.get(e.id);
|
|
76
|
+
a && (e.position = [e.position[0] + a[0] * d, e.position[1] + a[1] * d]);
|
|
77
|
+
}
|
|
78
|
+
if (n.kind === "ring") {
|
|
79
|
+
const e = o.objects.find((l) => l.id === n.id), a = x(e.rings[n.ringIndex]);
|
|
80
|
+
a[n.index] = [
|
|
81
|
+
r[0] + (n.point[0] - r[0]) * d,
|
|
82
|
+
r[1] + (n.point[1] - r[1]) * d
|
|
83
|
+
], e.rings[n.ringIndex] = _(a), e.position = I(e.rings[0]);
|
|
84
|
+
} else
|
|
85
|
+
C(o, new Set(t.keys()));
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
function Z(i, n) {
|
|
89
|
+
var t;
|
|
90
|
+
(t = Y(i, n)) == null || t(i, 1);
|
|
91
|
+
}
|
|
92
|
+
export {
|
|
93
|
+
U as appendAreaPoint,
|
|
94
|
+
Z as applyGeometryDrag,
|
|
95
|
+
V as drawBarrier,
|
|
96
|
+
X as encloseRoom,
|
|
97
|
+
W as endsBoundaryStroke
|
|
98
|
+
};
|
|
99
|
+
//# sourceMappingURL=authoring.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authoring.js","sources":["../../../../src/model/authoring.ts"],"sourcesContent":["// Geometry gestures shared by the editor and the randomized authoring tests.\nimport {\n addBarrier,\n barrierEnds,\n barrierJoinPoint,\n barrierStrokeIssue,\n centroid,\n closeRing,\n distance,\n intersects,\n objectArea,\n openRing,\n segmentProjection,\n MIN_SEGMENT,\n} from './geometry';\nimport { inSpace } from './spaces';\nimport { createObject } from './factory';\nimport { divideSpaces } from './inference';\nimport type { Point, ProjectDocument, Ring } from './types';\nimport { transact } from './validate';\nimport { GEOMETRY_EPS, JOIN_EPS, MIN_RING_EDGE } from './precision';\nimport {\n bindSpaceToRegion,\n boundaryEdges,\n boundaryRegionAt,\n derivedSpaceRings,\n normalizeBoundaries,\n} from './boundaries';\n\n/** Ignore double-click duplicates; reject a crossing while the last good draft is still editable. */\nexport function appendAreaPoint(draft: Ring, point: Point): { points: Ring; error?: string } {\n if (!draft.length) return { points: [point] };\n if (distance(draft.at(-1)!, point) < 0.001) return { points: draft };\n for (let i = 0; i + 2 < draft.length; i++)\n if (intersects(draft.at(-1)!, point, draft[i], draft[i + 1]))\n return { points: draft, error: 'That edge crosses the outline. Choose another point, or undo the last point.' };\n return { points: [...draft, point] };\n}\n\n/** The wall/partition tool: divide along the endpoints actually used after welding. */\nexport function drawBarrier(\n project: ProjectDocument,\n floorId: string | null,\n a: Point,\n b: Point,\n kind: 'wall' | 'fence' = 'wall',\n) {\n if (distance(a, b) < MIN_SEGMENT - 1e-6) return;\n const issue = barrierStrokeIssue(project, a, b, floorId);\n if (issue) throw new Error(issue);\n const barrier = addBarrier(project, a, b, floorId, kind);\n if (barrier && kind === 'wall') divideSpaces(project, floorId, ...barrierEnds(project, barrier));\n return barrier;\n}\n\n/** A completed stroke stops chaining when its tip meets an existing boundary, including an\n * earlier segment of the same outline. Test before adding the segment: afterwards every tip\n * belongs to a boundary. Use the model's joining tolerance, not the pointer's snap reach. */\nexport function endsBoundaryStroke(\n project: ProjectDocument,\n floorId: string | null,\n a: Point,\n b: Point,\n kind: 'wall' | 'fence' | 'boundary' = 'wall',\n): boolean {\n const physical = kind !== 'boundary';\n const start = physical ? barrierJoinPoint(project, a, floorId) : a;\n const end = physical ? barrierJoinPoint(project, b, floorId) : b;\n if (distance(start, end) < (physical ? MIN_SEGMENT - GEOMETRY_EPS : MIN_RING_EDGE)) return false;\n return boundaryEdges(project).some(edge => {\n if (edge.floorId !== floorId) return false;\n const tolerance = physical && 'kind' in edge ? JOIN_EPS : GEOMETRY_EPS;\n return segmentProjection(end, ...barrierEnds(project, edge)).distance <= tolerance;\n });\n}\n\n/** The enclose tool: creating and re-fitting use exactly the same generated outline. */\nexport function encloseRoom(project: ProjectDocument, floorId: string | null, point: Point) {\n normalizeBoundaries(project, floorId);\n const region = boundaryRegionAt(project, floorId, point);\n if (!region) return null;\n const ring = region.rings[0];\n const existing = project.objects\n .filter(o => o.floorId === floorId && o.kind === 'room' && inSpace(o, point))\n .sort((a, b) => objectArea(a) - objectArea(b))[0];\n const room = existing ?? createObject('room', centroid(ring), floorId, 'Room');\n const probe = { ...room, geometry: { mode: 'boundaries' as const, loops: region.loops } };\n probe.rings = derivedSpaceRings(project, probe);\n if (!inSpace(probe, point)) return null;\n const key = (loops: import('./types').BoundaryUse[][]) =>\n loops\n .flat()\n .map(u => `${u.edgeId}:${!!u.reversed}`)\n .sort()\n .join('|');\n const same = project.objects.find(\n o => o.geometry?.mode === 'boundaries' && key(o.geometry.loops) === key(region.loops),\n );\n if (same) return { room: same, existing: true };\n bindSpaceToRegion(project, room, region);\n if (!existing) project.objects.push(room);\n return { room, existing: !!existing };\n}\n\nexport type GeometryDrag =\n | { kind: 'junction'; id: string; point: Point }\n | { kind: 'barrier'; id: string; point: Point }\n | { kind: 'ring'; id: string; point: Point; ringIndex: number; index: number };\n\n/** Keep ordered openings on a resized wall. Move them only as far as necessary to fit;\n * when their total width cannot fit, validation limits the wall movement instead. */\nfunction fitAttachedOpenings(project: ProjectDocument, changed: Set<string>) {\n for (const barrier of project.barriers) {\n if (!changed.has(barrier.startId) && !changed.has(barrier.endId)) continue;\n const length = distance(...barrierEnds(project, barrier));\n const openings = project.objects\n .filter(o => o.barrierId === barrier.id)\n .sort((a, b) => (a.offset ?? 0) - (b.offset ?? 0));\n let end = length;\n for (const o of [...openings].reverse()) {\n o.offset = Math.min(o.offset ?? 0, end - o.width / 2);\n end = o.offset - o.width / 2;\n }\n let start = 0;\n for (const o of openings) {\n o.offset = Math.max(o.offset ?? 0, start + o.width / 2);\n start = o.offset + o.width / 2;\n }\n }\n}\n\nfunction geometryMutation(project: ProjectDocument, move: GeometryDrag) {\n const deltas = new Map<string, Point>();\n let vertex: Point | undefined;\n if (move.kind === 'junction') {\n const j = project.junctions.find(j => j.id === move.id);\n if (!j) return;\n deltas.set(j.id, [move.point[0] - j.position[0], move.point[1] - j.position[1]]);\n } else if (move.kind === 'barrier') {\n const b = boundaryEdges(project).find(b => b.id === move.id);\n if (!b) return;\n const [a, c] = barrierEnds(project, b);\n const length = distance(a, c);\n const nx = -(c[1] - a[1]) / length,\n ny = (c[0] - a[0]) / length;\n const slide = (move.point[0] - (a[0] + c[0]) / 2) * nx + (move.point[1] - (a[1] + c[1]) / 2) * ny;\n for (const id of [b.startId, b.endId]) deltas.set(id, [nx * slide, ny * slide]);\n } else {\n const ring = project.objects.find(o => o.id === move.id)?.rings?.[move.ringIndex];\n if (project.objects.find(o => o.id === move.id)?.geometry?.mode === 'boundaries') return;\n vertex = ring && openRing(ring)[move.index];\n if (!vertex) return;\n }\n return (draft: ProjectDocument, fraction: number) => {\n for (const j of draft.junctions) {\n const delta = deltas.get(j.id);\n if (delta) j.position = [j.position[0] + delta[0] * fraction, j.position[1] + delta[1] * fraction];\n }\n if (move.kind === 'ring') {\n const o = draft.objects.find(o => o.id === move.id)!;\n const ring = openRing(o.rings![move.ringIndex]);\n ring[move.index] = [\n vertex![0] + (move.point[0] - vertex![0]) * fraction,\n vertex![1] + (move.point[1] - vertex![1]) * fraction,\n ];\n o.rings![move.ringIndex] = closeRing(ring);\n o.position = centroid(o.rings![0]);\n } else {\n fitAttachedOpenings(draft, new Set(deltas.keys()));\n }\n };\n}\n\n/** Apply one requested drop to a transaction draft. The editor previews snapping alone and calls\n * this once on release; transact either accepts the entire move or leaves the original intact. */\nexport function applyGeometryDrag(project: ProjectDocument, move: GeometryDrag) {\n geometryMutation(project, move)?.(project, 1);\n}\n\n/** Optional constrained authoring operation for hosts and scripted drawing: find a valid position\n * along a requested movement. Interactive editor drags use applyGeometryDrag on release instead. */\nexport function dragGeometry(project: ProjectDocument, _floorId: string | null, move: GeometryDrag): ProjectDocument {\n const mutation = geometryMutation(project, move);\n if (!mutation) return project;\n const trial = (fraction: number) => transact(project, draft => mutation(draft, fraction));\n const full = trial(1);\n if (full.ok) return full.project;\n let low = 0,\n high = 1,\n best = project;\n // Eighteen trials resolve even a 100 m gesture to less than a millimetre.\n for (let i = 0; i < 18; i++) {\n const mid = (low + high) / 2;\n const result = trial(mid);\n if (result.ok) {\n low = mid;\n best = result.project;\n } else high = mid;\n }\n return best;\n}\n"],"names":["appendAreaPoint","draft","point","distance","i","intersects","drawBarrier","project","floorId","a","b","kind","MIN_SEGMENT","issue","barrierStrokeIssue","barrier","addBarrier","divideSpaces","barrierEnds","endsBoundaryStroke","physical","start","barrierJoinPoint","end","GEOMETRY_EPS","MIN_RING_EDGE","boundaryEdges","edge","tolerance","JOIN_EPS","segmentProjection","encloseRoom","normalizeBoundaries","region","boundaryRegionAt","ring","existing","o","inSpace","objectArea","room","createObject","centroid","probe","derivedSpaceRings","key","loops","u","same","_a","bindSpaceToRegion","fitAttachedOpenings","changed","length","openings","geometryMutation","move","deltas","vertex","j","c","nx","ny","slide","id","_b","_d","_c","openRing","fraction","delta","closeRing","applyGeometryDrag"],"mappings":"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"}
|