@pascal-app/viewer 1.0.0-beta.4 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +107 -0
- package/dist/capture/asset-url.d.ts +2 -0
- package/dist/capture/asset-url.d.ts.map +1 -0
- package/dist/capture/asset-url.js +13 -0
- package/dist/capture/capture-runtime.d.ts +43 -0
- package/dist/capture/capture-runtime.d.ts.map +1 -0
- package/dist/capture/capture-runtime.js +228 -0
- package/dist/capture/frame.d.ts +4 -0
- package/dist/capture/frame.d.ts.map +1 -0
- package/dist/capture/frame.js +21 -0
- package/dist/capture/index.d.ts +12 -0
- package/dist/capture/index.d.ts.map +1 -0
- package/dist/capture/index.js +11 -0
- package/dist/capture/layer-visibility.d.ts +5 -0
- package/dist/capture/layer-visibility.d.ts.map +1 -0
- package/dist/capture/layer-visibility.js +11 -0
- package/dist/capture/layers/clay-matcap.d.ts +3 -0
- package/dist/capture/layers/clay-matcap.d.ts.map +1 -0
- package/dist/capture/layers/clay-matcap.js +26 -0
- package/dist/capture/layers/device-motion-layer.d.ts +7 -0
- package/dist/capture/layers/device-motion-layer.d.ts.map +1 -0
- package/dist/capture/layers/device-motion-layer.js +78 -0
- package/dist/capture/layers/point-cloud-layer.d.ts +15 -0
- package/dist/capture/layers/point-cloud-layer.d.ts.map +1 -0
- package/dist/capture/layers/point-cloud-layer.js +106 -0
- package/dist/capture/layers/room-model-layer.d.ts +9 -0
- package/dist/capture/layers/room-model-layer.d.ts.map +1 -0
- package/dist/capture/layers/room-model-layer.js +71 -0
- package/dist/capture/layers/surface-mesh-data.d.ts +9 -0
- package/dist/capture/layers/surface-mesh-data.d.ts.map +1 -0
- package/dist/capture/layers/surface-mesh-data.js +74 -0
- package/dist/capture/layers/surface-mesh-layer.d.ts +7 -0
- package/dist/capture/layers/surface-mesh-layer.d.ts.map +1 -0
- package/dist/capture/layers/surface-mesh-layer.js +16 -0
- package/dist/capture/preview.d.ts +3 -0
- package/dist/capture/preview.d.ts.map +1 -0
- package/dist/capture/preview.js +2 -0
- package/dist/capture/source-state.d.ts +22 -0
- package/dist/capture/source-state.d.ts.map +1 -0
- package/dist/capture/source-state.js +182 -0
- package/dist/capture/stream-rendering.d.ts +14 -0
- package/dist/capture/stream-rendering.d.ts.map +1 -0
- package/dist/capture/stream-rendering.js +69 -0
- package/dist/capture/trajectory.d.ts +20 -0
- package/dist/capture/trajectory.d.ts.map +1 -0
- package/dist/capture/trajectory.js +77 -0
- package/dist/components/viewer/batched-mesh-spike.d.ts +14 -0
- package/dist/components/viewer/batched-mesh-spike.d.ts.map +1 -0
- package/dist/components/viewer/batched-mesh-spike.js +64 -0
- package/dist/components/viewer/frame-limiter.d.ts +11 -0
- package/dist/components/viewer/frame-limiter.d.ts.map +1 -1
- package/dist/components/viewer/frame-limiter.js +59 -18
- package/dist/components/viewer/index.d.ts +15 -0
- package/dist/components/viewer/index.d.ts.map +1 -1
- package/dist/components/viewer/index.js +74 -82
- package/dist/components/viewer/lights.d.ts.map +1 -1
- package/dist/components/viewer/lights.js +123 -89
- package/dist/components/viewer/perf-monitor.d.ts +6 -1
- package/dist/components/viewer/perf-monitor.d.ts.map +1 -1
- package/dist/components/viewer/perf-monitor.js +306 -115
- package/dist/components/viewer/perf-panel.d.ts +3 -0
- package/dist/components/viewer/perf-panel.d.ts.map +1 -0
- package/dist/components/viewer/perf-panel.js +178 -0
- package/dist/components/viewer/pointer-raycast-layers.d.ts +15 -0
- package/dist/components/viewer/pointer-raycast-layers.d.ts.map +1 -0
- package/dist/components/viewer/pointer-raycast-layers.js +28 -0
- package/dist/components/viewer/post-processing.d.ts.map +1 -1
- package/dist/components/viewer/post-processing.js +124 -83
- package/dist/components/viewer/registered-systems.d.ts +14 -5
- package/dist/components/viewer/registered-systems.d.ts.map +1 -1
- package/dist/components/viewer/registered-systems.js +22 -10
- package/dist/components/viewer/scene-atmosphere.d.ts +30 -0
- package/dist/components/viewer/scene-atmosphere.d.ts.map +1 -0
- package/dist/components/viewer/scene-atmosphere.js +89 -0
- package/dist/components/viewer/scene-environment.d.ts.map +1 -1
- package/dist/components/viewer/scene-environment.js +19 -5
- package/dist/components/viewer/scene-ground-replacement.d.ts +5 -0
- package/dist/components/viewer/scene-ground-replacement.d.ts.map +1 -0
- package/dist/components/viewer/scene-ground-replacement.js +35 -0
- package/dist/components/viewer/selection-manager.d.ts.map +1 -1
- package/dist/components/viewer/selection-manager.js +7 -2
- package/dist/components/viewer/unsupported-gpu-fallback.d.ts +2 -0
- package/dist/components/viewer/unsupported-gpu-fallback.d.ts.map +1 -0
- package/dist/components/viewer/unsupported-gpu-fallback.js +4 -0
- package/dist/components/viewer/viewer-camera.d.ts.map +1 -1
- package/dist/components/viewer/viewer-camera.js +4 -1
- package/dist/components/viewer/viewer-presentations.d.ts +67 -0
- package/dist/components/viewer/viewer-presentations.d.ts.map +1 -0
- package/dist/components/viewer/viewer-presentations.js +120 -0
- package/dist/hooks/use-library-materials-version.d.ts +7 -0
- package/dist/hooks/use-library-materials-version.d.ts.map +1 -0
- package/dist/hooks/use-library-materials-version.js +10 -0
- package/dist/hooks/use-node-events.d.ts.map +1 -1
- package/dist/hooks/use-node-events.js +1 -0
- package/dist/index.d.ts +20 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +19 -7
- package/dist/lib/csg-utils.d.ts +1 -0
- package/dist/lib/csg-utils.d.ts.map +1 -1
- package/dist/lib/csg-utils.js +6 -1
- package/dist/lib/dispose-object3d.d.ts +4 -0
- package/dist/lib/dispose-object3d.d.ts.map +1 -0
- package/dist/lib/dispose-object3d.js +27 -0
- package/dist/lib/geometry-groups.d.ts +17 -0
- package/dist/lib/geometry-groups.d.ts.map +1 -0
- package/dist/lib/geometry-groups.js +53 -0
- package/dist/lib/gpu-perf.d.ts +0 -2
- package/dist/lib/gpu-perf.d.ts.map +1 -1
- package/dist/lib/gpu-perf.js +7 -21
- package/dist/lib/isolation.d.ts +8 -8
- package/dist/lib/isolation.d.ts.map +1 -1
- package/dist/lib/isolation.js +18 -30
- package/dist/lib/layer-pass.d.ts +28 -0
- package/dist/lib/layer-pass.d.ts.map +1 -0
- package/dist/lib/layer-pass.js +254 -0
- package/dist/lib/layers.d.ts +16 -0
- package/dist/lib/layers.d.ts.map +1 -1
- package/dist/lib/layers.js +18 -0
- package/dist/lib/materials.d.ts +12 -0
- package/dist/lib/materials.d.ts.map +1 -1
- package/dist/lib/materials.js +51 -17
- package/dist/lib/merged-outline-node.d.ts +17 -8
- package/dist/lib/merged-outline-node.d.ts.map +1 -1
- package/dist/lib/merged-outline-node.js +194 -52
- package/dist/lib/perf-actions.d.ts +55 -0
- package/dist/lib/perf-actions.d.ts.map +1 -0
- package/dist/lib/perf-actions.js +211 -0
- package/dist/lib/perf-observers.d.ts +2 -0
- package/dist/lib/perf-observers.d.ts.map +1 -0
- package/dist/lib/perf-observers.js +17 -0
- package/dist/lib/perf-panel-store.d.ts +71 -0
- package/dist/lib/perf-panel-store.d.ts.map +1 -0
- package/dist/lib/perf-panel-store.js +29 -0
- package/dist/lib/perf-tracks.d.ts +43 -0
- package/dist/lib/perf-tracks.d.ts.map +1 -0
- package/dist/lib/perf-tracks.js +113 -0
- package/dist/lib/pointer-events.d.ts +7 -0
- package/dist/lib/pointer-events.d.ts.map +1 -0
- package/dist/lib/pointer-events.js +680 -0
- package/dist/lib/post-processing-resources.d.ts +12 -0
- package/dist/lib/post-processing-resources.d.ts.map +1 -0
- package/dist/lib/post-processing-resources.js +19 -0
- package/dist/lib/renderer-capability.d.ts +56 -0
- package/dist/lib/renderer-capability.d.ts.map +1 -0
- package/dist/lib/renderer-capability.js +116 -0
- package/dist/lib/scene-visibility.d.ts +15 -0
- package/dist/lib/scene-visibility.d.ts.map +1 -0
- package/dist/lib/scene-visibility.js +69 -0
- package/dist/lib/shadow-only.d.ts +16 -0
- package/dist/lib/shadow-only.d.ts.map +1 -1
- package/dist/lib/shadow-only.js +8 -17
- package/dist/lib/snapshot-pipeline.d.ts +9 -0
- package/dist/lib/snapshot-pipeline.d.ts.map +1 -1
- package/dist/lib/snapshot-pipeline.js +33 -9
- package/dist/store/use-viewer.d.ts +7 -6
- package/dist/store/use-viewer.d.ts.map +1 -1
- package/dist/systems/ceiling/ceiling-system.js +1 -1
- package/dist/systems/door/door-animation-system.d.ts.map +1 -1
- package/dist/systems/door/door-animation-system.js +6 -9
- package/dist/systems/door/door-system.d.ts.map +1 -1
- package/dist/systems/door/door-system.js +13 -2
- package/dist/systems/floor-elevation/floor-elevation-system.d.ts.map +1 -1
- package/dist/systems/floor-elevation/floor-elevation-system.js +9 -0
- package/dist/systems/geometry/geometry-system.d.ts.map +1 -1
- package/dist/systems/geometry/geometry-system.js +5 -28
- package/dist/systems/level/level-system.d.ts.map +1 -1
- package/dist/systems/level/level-system.js +8 -2
- package/dist/systems/level/level-utils.d.ts +9 -0
- package/dist/systems/level/level-utils.d.ts.map +1 -1
- package/dist/systems/level/level-utils.js +14 -0
- package/dist/systems/perf-action-settle/perf-action-settle-system.d.ts +8 -0
- package/dist/systems/perf-action-settle/perf-action-settle-system.d.ts.map +1 -0
- package/dist/systems/perf-action-settle/perf-action-settle-system.js +32 -0
- package/dist/systems/roof/roof-system.d.ts.map +1 -1
- package/dist/systems/roof/roof-system.js +1196 -58
- package/dist/systems/scan/scan-system.js +6 -5
- package/dist/systems/slab/slab-system.d.ts.map +1 -1
- package/dist/systems/slab/slab-system.js +1 -2
- package/dist/systems/stair/stair-system.d.ts.map +1 -1
- package/dist/systems/stair/stair-system.js +9 -8
- package/dist/systems/surface-hole-geometry.d.ts +1 -1
- package/dist/systems/surface-hole-geometry.d.ts.map +1 -1
- package/dist/systems/surface-hole-geometry.js +1 -1
- package/dist/systems/wall/level-miter-cache.d.ts +10 -0
- package/dist/systems/wall/level-miter-cache.d.ts.map +1 -0
- package/dist/systems/wall/level-miter-cache.js +40 -0
- package/dist/systems/wall/wall-build-lifecycle.d.ts +9 -0
- package/dist/systems/wall/wall-build-lifecycle.d.ts.map +1 -0
- package/dist/systems/wall/wall-build-lifecycle.js +74 -0
- package/dist/systems/wall/wall-cutout-cache.d.ts +57 -0
- package/dist/systems/wall/wall-cutout-cache.d.ts.map +1 -0
- package/dist/systems/wall/wall-cutout-cache.js +304 -0
- package/dist/systems/wall/wall-cutout.d.ts +24 -1
- package/dist/systems/wall/wall-cutout.d.ts.map +1 -1
- package/dist/systems/wall/wall-cutout.js +33 -140
- package/dist/systems/wall/wall-material-variant.d.ts +31 -0
- package/dist/systems/wall/wall-material-variant.d.ts.map +1 -0
- package/dist/systems/wall/wall-material-variant.js +10 -0
- package/dist/systems/wall/wall-materials.d.ts +6 -0
- package/dist/systems/wall/wall-materials.d.ts.map +1 -1
- package/dist/systems/wall/wall-materials.js +29 -13
- package/dist/systems/wall/wall-placeholder-sweep.d.ts +50 -0
- package/dist/systems/wall/wall-placeholder-sweep.d.ts.map +1 -0
- package/dist/systems/wall/wall-placeholder-sweep.js +51 -0
- package/dist/systems/wall/wall-rebuild-notifications.d.ts +5 -0
- package/dist/systems/wall/wall-rebuild-notifications.d.ts.map +1 -0
- package/dist/systems/wall/wall-rebuild-notifications.js +23 -0
- package/dist/systems/wall/wall-system.d.ts +13 -2
- package/dist/systems/wall/wall-system.d.ts.map +1 -1
- package/dist/systems/wall/wall-system.js +413 -148
- package/dist/systems/window/window-animation-system.d.ts.map +1 -1
- package/dist/systems/window/window-animation-system.js +9 -8
- package/dist/systems/window/window-system.d.ts +1 -0
- package/dist/systems/window/window-system.d.ts.map +1 -1
- package/dist/systems/window/window-system.js +16 -2
- package/package.json +18 -3
- package/dist/components/viewer/scene-bvh.test.d.ts +0 -2
- package/dist/components/viewer/scene-bvh.test.d.ts.map +0 -1
- package/dist/components/viewer/scene-bvh.test.js +0 -13
- package/dist/lib/csg-utils.test.d.ts +0 -2
- package/dist/lib/csg-utils.test.d.ts.map +0 -1
- package/dist/lib/csg-utils.test.js +0 -45
- package/dist/lib/materials.test.d.ts +0 -2
- package/dist/lib/materials.test.d.ts.map +0 -1
- package/dist/lib/materials.test.js +0 -25
- package/dist/lib/merged-outline-node.test.d.ts +0 -2
- package/dist/lib/merged-outline-node.test.d.ts.map +0 -1
- package/dist/lib/merged-outline-node.test.js +0 -22
- package/dist/lib/polygon-union.d.ts +0 -4
- package/dist/lib/polygon-union.d.ts.map +0 -1
- package/dist/lib/polygon-union.js +0 -289
- package/dist/lib/polygon-union.test.d.ts +0 -2
- package/dist/lib/polygon-union.test.d.ts.map +0 -1
- package/dist/lib/polygon-union.test.js +0 -105
- package/dist/lib/texture-reference.test.d.ts +0 -2
- package/dist/lib/texture-reference.test.d.ts.map +0 -1
- package/dist/lib/texture-reference.test.js +0 -86
- package/dist/store/use-viewer.test.d.ts +0 -2
- package/dist/store/use-viewer.test.d.ts.map +0 -1
- package/dist/store/use-viewer.test.js +0 -61
- package/dist/systems/door/door-animation.test.d.ts +0 -2
- package/dist/systems/door/door-animation.test.d.ts.map +0 -1
- package/dist/systems/door/door-animation.test.js +0 -51
- package/dist/systems/door/door-floor-alignment.test.d.ts +0 -2
- package/dist/systems/door/door-floor-alignment.test.d.ts.map +0 -1
- package/dist/systems/door/door-floor-alignment.test.js +0 -50
- package/dist/systems/geometry/geometry-system.test.d.ts +0 -2
- package/dist/systems/geometry/geometry-system.test.d.ts.map +0 -1
- package/dist/systems/geometry/geometry-system.test.js +0 -30
- package/dist/systems/slab/slab-system.test.d.ts +0 -2
- package/dist/systems/slab/slab-system.test.d.ts.map +0 -1
- package/dist/systems/slab/slab-system.test.js +0 -102
- package/dist/systems/wall/opening-cutout-geometry.test.d.ts +0 -2
- package/dist/systems/wall/opening-cutout-geometry.test.d.ts.map +0 -1
- package/dist/systems/wall/opening-cutout-geometry.test.js +0 -172
- package/dist/systems/wall/wall-opening-cutout.test.d.ts +0 -2
- package/dist/systems/wall/wall-opening-cutout.test.d.ts.map +0 -1
- package/dist/systems/wall/wall-opening-cutout.test.js +0 -59
- package/dist/systems/wall/wall-support-extension.test.d.ts +0 -2
- package/dist/systems/wall/wall-support-extension.test.d.ts.map +0 -1
- package/dist/systems/wall/wall-support-extension.test.js +0 -116
- package/dist/systems/wall/wall-terrain-bottom.test.d.ts +0 -2
- package/dist/systems/wall/wall-terrain-bottom.test.d.ts.map +0 -1
- package/dist/systems/wall/wall-terrain-bottom.test.js +0 -34
|
@@ -1,172 +0,0 @@
|
|
|
1
|
-
// @ts-expect-error — bun:test is provided by the Bun runtime; viewer does not
|
|
2
|
-
// depend on @types/bun so the import type is unresolved at compile time.
|
|
3
|
-
import { describe, expect, test } from 'bun:test';
|
|
4
|
-
import { DoorNode, WindowNode } from '@pascal-app/core';
|
|
5
|
-
import { buildOpeningCutoutGeometry, buildOpeningCutoutShape, getOpeningCutoutBottomPadding, hasFlatOpeningCutoutBottom, } from './opening-cutout-geometry';
|
|
6
|
-
function containsPoint(points, x, y) {
|
|
7
|
-
return points.some((point) => Math.abs(point.x - x) < 1e-6 && Math.abs(point.y - y) < 1e-6);
|
|
8
|
-
}
|
|
9
|
-
function getBounds(points) {
|
|
10
|
-
let minX = Number.POSITIVE_INFINITY;
|
|
11
|
-
let maxX = Number.NEGATIVE_INFINITY;
|
|
12
|
-
let minY = Number.POSITIVE_INFINITY;
|
|
13
|
-
let maxY = Number.NEGATIVE_INFINITY;
|
|
14
|
-
for (const point of points) {
|
|
15
|
-
minX = Math.min(minX, point.x);
|
|
16
|
-
maxX = Math.max(maxX, point.x);
|
|
17
|
-
minY = Math.min(minY, point.y);
|
|
18
|
-
maxY = Math.max(maxY, point.y);
|
|
19
|
-
}
|
|
20
|
-
return { minX, maxX, minY, maxY };
|
|
21
|
-
}
|
|
22
|
-
describe('buildOpeningCutoutShape', () => {
|
|
23
|
-
test('rectangle profile passes the rect through unchanged', () => {
|
|
24
|
-
const door = DoorNode.parse({});
|
|
25
|
-
const rect = { left: 1.2, right: 2.1, bottom: 0, top: 2.1 };
|
|
26
|
-
const points = buildOpeningCutoutShape(door, rect).getPoints();
|
|
27
|
-
expect(containsPoint(points, rect.left, rect.bottom)).toBe(true);
|
|
28
|
-
expect(containsPoint(points, rect.right, rect.bottom)).toBe(true);
|
|
29
|
-
expect(containsPoint(points, rect.right, rect.top)).toBe(true);
|
|
30
|
-
expect(containsPoint(points, rect.left, rect.top)).toBe(true);
|
|
31
|
-
const bounds = getBounds(points);
|
|
32
|
-
expect(bounds.minX).toBeCloseTo(rect.left, 9);
|
|
33
|
-
expect(bounds.maxX).toBeCloseTo(rect.right, 9);
|
|
34
|
-
expect(bounds.minY).toBeCloseTo(rect.bottom, 9);
|
|
35
|
-
expect(bounds.maxY).toBeCloseTo(rect.top, 9);
|
|
36
|
-
});
|
|
37
|
-
test('door rounded profile rounds only the top corners', () => {
|
|
38
|
-
const door = DoorNode.parse({ openingShape: 'rounded', cornerRadius: 0.2 });
|
|
39
|
-
const rect = { left: -0.45, right: 0.45, bottom: 0, top: 2.1 };
|
|
40
|
-
const points = buildOpeningCutoutShape(door, rect).getPoints();
|
|
41
|
-
expect(containsPoint(points, rect.left, rect.bottom)).toBe(true);
|
|
42
|
-
expect(containsPoint(points, rect.right, rect.bottom)).toBe(true);
|
|
43
|
-
expect(containsPoint(points, rect.left, rect.top)).toBe(false);
|
|
44
|
-
expect(containsPoint(points, rect.right, rect.top)).toBe(false);
|
|
45
|
-
expect(containsPoint(points, rect.left, rect.top - 0.2)).toBe(true);
|
|
46
|
-
expect(containsPoint(points, rect.left + 0.2, rect.top)).toBe(true);
|
|
47
|
-
expect(containsPoint(points, rect.right, rect.top - 0.2)).toBe(true);
|
|
48
|
-
expect(containsPoint(points, rect.right - 0.2, rect.top)).toBe(true);
|
|
49
|
-
});
|
|
50
|
-
test('window rounded profile rounds all four corners', () => {
|
|
51
|
-
const window = WindowNode.parse({ openingShape: 'rounded', cornerRadius: 0.2 });
|
|
52
|
-
const rect = { left: -0.75, right: 0.75, bottom: 0.9, top: 2.4 };
|
|
53
|
-
const points = buildOpeningCutoutShape(window, rect).getPoints();
|
|
54
|
-
expect(containsPoint(points, rect.left, rect.bottom)).toBe(false);
|
|
55
|
-
expect(containsPoint(points, rect.right, rect.bottom)).toBe(false);
|
|
56
|
-
expect(containsPoint(points, rect.left, rect.top)).toBe(false);
|
|
57
|
-
expect(containsPoint(points, rect.right, rect.top)).toBe(false);
|
|
58
|
-
expect(containsPoint(points, rect.left + 0.2, rect.bottom)).toBe(true);
|
|
59
|
-
expect(containsPoint(points, rect.left, rect.bottom + 0.2)).toBe(true);
|
|
60
|
-
expect(containsPoint(points, rect.right - 0.2, rect.top)).toBe(true);
|
|
61
|
-
expect(containsPoint(points, rect.right, rect.top - 0.2)).toBe(true);
|
|
62
|
-
const bounds = getBounds(points);
|
|
63
|
-
expect(bounds.minX).toBeCloseTo(rect.left, 9);
|
|
64
|
-
expect(bounds.maxX).toBeCloseTo(rect.right, 9);
|
|
65
|
-
expect(bounds.minY).toBeCloseTo(rect.bottom, 9);
|
|
66
|
-
expect(bounds.maxY).toBeCloseTo(rect.top, 9);
|
|
67
|
-
});
|
|
68
|
-
test('shared corner radius is clamped to the opening half-extent', () => {
|
|
69
|
-
const window = WindowNode.parse({ openingShape: 'rounded', cornerRadius: 10 });
|
|
70
|
-
const rect = { left: -0.75, right: 0.75, bottom: 0.9, top: 2.4 };
|
|
71
|
-
const points = buildOpeningCutoutShape(window, rect).getPoints();
|
|
72
|
-
// 1.5 × 1.5 opening → radius clamps to 0.75; arcs meet at edge midpoints.
|
|
73
|
-
expect(containsPoint(points, rect.left, rect.bottom + 0.75)).toBe(true);
|
|
74
|
-
expect(containsPoint(points, rect.right, rect.top - 0.75)).toBe(true);
|
|
75
|
-
const bounds = getBounds(points);
|
|
76
|
-
expect(bounds.minX).toBeCloseTo(rect.left, 9);
|
|
77
|
-
expect(bounds.maxX).toBeCloseTo(rect.right, 9);
|
|
78
|
-
});
|
|
79
|
-
test('individual radii normalize when their sum exceeds the opening width', () => {
|
|
80
|
-
const window = WindowNode.parse({
|
|
81
|
-
openingShape: 'rounded',
|
|
82
|
-
openingRadiusMode: 'individual',
|
|
83
|
-
openingCornerRadii: [4, 4, 0, 0],
|
|
84
|
-
});
|
|
85
|
-
const rect = { left: -0.5, right: 0.5, bottom: 0, top: 2 };
|
|
86
|
-
const points = buildOpeningCutoutShape(window, rect).getPoints();
|
|
87
|
-
// Width 1 with top radii summing to 8 → scaled down to 0.5 each.
|
|
88
|
-
expect(containsPoint(points, rect.left, rect.top - 0.5)).toBe(true);
|
|
89
|
-
expect(containsPoint(points, rect.left + 0.5, rect.top)).toBe(true);
|
|
90
|
-
expect(containsPoint(points, rect.left, rect.bottom)).toBe(true);
|
|
91
|
-
expect(containsPoint(points, rect.right, rect.bottom)).toBe(true);
|
|
92
|
-
});
|
|
93
|
-
test('arch profile springs at top - archHeight and peaks at the rect top', () => {
|
|
94
|
-
const door = DoorNode.parse({ openingShape: 'arch', archHeight: 0.45 });
|
|
95
|
-
const rect = { left: -0.45, right: 0.45, bottom: 0, top: 2.1 };
|
|
96
|
-
const points = buildOpeningCutoutShape(door, rect).getPoints();
|
|
97
|
-
expect(containsPoint(points, rect.left, rect.bottom)).toBe(true);
|
|
98
|
-
expect(containsPoint(points, rect.right, rect.bottom)).toBe(true);
|
|
99
|
-
expect(containsPoint(points, rect.right, rect.top - 0.45)).toBe(true);
|
|
100
|
-
expect(containsPoint(points, 0, rect.top)).toBe(true);
|
|
101
|
-
expect(containsPoint(points, rect.left, rect.top)).toBe(false);
|
|
102
|
-
expect(containsPoint(points, rect.right, rect.top)).toBe(false);
|
|
103
|
-
});
|
|
104
|
-
test('profiles are origin-agnostic — offset rects yield translated points', () => {
|
|
105
|
-
const window = WindowNode.parse({ openingShape: 'rounded', cornerRadius: 0.2 });
|
|
106
|
-
const centered = buildOpeningCutoutShape(window, {
|
|
107
|
-
left: -0.75,
|
|
108
|
-
right: 0.75,
|
|
109
|
-
bottom: -0.75,
|
|
110
|
-
top: 0.75,
|
|
111
|
-
}).getPoints();
|
|
112
|
-
const offset = buildOpeningCutoutShape(window, {
|
|
113
|
-
left: 2.25,
|
|
114
|
-
right: 3.75,
|
|
115
|
-
bottom: 0.9,
|
|
116
|
-
top: 2.4,
|
|
117
|
-
}).getPoints();
|
|
118
|
-
for (const point of centered) {
|
|
119
|
-
expect(containsPoint(offset, point.x + 3, point.y + 1.65)).toBe(true);
|
|
120
|
-
}
|
|
121
|
-
for (const point of offset) {
|
|
122
|
-
expect(containsPoint(centered, point.x - 3, point.y - 1.65)).toBe(true);
|
|
123
|
-
}
|
|
124
|
-
});
|
|
125
|
-
});
|
|
126
|
-
describe('buildOpeningCutoutGeometry', () => {
|
|
127
|
-
test('extrudes the rect through the depth, centered on the mid-plane', () => {
|
|
128
|
-
const door = DoorNode.parse({});
|
|
129
|
-
const geometry = buildOpeningCutoutGeometry(door, { left: -0.45, right: 0.45, bottom: -1.05, top: 1.05 }, 0.24, 0.1);
|
|
130
|
-
geometry.computeBoundingBox();
|
|
131
|
-
// Float32 position buffer → ~1e-7 relative precision.
|
|
132
|
-
const box = geometry.boundingBox;
|
|
133
|
-
expect(box.min.x).toBeCloseTo(-0.45, 6);
|
|
134
|
-
expect(box.max.x).toBeCloseTo(0.45, 6);
|
|
135
|
-
expect(box.min.y).toBeCloseTo(-1.05, 6);
|
|
136
|
-
expect(box.max.y).toBeCloseTo(1.05, 6);
|
|
137
|
-
expect(box.min.z).toBeCloseTo(-0.12, 6);
|
|
138
|
-
expect(box.max.z).toBeCloseTo(0.12, 6);
|
|
139
|
-
});
|
|
140
|
-
});
|
|
141
|
-
describe('hasFlatOpeningCutoutBottom', () => {
|
|
142
|
-
test('flat for rectangles, arches, and door rounded (top-only radii)', () => {
|
|
143
|
-
expect(hasFlatOpeningCutoutBottom(DoorNode.parse({}))).toBe(true);
|
|
144
|
-
expect(hasFlatOpeningCutoutBottom(WindowNode.parse({ openingShape: 'arch' }))).toBe(true);
|
|
145
|
-
expect(hasFlatOpeningCutoutBottom(DoorNode.parse({ openingShape: 'rounded' }))).toBe(true);
|
|
146
|
-
});
|
|
147
|
-
test('rounded windows depend on their bottom radii', () => {
|
|
148
|
-
expect(hasFlatOpeningCutoutBottom(WindowNode.parse({ openingShape: 'rounded' }))).toBe(false);
|
|
149
|
-
expect(hasFlatOpeningCutoutBottom(WindowNode.parse({ openingShape: 'rounded', cornerRadius: 0 }))).toBe(true);
|
|
150
|
-
expect(hasFlatOpeningCutoutBottom(WindowNode.parse({
|
|
151
|
-
openingShape: 'rounded',
|
|
152
|
-
openingRadiusMode: 'individual',
|
|
153
|
-
openingCornerRadii: [0.2, 0.2, 0, 0],
|
|
154
|
-
}))).toBe(true);
|
|
155
|
-
expect(hasFlatOpeningCutoutBottom(WindowNode.parse({
|
|
156
|
-
openingShape: 'rounded',
|
|
157
|
-
openingRadiusMode: 'individual',
|
|
158
|
-
openingCornerRadii: [0.2, 0.2, 0.2, 0.2],
|
|
159
|
-
}))).toBe(false);
|
|
160
|
-
});
|
|
161
|
-
});
|
|
162
|
-
describe('getOpeningCutoutBottomPadding', () => {
|
|
163
|
-
test('pads floor-level openings with a flat bottom', () => {
|
|
164
|
-
expect(getOpeningCutoutBottomPadding(DoorNode.parse({}), 0)).toBe(0.02);
|
|
165
|
-
expect(getOpeningCutoutBottomPadding(DoorNode.parse({ openingShape: 'rounded' }), 0)).toBe(0.02);
|
|
166
|
-
expect(getOpeningCutoutBottomPadding(WindowNode.parse({ openingShape: 'arch' }), 0)).toBe(0.02);
|
|
167
|
-
});
|
|
168
|
-
test('does not pad openings above the floor or rounded window bottoms', () => {
|
|
169
|
-
expect(getOpeningCutoutBottomPadding(DoorNode.parse({}), 0.9)).toBe(0);
|
|
170
|
-
expect(getOpeningCutoutBottomPadding(WindowNode.parse({ openingShape: 'rounded' }), 0)).toBe(0);
|
|
171
|
-
});
|
|
172
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"wall-opening-cutout.test.d.ts","sourceRoot":"","sources":["../../../src/systems/wall/wall-opening-cutout.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
// @ts-expect-error — bun:test is provided by the Bun runtime; viewer does not
|
|
2
|
-
// depend on @types/bun so the import type is unresolved at compile time.
|
|
3
|
-
import { describe, expect, test } from 'bun:test';
|
|
4
|
-
import { calculateLevelMiters, DoorNode, sceneRegistry, WallNode } from '@pascal-app/core';
|
|
5
|
-
import * as THREE from 'three';
|
|
6
|
-
import { generateExtrudedWall } from './wall-system';
|
|
7
|
-
describe('wall opening cutout', () => {
|
|
8
|
-
test('cuts a floor-level door directly from node geometry without a proxy mesh', () => {
|
|
9
|
-
const wall = WallNode.parse({
|
|
10
|
-
id: 'wall_floor-opening-cutout',
|
|
11
|
-
start: [0, 0],
|
|
12
|
-
end: [2, 0],
|
|
13
|
-
height: 2.5,
|
|
14
|
-
thickness: 0.1,
|
|
15
|
-
});
|
|
16
|
-
const door = DoorNode.parse({
|
|
17
|
-
id: 'door_floor-opening-cutout',
|
|
18
|
-
wallId: wall.id,
|
|
19
|
-
position: [1, 1.05, 0],
|
|
20
|
-
width: 0.9,
|
|
21
|
-
height: 2.1,
|
|
22
|
-
});
|
|
23
|
-
const wallMesh = new THREE.Mesh();
|
|
24
|
-
sceneRegistry.nodes.set(wall.id, wallMesh);
|
|
25
|
-
try {
|
|
26
|
-
const geometry = generateExtrudedWall(wall, [door], calculateLevelMiters([wall]));
|
|
27
|
-
const position = geometry.getAttribute('position');
|
|
28
|
-
const index = geometry.index;
|
|
29
|
-
const openingLeft = door.position[0] - door.width / 2;
|
|
30
|
-
const openingRight = door.position[0] + door.width / 2;
|
|
31
|
-
let wallFaceTrianglesInsideOpening = 0;
|
|
32
|
-
let baseTrianglesInsideOpening = 0;
|
|
33
|
-
for (let offset = 0; offset < (index?.count ?? position.count); offset += 3) {
|
|
34
|
-
const indices = [0, 1, 2].map((corner) => index ? index.getX(offset + corner) : offset + corner);
|
|
35
|
-
const vertices = indices.map((vertexIndex) => new THREE.Vector3(position.getX(vertexIndex), position.getY(vertexIndex), position.getZ(vertexIndex)));
|
|
36
|
-
const centroid = vertices
|
|
37
|
-
.reduce((sum, vertex) => sum.add(vertex), new THREE.Vector3())
|
|
38
|
-
.multiplyScalar(1 / 3);
|
|
39
|
-
const insideOpeningX = centroid.x > openingLeft + 1e-4 && centroid.x < openingRight - 1e-4;
|
|
40
|
-
if (!insideOpeningX)
|
|
41
|
-
continue;
|
|
42
|
-
const onWallFace = vertices.every((vertex) => Math.abs(Math.abs(vertex.z) - (wall.thickness ?? 0.1) / 2) < 1e-5);
|
|
43
|
-
if (onWallFace && centroid.y > 1e-4 && centroid.y < door.height - 1e-4) {
|
|
44
|
-
wallFaceTrianglesInsideOpening += 1;
|
|
45
|
-
}
|
|
46
|
-
if (vertices.every((vertex) => Math.abs(vertex.y) < 1e-5)) {
|
|
47
|
-
baseTrianglesInsideOpening += 1;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
expect(wallFaceTrianglesInsideOpening).toBe(0);
|
|
51
|
-
expect(baseTrianglesInsideOpening).toBe(0);
|
|
52
|
-
geometry.dispose();
|
|
53
|
-
}
|
|
54
|
-
finally {
|
|
55
|
-
sceneRegistry.nodes.delete(wall.id);
|
|
56
|
-
wallMesh.geometry.dispose();
|
|
57
|
-
}
|
|
58
|
-
});
|
|
59
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"wall-support-extension.test.d.ts","sourceRoot":"","sources":["../../../src/systems/wall/wall-support-extension.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
// @ts-expect-error — bun:test is provided by the Bun runtime; viewer does not
|
|
2
|
-
// depend on @types/bun so the import type is unresolved at compile time.
|
|
3
|
-
import { describe, expect, test } from 'bun:test';
|
|
4
|
-
import { calculateLevelMiters, getWallPlaneTop, WallNode, } from '@pascal-app/core';
|
|
5
|
-
import { generateExtrudedWall } from './wall-system';
|
|
6
|
-
describe('wall support extension', () => {
|
|
7
|
-
test('preserves the raised wall origin and top while filling down to the lower support', () => {
|
|
8
|
-
const wall = WallNode.parse({ start: [0, 0], end: [4, 0], height: 2.5, thickness: 0.1 });
|
|
9
|
-
const geometry = generateExtrudedWall(wall, [], calculateLevelMiters([wall]), 0.6, 0.05);
|
|
10
|
-
geometry.computeBoundingBox();
|
|
11
|
-
expect(geometry.boundingBox?.min.y).toBeCloseTo(-0.55);
|
|
12
|
-
expect(geometry.boundingBox?.max.y).toBeCloseTo(2.5);
|
|
13
|
-
// With mesh.position.y = 0.6, the wall spans world Y=0.05..3.1.
|
|
14
|
-
expect((geometry.boundingBox?.min.y ?? 0) + 0.6).toBeCloseTo(0.05);
|
|
15
|
-
expect((geometry.boundingBox?.max.y ?? 0) + 0.6).toBeCloseTo(3.1);
|
|
16
|
-
geometry.dispose();
|
|
17
|
-
});
|
|
18
|
-
test('retains the existing negative-slab top constraint', () => {
|
|
19
|
-
const wall = WallNode.parse({ start: [0, 0], end: [4, 0], height: 2.5, thickness: 0.1 });
|
|
20
|
-
const geometry = generateExtrudedWall(wall, [], calculateLevelMiters([wall]), -0.4, -0.4);
|
|
21
|
-
geometry.computeBoundingBox();
|
|
22
|
-
expect(geometry.boundingBox?.min.y).toBeCloseTo(0);
|
|
23
|
-
expect(geometry.boundingBox?.max.y).toBeCloseTo(2.9);
|
|
24
|
-
expect((geometry.boundingBox?.max.y ?? 0) - 0.4).toBeCloseTo(2.5);
|
|
25
|
-
geometry.dispose();
|
|
26
|
-
});
|
|
27
|
-
test('plane-bound wall tops out at the storey plane regardless of slab elevation', () => {
|
|
28
|
-
const wall = WallNode.parse({ start: [0, 0], end: [4, 0], thickness: 0.1 });
|
|
29
|
-
const flat = generateExtrudedWall(wall, [], calculateLevelMiters([wall]), 0, 0, undefined, 3);
|
|
30
|
-
flat.computeBoundingBox();
|
|
31
|
-
expect(flat.boundingBox?.max.y).toBeCloseTo(3);
|
|
32
|
-
expect(flat.boundingBox?.min.y).toBeCloseTo(0);
|
|
33
|
-
flat.dispose();
|
|
34
|
-
const raised = generateExtrudedWall(wall, [], calculateLevelMiters([wall]), 0.6, 0.6, undefined, 3);
|
|
35
|
-
raised.computeBoundingBox();
|
|
36
|
-
// Mesh sits at Y=0.6, so a 2.4 local top keeps the world top at the 3m
|
|
37
|
-
// plane — the raised slab shortens the wall instead of lifting its top.
|
|
38
|
-
expect(raised.boundingBox?.max.y).toBeCloseTo(2.4);
|
|
39
|
-
expect(raised.boundingBox?.min.y).toBeCloseTo(0);
|
|
40
|
-
raised.dispose();
|
|
41
|
-
});
|
|
42
|
-
test('plane-bound wall under a flush thick deck tops out at the deck underside', () => {
|
|
43
|
-
// level_1 carries a flush deck occupying [-0.3, 0] above the storey
|
|
44
|
-
// plane: the covering-clamped plane for level_0 is 2.5 − 0.3 = 2.2.
|
|
45
|
-
const wall = WallNode.parse({
|
|
46
|
-
start: [0.5, 2],
|
|
47
|
-
end: [3.5, 2],
|
|
48
|
-
thickness: 0.1,
|
|
49
|
-
parentId: 'level_0',
|
|
50
|
-
});
|
|
51
|
-
const base = { object: 'node', parentId: null, visible: true, metadata: {}, children: [] };
|
|
52
|
-
const nodes = {
|
|
53
|
-
level_0: {
|
|
54
|
-
...base,
|
|
55
|
-
id: 'level_0',
|
|
56
|
-
type: 'level',
|
|
57
|
-
level: 0,
|
|
58
|
-
height: 2.5,
|
|
59
|
-
children: [wall.id],
|
|
60
|
-
},
|
|
61
|
-
level_1: {
|
|
62
|
-
...base,
|
|
63
|
-
id: 'level_1',
|
|
64
|
-
type: 'level',
|
|
65
|
-
level: 1,
|
|
66
|
-
height: 2.5,
|
|
67
|
-
children: ['slab_deck'],
|
|
68
|
-
},
|
|
69
|
-
slab_deck: {
|
|
70
|
-
...base,
|
|
71
|
-
id: 'slab_deck',
|
|
72
|
-
type: 'slab',
|
|
73
|
-
parentId: 'level_1',
|
|
74
|
-
polygon: [
|
|
75
|
-
[0, 0],
|
|
76
|
-
[4, 0],
|
|
77
|
-
[4, 4],
|
|
78
|
-
[0, 4],
|
|
79
|
-
],
|
|
80
|
-
holes: [],
|
|
81
|
-
elevation: 0,
|
|
82
|
-
thickness: 0.3,
|
|
83
|
-
},
|
|
84
|
-
};
|
|
85
|
-
const planeTop = getWallPlaneTop(wall, 'level_0', nodes);
|
|
86
|
-
expect(planeTop).toBeCloseTo(2.2);
|
|
87
|
-
const geometry = generateExtrudedWall(wall, [], calculateLevelMiters([wall]), 0, 0, undefined, planeTop);
|
|
88
|
-
geometry.computeBoundingBox();
|
|
89
|
-
// Mesh sits at Y=0, so the world top lands at the deck underside instead
|
|
90
|
-
// of colliding with the slab solid above.
|
|
91
|
-
expect(geometry.boundingBox?.max.y).toBeCloseTo(2.2);
|
|
92
|
-
expect(geometry.boundingBox?.min.y).toBeCloseTo(0);
|
|
93
|
-
geometry.dispose();
|
|
94
|
-
});
|
|
95
|
-
test('raises only the high-supported part of a mixed wall run', () => {
|
|
96
|
-
const wall = WallNode.parse({ start: [0, 0], end: [4, 0], height: 2.5, thickness: 0.1 });
|
|
97
|
-
const geometry = generateExtrudedWall(wall, [], calculateLevelMiters([wall]), 0.6, 0.05, [
|
|
98
|
-
{ start: 0, end: 0.5, elevation: 0.6 },
|
|
99
|
-
{ start: 0.5, end: 1, elevation: 0.05 },
|
|
100
|
-
]);
|
|
101
|
-
const position = geometry.getAttribute('position');
|
|
102
|
-
let highSpanMinY = Number.POSITIVE_INFINITY;
|
|
103
|
-
let lowSpanMinY = Number.POSITIVE_INFINITY;
|
|
104
|
-
for (let index = 0; index < position.count; index++) {
|
|
105
|
-
const x = position.getX(index);
|
|
106
|
-
const y = position.getY(index);
|
|
107
|
-
if (x < 1.9)
|
|
108
|
-
highSpanMinY = Math.min(highSpanMinY, y);
|
|
109
|
-
if (x > 2.1)
|
|
110
|
-
lowSpanMinY = Math.min(lowSpanMinY, y);
|
|
111
|
-
}
|
|
112
|
-
expect(highSpanMinY).toBeCloseTo(0);
|
|
113
|
-
expect(lowSpanMinY).toBeCloseTo(-0.55);
|
|
114
|
-
geometry.dispose();
|
|
115
|
-
});
|
|
116
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"wall-terrain-bottom.test.d.ts","sourceRoot":"","sources":["../../../src/systems/wall/wall-terrain-bottom.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
// @ts-expect-error — bun:test is provided by the Bun runtime; viewer does not
|
|
2
|
-
// depend on @types/bun so the import type is unresolved at compile time.
|
|
3
|
-
import { describe, expect, test } from 'bun:test';
|
|
4
|
-
import { calculateLevelMiters, WallNode } from '@pascal-app/core';
|
|
5
|
-
import { generateExtrudedWall } from './wall-system';
|
|
6
|
-
describe('wall terrain bottom', () => {
|
|
7
|
-
test('samples both faces independently without changing the authored body height', () => {
|
|
8
|
-
const wall = WallNode.parse({
|
|
9
|
-
start: [0, 0],
|
|
10
|
-
end: [4, 0],
|
|
11
|
-
height: 2.5,
|
|
12
|
-
thickness: 1,
|
|
13
|
-
supportSlabId: 'ground',
|
|
14
|
-
fillToTerrain: true,
|
|
15
|
-
});
|
|
16
|
-
const geometry = generateExtrudedWall(wall, [], calculateLevelMiters([wall]), 1.5, 1.5, undefined, 4, (_x, z) => (z >= 0 ? 0.2 : 0.8));
|
|
17
|
-
geometry.computeBoundingBox();
|
|
18
|
-
const position = geometry.getAttribute('position');
|
|
19
|
-
let positiveFaceMinY = Number.POSITIVE_INFINITY;
|
|
20
|
-
let negativeFaceMinY = Number.POSITIVE_INFINITY;
|
|
21
|
-
for (let index = 0; index < position.count; index += 1) {
|
|
22
|
-
const z = position.getZ(index);
|
|
23
|
-
const y = position.getY(index);
|
|
24
|
-
if (z > 0.49)
|
|
25
|
-
positiveFaceMinY = Math.min(positiveFaceMinY, y);
|
|
26
|
-
if (z < -0.49)
|
|
27
|
-
negativeFaceMinY = Math.min(negativeFaceMinY, y);
|
|
28
|
-
}
|
|
29
|
-
expect(positiveFaceMinY).toBeCloseTo(-1.3);
|
|
30
|
-
expect(negativeFaceMinY).toBeCloseTo(-0.7);
|
|
31
|
-
expect(geometry.boundingBox?.max.y).toBeCloseTo(2.5);
|
|
32
|
-
geometry.dispose();
|
|
33
|
-
});
|
|
34
|
-
});
|