@pascal-app/viewer 1.0.7 → 1.1.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/README.md +209 -19
- package/dist/components/viewer/drone-controller.d.ts.map +1 -1
- package/dist/components/viewer/drone-controller.js +1 -5
- package/dist/components/viewer/first-person/build-collider-world.d.ts +14 -0
- package/dist/components/viewer/first-person/build-collider-world.d.ts.map +1 -0
- package/dist/components/viewer/first-person/build-collider-world.js +426 -0
- package/dist/components/viewer/first-person/ground-holes.d.ts +22 -0
- package/dist/components/viewer/first-person/ground-holes.d.ts.map +1 -0
- package/dist/components/viewer/first-person/ground-holes.js +116 -0
- package/dist/components/viewer/first-person/mechanism-targets.d.ts +27 -0
- package/dist/components/viewer/first-person/mechanism-targets.d.ts.map +1 -0
- package/dist/components/viewer/first-person/mechanism-targets.js +79 -0
- package/dist/components/viewer/first-person/terrain-collider.d.ts +32 -0
- package/dist/components/viewer/first-person/terrain-collider.d.ts.map +1 -0
- package/dist/components/viewer/first-person/terrain-collider.js +175 -0
- package/dist/components/viewer/glb-replace-instances.d.ts +6 -3
- package/dist/components/viewer/glb-replace-instances.d.ts.map +1 -1
- package/dist/components/viewer/glb-replace-instances.js +5 -4
- package/dist/components/viewer/glb-scene.d.ts +3 -1
- package/dist/components/viewer/glb-scene.d.ts.map +1 -1
- package/dist/components/viewer/glb-scene.js +2 -1
- package/dist/components/viewer/glb-walkthrough-controller.d.ts +1 -1
- package/dist/components/viewer/glb-walkthrough-controller.d.ts.map +1 -1
- package/dist/components/viewer/glb-walkthrough-controller.js +3 -4
- package/dist/components/viewer/index.d.ts.map +1 -1
- package/dist/components/viewer/index.js +8 -4
- package/dist/components/viewer/live-walkthrough-controller.d.ts +17 -0
- package/dist/components/viewer/live-walkthrough-controller.d.ts.map +1 -0
- package/dist/components/viewer/live-walkthrough-controller.js +1449 -0
- package/dist/components/viewer/post-processing.d.ts.map +1 -1
- package/dist/components/viewer/post-processing.js +22 -1
- package/dist/components/viewer/registered-systems.d.ts.map +1 -1
- package/dist/components/viewer/registered-systems.js +2 -2
- package/dist/components/viewer/unsupported-gpu-fallback.d.ts.map +1 -1
- package/dist/components/viewer/unsupported-gpu-fallback.js +21 -1
- package/dist/components/viewer/use-daylight-rooms.d.ts.map +1 -1
- package/dist/components/viewer/use-daylight-rooms.js +75 -33
- package/dist/hooks/use-gltf-ktx2.d.ts.map +1 -1
- package/dist/hooks/use-gltf-ktx2.js +2 -1
- package/dist/index.d.ts +8 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -1
- package/dist/lib/asset-url.d.ts +0 -1
- package/dist/lib/asset-url.d.ts.map +1 -1
- package/dist/lib/asset-url.js +3 -3
- package/dist/lib/daylight/room-context.d.ts.map +1 -1
- package/dist/lib/daylight/room-context.js +3 -2
- package/dist/lib/daylight/room-map.d.ts +149 -0
- package/dist/lib/daylight/room-map.d.ts.map +1 -0
- package/dist/lib/daylight/room-map.js +813 -0
- package/dist/lib/daylight/room-scene.d.ts +4 -2
- package/dist/lib/daylight/room-scene.d.ts.map +1 -1
- package/dist/lib/daylight/room-scene.js +81 -40
- package/dist/lib/daylight/rooms.d.ts +71 -32
- package/dist/lib/daylight/rooms.d.ts.map +1 -1
- package/dist/lib/daylight/rooms.js +371 -286
- package/dist/lib/ktx2-loader.d.ts.map +1 -1
- package/dist/lib/ktx2-loader.js +2 -1
- package/dist/lib/measurement-label.d.ts +4 -0
- package/dist/lib/measurement-label.d.ts.map +1 -0
- package/dist/lib/measurement-label.js +54 -0
- package/dist/lib/measurement-presentation.d.ts +7 -0
- package/dist/lib/measurement-presentation.d.ts.map +1 -0
- package/dist/lib/measurement-presentation.js +14 -0
- package/dist/lib/pointer-events.d.ts.map +1 -1
- package/dist/lib/pointer-events.js +3 -3
- package/dist/lib/texture-reference.d.ts.map +1 -1
- package/dist/lib/texture-reference.js +4 -3
- package/dist/lib/typing-target.d.ts +2 -0
- package/dist/lib/typing-target.d.ts.map +1 -0
- package/dist/lib/typing-target.js +7 -0
- package/dist/lib/viewer-config.d.ts +27 -0
- package/dist/lib/viewer-config.d.ts.map +1 -0
- package/dist/lib/viewer-config.js +23 -0
- package/dist/lib/viewer-preference-storage.d.ts +7 -0
- package/dist/lib/viewer-preference-storage.d.ts.map +1 -0
- package/dist/lib/viewer-preference-storage.js +23 -0
- package/dist/sfx/audio-settings.d.ts +81 -0
- package/dist/sfx/audio-settings.d.ts.map +1 -0
- package/dist/sfx/audio-settings.js +166 -0
- package/dist/sfx/index.d.ts +6 -0
- package/dist/sfx/index.d.ts.map +1 -0
- package/dist/sfx/index.js +5 -0
- package/dist/sfx/interaction-sounds.d.ts +7 -0
- package/dist/sfx/interaction-sounds.d.ts.map +1 -0
- package/dist/sfx/interaction-sounds.js +39 -0
- package/dist/sfx/player.d.ts +31 -0
- package/dist/sfx/player.d.ts.map +1 -0
- package/dist/sfx/player.js +255 -0
- package/dist/sfx/sounds.d.ts +60 -0
- package/dist/sfx/sounds.d.ts.map +1 -0
- package/dist/sfx/sounds.js +25 -0
- package/dist/sfx/use-viewer-ui-sounds.d.ts +8 -0
- package/dist/sfx/use-viewer-ui-sounds.d.ts.map +1 -0
- package/dist/sfx/use-viewer-ui-sounds.js +42 -0
- package/dist/store/use-first-person-hud.d.ts +13 -0
- package/dist/store/use-first-person-hud.d.ts.map +1 -0
- package/dist/store/use-first-person-hud.js +21 -0
- package/dist/store/use-viewer.d.ts +2 -56
- package/dist/store/use-viewer.d.ts.map +1 -1
- package/dist/store/use-viewer.js +3 -1
- package/dist/systems/elevator/elevator-interaction-system.d.ts +16 -2
- package/dist/systems/elevator/elevator-interaction-system.d.ts.map +1 -1
- package/dist/systems/elevator/elevator-interaction-system.js +41 -8
- package/dist/systems/geometry/geometry-status.d.ts +8 -0
- package/dist/systems/geometry/geometry-status.d.ts.map +1 -0
- package/dist/systems/geometry/geometry-status.js +24 -0
- package/dist/systems/roof/roof-system.d.ts.map +1 -1
- package/dist/systems/roof/roof-system.js +134 -78
- package/dist/systems/wall/wall-system.d.ts +11 -0
- package/dist/systems/wall/wall-system.d.ts.map +1 -1
- package/dist/systems/wall/wall-system.js +56 -10
- package/dist/ui/components/action-button.d.ts +14 -0
- package/dist/ui/components/action-button.d.ts.map +1 -0
- package/dist/ui/components/action-button.js +21 -0
- package/dist/ui/components/action-menu-placement.d.ts +7 -0
- package/dist/ui/components/action-menu-placement.d.ts.map +1 -0
- package/dist/ui/components/action-menu-placement.js +10 -0
- package/dist/ui/components/floorplan-compass-button.d.ts +10 -0
- package/dist/ui/components/floorplan-compass-button.d.ts.map +1 -0
- package/dist/ui/components/floorplan-compass-button.js +22 -0
- package/dist/ui/components/follow-pascal-toggle.d.ts +6 -0
- package/dist/ui/components/follow-pascal-toggle.d.ts.map +1 -0
- package/dist/ui/components/follow-pascal-toggle.js +21 -0
- package/dist/ui/components/localized-html.d.ts +9 -0
- package/dist/ui/components/localized-html.d.ts.map +1 -0
- package/dist/ui/components/localized-html.js +16 -0
- package/dist/ui/components/render-settings-menu.d.ts +9 -0
- package/dist/ui/components/render-settings-menu.d.ts.map +1 -0
- package/dist/ui/components/render-settings-menu.js +145 -0
- package/dist/ui/components/rooms/room-highlight.d.ts +10 -0
- package/dist/ui/components/rooms/room-highlight.d.ts.map +1 -0
- package/dist/ui/components/rooms/room-highlight.js +56 -0
- package/dist/ui/components/rooms/room-labels.d.ts +278 -0
- package/dist/ui/components/rooms/room-labels.d.ts.map +1 -0
- package/dist/ui/components/rooms/room-labels.js +1295 -0
- package/dist/ui/components/rooms/viewer-rooms.d.ts +14 -0
- package/dist/ui/components/rooms/viewer-rooms.d.ts.map +1 -0
- package/dist/ui/components/rooms/viewer-rooms.js +123 -0
- package/dist/ui/components/scene-loader.d.ts +18 -0
- package/dist/ui/components/scene-loader.d.ts.map +1 -0
- package/dist/ui/components/scene-loader.js +29 -0
- package/dist/ui/components/sky-control.d.ts +37 -0
- package/dist/ui/components/sky-control.d.ts.map +1 -0
- package/dist/ui/components/sky-control.js +382 -0
- package/dist/ui/components/use-viewer-camera-navigation-sync.d.ts +4 -0
- package/dist/ui/components/use-viewer-camera-navigation-sync.d.ts.map +1 -0
- package/dist/ui/components/use-viewer-camera-navigation-sync.js +117 -0
- package/dist/ui/components/viewer-controls-bar.d.ts +20 -0
- package/dist/ui/components/viewer-controls-bar.d.ts.map +1 -0
- package/dist/ui/components/viewer-controls-bar.js +130 -0
- package/dist/ui/components/viewer-hover-label.d.ts +14 -0
- package/dist/ui/components/viewer-hover-label.d.ts.map +1 -0
- package/dist/ui/components/viewer-hover-label.js +28 -0
- package/dist/ui/components/viewer-scene-header.d.ts +34 -0
- package/dist/ui/components/viewer-scene-header.d.ts.map +1 -0
- package/dist/ui/components/viewer-scene-header.js +92 -0
- package/dist/ui/components/viewer-selection-manager.d.ts +7 -0
- package/dist/ui/components/viewer-selection-manager.d.ts.map +1 -0
- package/dist/ui/components/viewer-selection-manager.js +116 -0
- package/dist/ui/components/viewer-stage-modes.d.ts +7 -0
- package/dist/ui/components/viewer-stage-modes.d.ts.map +1 -0
- package/dist/ui/components/viewer-stage-modes.js +42 -0
- package/dist/ui/components/viewer-stage-switcher.d.ts +10 -0
- package/dist/ui/components/viewer-stage-switcher.d.ts.map +1 -0
- package/dist/ui/components/viewer-stage-switcher.js +16 -0
- package/dist/ui/components/viewer-stage.d.ts +25 -0
- package/dist/ui/components/viewer-stage.d.ts.map +1 -0
- package/dist/ui/components/viewer-stage.js +107 -0
- package/dist/ui/components/walkthrough-hud.d.ts +29 -0
- package/dist/ui/components/walkthrough-hud.d.ts.map +1 -0
- package/dist/ui/components/walkthrough-hud.js +55 -0
- package/dist/ui/floorplan/floorplan-camera-sync.d.ts +22 -0
- package/dist/ui/floorplan/floorplan-camera-sync.d.ts.map +1 -0
- package/dist/ui/floorplan/floorplan-camera-sync.js +157 -0
- package/dist/ui/floorplan/floorplan-dimension-renderer.d.ts +61 -0
- package/dist/ui/floorplan/floorplan-dimension-renderer.d.ts.map +1 -0
- package/dist/ui/floorplan/floorplan-dimension-renderer.js +280 -0
- package/dist/ui/floorplan/floorplan-geometry-renderer.d.ts +54 -0
- package/dist/ui/floorplan/floorplan-geometry-renderer.d.ts.map +1 -0
- package/dist/ui/floorplan/floorplan-geometry-renderer.js +317 -0
- package/dist/ui/floorplan/floorplan-label-angle.d.ts +24 -0
- package/dist/ui/floorplan/floorplan-label-angle.d.ts.map +1 -0
- package/dist/ui/floorplan/floorplan-label-angle.js +60 -0
- package/dist/ui/floorplan/floorplan-navigation-sync.d.ts +18 -0
- package/dist/ui/floorplan/floorplan-navigation-sync.d.ts.map +1 -0
- package/dist/ui/floorplan/floorplan-navigation-sync.js +41 -0
- package/dist/ui/floorplan/floorplan-preview-geometry.d.ts +26 -0
- package/dist/ui/floorplan/floorplan-preview-geometry.d.ts.map +1 -0
- package/dist/ui/floorplan/floorplan-preview-geometry.js +173 -0
- package/dist/ui/floorplan/floorplan-preview-navigation.d.ts +22 -0
- package/dist/ui/floorplan/floorplan-preview-navigation.d.ts.map +1 -0
- package/dist/ui/floorplan/floorplan-preview-navigation.js +41 -0
- package/dist/ui/floorplan/floorplan-preview-visibility.d.ts +8 -0
- package/dist/ui/floorplan/floorplan-preview-visibility.d.ts.map +1 -0
- package/dist/ui/floorplan/floorplan-preview-visibility.js +21 -0
- package/dist/ui/floorplan/floorplan-preview.d.ts +19 -0
- package/dist/ui/floorplan/floorplan-preview.d.ts.map +1 -0
- package/dist/ui/floorplan/floorplan-preview.js +700 -0
- package/dist/ui/hooks/use-mobile.d.ts +2 -0
- package/dist/ui/hooks/use-mobile.d.ts.map +1 -0
- package/dist/ui/hooks/use-mobile.js +14 -0
- package/dist/ui/index.d.ts +49 -0
- package/dist/ui/index.d.ts.map +1 -0
- package/dist/ui/index.js +48 -0
- package/dist/ui/lib/below-grade.d.ts +38 -0
- package/dist/ui/lib/below-grade.d.ts.map +1 -0
- package/dist/ui/lib/below-grade.js +102 -0
- package/dist/ui/lib/camera-pose.d.ts +19 -0
- package/dist/ui/lib/camera-pose.d.ts.map +1 -0
- package/dist/ui/lib/camera-pose.js +113 -0
- package/dist/ui/lib/cn.d.ts +3 -0
- package/dist/ui/lib/cn.d.ts.map +1 -0
- package/dist/ui/lib/cn.js +5 -0
- package/dist/ui/lib/level-name.d.ts +10 -0
- package/dist/ui/lib/level-name.d.ts.map +1 -0
- package/dist/ui/lib/level-name.js +33 -0
- package/dist/ui/lib/node-display-label.d.ts +8 -0
- package/dist/ui/lib/node-display-label.d.ts.map +1 -0
- package/dist/ui/lib/node-display-label.js +36 -0
- package/dist/ui/lib/room-area.d.ts +3 -0
- package/dist/ui/lib/room-area.d.ts.map +1 -0
- package/dist/ui/lib/room-area.js +8 -0
- package/dist/ui/lib/room-assembly-overlay.d.ts +46 -0
- package/dist/ui/lib/room-assembly-overlay.d.ts.map +1 -0
- package/dist/ui/lib/room-assembly-overlay.js +222 -0
- package/dist/ui/lib/room-name-messages.d.ts +28 -0
- package/dist/ui/lib/room-name-messages.d.ts.map +1 -0
- package/dist/ui/lib/room-name-messages.js +112 -0
- package/dist/ui/lib/room-selection.d.ts +58 -0
- package/dist/ui/lib/room-selection.d.ts.map +1 -0
- package/dist/ui/lib/room-selection.js +297 -0
- package/dist/ui/lib/scene-theme-name.d.ts +4 -0
- package/dist/ui/lib/scene-theme-name.d.ts.map +1 -0
- package/dist/ui/lib/scene-theme-name.js +22 -0
- package/dist/ui/lib/selection-modifiers.d.ts +20 -0
- package/dist/ui/lib/selection-modifiers.d.ts.map +1 -0
- package/dist/ui/lib/selection-modifiers.js +15 -0
- package/dist/ui/lib/viewer-selection.d.ts +132 -0
- package/dist/ui/lib/viewer-selection.d.ts.map +1 -0
- package/dist/ui/lib/viewer-selection.js +462 -0
- package/dist/ui/lib/zone-content.d.ts +6 -0
- package/dist/ui/lib/zone-content.d.ts.map +1 -0
- package/dist/ui/lib/zone-content.js +96 -0
- package/dist/ui/primitives/button.d.ts +11 -0
- package/dist/ui/primitives/button.d.ts.map +1 -0
- package/dist/ui/primitives/button.js +35 -0
- package/dist/ui/primitives/dropdown-menu.d.ts +26 -0
- package/dist/ui/primitives/dropdown-menu.d.ts.map +1 -0
- package/dist/ui/primitives/dropdown-menu.js +53 -0
- package/dist/ui/primitives/popover.d.ts +8 -0
- package/dist/ui/primitives/popover.d.ts.map +1 -0
- package/dist/ui/primitives/popover.js +17 -0
- package/dist/ui/primitives/switch.d.ts +5 -0
- package/dist/ui/primitives/switch.d.ts.map +1 -0
- package/dist/ui/primitives/switch.js +8 -0
- package/dist/ui/primitives/tooltip.d.ts +8 -0
- package/dist/ui/primitives/tooltip.d.ts.map +1 -0
- package/dist/ui/primitives/tooltip.js +17 -0
- package/dist/ui/store/camera-pose-store.d.ts +17 -0
- package/dist/ui/store/camera-pose-store.d.ts.map +1 -0
- package/dist/ui/store/camera-pose-store.js +15 -0
- package/dist/ui/store/navigation-sync-pose-store.d.ts +26 -0
- package/dist/ui/store/navigation-sync-pose-store.d.ts.map +1 -0
- package/dist/ui/store/navigation-sync-pose-store.js +16 -0
- package/dist/ui/store/use-follow-pascal.d.ts +30 -0
- package/dist/ui/store/use-follow-pascal.d.ts.map +1 -0
- package/dist/ui/store/use-follow-pascal.js +28 -0
- package/dist/ui/theme.css +339 -0
- package/package.json +34 -7
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { floorplanAnnotationObstacleMode, isFloorplanAnnotationObstacleGeometry, loadAssetUrl, readFloorplanGeometryMetadata, } from '@pascal-app/core';
|
|
4
|
+
import { memo, useEffect, useState } from 'react';
|
|
5
|
+
import { FloorplanDimensionRenderer, FloorplanDimensionStringRenderer, } from './floorplan-dimension-renderer';
|
|
6
|
+
import { resolveFloorplanLabelAngle } from './floorplan-label-angle';
|
|
7
|
+
const STATIC_LABEL_UNITS_PER_PIXEL = 0.01;
|
|
8
|
+
const DOCUMENT_DEFAULT_TEXT_SIZE_PT = 8;
|
|
9
|
+
const DOCUMENT_ROOM_NAME_TEXT_SIZE_PT = 8;
|
|
10
|
+
const DOCUMENT_ROOM_NUMBER_TEXT_SIZE_PT = 7;
|
|
11
|
+
const DOCUMENT_ROOM_DETAIL_TEXT_SIZE_PT = 5.5;
|
|
12
|
+
const DOCUMENT_COLUMN_MARK_TEXT_SIZE_PT = 7;
|
|
13
|
+
const DOCUMENT_DEFAULT_STROKE_WIDTH_PT = 0.5;
|
|
14
|
+
const DOCUMENT_TEXT_OUTLINE_MIN_WIDTH_PT = 0.75;
|
|
15
|
+
const DOCUMENT_MARK_HEIGHT_PT = 14;
|
|
16
|
+
const PDF_ANNOTATION_STROKE_WIDTH_PT = 0.5;
|
|
17
|
+
/**
|
|
18
|
+
* Pure-data → SVG converter. Walks a `FloorplanGeometry` tree returned by
|
|
19
|
+
* `def.floorplan(node, ctx)` and emits the matching React-SVG nodes.
|
|
20
|
+
*
|
|
21
|
+
* Coordinates are level-local meters. The wrapping floor-plan panel
|
|
22
|
+
* applies the world→SVG transform via its viewBox, so kinds emit
|
|
23
|
+
* geometry in the same units they reason about in 3D.
|
|
24
|
+
*
|
|
25
|
+
* Group transforms compose: `transform={translate(x y) rotate(deg)}`.
|
|
26
|
+
* Rotations are radians at the data layer (consistent with three.js
|
|
27
|
+
* conventions used by `def.geometry`) and converted to degrees for SVG
|
|
28
|
+
* here — kinds never touch units.
|
|
29
|
+
*
|
|
30
|
+
* Styling props map straight onto SVG attributes. Builders that need
|
|
31
|
+
* theme colors should declare them inline or expose them as registry
|
|
32
|
+
* tokens later (deferred until a real need surfaces — AI-authored kinds
|
|
33
|
+
* can pick safe defaults today).
|
|
34
|
+
*/
|
|
35
|
+
export const FloorplanGeometryRenderer = memo(function FloorplanGeometryRenderer({ geometry, pointerEventsOverride, sceneRotationDeg = 0, annotationUnitsPerPoint, screenUnitsPerPixel, renderMode = 'screen', }) {
|
|
36
|
+
return renderNode(geometry, 0, pointerEventsOverride, sceneRotationDeg, annotationUnitsPerPoint, screenUnitsPerPixel, renderMode);
|
|
37
|
+
});
|
|
38
|
+
function styleAttrs(g, pointerEventsOverride, annotationUnitsPerPoint, renderMode = 'screen') {
|
|
39
|
+
// Shared SVG attribute mapping for any styled primitive. Keeps the per-
|
|
40
|
+
// primitive switch arms terse and ensures new style fields land
|
|
41
|
+
// everywhere at once. `as any` avoids re-asserting every variant
|
|
42
|
+
// includes the style fields — they all do, except `group` (which is
|
|
43
|
+
// filtered out by the caller's type bound).
|
|
44
|
+
const s = g;
|
|
45
|
+
const annotationMetadata = readFloorplanGeometryMetadata(g);
|
|
46
|
+
const documentStyle = resolveDocumentFloorplanAnnotationStyle(g, annotationUnitsPerPoint);
|
|
47
|
+
const vectorEffect = documentStyle.vectorEffect ?? s.vectorEffect;
|
|
48
|
+
const resolvedStrokeWidth = documentStyle.strokeWidth ?? s.strokeWidth;
|
|
49
|
+
const strokeWidth = renderMode === 'pdf' &&
|
|
50
|
+
vectorEffect === 'non-scaling-stroke' &&
|
|
51
|
+
resolvedStrokeWidth !== undefined
|
|
52
|
+
? Math.min(PDF_ANNOTATION_STROKE_WIDTH_PT, resolvedStrokeWidth)
|
|
53
|
+
: resolvedStrokeWidth;
|
|
54
|
+
return {
|
|
55
|
+
'data-floorplan-annotation-obstacle': floorplanAnnotationObstacleMode(g),
|
|
56
|
+
'data-floorplan-annotation-role': annotationMetadata.annotationRole,
|
|
57
|
+
fill: documentStyle.fill ?? s.fill ?? 'none',
|
|
58
|
+
fillRule: s.fillRule,
|
|
59
|
+
fillOpacity: s.fillOpacity,
|
|
60
|
+
stroke: documentStyle.stroke ?? s.stroke,
|
|
61
|
+
strokeWidth,
|
|
62
|
+
strokeDasharray: s.strokeDasharray,
|
|
63
|
+
strokeLinecap: s.strokeLinecap,
|
|
64
|
+
strokeLinejoin: s.strokeLinejoin,
|
|
65
|
+
strokeOpacity: s.strokeOpacity,
|
|
66
|
+
opacity: s.opacity,
|
|
67
|
+
vectorEffect,
|
|
68
|
+
pointerEvents: pointerEventsOverride ?? s.pointerEvents,
|
|
69
|
+
style: s.cursor ? { cursor: s.cursor } : undefined,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
export function resolveDocumentFloorplanAnnotationStyle(geometry, annotationUnitsPerPoint) {
|
|
73
|
+
if (annotationUnitsPerPoint === undefined)
|
|
74
|
+
return {};
|
|
75
|
+
const styled = geometry;
|
|
76
|
+
if (!styled.stroke && geometry.kind !== 'text')
|
|
77
|
+
return {};
|
|
78
|
+
if (geometry.kind === 'text') {
|
|
79
|
+
const sourceFontSize = Math.max(geometry.fontSize, 1e-6);
|
|
80
|
+
const sourceStrokeWidth = geometry.strokeWidth ?? 0;
|
|
81
|
+
const outlineRatio = sourceStrokeWidth > 0 ? sourceStrokeWidth / sourceFontSize : 0;
|
|
82
|
+
const fontSize = documentTextFontSize(geometry, annotationUnitsPerPoint);
|
|
83
|
+
return {
|
|
84
|
+
strokeWidth: geometry.stroke && outlineRatio > 0
|
|
85
|
+
? Math.max(DOCUMENT_TEXT_OUTLINE_MIN_WIDTH_PT * annotationUnitsPerPoint, fontSize * outlineRatio)
|
|
86
|
+
: undefined,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
return {
|
|
90
|
+
strokeWidth: documentStrokeWidth(styled, annotationUnitsPerPoint),
|
|
91
|
+
vectorEffect: 'non-scaling-stroke',
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
function documentTextFontSize(geometry, annotationUnitsPerPoint) {
|
|
95
|
+
return documentTextSizePt(geometry) * annotationUnitsPerPoint;
|
|
96
|
+
}
|
|
97
|
+
function documentTextSizePt(geometry) {
|
|
98
|
+
const metadata = readFloorplanGeometryMetadata(geometry);
|
|
99
|
+
// an explicit paper size wins over the role's (a sheet's room tag sets its own)
|
|
100
|
+
if (typeof metadata.textSizePt === 'number' && metadata.textSizePt > 0)
|
|
101
|
+
return metadata.textSizePt;
|
|
102
|
+
switch (metadata.annotationRole) {
|
|
103
|
+
case 'room-label':
|
|
104
|
+
if (geometry.fontSize >= 0.18)
|
|
105
|
+
return DOCUMENT_ROOM_NAME_TEXT_SIZE_PT;
|
|
106
|
+
if (geometry.fontSize >= 0.145)
|
|
107
|
+
return DOCUMENT_ROOM_NUMBER_TEXT_SIZE_PT;
|
|
108
|
+
return DOCUMENT_ROOM_DETAIL_TEXT_SIZE_PT;
|
|
109
|
+
case 'column-center':
|
|
110
|
+
case 'stair-annotation':
|
|
111
|
+
return DOCUMENT_COLUMN_MARK_TEXT_SIZE_PT;
|
|
112
|
+
default:
|
|
113
|
+
return DOCUMENT_DEFAULT_TEXT_SIZE_PT;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
function documentStrokeWidth(geometry, annotationUnitsPerPoint) {
|
|
117
|
+
return Math.max(DOCUMENT_DEFAULT_STROKE_WIDTH_PT, Math.min(1.2, geometry.strokeWidth ?? 0.5));
|
|
118
|
+
}
|
|
119
|
+
export function documentRectGeometryAttrs(geometry, annotationUnitsPerPoint) {
|
|
120
|
+
if (annotationUnitsPerPoint === undefined || !isAnnotationMarkRect(geometry)) {
|
|
121
|
+
return {
|
|
122
|
+
x: geometry.x,
|
|
123
|
+
y: geometry.y,
|
|
124
|
+
width: geometry.width,
|
|
125
|
+
height: geometry.height,
|
|
126
|
+
rx: geometry.rx,
|
|
127
|
+
ry: geometry.ry,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
const centerX = geometry.x + geometry.width / 2;
|
|
131
|
+
const centerY = geometry.y + geometry.height / 2;
|
|
132
|
+
const height = DOCUMENT_MARK_HEIGHT_PT * annotationUnitsPerPoint;
|
|
133
|
+
const width = Math.max(height * 1.6, (geometry.width / Math.max(geometry.height, 1e-6)) * height);
|
|
134
|
+
return {
|
|
135
|
+
x: centerX - width / 2,
|
|
136
|
+
y: centerY - height / 2,
|
|
137
|
+
width,
|
|
138
|
+
height,
|
|
139
|
+
rx: height / 2,
|
|
140
|
+
ry: height / 2,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
function isAnnotationMarkRect(geometry) {
|
|
144
|
+
return geometry.fill === '#ffffff' && !!geometry.stroke && geometry.height <= 0.5;
|
|
145
|
+
}
|
|
146
|
+
export function documentCircleGeometryAttrs(geometry, annotationUnitsPerPoint) {
|
|
147
|
+
if (annotationUnitsPerPoint === undefined || !isAnnotationMarkCircle(geometry)) {
|
|
148
|
+
return { r: geometry.r };
|
|
149
|
+
}
|
|
150
|
+
return { r: Math.max(geometry.r, (DOCUMENT_MARK_HEIGHT_PT / 2) * annotationUnitsPerPoint) };
|
|
151
|
+
}
|
|
152
|
+
function isAnnotationMarkCircle(geometry) {
|
|
153
|
+
return geometry.fill === '#ffffff' && !!geometry.stroke && geometry.r <= 0.25;
|
|
154
|
+
}
|
|
155
|
+
export function resolveDocumentAnnotationGroupChildren(children, annotationUnitsPerPoint) {
|
|
156
|
+
if (annotationUnitsPerPoint === undefined)
|
|
157
|
+
return children;
|
|
158
|
+
const next = [...children];
|
|
159
|
+
let start = 0;
|
|
160
|
+
while (start < next.length) {
|
|
161
|
+
const first = next[start];
|
|
162
|
+
if (!isDocumentTextLine(first)) {
|
|
163
|
+
start++;
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
let end = start + 1;
|
|
167
|
+
while (end < next.length && isSameDocumentTextRun(first, next[end]))
|
|
168
|
+
end++;
|
|
169
|
+
if (end - start > 1) {
|
|
170
|
+
const run = next.slice(start, end);
|
|
171
|
+
const adjusted = positionDocumentTextRun(run, annotationUnitsPerPoint);
|
|
172
|
+
for (let index = 0; index < adjusted.length; index++) {
|
|
173
|
+
const line = adjusted[index];
|
|
174
|
+
if (line)
|
|
175
|
+
next[start + index] = line;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
start = end;
|
|
179
|
+
}
|
|
180
|
+
return next;
|
|
181
|
+
}
|
|
182
|
+
function isDocumentTextLine(geometry) {
|
|
183
|
+
return geometry?.kind === 'text' && geometry.upright === true;
|
|
184
|
+
}
|
|
185
|
+
function isSameDocumentTextRun(first, candidate) {
|
|
186
|
+
return (isDocumentTextLine(candidate) &&
|
|
187
|
+
Math.abs(candidate.x - first.x) < 1e-6 &&
|
|
188
|
+
candidate.textAnchor === first.textAnchor &&
|
|
189
|
+
readFloorplanGeometryMetadata(candidate).annotationRole ===
|
|
190
|
+
readFloorplanGeometryMetadata(first).annotationRole);
|
|
191
|
+
}
|
|
192
|
+
function positionDocumentTextRun(run, annotationUnitsPerPoint) {
|
|
193
|
+
const centerY = run.reduce((sum, line) => sum + line.y, 0) / run.length;
|
|
194
|
+
const steps = run.slice(0, -1).map((line, index) => {
|
|
195
|
+
const next = run[index + 1] ?? line;
|
|
196
|
+
const largerFontPt = Math.max(documentTextSizePt(line), documentTextSizePt(next));
|
|
197
|
+
return largerFontPt * 1.25 * annotationUnitsPerPoint;
|
|
198
|
+
});
|
|
199
|
+
const totalHeight = steps.reduce((sum, step) => sum + step, 0);
|
|
200
|
+
let y = centerY - totalHeight / 2;
|
|
201
|
+
return run.map((line, index) => {
|
|
202
|
+
if (index > 0)
|
|
203
|
+
y += steps[index - 1] ?? 0;
|
|
204
|
+
return { ...line, y };
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
function renderNode(g, keyHint, pointerEventsOverride, sceneRotationDeg = 0, annotationUnitsPerPoint, screenUnitsPerPixel, renderMode = 'screen') {
|
|
208
|
+
switch (g.kind) {
|
|
209
|
+
case 'path':
|
|
210
|
+
return (_jsx("path", { d: g.d, ...styleAttrs(g, pointerEventsOverride, annotationUnitsPerPoint, renderMode) }, keyHint));
|
|
211
|
+
case 'polygon':
|
|
212
|
+
return (_jsx("polygon", { points: pointsToAttr(g.points), ...styleAttrs(g, pointerEventsOverride, annotationUnitsPerPoint, renderMode) }, keyHint));
|
|
213
|
+
case 'polyline':
|
|
214
|
+
return (_jsx("polyline", { points: pointsToAttr(g.points), ...styleAttrs(g, pointerEventsOverride, annotationUnitsPerPoint, renderMode) }, keyHint));
|
|
215
|
+
case 'rect': {
|
|
216
|
+
const attrs = documentRectGeometryAttrs(g, annotationUnitsPerPoint);
|
|
217
|
+
return (_jsx("rect", { height: attrs.height, rx: attrs.rx, ry: attrs.ry, width: attrs.width, x: attrs.x, y: attrs.y, ...styleAttrs(g, pointerEventsOverride, annotationUnitsPerPoint, renderMode) }, keyHint));
|
|
218
|
+
}
|
|
219
|
+
case 'circle': {
|
|
220
|
+
const attrs = documentCircleGeometryAttrs(g, annotationUnitsPerPoint);
|
|
221
|
+
return (_jsx("circle", { cx: g.cx, cy: g.cy, r: attrs.r, ...styleAttrs(g, pointerEventsOverride, annotationUnitsPerPoint, renderMode) }, keyHint));
|
|
222
|
+
}
|
|
223
|
+
case 'line':
|
|
224
|
+
return (_jsx("line", { x1: g.x1, x2: g.x2, y1: g.y1, y2: g.y2, ...styleAttrs(g, pointerEventsOverride, annotationUnitsPerPoint, renderMode) }, keyHint));
|
|
225
|
+
case 'text': {
|
|
226
|
+
const fontSize = annotationUnitsPerPoint !== undefined
|
|
227
|
+
? documentTextFontSize(g, annotationUnitsPerPoint)
|
|
228
|
+
: g.fontSize;
|
|
229
|
+
const textStyle = resolveDocumentFloorplanAnnotationStyle(g, annotationUnitsPerPoint);
|
|
230
|
+
const pdfOutlinedText = renderMode === 'pdf' && g.paintOrder === 'stroke' && !!g.stroke;
|
|
231
|
+
const fill = pdfOutlinedText && g.fill?.toLocaleLowerCase() === '#ffffff'
|
|
232
|
+
? g.stroke
|
|
233
|
+
: (g.fill ?? '#171717');
|
|
234
|
+
if (g.upright) {
|
|
235
|
+
return (_jsx("g", { "data-floorplan-annotation-obstacle": floorplanAnnotationObstacleMode(g), transform: `translate(${g.x} ${g.y}) rotate(${-sceneRotationDeg})`, children: _jsx("text", { dominantBaseline: g.dominantBaseline ?? 'middle', fill: fill, fontFamily: g.fontFamily, fontSize: fontSize, fontWeight: g.fontWeight, opacity: g.opacity, paintOrder: pdfOutlinedText ? undefined : g.paintOrder, pointerEvents: pointerEventsOverride, stroke: pdfOutlinedText ? undefined : g.stroke, strokeLinecap: !pdfOutlinedText && g.stroke ? 'round' : undefined, strokeLinejoin: !pdfOutlinedText && g.stroke ? 'round' : undefined, strokeWidth: pdfOutlinedText ? undefined : (textStyle.strokeWidth ?? g.strokeWidth), textAnchor: g.textAnchor ?? 'start', x: 0, y: 0, children: g.text }) }, keyHint));
|
|
236
|
+
}
|
|
237
|
+
return (_jsx("text", { "data-floorplan-annotation-obstacle": floorplanAnnotationObstacleMode(g), dominantBaseline: g.dominantBaseline ?? 'middle', fill: fill, fontFamily: g.fontFamily, fontSize: fontSize, fontWeight: g.fontWeight, opacity: g.opacity, paintOrder: pdfOutlinedText ? undefined : g.paintOrder, stroke: pdfOutlinedText ? undefined : g.stroke, strokeLinecap: !pdfOutlinedText && g.stroke ? 'round' : undefined, strokeLinejoin: !pdfOutlinedText && g.stroke ? 'round' : undefined, strokeWidth: pdfOutlinedText ? undefined : (textStyle.strokeWidth ?? g.strokeWidth), textAnchor: g.textAnchor ?? 'start', pointerEvents: pointerEventsOverride, x: g.x, y: g.y, children: g.text }, keyHint));
|
|
238
|
+
}
|
|
239
|
+
case 'dimension':
|
|
240
|
+
return (_jsx(FloorplanDimensionRenderer, { geometry: g, sceneRotationDeg: sceneRotationDeg, annotationUnitsPerPoint: annotationUnitsPerPoint, renderMode: renderMode }, keyHint));
|
|
241
|
+
case 'dimension-string':
|
|
242
|
+
return (_jsx(FloorplanDimensionStringRenderer, { annotationUnitsPerPoint: annotationUnitsPerPoint, geometry: g, renderMode: renderMode, sceneRotationDeg: sceneRotationDeg }, keyHint));
|
|
243
|
+
case 'dimension-label': {
|
|
244
|
+
const unitsPerPixel = annotationUnitsPerPoint ?? screenUnitsPerPixel ?? STATIC_LABEL_UNITS_PER_PIXEL;
|
|
245
|
+
const documentMode = annotationUnitsPerPoint !== undefined;
|
|
246
|
+
const outlined = g.appearance === 'outlined';
|
|
247
|
+
const pdfOutlined = outlined && renderMode === 'pdf';
|
|
248
|
+
const padX = unitsPerPixel * 6;
|
|
249
|
+
const padY = unitsPerPixel * 3;
|
|
250
|
+
const fontSize = unitsPerPixel * (documentMode ? 8 : outlined ? 12 : 10);
|
|
251
|
+
const textWidth = g.text.length * unitsPerPixel * 6.2;
|
|
252
|
+
const plateW = textWidth + padX * 2;
|
|
253
|
+
const plateH = fontSize + padY * 2;
|
|
254
|
+
const degrees = resolveFloorplanLabelAngle(g.angle, sceneRotationDeg, g.screenUpright);
|
|
255
|
+
const labelTransform = `translate(${g.cx} ${g.cy}) rotate(${degrees}) translate(0 ${-(g.offsetPx ?? 0) * unitsPerPixel})`;
|
|
256
|
+
return (_jsxs("g", { "data-floorplan-annotation-angle-radians": g.angle, "data-floorplan-annotation-default-transform": labelTransform, "data-floorplan-annotation-label": "", "data-floorplan-annotation-priority": "20", "data-floorplan-annotation-screen-upright": g.screenUpright ? 'true' : undefined, "data-floorplan-annotation-transform-after-rotation": `translate(0 ${-(g.offsetPx ?? 0) * unitsPerPixel})`, "data-floorplan-annotation-transform-before-rotation": `translate(${g.cx} ${g.cy})`, pointerEvents: "none", transform: labelTransform, children: [outlined && !pdfOutlined ? null : (_jsx("rect", { "data-floorplan-dimension-label-plate": pdfOutlined ? '' : undefined, fill: "#ffffff", height: plateH, opacity: 0.92, rx: unitsPerPixel * 3, ry: unitsPerPixel * 3, stroke: pdfOutlined ? undefined : '#334155', strokeWidth: pdfOutlined ? undefined : unitsPerPixel * 0.5, width: plateW, x: -plateW / 2, y: -plateH / 2 })), _jsx("text", { dominantBaseline: "middle", fill: pdfOutlined ? '#111827' : outlined ? '#ffffff' : '#111827', fontFamily: outlined && !pdfOutlined
|
|
257
|
+
? 'system-ui, -apple-system, sans-serif'
|
|
258
|
+
: 'ui-monospace, SFMono-Regular, Menlo, monospace', fontSize: fontSize, fontWeight: outlined ? 500 : 600, paintOrder: outlined && !pdfOutlined ? 'stroke' : undefined, stroke: outlined && !pdfOutlined ? '#334155' : undefined, strokeLinecap: outlined && !pdfOutlined ? 'round' : undefined, strokeLinejoin: outlined && !pdfOutlined ? 'round' : undefined, strokeWidth: outlined && !pdfOutlined ? fontSize * 0.35 : undefined, textAnchor: "middle", x: 0, y: 0, children: g.text })] }, keyHint));
|
|
259
|
+
}
|
|
260
|
+
case 'image':
|
|
261
|
+
return (_jsx(FloorplanImage, { center: g.center, height: g.height, opacity: g.opacity, preserveAspectRatio: g.preserveAspectRatio ?? 'xMidYMid meet', rotation: g.rotation ?? 0, url: g.url, width: g.width }, keyHint));
|
|
262
|
+
case 'group': {
|
|
263
|
+
const transform = formatTransform(g.transform);
|
|
264
|
+
const children = resolveDocumentAnnotationGroupChildren(g.children, annotationUnitsPerPoint);
|
|
265
|
+
return (_jsx("g", { "data-floorplan-annotation-obstacle": isFloorplanAnnotationObstacleGeometry(g) ? '' : undefined, transform: transform, children: children.map((child, i) => renderNode(child, i, pointerEventsOverride, sceneRotationDeg, annotationUnitsPerPoint, screenUnitsPerPixel, renderMode)) }, keyHint));
|
|
266
|
+
}
|
|
267
|
+
// The remaining interactive primitives (hatch / hit-line / endpoint-handle)
|
|
268
|
+
// need the SVG context + theme palette + units-per-
|
|
269
|
+
// pixel that only the registry layer has access to. They're rendered
|
|
270
|
+
// by `floorplan-registry-layer.tsx`'s interactive walker instead. If
|
|
271
|
+
// a caller routes one of these through this pure renderer it
|
|
272
|
+
// silently drops — the static renderer is for static output.
|
|
273
|
+
default:
|
|
274
|
+
return null;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
function pointsToAttr(points) {
|
|
278
|
+
return points.map(([x, y]) => `${x},${y}`).join(' ');
|
|
279
|
+
}
|
|
280
|
+
function formatTransform(t) {
|
|
281
|
+
if (!t)
|
|
282
|
+
return undefined;
|
|
283
|
+
const parts = [];
|
|
284
|
+
if (t.translate)
|
|
285
|
+
parts.push(`translate(${t.translate[0]} ${t.translate[1]})`);
|
|
286
|
+
if (t.rotate !== undefined)
|
|
287
|
+
parts.push(`rotate(${(t.rotate * 180) / Math.PI})`);
|
|
288
|
+
return parts.length > 0 ? parts.join(' ') : undefined;
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* `image` primitive renderer. Resolves the URL asynchronously via
|
|
292
|
+
* `loadAssetUrl` (handles CDN / Supabase storage) and renders an SVG
|
|
293
|
+
* `<image>` centered at `center`, rotated around it, sized in plan-local
|
|
294
|
+
* metres. While the resolution is in flight, renders nothing.
|
|
295
|
+
*/
|
|
296
|
+
function FloorplanImage({ url, center, width, height, rotation, preserveAspectRatio, opacity, }) {
|
|
297
|
+
const [resolvedUrl, setResolvedUrl] = useState(null);
|
|
298
|
+
useEffect(() => {
|
|
299
|
+
if (!url) {
|
|
300
|
+
setResolvedUrl(null);
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
let cancelled = false;
|
|
304
|
+
setResolvedUrl(null);
|
|
305
|
+
loadAssetUrl(url).then((next) => {
|
|
306
|
+
if (!cancelled)
|
|
307
|
+
setResolvedUrl(next);
|
|
308
|
+
});
|
|
309
|
+
return () => {
|
|
310
|
+
cancelled = true;
|
|
311
|
+
};
|
|
312
|
+
}, [url]);
|
|
313
|
+
if (!resolvedUrl)
|
|
314
|
+
return null;
|
|
315
|
+
const rotationDeg = (rotation * 180) / Math.PI;
|
|
316
|
+
return (_jsx("g", { pointerEvents: "none", transform: `translate(${center[0]} ${center[1]}) rotate(${rotationDeg})`, children: _jsx("image", { height: height, href: resolvedUrl, opacity: opacity, preserveAspectRatio: preserveAspectRatio, width: width, x: -width / 2, y: -height / 2 }) }));
|
|
317
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export declare function resolveFloorplanLabelAngle(angleRadians: number, sceneRotationDeg: number, screenUpright?: boolean): number;
|
|
2
|
+
export declare function shouldUpdateFloorplanLabelRotation(previousRotationDeg: number | null, nextRotationDeg: number, minimumDeltaDeg?: number): boolean;
|
|
3
|
+
export declare function resolveFloorplanAnnotationUpdate({ layoutInputsChanged, previousRotationDeg, nextRotationDeg, }: {
|
|
4
|
+
layoutInputsChanged: boolean;
|
|
5
|
+
previousRotationDeg: number | null;
|
|
6
|
+
nextRotationDeg: number;
|
|
7
|
+
}): {
|
|
8
|
+
resolveCollisions: boolean;
|
|
9
|
+
updateLabelPresentation: boolean;
|
|
10
|
+
};
|
|
11
|
+
export declare function resolveFloorplanAnnotationLabelTransform({ angleRadians, sceneRotationDeg, screenUpright, beforeRotation, afterRotation, layoutDx, layoutDy, }: {
|
|
12
|
+
angleRadians: number;
|
|
13
|
+
sceneRotationDeg: number;
|
|
14
|
+
screenUpright: boolean;
|
|
15
|
+
beforeRotation: string;
|
|
16
|
+
afterRotation: string;
|
|
17
|
+
layoutDx?: number;
|
|
18
|
+
layoutDy?: number;
|
|
19
|
+
}): {
|
|
20
|
+
defaultTransform: string;
|
|
21
|
+
transform: string;
|
|
22
|
+
};
|
|
23
|
+
export declare function updateSvgFloorplanLabelOrientations(labels: Iterable<SVGGElement>, sceneRotationDeg: number): number;
|
|
24
|
+
//# sourceMappingURL=floorplan-label-angle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"floorplan-label-angle.d.ts","sourceRoot":"","sources":["../../../src/ui/floorplan/floorplan-label-angle.ts"],"names":[],"mappings":"AAAA,wBAAgB,0BAA0B,CACxC,YAAY,EAAE,MAAM,EACpB,gBAAgB,EAAE,MAAM,EACxB,aAAa,UAAQ,GACpB,MAAM,CASR;AAED,wBAAgB,kCAAkC,CAChD,mBAAmB,EAAE,MAAM,GAAG,IAAI,EAClC,eAAe,EAAE,MAAM,EACvB,eAAe,SAAI,GAClB,OAAO,CAIT;AAED,wBAAgB,gCAAgC,CAAC,EAC/C,mBAAmB,EACnB,mBAAmB,EACnB,eAAe,GAChB,EAAE;IACD,mBAAmB,EAAE,OAAO,CAAA;IAC5B,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAA;IAClC,eAAe,EAAE,MAAM,CAAA;CACxB,GAAG;IACF,iBAAiB,EAAE,OAAO,CAAA;IAC1B,uBAAuB,EAAE,OAAO,CAAA;CACjC,CAOA;AAED,wBAAgB,wCAAwC,CAAC,EACvD,YAAY,EACZ,gBAAgB,EAChB,aAAa,EACb,cAAc,EACd,aAAa,EACb,QAAY,EACZ,QAAY,GACb,EAAE;IACD,YAAY,EAAE,MAAM,CAAA;IACpB,gBAAgB,EAAE,MAAM,CAAA;IACxB,aAAa,EAAE,OAAO,CAAA;IACtB,cAAc,EAAE,MAAM,CAAA;IACtB,aAAa,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB,GAAG;IACF,gBAAgB,EAAE,MAAM,CAAA;IACxB,SAAS,EAAE,MAAM,CAAA;CAClB,CAYA;AAED,wBAAgB,mCAAmC,CACjD,MAAM,EAAE,QAAQ,CAAC,WAAW,CAAC,EAC7B,gBAAgB,EAAE,MAAM,GACvB,MAAM,CAwBR"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
export function resolveFloorplanLabelAngle(angleRadians, sceneRotationDeg, screenUpright = false) {
|
|
2
|
+
if (screenUpright)
|
|
3
|
+
return -sceneRotationDeg;
|
|
4
|
+
let localAngleDeg = (angleRadians * 180) / Math.PI;
|
|
5
|
+
let screenAngleDeg = localAngleDeg + sceneRotationDeg;
|
|
6
|
+
screenAngleDeg = ((((screenAngleDeg + 180) % 360) + 360) % 360) - 180;
|
|
7
|
+
if (screenAngleDeg > 90)
|
|
8
|
+
localAngleDeg -= 180;
|
|
9
|
+
else if (screenAngleDeg <= -90)
|
|
10
|
+
localAngleDeg += 180;
|
|
11
|
+
return ((((localAngleDeg + 180) % 360) + 360) % 360) - 180;
|
|
12
|
+
}
|
|
13
|
+
export function shouldUpdateFloorplanLabelRotation(previousRotationDeg, nextRotationDeg, minimumDeltaDeg = 1) {
|
|
14
|
+
if (previousRotationDeg === null)
|
|
15
|
+
return true;
|
|
16
|
+
const deltaDeg = ((((nextRotationDeg - previousRotationDeg + 180) % 360) + 360) % 360) - 180;
|
|
17
|
+
return Math.abs(deltaDeg) >= minimumDeltaDeg;
|
|
18
|
+
}
|
|
19
|
+
export function resolveFloorplanAnnotationUpdate({ layoutInputsChanged, previousRotationDeg, nextRotationDeg, }) {
|
|
20
|
+
return {
|
|
21
|
+
resolveCollisions: layoutInputsChanged,
|
|
22
|
+
updateLabelPresentation: layoutInputsChanged ||
|
|
23
|
+
shouldUpdateFloorplanLabelRotation(previousRotationDeg, nextRotationDeg),
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
export function resolveFloorplanAnnotationLabelTransform({ angleRadians, sceneRotationDeg, screenUpright, beforeRotation, afterRotation, layoutDx = 0, layoutDy = 0, }) {
|
|
27
|
+
const degrees = resolveFloorplanLabelAngle(angleRadians, sceneRotationDeg, screenUpright);
|
|
28
|
+
const defaultTransform = [beforeRotation, `rotate(${degrees})`, afterRotation]
|
|
29
|
+
.filter(Boolean)
|
|
30
|
+
.join(' ');
|
|
31
|
+
return {
|
|
32
|
+
defaultTransform,
|
|
33
|
+
transform: layoutDx === 0 && layoutDy === 0
|
|
34
|
+
? defaultTransform
|
|
35
|
+
: `${defaultTransform} translate(${layoutDx} ${layoutDy})`,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
export function updateSvgFloorplanLabelOrientations(labels, sceneRotationDeg) {
|
|
39
|
+
let updated = 0;
|
|
40
|
+
for (const label of labels) {
|
|
41
|
+
const angleRadians = Number(label.dataset.floorplanAnnotationAngleRadians);
|
|
42
|
+
if (!Number.isFinite(angleRadians))
|
|
43
|
+
continue;
|
|
44
|
+
const { defaultTransform, transform } = resolveFloorplanAnnotationLabelTransform({
|
|
45
|
+
angleRadians,
|
|
46
|
+
sceneRotationDeg,
|
|
47
|
+
screenUpright: label.dataset.floorplanAnnotationScreenUpright === 'true',
|
|
48
|
+
beforeRotation: label.dataset.floorplanAnnotationTransformBeforeRotation ?? '',
|
|
49
|
+
afterRotation: label.dataset.floorplanAnnotationTransformAfterRotation ?? '',
|
|
50
|
+
layoutDx: Number(label.dataset.floorplanAnnotationLayoutDx ?? 0),
|
|
51
|
+
layoutDy: Number(label.dataset.floorplanAnnotationLayoutDy ?? 0),
|
|
52
|
+
});
|
|
53
|
+
if (label.getAttribute('transform') === transform)
|
|
54
|
+
continue;
|
|
55
|
+
label.dataset.floorplanAnnotationDefaultTransform = defaultTransform;
|
|
56
|
+
label.setAttribute('transform', transform);
|
|
57
|
+
updated += 1;
|
|
58
|
+
}
|
|
59
|
+
return updated;
|
|
60
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export declare function setFloorplanCompassRotation(compass: {
|
|
2
|
+
style: {
|
|
3
|
+
transform: string;
|
|
4
|
+
};
|
|
5
|
+
} | null, rotationDeg: number): void;
|
|
6
|
+
export type FloorplanNavigationSyncScheduler<Pose> = {
|
|
7
|
+
update: (pose: Pose) => void;
|
|
8
|
+
flush: () => void;
|
|
9
|
+
discard: () => void;
|
|
10
|
+
};
|
|
11
|
+
export declare function createFloorplanNavigationSyncScheduler<Pose>({ applyPresentation, commit, settleMs, schedule, cancel, }: {
|
|
12
|
+
applyPresentation: (pose: Pose) => void;
|
|
13
|
+
commit: (pose: Pose) => void;
|
|
14
|
+
settleMs?: number;
|
|
15
|
+
schedule?: (callback: () => void, delay: number) => ReturnType<typeof globalThis.setTimeout>;
|
|
16
|
+
cancel?: (timer: ReturnType<typeof globalThis.setTimeout>) => void;
|
|
17
|
+
}): FloorplanNavigationSyncScheduler<Pose>;
|
|
18
|
+
//# sourceMappingURL=floorplan-navigation-sync.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"floorplan-navigation-sync.d.ts","sourceRoot":"","sources":["../../../src/ui/floorplan/floorplan-navigation-sync.ts"],"names":[],"mappings":"AAAA,wBAAgB,2BAA2B,CACzC,OAAO,EAAE;IAAE,KAAK,EAAE;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,GAAG,IAAI,EAChD,WAAW,EAAE,MAAM,GAClB,IAAI,CAIN;AAED,MAAM,MAAM,gCAAgC,CAAC,IAAI,IAAI;IACnD,MAAM,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAA;IAC5B,KAAK,EAAE,MAAM,IAAI,CAAA;IACjB,OAAO,EAAE,MAAM,IAAI,CAAA;CACpB,CAAA;AAED,wBAAgB,sCAAsC,CAAC,IAAI,EAAE,EAC3D,iBAAiB,EACjB,MAAM,EACN,QAAc,EACd,QAAgC,EAChC,MAAgC,GACjC,EAAE;IACD,iBAAiB,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAA;IACvC,MAAM,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAA;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,IAAI,EAAE,KAAK,EAAE,MAAM,KAAK,UAAU,CAAC,OAAO,UAAU,CAAC,UAAU,CAAC,CAAA;IAC5F,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,CAAC,OAAO,UAAU,CAAC,UAAU,CAAC,KAAK,IAAI,CAAA;CACnE,GAAG,gCAAgC,CAAC,IAAI,CAAC,CAqCzC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export function setFloorplanCompassRotation(compass, rotationDeg) {
|
|
2
|
+
if (compass) {
|
|
3
|
+
compass.style.transform = `rotate(${rotationDeg}deg)`;
|
|
4
|
+
}
|
|
5
|
+
}
|
|
6
|
+
export function createFloorplanNavigationSyncScheduler({ applyPresentation, commit, settleMs = 120, schedule = globalThis.setTimeout, cancel = globalThis.clearTimeout, }) {
|
|
7
|
+
let latestPose = null;
|
|
8
|
+
let settleTimer = null;
|
|
9
|
+
const clearSettleTimer = () => {
|
|
10
|
+
if (settleTimer === null)
|
|
11
|
+
return;
|
|
12
|
+
cancel(settleTimer);
|
|
13
|
+
settleTimer = null;
|
|
14
|
+
};
|
|
15
|
+
const flush = () => {
|
|
16
|
+
clearSettleTimer();
|
|
17
|
+
if (latestPose === null)
|
|
18
|
+
return;
|
|
19
|
+
const pose = latestPose;
|
|
20
|
+
latestPose = null;
|
|
21
|
+
commit(pose);
|
|
22
|
+
};
|
|
23
|
+
const update = (pose) => {
|
|
24
|
+
latestPose = pose;
|
|
25
|
+
applyPresentation(pose);
|
|
26
|
+
clearSettleTimer();
|
|
27
|
+
settleTimer = schedule(() => {
|
|
28
|
+
settleTimer = null;
|
|
29
|
+
if (latestPose === null)
|
|
30
|
+
return;
|
|
31
|
+
const settledPose = latestPose;
|
|
32
|
+
latestPose = null;
|
|
33
|
+
commit(settledPose);
|
|
34
|
+
}, settleMs);
|
|
35
|
+
};
|
|
36
|
+
const discard = () => {
|
|
37
|
+
clearSettleTimer();
|
|
38
|
+
latestPose = null;
|
|
39
|
+
};
|
|
40
|
+
return { update, flush, discard };
|
|
41
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { FloorplanGeometry, FloorplanPoint } from '@pascal-app/core';
|
|
2
|
+
export type FloorplanBounds = {
|
|
3
|
+
minX: number;
|
|
4
|
+
minY: number;
|
|
5
|
+
maxX: number;
|
|
6
|
+
maxY: number;
|
|
7
|
+
};
|
|
8
|
+
export type FloorplanViewBox = {
|
|
9
|
+
x: number;
|
|
10
|
+
y: number;
|
|
11
|
+
width: number;
|
|
12
|
+
height: number;
|
|
13
|
+
};
|
|
14
|
+
export type FloorplanViewport = {
|
|
15
|
+
left: number;
|
|
16
|
+
top: number;
|
|
17
|
+
width: number;
|
|
18
|
+
height: number;
|
|
19
|
+
};
|
|
20
|
+
export declare function clientToFloorplanPoint(viewBox: FloorplanViewBox, viewport: FloorplanViewport, clientX: number, clientY: number): FloorplanPoint;
|
|
21
|
+
export declare function scaleFloorplanViewBox(viewBox: FloorplanViewBox, factor: number, anchorX?: number, anchorY?: number): FloorplanViewBox;
|
|
22
|
+
export declare function scaleFloorplanViewBoxBetweenClients(viewBox: FloorplanViewBox, factor: number, viewport: FloorplanViewport, sourceClient: FloorplanPoint, targetClient: FloorplanPoint): FloorplanViewBox;
|
|
23
|
+
export declare function panFloorplanViewBox(viewBox: FloorplanViewBox, viewport: FloorplanViewport, sourceClient: FloorplanPoint, targetClient: FloorplanPoint): FloorplanViewBox;
|
|
24
|
+
export declare function getFloorplanBounds(geometries: readonly FloorplanGeometry[]): FloorplanBounds | null;
|
|
25
|
+
export declare function padFloorplanBounds(bounds: FloorplanBounds, ratio?: number): FloorplanBounds;
|
|
26
|
+
//# sourceMappingURL=floorplan-preview-geometry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"floorplan-preview-geometry.d.ts","sourceRoot":"","sources":["../../../src/ui/floorplan/floorplan-preview-geometry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AAEzE,MAAM,MAAM,eAAe,GAAG;IAC5B,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;CACb,CAAA;AAED,MAAM,MAAM,gBAAgB,GAAG;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAA;AAEtF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;CACf,CAAA;AAwBD,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,gBAAgB,EACzB,QAAQ,EAAE,iBAAiB,EAC3B,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,GACd,cAAc,CAGhB;AAED,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,gBAAgB,EACzB,MAAM,EAAE,MAAM,EACd,OAAO,SAAM,EACb,OAAO,SAAM,GACZ,gBAAgB,CAYlB;AAED,wBAAgB,mCAAmC,CACjD,OAAO,EAAE,gBAAgB,EACzB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,iBAAiB,EAC3B,YAAY,EAAE,cAAc,EAC5B,YAAY,EAAE,cAAc,GAC3B,gBAAgB,CAclB;AAED,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,gBAAgB,EACzB,QAAQ,EAAE,iBAAiB,EAC3B,YAAY,EAAE,cAAc,EAC5B,YAAY,EAAE,cAAc,GAC3B,gBAAgB,CAQlB;AA6JD,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,SAAS,iBAAiB,EAAE,GACvC,eAAe,GAAG,IAAI,CAKxB;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,eAAe,EAAE,KAAK,SAAO,GAAG,eAAe,CAUzF"}
|