@pascal-app/editor 1.0.0-beta.5 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/bunfig.toml +4 -0
- package/package.json +15 -8
- package/scripts/generate-print-golden-house.ts +89 -0
- package/src/components/editor/bake-exporter.tsx +5 -0
- package/src/components/editor/bake-thumbnail.tsx +4 -1
- package/src/components/editor/camera-dragging-lifecycle.test.ts +41 -0
- package/src/components/editor/camera-dragging-lifecycle.ts +10 -2
- package/src/components/editor/capture-camera-rig.tsx +59 -0
- package/src/components/editor/custom-camera-controls.tsx +20 -11
- package/src/components/editor/editor-layout-mobile.tsx +6 -6
- package/src/components/editor/editor-layout-v2.tsx +9 -2
- package/src/components/editor/export-manager.tsx +175 -33
- package/src/components/editor/first-person/build-collider-world.test.ts +30 -0
- package/src/components/editor/first-person-controls.tsx +180 -12
- package/src/components/editor/floating-action-menu.tsx +55 -51
- package/src/components/editor/floorplan-panel.tsx +99 -15
- package/src/components/editor/grid.tsx +12 -7
- package/src/components/editor/group-actions.ts +34 -50
- package/src/components/editor/group-move-3d.ts +51 -21
- package/src/components/editor/group-transform-shared.test.ts +78 -0
- package/src/components/editor/group-transform-shared.ts +74 -7
- package/src/components/editor/handles/handle-arrow-raycast.test.ts +86 -0
- package/src/components/editor/handles/handle-arrow.tsx +109 -48
- package/src/components/editor/handles/handle-placement.test.ts +111 -0
- package/src/components/editor/handles/handle-placement.ts +69 -0
- package/src/components/editor/handles/linear-resize-drag.ts +78 -0
- package/src/components/editor/handles/resize-snap.test.ts +46 -0
- package/src/components/editor/handles/resize-snap.ts +10 -1
- package/src/components/editor/handles/use-handle-drag.ts +23 -3
- package/src/components/editor/index.tsx +151 -24
- package/src/components/editor/node-action-menu.tsx +14 -1
- package/src/components/editor/node-arrow-handles.tsx +67 -119
- package/src/components/editor/selection-manager.tsx +153 -42
- package/src/components/editor/snapshot-capture-overlay.tsx +298 -39
- package/src/components/editor/snapshot-capture.test.ts +422 -0
- package/src/components/editor/snapshot-capture.ts +145 -0
- package/src/components/editor/thumbnail-generator.tsx +295 -239
- package/src/components/editor/use-floorplan-background-placement.ts +53 -5
- package/src/components/editor/wall-move-side-handles.tsx +299 -7
- package/src/components/editor-2d/floorplan-group-move.tsx +67 -33
- package/src/components/editor-2d/floorplan-registered-tool-layer.tsx +3 -1
- package/src/components/editor-2d/floorplan-registry-action-menu.tsx +12 -1
- package/src/components/editor-2d/floorplan-registry-move-overlay.tsx +129 -38
- package/src/components/editor-2d/renderers/floorplan-dimension-renderer.tsx +14 -1
- package/src/components/editor-2d/renderers/floorplan-geometry-renderer.test.tsx +16 -0
- package/src/components/editor-2d/renderers/floorplan-geometry-renderer.tsx +2 -0
- package/src/components/editor-2d/renderers/floorplan-placement-preview-layer.tsx +52 -3
- package/src/components/editor-2d/renderers/floorplan-registry-layer.test.ts +196 -1
- package/src/components/editor-2d/renderers/floorplan-registry-layer.tsx +226 -35
- package/src/components/procedural-items/inspector.tsx +223 -0
- package/src/components/systems/ceiling/ceiling-bracket-batch.test.ts +402 -0
- package/src/components/systems/ceiling/ceiling-bracket-batch.ts +371 -0
- package/src/components/systems/ceiling/ceiling-selection-affordance-system.test.ts +347 -0
- package/src/components/systems/ceiling/ceiling-selection-affordance-system.tsx +281 -305
- package/src/components/systems/ceiling/ceiling-system.tsx +3 -1
- package/src/components/systems/roof/roof-edit-system.test.tsx +120 -0
- package/src/components/systems/roof/roof-edit-system.tsx +10 -19
- package/src/components/systems/selection-affordance-manager.tsx +59 -9
- package/src/components/systems/selection-affordance-services.ts +19 -0
- package/src/components/systems/stair/stair-edit-system.tsx +5 -2
- package/src/components/tools/fence/fence-drafting.test.ts +100 -0
- package/src/components/tools/fence/fence-drafting.ts +6 -25
- package/src/components/tools/item/draft-creation.test.ts +12 -0
- package/src/components/tools/item/draft-creation.ts +10 -0
- package/src/components/tools/item/face-host-commit.test.ts +65 -0
- package/src/components/tools/item/face-host-commit.ts +56 -0
- package/src/components/tools/item/face-host-preview.test.ts +77 -0
- package/src/components/tools/item/face-host-preview.ts +82 -0
- package/src/components/tools/item/item-surface-placement.test.ts +175 -0
- package/src/components/tools/item/item-surface-strategy-resolver.test.ts +210 -0
- package/src/components/tools/item/move-tool.tsx +4 -3
- package/src/components/tools/item/placement-math.test.ts +11 -1
- package/src/components/tools/item/placement-math.ts +0 -20
- package/src/components/tools/item/placement-strategies.test.ts +331 -4
- package/src/components/tools/item/placement-strategies.ts +304 -171
- package/src/components/tools/item/placement-surface.test.ts +61 -0
- package/src/components/tools/item/placement-surface.ts +24 -0
- package/src/components/tools/item/placement-types.ts +10 -0
- package/src/components/tools/item/shelf-surface-strategy-resolver.test.ts +257 -0
- package/src/components/tools/item/test-face-host.ts +121 -0
- package/src/components/tools/item/use-draft-node.test.tsx +336 -0
- package/src/components/tools/item/use-draft-node.ts +116 -44
- package/src/components/tools/item/use-placement-coordinator.tsx +672 -256
- package/src/components/tools/registered-tool-install-lifecycle.test.tsx +256 -0
- package/src/components/tools/registry/item-surface-child-frame.test.ts +90 -0
- package/src/components/tools/registry/item-surface-move.test.ts +501 -0
- package/src/components/tools/registry/item-surface-move.ts +441 -0
- package/src/components/tools/registry/item-surface-resolver.test.ts +257 -0
- package/src/components/tools/registry/move-registry-node-tool.test.ts +10 -0
- package/src/components/tools/registry/move-registry-node-tool.tsx +505 -164
- package/src/components/tools/registry-tool-context.tsx +30 -0
- package/src/components/tools/select/box-select-tool.tsx +3 -1
- package/src/components/tools/select/marquee-footprint.test.ts +53 -0
- package/src/components/tools/select/marquee-footprint.ts +29 -0
- package/src/components/tools/shared/placement-box.tsx +57 -7
- package/src/components/tools/shared/placement-dimension-guides.tsx +121 -0
- package/src/components/tools/shared/pointer-support-cap.test.ts +319 -17
- package/src/components/tools/shared/pointer-support-cap.ts +56 -17
- package/src/components/tools/shared/shelf-stickiness.ts +54 -0
- package/src/components/tools/shared/surface-hit.test.ts +56 -0
- package/src/components/tools/shared/surface-hit.ts +23 -0
- package/src/components/tools/shared/surface-rejection.test.tsx +106 -0
- package/src/components/tools/shared/surface-rejection.tsx +29 -0
- package/src/components/tools/site/terrain-sculpt-grid.tsx +9 -5
- package/src/components/tools/site/terrain-sculpt-tool.tsx +1 -1
- package/src/components/tools/stair/stair-tool.tsx +127 -74
- package/src/components/tools/tool-manager.tsx +25 -8
- package/src/components/tools/wall/wall-drafting.test.ts +320 -2
- package/src/components/tools/wall/wall-drafting.ts +23 -114
- package/src/components/tools/zone/zone-tool.tsx +13 -4
- package/src/components/ui/action-menu/control-modes.tsx +9 -6
- package/src/components/ui/action-menu/index.tsx +11 -1
- package/src/components/ui/command-palette/editor-commands.tsx +63 -13
- package/src/components/ui/command-palette/index.tsx +7 -2
- package/src/components/ui/controls/material-paint-panel.tsx +6 -4
- package/src/components/ui/controls/material-picker.tsx +4 -4
- package/src/components/ui/controls/metric-control.tsx +29 -13
- package/src/components/ui/controls/scene-material-list.tsx +6 -5
- package/src/components/ui/controls/segmented-control.tsx +8 -1
- package/src/components/ui/controls/slider-control.tsx +44 -21
- package/src/components/ui/controls/toggle-control.tsx +18 -5
- package/src/components/ui/controls/tool-options-panel.tsx +126 -0
- package/src/components/ui/floating-level-selector.tsx +103 -10
- package/src/components/ui/helpers/contextual-helper-panel.tsx +9 -0
- package/src/components/ui/helpers/helper-manager.tsx +47 -11
- package/src/components/ui/helpers/item-helper.tsx +1 -1
- package/src/components/ui/helpers/registered-tool-helper.tsx +21 -0
- package/src/components/ui/panels/homogeneous-selection.test.ts +80 -0
- package/src/components/ui/panels/homogeneous-selection.ts +47 -0
- package/src/components/ui/panels/mobile-selection-bar.tsx +11 -5
- package/src/components/ui/panels/multi-field-value.test.ts +209 -0
- package/src/components/ui/panels/multi-field-value.ts +163 -0
- package/src/components/ui/panels/multi-height-mode.tsx +185 -0
- package/src/components/ui/panels/multi-parametric-inspector.tsx +266 -0
- package/src/components/ui/panels/multi-selection-panel.tsx +48 -37
- package/src/components/ui/panels/node-display.ts +4 -0
- package/src/components/ui/panels/panel-manager.tsx +77 -3
- package/src/components/ui/panels/parametric-field-control.tsx +173 -0
- package/src/components/ui/panels/parametric-field-utils.ts +18 -0
- package/src/components/ui/panels/parametric-inspector.tsx +35 -164
- package/src/components/ui/panels/reference-panel.tsx +34 -4
- package/src/components/ui/primitives/checkbox.tsx +34 -0
- package/src/components/ui/primitives/shortcut-token.tsx +23 -1
- package/src/components/ui/scene-loader.tsx +35 -0
- package/src/components/ui/sidebar/panels/settings-panel/index.tsx +502 -31
- package/src/components/ui/sidebar/panels/settings-panel/keyboard-shortcuts-dialog.tsx +20 -23
- package/src/components/ui/sidebar/panels/settings-panel/print-export-button.test.ts +167 -0
- package/src/components/ui/sidebar/panels/settings-panel/print-export-button.tsx +128 -0
- package/src/components/ui/sidebar/panels/site-panel/building-tree-node.tsx +8 -1
- package/src/components/ui/sidebar/panels/site-panel/dormer-tree-node.tsx +19 -5
- package/src/components/ui/sidebar/panels/site-panel/index.tsx +222 -50
- package/src/components/ui/sidebar/panels/site-panel/item-tree-node.tsx +2 -7
- package/src/components/ui/sidebar/panels/site-panel/level-tree-node.tsx +5 -2
- package/src/components/ui/sidebar/panels/site-panel/registry-tree-node.tsx +28 -11
- package/src/components/ui/sidebar/panels/site-panel/shelf-tree-node.tsx +3 -8
- package/src/components/ui/sidebar/panels/site-panel/tree-node.test.ts +8 -0
- package/src/components/ui/sidebar/panels/site-panel/tree-node.tsx +20 -7
- package/src/components/ui/sidebar/panels/site-panel/unit-tree-node.tsx +224 -0
- package/src/components/ui/sidebar/panels/site-panel/zone-membership-checkbox.tsx +22 -0
- package/src/components/ui/sidebar/panels/site-panel/zone-tree-node.tsx +24 -7
- package/src/components/ui/sidebar/tab-bar.tsx +2 -0
- package/src/components/ui/sidebar/use-plugin-panels.tsx +17 -2
- package/src/components/ui/snap-target-badge.tsx +2 -0
- package/src/components/viewer/viewer-scene-header.tsx +5 -1
- package/src/components/viewer/viewer-stage.tsx +4 -1
- package/src/components/viewer/viewer-units-panel.tsx +116 -0
- package/src/components/viewer-overlay.tsx +1 -22
- package/src/components/viewer-zone-system.tsx +6 -2
- package/src/components/walkthrough-hud.tsx +19 -10
- package/src/hooks/use-auto-save.test.ts +73 -1
- package/src/hooks/use-auto-save.ts +76 -5
- package/src/hooks/use-ceiling-events.test.ts +144 -0
- package/src/hooks/use-ceiling-events.ts +5 -1
- package/src/hooks/use-drag-action.ts +17 -3
- package/src/hooks/use-grid-events.test.ts +240 -0
- package/src/hooks/use-grid-events.ts +190 -11
- package/src/hooks/use-keyboard.test.ts +244 -0
- package/src/hooks/use-keyboard.ts +132 -63
- package/src/hooks/use-registered-tool-enabled.ts +20 -0
- package/src/hooks/use-save-shortcut.ts +33 -0
- package/src/index.tsx +87 -4
- package/src/lib/active-placement-surface.test.ts +16 -0
- package/src/lib/active-placement-surface.ts +14 -0
- package/src/lib/ceiling-surface-raycast.test.ts +64 -0
- package/src/lib/ceiling-surface-raycast.ts +12 -0
- package/src/lib/contextual-help-extension.ts +17 -0
- package/src/lib/continuation.test.ts +11 -0
- package/src/lib/continuation.ts +8 -1
- package/src/lib/direct-manipulation.test.ts +132 -0
- package/src/lib/direct-manipulation.ts +38 -1
- package/src/lib/editor-api.ts +6 -3
- package/src/lib/elevation-guides.test.ts +28 -1
- package/src/lib/elevation-guides.ts +22 -7
- package/src/lib/export-texture-utils.test.ts +167 -0
- package/src/lib/export-texture-utils.ts +121 -0
- package/src/lib/export-user-data.test.ts +48 -0
- package/src/lib/export-user-data.ts +69 -0
- package/src/lib/floating-menu-scale.test.ts +29 -0
- package/src/lib/floating-menu-scale.ts +14 -0
- package/src/lib/floorplan/apply-alignment.test.ts +47 -2
- package/src/lib/floorplan/apply-alignment.ts +10 -1
- package/src/lib/floorplan/floorplan-export.test.ts +536 -2
- package/src/lib/floorplan/floorplan-export.tsx +120 -19
- package/src/lib/floorplan/floorplan-pdfkit-renderer.test.ts +26 -0
- package/src/lib/floorplan/floorplan-pdfkit-renderer.ts +32 -24
- package/src/lib/floorplan/floorplan-readonly.ts +5 -0
- package/src/lib/floorplan-grid-event-point.test.ts +29 -0
- package/src/lib/floorplan-grid-event-point.ts +13 -0
- package/src/lib/fresh-planar-placement.ts +13 -5
- package/src/lib/glb-export-serialize.test.ts +280 -0
- package/src/lib/glb-export.test.ts +1375 -21
- package/src/lib/glb-export.ts +781 -106
- package/src/lib/grid-event-presentation.ts +16 -0
- package/src/lib/history.test.ts +528 -147
- package/src/lib/history.ts +81 -7
- package/src/lib/host-tree-children.test.ts +27 -0
- package/src/lib/host-tree-children.ts +79 -0
- package/src/lib/interaction/hot-set.test.ts +10 -1
- package/src/lib/interaction/hot-set.ts +3 -1
- package/src/lib/interaction/overlay-policy.test.ts +33 -1
- package/src/lib/interaction/overlay-policy.ts +16 -0
- package/src/lib/interaction/registered-drafting.ts +36 -0
- package/src/lib/interaction/scope.ts +31 -0
- package/src/lib/level-duplication.test.ts +73 -0
- package/src/lib/level-duplication.ts +32 -2
- package/src/lib/level-print-export.test.ts +484 -0
- package/src/lib/level-print-export.ts +526 -0
- package/src/lib/linear-display.test.ts +59 -0
- package/src/lib/linear-display.ts +39 -0
- package/src/lib/local-guide-image.test.ts +24 -0
- package/src/lib/local-guide-image.ts +34 -1
- package/src/lib/local-project-presentation-persistence.test.ts +302 -0
- package/src/lib/local-project-presentation-persistence.ts +335 -0
- package/src/lib/material-paint.ts +2 -0
- package/src/lib/model-export.ts +29 -0
- package/src/lib/node-action-movement.ts +21 -0
- package/src/lib/paint-preview-owner.ts +71 -0
- package/src/lib/planar-cursor-placement.test.ts +178 -1
- package/src/lib/planar-cursor-placement.ts +61 -5
- package/src/lib/plugin-panels.test.ts +92 -1
- package/src/lib/plugin-panels.ts +48 -0
- package/src/lib/portable-export.test.ts +455 -0
- package/src/lib/portable-export.ts +1042 -0
- package/src/lib/print-3mf.test.ts +86 -0
- package/src/lib/print-3mf.ts +161 -0
- package/src/lib/print-content-scope.test.ts +107 -0
- package/src/lib/print-content-scope.ts +90 -0
- package/src/lib/print-export.test.ts +294 -0
- package/src/lib/print-export.ts +735 -0
- package/src/lib/print-feature-thickness.test.ts +147 -0
- package/src/lib/print-feature-thickness.ts +180 -0
- package/src/lib/print-golden-house.test-fixture.ts +352 -0
- package/src/lib/print-golden-house.test.ts +249 -0
- package/src/lib/print-roof-solids.test.ts +133 -0
- package/src/lib/print-roof-solids.ts +630 -0
- package/src/lib/print-shell-compiler-baseline.test.ts +419 -0
- package/src/lib/print-shell-compiler-baseline.ts +225 -0
- package/src/lib/print-shell-compiler-manifold-core.ts +379 -0
- package/src/lib/print-shell-compiler-manifold-worker.ts +200 -0
- package/src/lib/print-shell-compiler-manifold.worker.ts +20 -0
- package/src/lib/print-shell-compiler-mesh-data.ts +32 -0
- package/src/lib/print-shell-compiler-protocol.ts +38 -0
- package/src/lib/print-shell-compiler.ts +273 -0
- package/src/lib/print-wall-solids.test.ts +194 -0
- package/src/lib/print-wall-solids.ts +328 -0
- package/src/lib/rigid-plan-svg-transform.test.ts +35 -0
- package/src/lib/rigid-plan-svg-transform.ts +24 -0
- package/src/lib/scene-clipboard.ts +1 -1
- package/src/lib/scene.ts +22 -8
- package/src/lib/selection-routing.test.ts +105 -1
- package/src/lib/selection-routing.ts +19 -0
- package/src/lib/send-to-app.test.ts +108 -0
- package/src/lib/send-to-app.ts +121 -0
- package/src/lib/sfx/movement-tick.ts +37 -0
- package/src/lib/sfx-bus.ts +4 -0
- package/src/lib/sfx-player.ts +8 -0
- package/src/lib/snapping-mode.test.ts +32 -4
- package/src/lib/snapping-mode.ts +11 -7
- package/src/lib/surface-attachment.ts +77 -0
- package/src/lib/units.ts +254 -0
- package/src/lib/usdz-export.ts +65 -0
- package/src/lib/use-linear-display.ts +8 -32
- package/src/lib/walkthrough-pointer-lock.ts +47 -0
- package/src/store/live-draft-preview-stores.test.ts +3 -0
- package/src/store/terrain-sculpt-mode.test.ts +3 -6
- package/src/store/tool-mode.test.ts +87 -0
- package/src/store/use-camera-hint-focus.ts +48 -0
- package/src/store/use-editor.tsx +299 -188
- package/src/store/use-floorplan-draft-preview.ts +9 -0
- package/src/store/use-handle-group.ts +4 -0
- package/src/store/use-interaction-scope.test.ts +23 -1
- package/src/store/use-interaction-scope.ts +63 -2
- package/src/store/use-placement-preview.ts +56 -3
- package/src/store/use-send-to-app.ts +21 -0
- package/src/components/tools/roof/roof-tool.tsx +0 -656
- package/src/components/ui/helpers/roof-helper.tsx +0 -14
|
@@ -1,12 +1,30 @@
|
|
|
1
|
-
import { describe, expect, test } from 'bun:test'
|
|
2
|
-
import
|
|
1
|
+
import { afterEach, describe, expect, test } from 'bun:test'
|
|
2
|
+
import {
|
|
3
|
+
type AnyNode,
|
|
4
|
+
type AnyNodeDefinition,
|
|
5
|
+
BuildingNode,
|
|
6
|
+
type FloorplanGeometry,
|
|
7
|
+
type GeometryContext,
|
|
8
|
+
LevelNode,
|
|
9
|
+
loadPlugin,
|
|
10
|
+
type NodeCategory,
|
|
11
|
+
nodeRegistry,
|
|
12
|
+
registerNode,
|
|
13
|
+
} from '@pascal-app/core'
|
|
14
|
+
import { useViewer } from '@pascal-app/viewer'
|
|
15
|
+
import PDFDocument from 'pdfkit'
|
|
16
|
+
import { z } from 'zod'
|
|
3
17
|
import { splitFloorplanOverlay } from '../../components/editor-2d/renderers/floorplan-registry-layer'
|
|
4
18
|
import { DEFAULT_FLOORPLAN_ANNOTATION_VISIBILITY } from './annotation-visibility'
|
|
5
19
|
import {
|
|
20
|
+
collectFloorplanGeometry,
|
|
21
|
+
collectFloorplanSchedules,
|
|
6
22
|
filterFloorplanExportOverlay,
|
|
7
23
|
fitPlanToBox,
|
|
8
24
|
isFloorplanExportAnnotationGeometry,
|
|
25
|
+
isFloorplanNodeInExportScope,
|
|
9
26
|
partitionFloorplanExportOverlay,
|
|
27
|
+
resolveExportLevels,
|
|
10
28
|
resolveFloorplanExportAnnotationVisibility,
|
|
11
29
|
resolveFloorplanExportNodeGeometry,
|
|
12
30
|
resolveFloorplanExportPlacement,
|
|
@@ -19,6 +37,63 @@ import {
|
|
|
19
37
|
rotateFloorplanExportBounds,
|
|
20
38
|
} from './floorplan-export'
|
|
21
39
|
import { floorplanGeometryMetadata } from './floorplan-extension'
|
|
40
|
+
import { FloorplanPdfDocument } from './floorplan-pdfkit-document'
|
|
41
|
+
import { renderFloorplanGeometryToPdfKit } from './floorplan-pdfkit-renderer'
|
|
42
|
+
|
|
43
|
+
type GroupGeometry = Extract<FloorplanGeometry, { kind: 'group' }>
|
|
44
|
+
type GroupTransform = NonNullable<GroupGeometry['transform']>
|
|
45
|
+
|
|
46
|
+
function flattenGeometry(geometry: FloorplanGeometry | null): FloorplanGeometry[] {
|
|
47
|
+
if (!geometry) return []
|
|
48
|
+
if (geometry.kind !== 'group') return [geometry]
|
|
49
|
+
return [geometry, ...geometry.children.flatMap(flattenGeometry)]
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function applyGeometryTransforms(
|
|
53
|
+
point: readonly [number, number],
|
|
54
|
+
transforms: readonly GroupTransform[],
|
|
55
|
+
): [number, number] {
|
|
56
|
+
let x = point[0]
|
|
57
|
+
let y = point[1]
|
|
58
|
+
for (let index = transforms.length - 1; index >= 0; index -= 1) {
|
|
59
|
+
const transform = transforms[index]!
|
|
60
|
+
if (transform.rotate !== undefined) {
|
|
61
|
+
const cos = Math.cos(transform.rotate)
|
|
62
|
+
const sin = Math.sin(transform.rotate)
|
|
63
|
+
const rotatedX = x * cos - y * sin
|
|
64
|
+
y = x * sin + y * cos
|
|
65
|
+
x = rotatedX
|
|
66
|
+
}
|
|
67
|
+
if (transform.translate) {
|
|
68
|
+
x += transform.translate[0]
|
|
69
|
+
y += transform.translate[1]
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return [x, y]
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function projectedGeometryPoint(
|
|
76
|
+
geometry: FloorplanGeometry,
|
|
77
|
+
target: 'circle' | 'image',
|
|
78
|
+
transforms: readonly GroupTransform[] = [],
|
|
79
|
+
): [number, number] | null {
|
|
80
|
+
if (geometry.kind === target) {
|
|
81
|
+
const point =
|
|
82
|
+
geometry.kind === 'circle'
|
|
83
|
+
? ([geometry.cx, geometry.cy] as const)
|
|
84
|
+
: geometry.kind === 'image'
|
|
85
|
+
? geometry.center
|
|
86
|
+
: null
|
|
87
|
+
return point ? applyGeometryTransforms(point, transforms) : null
|
|
88
|
+
}
|
|
89
|
+
if (geometry.kind !== 'group') return null
|
|
90
|
+
const nestedTransforms = geometry.transform ? [...transforms, geometry.transform] : transforms
|
|
91
|
+
for (const child of geometry.children) {
|
|
92
|
+
const point = projectedGeometryPoint(child, target, nestedTransforms)
|
|
93
|
+
if (point) return point
|
|
94
|
+
}
|
|
95
|
+
return null
|
|
96
|
+
}
|
|
22
97
|
|
|
23
98
|
describe('filterFloorplanExportOverlay', () => {
|
|
24
99
|
test('preserves annotation metadata while splitting geometry passes', () => {
|
|
@@ -317,3 +392,462 @@ describe('resolveFloorplanPageLayout', () => {
|
|
|
317
392
|
})
|
|
318
393
|
})
|
|
319
394
|
})
|
|
395
|
+
|
|
396
|
+
describe('isFloorplanNodeInExportScope', () => {
|
|
397
|
+
const definition = (category?: NodeCategory) => ({ category })
|
|
398
|
+
|
|
399
|
+
test('includes structure-category nodes under structure and full', () => {
|
|
400
|
+
expect(isFloorplanNodeInExportScope(definition('structure'), 'structure')).toBe(true)
|
|
401
|
+
expect(isFloorplanNodeInExportScope(definition('structure'), 'full')).toBe(true)
|
|
402
|
+
})
|
|
403
|
+
|
|
404
|
+
test('excludes utility-category nodes under structure', () => {
|
|
405
|
+
expect(isFloorplanNodeInExportScope(definition('utility'), 'full')).toBe(true)
|
|
406
|
+
expect(isFloorplanNodeInExportScope(definition('utility'), 'structure')).toBe(false)
|
|
407
|
+
})
|
|
408
|
+
|
|
409
|
+
test('includes furnish-category nodes only under full', () => {
|
|
410
|
+
expect(isFloorplanNodeInExportScope(definition('furnish'), 'full')).toBe(true)
|
|
411
|
+
expect(isFloorplanNodeInExportScope(definition('furnish'), 'structure')).toBe(false)
|
|
412
|
+
})
|
|
413
|
+
|
|
414
|
+
test('includes analysis and site-category nodes only under full', () => {
|
|
415
|
+
for (const category of ['analysis', 'site'] as const) {
|
|
416
|
+
expect(isFloorplanNodeInExportScope(definition(category), 'full')).toBe(true)
|
|
417
|
+
expect(isFloorplanNodeInExportScope(definition(category), 'structure')).toBe(false)
|
|
418
|
+
}
|
|
419
|
+
})
|
|
420
|
+
|
|
421
|
+
test('excludes nodes with no category except under full', () => {
|
|
422
|
+
expect(isFloorplanNodeInExportScope(definition(undefined), 'full')).toBe(true)
|
|
423
|
+
expect(isFloorplanNodeInExportScope(definition(undefined), 'structure')).toBe(false)
|
|
424
|
+
})
|
|
425
|
+
|
|
426
|
+
test('handles an undefined definition like a no-category node', () => {
|
|
427
|
+
expect(isFloorplanNodeInExportScope(undefined, 'full')).toBe(true)
|
|
428
|
+
expect(isFloorplanNodeInExportScope(undefined, 'structure')).toBe(false)
|
|
429
|
+
})
|
|
430
|
+
})
|
|
431
|
+
|
|
432
|
+
describe('collectFloorplanSchedules', () => {
|
|
433
|
+
test('omits non-structure schedule contributors under structure scope', () => {
|
|
434
|
+
const restoreRegistry = nodeRegistry._snapshot()
|
|
435
|
+
const structureKind = 'test:structure-schedule'
|
|
436
|
+
const siteKind = 'test:site-schedule'
|
|
437
|
+
const levelId = 'level_schedules' as AnyNode['id']
|
|
438
|
+
const structureNodeId = 'structure_scheduled' as AnyNode['id']
|
|
439
|
+
const siteNodeId = 'site_scheduled' as AnyNode['id']
|
|
440
|
+
|
|
441
|
+
const scheduleFor = (title: string) => ({
|
|
442
|
+
id: title.toLowerCase(),
|
|
443
|
+
title,
|
|
444
|
+
columns: [{ key: 'id', label: 'ID' }],
|
|
445
|
+
rows: [{ id: 'row', cells: { id: '1' } }],
|
|
446
|
+
})
|
|
447
|
+
|
|
448
|
+
try {
|
|
449
|
+
nodeRegistry._reset()
|
|
450
|
+
registerNode({
|
|
451
|
+
kind: structureKind,
|
|
452
|
+
schemaVersion: 1,
|
|
453
|
+
schema: z.object({ type: z.literal(structureKind) }) as never,
|
|
454
|
+
category: 'structure',
|
|
455
|
+
defaults: () => ({}) as never,
|
|
456
|
+
capabilities: {},
|
|
457
|
+
extensions: {
|
|
458
|
+
'pascal:editor/floorplan': {
|
|
459
|
+
schedule: () => scheduleFor('Doors'),
|
|
460
|
+
},
|
|
461
|
+
},
|
|
462
|
+
} as AnyNodeDefinition)
|
|
463
|
+
registerNode({
|
|
464
|
+
kind: siteKind,
|
|
465
|
+
schemaVersion: 1,
|
|
466
|
+
schema: z.object({ type: z.literal(siteKind) }) as never,
|
|
467
|
+
category: 'site',
|
|
468
|
+
defaults: () => ({}) as never,
|
|
469
|
+
capabilities: {},
|
|
470
|
+
extensions: {
|
|
471
|
+
'pascal:editor/floorplan': {
|
|
472
|
+
schedule: () => scheduleFor('Rooms'),
|
|
473
|
+
},
|
|
474
|
+
},
|
|
475
|
+
} as AnyNodeDefinition)
|
|
476
|
+
|
|
477
|
+
const nodes = {
|
|
478
|
+
[levelId]: {
|
|
479
|
+
id: levelId,
|
|
480
|
+
type: 'level',
|
|
481
|
+
visible: true,
|
|
482
|
+
children: [structureNodeId, siteNodeId],
|
|
483
|
+
},
|
|
484
|
+
[structureNodeId]: {
|
|
485
|
+
id: structureNodeId,
|
|
486
|
+
type: structureKind,
|
|
487
|
+
visible: true,
|
|
488
|
+
},
|
|
489
|
+
[siteNodeId]: {
|
|
490
|
+
id: siteNodeId,
|
|
491
|
+
type: siteKind,
|
|
492
|
+
visible: true,
|
|
493
|
+
},
|
|
494
|
+
} as unknown as Record<string, AnyNode>
|
|
495
|
+
|
|
496
|
+
const full = collectFloorplanSchedules(nodes, levelId, 'metric', 'full')
|
|
497
|
+
expect(full.map((schedule) => schedule.title).sort()).toEqual(['Doors', 'Rooms'])
|
|
498
|
+
|
|
499
|
+
const structure = collectFloorplanSchedules(nodes, levelId, 'metric', 'structure')
|
|
500
|
+
expect(structure.map((schedule) => schedule.title)).toEqual(['Doors'])
|
|
501
|
+
} finally {
|
|
502
|
+
restoreRegistry()
|
|
503
|
+
}
|
|
504
|
+
})
|
|
505
|
+
})
|
|
506
|
+
|
|
507
|
+
describe('collectFloorplanGeometry', () => {
|
|
508
|
+
test('collects the active Site once below architecture with semantic context and projection', async () => {
|
|
509
|
+
const restoreRegistry = nodeRegistry._snapshot()
|
|
510
|
+
const activeSiteId = 'site_active'
|
|
511
|
+
const otherSiteId = 'site_other'
|
|
512
|
+
const enabledPluginId = 'test:site-pdf-enabled'
|
|
513
|
+
const disabledPluginId = 'test:site-pdf-disabled'
|
|
514
|
+
const enabledKind = 'test:site-pdf-overlay'
|
|
515
|
+
const disabledKind = 'test:site-pdf-disabled-overlay'
|
|
516
|
+
const architectureKind = 'test:level-architecture'
|
|
517
|
+
const semanticChildId = 'site_overlay_detail'
|
|
518
|
+
const referencedNodeId = 'site_reference'
|
|
519
|
+
const inlinePng =
|
|
520
|
+
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII='
|
|
521
|
+
|
|
522
|
+
const siteDefinition = (
|
|
523
|
+
kind: string,
|
|
524
|
+
floorplan: (node: Record<string, unknown>, context: GeometryContext) => FloorplanGeometry,
|
|
525
|
+
): AnyNodeDefinition =>
|
|
526
|
+
({
|
|
527
|
+
kind,
|
|
528
|
+
schemaVersion: 1,
|
|
529
|
+
schema: z.object({ type: z.literal(kind) }) as never,
|
|
530
|
+
category: 'utility',
|
|
531
|
+
defaults: () => ({}) as never,
|
|
532
|
+
capabilities: {},
|
|
533
|
+
floorplanScope: 'site',
|
|
534
|
+
floorplan,
|
|
535
|
+
}) as AnyNodeDefinition
|
|
536
|
+
|
|
537
|
+
const enabledDefinition = siteDefinition(enabledKind, (siteOverlay, context) => ({
|
|
538
|
+
kind: 'group',
|
|
539
|
+
children: [
|
|
540
|
+
{
|
|
541
|
+
kind: 'rect',
|
|
542
|
+
x: 10,
|
|
543
|
+
y: 3,
|
|
544
|
+
width: context.children[0]?.id === semanticChildId ? 3 : -3,
|
|
545
|
+
height: context.siblings.some(({ id }) => id === 'site_overlay_hidden') ? 4 : -4,
|
|
546
|
+
fill:
|
|
547
|
+
siteOverlay.id === 'site_overlay' &&
|
|
548
|
+
context.parent?.id === activeSiteId &&
|
|
549
|
+
context.resolve(referencedNodeId)?.id === referencedNodeId
|
|
550
|
+
? '#102030'
|
|
551
|
+
: '#ff0000',
|
|
552
|
+
},
|
|
553
|
+
{
|
|
554
|
+
kind: 'path',
|
|
555
|
+
d: 'M0,0H4V4H0ZM1,1H3V3H1Z',
|
|
556
|
+
fill: '#3f6b2f',
|
|
557
|
+
fillRule: 'evenodd',
|
|
558
|
+
},
|
|
559
|
+
{
|
|
560
|
+
kind: 'image',
|
|
561
|
+
url: inlinePng,
|
|
562
|
+
center: [10, 3],
|
|
563
|
+
width: 1,
|
|
564
|
+
height: 1,
|
|
565
|
+
},
|
|
566
|
+
{ kind: 'circle', cx: 12, cy: 5, r: 0.5 },
|
|
567
|
+
],
|
|
568
|
+
}))
|
|
569
|
+
|
|
570
|
+
try {
|
|
571
|
+
nodeRegistry._reset()
|
|
572
|
+
registerNode({
|
|
573
|
+
kind: architectureKind,
|
|
574
|
+
schemaVersion: 1,
|
|
575
|
+
schema: z.object({ type: z.literal(architectureKind) }) as never,
|
|
576
|
+
category: 'structure',
|
|
577
|
+
defaults: () => ({}) as never,
|
|
578
|
+
capabilities: {},
|
|
579
|
+
floorplan: () => ({
|
|
580
|
+
kind: 'polygon',
|
|
581
|
+
points: [
|
|
582
|
+
[0, 0],
|
|
583
|
+
[4, 0],
|
|
584
|
+
[4, 4],
|
|
585
|
+
],
|
|
586
|
+
}),
|
|
587
|
+
} as AnyNodeDefinition)
|
|
588
|
+
await loadPlugin({
|
|
589
|
+
id: enabledPluginId,
|
|
590
|
+
apiVersion: 1,
|
|
591
|
+
nodes: [enabledDefinition],
|
|
592
|
+
})
|
|
593
|
+
await loadPlugin({
|
|
594
|
+
id: disabledPluginId,
|
|
595
|
+
apiVersion: 1,
|
|
596
|
+
nodes: [siteDefinition(disabledKind, () => ({ kind: 'circle', cx: 0, cy: 0, r: 1 }))],
|
|
597
|
+
})
|
|
598
|
+
|
|
599
|
+
const activeSite = {
|
|
600
|
+
id: activeSiteId,
|
|
601
|
+
type: 'site',
|
|
602
|
+
parentId: null,
|
|
603
|
+
visible: true,
|
|
604
|
+
children: [
|
|
605
|
+
'building_active',
|
|
606
|
+
'site_overlay',
|
|
607
|
+
'site_overlay_hidden',
|
|
608
|
+
'site_overlay_disabled',
|
|
609
|
+
],
|
|
610
|
+
} as unknown as AnyNode
|
|
611
|
+
const activeBuilding = {
|
|
612
|
+
id: 'building_active',
|
|
613
|
+
type: 'building',
|
|
614
|
+
parentId: activeSiteId,
|
|
615
|
+
children: ['level_active', 'level_upper'],
|
|
616
|
+
position: [10, 0, 5],
|
|
617
|
+
rotation: [0, Math.PI / 2, 0],
|
|
618
|
+
} as unknown as AnyNode
|
|
619
|
+
const activeLevel = {
|
|
620
|
+
id: 'level_active',
|
|
621
|
+
type: 'level',
|
|
622
|
+
parentId: activeBuilding.id,
|
|
623
|
+
children: ['level_architecture'],
|
|
624
|
+
} as unknown as AnyNode
|
|
625
|
+
const upperLevel = {
|
|
626
|
+
id: 'level_upper',
|
|
627
|
+
type: 'level',
|
|
628
|
+
parentId: activeBuilding.id,
|
|
629
|
+
children: ['level_upper_architecture'],
|
|
630
|
+
} as unknown as AnyNode
|
|
631
|
+
const nodes = {
|
|
632
|
+
[activeSite.id]: activeSite,
|
|
633
|
+
[activeBuilding.id]: activeBuilding,
|
|
634
|
+
[activeLevel.id]: activeLevel,
|
|
635
|
+
[upperLevel.id]: upperLevel,
|
|
636
|
+
level_upper_architecture: {
|
|
637
|
+
id: 'level_upper_architecture',
|
|
638
|
+
type: architectureKind,
|
|
639
|
+
parentId: upperLevel.id,
|
|
640
|
+
visible: true,
|
|
641
|
+
} as unknown as AnyNode,
|
|
642
|
+
level_architecture: {
|
|
643
|
+
id: 'level_architecture',
|
|
644
|
+
type: architectureKind,
|
|
645
|
+
parentId: activeLevel.id,
|
|
646
|
+
visible: true,
|
|
647
|
+
} as unknown as AnyNode,
|
|
648
|
+
site_overlay: {
|
|
649
|
+
id: 'site_overlay',
|
|
650
|
+
type: enabledKind,
|
|
651
|
+
parentId: null,
|
|
652
|
+
children: [semanticChildId],
|
|
653
|
+
visible: true,
|
|
654
|
+
} as unknown as AnyNode,
|
|
655
|
+
[semanticChildId]: {
|
|
656
|
+
id: semanticChildId,
|
|
657
|
+
type: 'test:site-detail',
|
|
658
|
+
parentId: 'site_overlay',
|
|
659
|
+
} as unknown as AnyNode,
|
|
660
|
+
[referencedNodeId]: {
|
|
661
|
+
id: referencedNodeId,
|
|
662
|
+
type: 'test:site-reference',
|
|
663
|
+
parentId: activeSiteId,
|
|
664
|
+
} as unknown as AnyNode,
|
|
665
|
+
site_overlay_hidden: {
|
|
666
|
+
id: 'site_overlay_hidden',
|
|
667
|
+
type: enabledKind,
|
|
668
|
+
parentId: activeSiteId,
|
|
669
|
+
visible: false,
|
|
670
|
+
} as unknown as AnyNode,
|
|
671
|
+
site_overlay_disabled: {
|
|
672
|
+
id: 'site_overlay_disabled',
|
|
673
|
+
type: disabledKind,
|
|
674
|
+
parentId: activeSiteId,
|
|
675
|
+
visible: true,
|
|
676
|
+
} as unknown as AnyNode,
|
|
677
|
+
[otherSiteId]: {
|
|
678
|
+
id: otherSiteId,
|
|
679
|
+
type: 'site',
|
|
680
|
+
parentId: null,
|
|
681
|
+
children: ['site_overlay_other'],
|
|
682
|
+
} as unknown as AnyNode,
|
|
683
|
+
site_overlay_other: {
|
|
684
|
+
id: 'site_overlay_other',
|
|
685
|
+
type: enabledKind,
|
|
686
|
+
parentId: otherSiteId,
|
|
687
|
+
visible: true,
|
|
688
|
+
} as unknown as AnyNode,
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
const full = collectFloorplanGeometry(
|
|
692
|
+
nodes,
|
|
693
|
+
activeLevel.id,
|
|
694
|
+
'full',
|
|
695
|
+
'metric',
|
|
696
|
+
'meters',
|
|
697
|
+
DEFAULT_FLOORPLAN_ANNOTATION_VISIBILITY,
|
|
698
|
+
'floor-plan',
|
|
699
|
+
'finished-faces',
|
|
700
|
+
[enabledPluginId],
|
|
701
|
+
)
|
|
702
|
+
|
|
703
|
+
expect(full.map(({ id }) => id)).toEqual(['site_overlay', 'level_architecture'])
|
|
704
|
+
const siteModel = full[0]?.model
|
|
705
|
+
if (!siteModel) throw new Error('Expected Site geometry')
|
|
706
|
+
const siteParts = flattenGeometry(siteModel)
|
|
707
|
+
expect(siteParts).toEqual(
|
|
708
|
+
expect.arrayContaining([
|
|
709
|
+
expect.objectContaining({
|
|
710
|
+
kind: 'rect',
|
|
711
|
+
width: 3,
|
|
712
|
+
height: 4,
|
|
713
|
+
fill: '#102030',
|
|
714
|
+
}),
|
|
715
|
+
expect.objectContaining({ kind: 'path', fillRule: 'evenodd' }),
|
|
716
|
+
expect.objectContaining({ kind: 'image', url: inlinePng }),
|
|
717
|
+
]),
|
|
718
|
+
)
|
|
719
|
+
expect(projectedGeometryPoint(siteModel, 'image')).toEqual([
|
|
720
|
+
expect.closeTo(2),
|
|
721
|
+
expect.closeTo(0),
|
|
722
|
+
])
|
|
723
|
+
expect(projectedGeometryPoint(siteModel, 'circle')).toEqual([
|
|
724
|
+
expect.closeTo(0),
|
|
725
|
+
expect.closeTo(2),
|
|
726
|
+
])
|
|
727
|
+
|
|
728
|
+
const rawPdf = new PDFDocument({ autoFirstPage: false, compress: false })
|
|
729
|
+
const chunks: Buffer[] = []
|
|
730
|
+
rawPdf.on('data', (chunk: Buffer) => chunks.push(chunk))
|
|
731
|
+
const completedPdf = Promise.withResolvers<string>()
|
|
732
|
+
rawPdf.on('end', () => completedPdf.resolve(Buffer.concat(chunks).toString('latin1')))
|
|
733
|
+
const pdf = new FloorplanPdfDocument(rawPdf, [200, 200])
|
|
734
|
+
pdf.addPage()
|
|
735
|
+
for (const { model } of full) {
|
|
736
|
+
if (!model) continue
|
|
737
|
+
await renderFloorplanGeometryToPdfKit(pdf, model, {
|
|
738
|
+
annotationLayer: false,
|
|
739
|
+
placement: { x: 20, y: 20, width: 100, height: 100 },
|
|
740
|
+
rotationDeg: 0,
|
|
741
|
+
viewport: { x: -4, y: -4, width: 20, height: 20 },
|
|
742
|
+
})
|
|
743
|
+
}
|
|
744
|
+
rawPdf.end()
|
|
745
|
+
const renderedPdf = await completedPdf.promise
|
|
746
|
+
expect(renderedPdf).toMatch(/f\*/)
|
|
747
|
+
expect(renderedPdf).toContain('/Subtype /Image')
|
|
748
|
+
|
|
749
|
+
const upper = collectFloorplanGeometry(
|
|
750
|
+
nodes,
|
|
751
|
+
upperLevel.id,
|
|
752
|
+
'full',
|
|
753
|
+
'metric',
|
|
754
|
+
'meters',
|
|
755
|
+
DEFAULT_FLOORPLAN_ANNOTATION_VISIBILITY,
|
|
756
|
+
'floor-plan',
|
|
757
|
+
'finished-faces',
|
|
758
|
+
[enabledPluginId],
|
|
759
|
+
)
|
|
760
|
+
expect(upper.map(({ id }) => id)).toEqual(['site_overlay', 'level_upper_architecture'])
|
|
761
|
+
|
|
762
|
+
const hiddenSiteNodes = {
|
|
763
|
+
...nodes,
|
|
764
|
+
[activeSite.id]: { ...activeSite, visible: false } as AnyNode,
|
|
765
|
+
}
|
|
766
|
+
const hiddenSite = collectFloorplanGeometry(
|
|
767
|
+
hiddenSiteNodes,
|
|
768
|
+
activeLevel.id,
|
|
769
|
+
'full',
|
|
770
|
+
'metric',
|
|
771
|
+
'meters',
|
|
772
|
+
DEFAULT_FLOORPLAN_ANNOTATION_VISIBILITY,
|
|
773
|
+
'floor-plan',
|
|
774
|
+
'finished-faces',
|
|
775
|
+
[enabledPluginId],
|
|
776
|
+
)
|
|
777
|
+
expect(hiddenSite.map(({ id }) => id)).toEqual(['level_architecture'])
|
|
778
|
+
|
|
779
|
+
const structure = collectFloorplanGeometry(
|
|
780
|
+
nodes,
|
|
781
|
+
activeLevel.id,
|
|
782
|
+
'structure',
|
|
783
|
+
'metric',
|
|
784
|
+
'meters',
|
|
785
|
+
DEFAULT_FLOORPLAN_ANNOTATION_VISIBILITY,
|
|
786
|
+
'floor-plan',
|
|
787
|
+
'finished-faces',
|
|
788
|
+
[enabledPluginId],
|
|
789
|
+
)
|
|
790
|
+
expect(structure.map(({ id }) => id)).toEqual(['level_architecture'])
|
|
791
|
+
} finally {
|
|
792
|
+
restoreRegistry()
|
|
793
|
+
}
|
|
794
|
+
})
|
|
795
|
+
})
|
|
796
|
+
|
|
797
|
+
describe('resolveExportLevels', () => {
|
|
798
|
+
const ground = LevelNode.parse({ id: 'level_ground', parentId: 'building_a', level: 0 })
|
|
799
|
+
const upper = LevelNode.parse({ id: 'level_upper', parentId: 'building_a', level: 1 })
|
|
800
|
+
const roof = LevelNode.parse({
|
|
801
|
+
id: 'level_roof',
|
|
802
|
+
parentId: 'building_a',
|
|
803
|
+
level: 2,
|
|
804
|
+
metadata: { role: 'roof', referenceLevelId: upper.id },
|
|
805
|
+
})
|
|
806
|
+
const attic = LevelNode.parse({
|
|
807
|
+
id: 'level_attic',
|
|
808
|
+
parentId: 'building_a',
|
|
809
|
+
level: 3,
|
|
810
|
+
metadata: { role: 'attic' },
|
|
811
|
+
})
|
|
812
|
+
const building = BuildingNode.parse({
|
|
813
|
+
id: 'building_a',
|
|
814
|
+
children: [ground.id, upper.id, roof.id, attic.id],
|
|
815
|
+
})
|
|
816
|
+
const nodes: Record<string, AnyNode> = Object.fromEntries(
|
|
817
|
+
[building, ground, upper, roof, attic].map((node) => [node.id, node]),
|
|
818
|
+
)
|
|
819
|
+
|
|
820
|
+
// The viewer store is a process-wide singleton, so an earlier test file can
|
|
821
|
+
// leak a selection into these tests; restore it instead of leaving ours.
|
|
822
|
+
const previousSelection = useViewer.getState().selection
|
|
823
|
+
|
|
824
|
+
const selectLevel = (levelId: string | null) => {
|
|
825
|
+
useViewer.setState({
|
|
826
|
+
selection: { ...previousSelection, buildingId: building.id, levelId },
|
|
827
|
+
} as never)
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
afterEach(() => {
|
|
831
|
+
useViewer.setState({ selection: previousSelection } as never)
|
|
832
|
+
})
|
|
833
|
+
|
|
834
|
+
test('skips a dedicated roof support level', () => {
|
|
835
|
+
selectLevel(ground.id)
|
|
836
|
+
|
|
837
|
+
expect(resolveExportLevels(nodes)).toEqual([
|
|
838
|
+
{ id: ground.id, label: 'Level 0' },
|
|
839
|
+
{ id: upper.id, label: 'Level 1' },
|
|
840
|
+
{ id: attic.id, label: 'Level 3' },
|
|
841
|
+
])
|
|
842
|
+
})
|
|
843
|
+
|
|
844
|
+
test('skips the roof level when it is the selected level', () => {
|
|
845
|
+
selectLevel(roof.id)
|
|
846
|
+
|
|
847
|
+
expect(resolveExportLevels(nodes)).toEqual([
|
|
848
|
+
{ id: ground.id, label: 'Level 0' },
|
|
849
|
+
{ id: upper.id, label: 'Level 1' },
|
|
850
|
+
{ id: attic.id, label: 'Level 3' },
|
|
851
|
+
])
|
|
852
|
+
})
|
|
853
|
+
})
|