@terraware/web-components 5.0.2 → 5.0.4
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/components/VirtualWalkthrough/AnnotationPanel.d.ts.map +1 -1
- package/components/VirtualWalkthrough/AnnotationPanel.js +24 -10
- package/components/VirtualWalkthrough/SplatModel.js +2 -1
- package/components/VirtualWalkthrough/SplatRevealRain.d.ts +3 -1
- package/components/VirtualWalkthrough/SplatRevealRain.d.ts.map +1 -1
- package/components/VirtualWalkthrough/SplatRevealRain.js +15 -8
- package/components/VirtualWalkthrough/useRestartOnVr.d.ts +16 -0
- package/components/VirtualWalkthrough/useRestartOnVr.d.ts.map +1 -0
- package/components/VirtualWalkthrough/useRestartOnVr.js +38 -0
- package/components/VirtualWalkthrough/vr-annotation-panel-layout.d.ts +65 -0
- package/components/VirtualWalkthrough/vr-annotation-panel-layout.d.ts.map +1 -1
- package/components/VirtualWalkthrough/vr-annotation-panel-layout.js +99 -1
- package/components/VirtualWalkthrough/vr-annotation-panel.d.ts +40 -7
- package/components/VirtualWalkthrough/vr-annotation-panel.d.ts.map +1 -1
- package/components/VirtualWalkthrough/vr-annotation-panel.js +265 -104
- package/components/VirtualWalkthrough/vr-annotation-scroll.d.ts +23 -0
- package/components/VirtualWalkthrough/vr-annotation-scroll.d.ts.map +1 -0
- package/components/VirtualWalkthrough/vr-annotation-scroll.js +33 -0
- package/components/VirtualWalkthrough/xr-annotation-candidate-buffer.d.ts +32 -0
- package/components/VirtualWalkthrough/xr-annotation-candidate-buffer.d.ts.map +1 -0
- package/components/VirtualWalkthrough/xr-annotation-candidate-buffer.js +88 -0
- package/components/VirtualWalkthrough/xr-annotation-candidates.d.ts +8 -12
- package/components/VirtualWalkthrough/xr-annotation-candidates.d.ts.map +1 -1
- package/components/VirtualWalkthrough/xr-annotation-candidates.js +11 -32
- package/components/VirtualWalkthrough/xr-annotation-targeting.d.ts.map +1 -1
- package/components/VirtualWalkthrough/xr-annotation-targeting.js +9 -4
- package/components/VirtualWalkthrough/xr-gaze-dwell.d.ts +1 -0
- package/components/VirtualWalkthrough/xr-gaze-dwell.d.ts.map +1 -1
- package/components/VirtualWalkthrough/xr-gaze-dwell.js +4 -3
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AnnotationPanel.d.ts","sourceRoot":"","sources":["../../../src/components/VirtualWalkthrough/AnnotationPanel.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAEpD,UAAU,oBAAoB;IAC5B,UAAU,EAAE,eAAe,GAAG,IAAI,CAAC;IAInC,eAAe,CAAC,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACjE,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;
|
|
1
|
+
{"version":3,"file":"AnnotationPanel.d.ts","sourceRoot":"","sources":["../../../src/components/VirtualWalkthrough/AnnotationPanel.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAEpD,UAAU,oBAAoB;IAC5B,UAAU,EAAE,eAAe,GAAG,IAAI,CAAC;IAInC,eAAe,CAAC,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACjE,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAiED,QAAA,MAAM,eAAe,GAAI,0CAA0C,oBAAoB,mDAsItF,CAAC;AAEF,eAAe,eAAe,CAAC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useRef, useState, useEffect, useLayoutEffect } from 'react';
|
|
2
|
-
import { useTheme } from '@mui/material';
|
|
2
|
+
import { useTheme, Box } from '@mui/material';
|
|
3
3
|
import PhotosCarousel from '../PhotosCarousel/index.js';
|
|
4
4
|
import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
|
|
5
5
|
|
|
@@ -36,17 +36,33 @@ const PANEL_STYLE = {
|
|
|
36
36
|
display: 'flex',
|
|
37
37
|
flexDirection: 'column'
|
|
38
38
|
};
|
|
39
|
-
const
|
|
39
|
+
const CAROUSEL_SX = {
|
|
40
40
|
width: 'min(60vw, 640px)',
|
|
41
41
|
borderRadius: '12px 12px 0 0',
|
|
42
42
|
overflow: 'hidden',
|
|
43
|
-
flexShrink: 0
|
|
43
|
+
flexShrink: 0,
|
|
44
|
+
'& .photos-carousel-image': {
|
|
45
|
+
height: 'min(400px, 40vh)'
|
|
46
|
+
},
|
|
47
|
+
'& .photos-carousel-container': {
|
|
48
|
+
minHeight: 'min(200px, 20vh)'
|
|
49
|
+
}
|
|
44
50
|
};
|
|
45
51
|
const TEXT_BLOCK_STYLE = {
|
|
46
52
|
padding: '24px',
|
|
47
53
|
display: 'flex',
|
|
48
54
|
flexDirection: 'column',
|
|
49
|
-
gap: 8
|
|
55
|
+
gap: 8,
|
|
56
|
+
minHeight: 0
|
|
57
|
+
};
|
|
58
|
+
const BODY_STYLE = {
|
|
59
|
+
fontSize: 16,
|
|
60
|
+
lineHeight: '24px',
|
|
61
|
+
margin: 0,
|
|
62
|
+
minHeight: 0,
|
|
63
|
+
overflowY: 'auto',
|
|
64
|
+
overscrollBehavior: 'contain',
|
|
65
|
+
scrollbarWidth: 'thin'
|
|
50
66
|
};
|
|
51
67
|
const AnnotationPanel = ({
|
|
52
68
|
annotation,
|
|
@@ -149,8 +165,8 @@ const AnnotationPanel = ({
|
|
|
149
165
|
ref: panelRef,
|
|
150
166
|
"data-testid": "annotation-panel",
|
|
151
167
|
style: panelStyle,
|
|
152
|
-
children: [annotation.imageUrls && annotation.imageUrls.length > 0 && /*#__PURE__*/jsx(
|
|
153
|
-
|
|
168
|
+
children: [annotation.imageUrls && annotation.imageUrls.length > 0 && /*#__PURE__*/jsx(Box, {
|
|
169
|
+
sx: CAROUSEL_SX,
|
|
154
170
|
children: /*#__PURE__*/jsx(PhotosCarousel, {
|
|
155
171
|
photos: annotation.imageUrls.map(url => ({
|
|
156
172
|
url,
|
|
@@ -187,10 +203,8 @@ const AnnotationPanel = ({
|
|
|
187
203
|
}), annotation.bodyText && /*#__PURE__*/jsx("p", {
|
|
188
204
|
"data-testid": "annotation-panel-description",
|
|
189
205
|
style: {
|
|
190
|
-
|
|
191
|
-
color: theme.palette.TwClrTxt
|
|
192
|
-
lineHeight: '24px',
|
|
193
|
-
margin: 0
|
|
206
|
+
...BODY_STYLE,
|
|
207
|
+
color: theme.palette.TwClrTxt
|
|
194
208
|
},
|
|
195
209
|
children: annotation.bodyText
|
|
196
210
|
})]
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
interface SplatRevealRainProps {
|
|
2
2
|
enabled?: boolean;
|
|
3
|
+
restartOnVr?: boolean;
|
|
4
|
+
scaleFactor?: number;
|
|
3
5
|
center?: [number, number, number];
|
|
4
6
|
distance?: number;
|
|
5
7
|
speed?: number;
|
|
@@ -13,6 +15,6 @@ interface SplatRevealRainProps {
|
|
|
13
15
|
hitDuration?: number;
|
|
14
16
|
endRadius?: number;
|
|
15
17
|
}
|
|
16
|
-
declare const SplatRevealRain: ({ enabled, center, distance, speed, acceleration, flightTime, rainSize, rotation, fallTint, fallTintIntensity, hitTint, hitDuration, endRadius, }: SplatRevealRainProps) => import("react/jsx-runtime").JSX.Element;
|
|
18
|
+
declare const SplatRevealRain: ({ enabled, restartOnVr, scaleFactor, center, distance, speed, acceleration, flightTime, rainSize, rotation, fallTint, fallTintIntensity, hitTint, hitDuration, endRadius, }: SplatRevealRainProps) => import("react/jsx-runtime").JSX.Element;
|
|
17
19
|
export default SplatRevealRain;
|
|
18
20
|
//# sourceMappingURL=SplatRevealRain.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SplatRevealRain.d.ts","sourceRoot":"","sources":["../../../src/components/VirtualWalkthrough/SplatRevealRain.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"SplatRevealRain.d.ts","sourceRoot":"","sources":["../../../src/components/VirtualWalkthrough/SplatRevealRain.tsx"],"names":[],"mappings":"AAQA,UAAU,oBAAoB;IAC5B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,QAAA,MAAM,eAAe,GAAI,6KAgBtB,oBAAoB,4CAyBtB,CAAC;AAEF,eAAe,eAAe,CAAC"}
|
|
@@ -1,14 +1,17 @@
|
|
|
1
|
-
import 'react';
|
|
1
|
+
import { useRef } from 'react';
|
|
2
2
|
import { Script } from '@playcanvas/react/components';
|
|
3
3
|
import { Color, Vec3 } from 'playcanvas';
|
|
4
4
|
import { GSplatRevealRain } from 'playcanvas/scripts/esm/gsplat/reveal-rain.mjs';
|
|
5
|
+
import useRestartOnVr from './useRestartOnVr.js';
|
|
5
6
|
import { jsx } from 'react/jsx-runtime';
|
|
6
7
|
|
|
7
8
|
const SplatRevealRain = ({
|
|
8
9
|
enabled = true,
|
|
10
|
+
restartOnVr = true,
|
|
11
|
+
scaleFactor = 1,
|
|
9
12
|
center = [0, 0, 0],
|
|
10
13
|
distance = 3,
|
|
11
|
-
speed =
|
|
14
|
+
speed = 1,
|
|
12
15
|
acceleration = 5,
|
|
13
16
|
flightTime = 0.25,
|
|
14
17
|
rainSize = 0.0,
|
|
@@ -19,21 +22,25 @@ const SplatRevealRain = ({
|
|
|
19
22
|
hitDuration = 0,
|
|
20
23
|
endRadius = 5
|
|
21
24
|
}) => {
|
|
25
|
+
const scale = value => value * scaleFactor;
|
|
26
|
+
const scriptRef = useRef(null);
|
|
27
|
+
useRestartOnVr(scriptRef, enabled && restartOnVr);
|
|
22
28
|
return /*#__PURE__*/jsx(Script, {
|
|
29
|
+
ref: scriptRef,
|
|
23
30
|
script: GSplatRevealRain,
|
|
24
31
|
enabled: enabled,
|
|
25
|
-
center: new Vec3(...center),
|
|
26
|
-
distance: distance,
|
|
27
|
-
speed: speed,
|
|
28
|
-
acceleration: acceleration,
|
|
32
|
+
center: new Vec3(...center).mulScalar(scaleFactor),
|
|
33
|
+
distance: scale(distance),
|
|
34
|
+
speed: scale(speed),
|
|
35
|
+
acceleration: scale(acceleration),
|
|
29
36
|
flightTime: flightTime,
|
|
30
|
-
rainSize: rainSize,
|
|
37
|
+
rainSize: scale(rainSize),
|
|
31
38
|
rotation: rotation,
|
|
32
39
|
fallTint: new Color(...fallTint),
|
|
33
40
|
fallTintIntensity: fallTintIntensity,
|
|
34
41
|
hitTint: new Color(...hitTint),
|
|
35
42
|
hitDuration: hitDuration,
|
|
36
|
-
endRadius: endRadius
|
|
43
|
+
endRadius: scale(endRadius)
|
|
37
44
|
});
|
|
38
45
|
};
|
|
39
46
|
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { RefObject } from 'react';
|
|
2
|
+
import { Script } from 'playcanvas';
|
|
3
|
+
/**
|
|
4
|
+
* Replays a one-shot script effect at the start of every VR session.
|
|
5
|
+
*
|
|
6
|
+
* `GSplatShaderEffect` zeroes its timeline on the script's `enable` event and switches itself off
|
|
7
|
+
* once the animation has played out, so re-enabling a finished effect runs it again. The `enabled`
|
|
8
|
+
* setter only fires the event on a change of state, which is why an effect still mid-play is taken
|
|
9
|
+
* back through disabled first.
|
|
10
|
+
*
|
|
11
|
+
* @param scriptRef - The script instance to replay, as handed back by `Script`'s ref.
|
|
12
|
+
* @param enabled - Whether the effect is in use at all. No session is watched while false.
|
|
13
|
+
*/
|
|
14
|
+
export declare const useRestartOnVr: (scriptRef: RefObject<Script | null>, enabled: boolean) => void;
|
|
15
|
+
export default useRestartOnVr;
|
|
16
|
+
//# sourceMappingURL=useRestartOnVr.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useRestartOnVr.d.ts","sourceRoot":"","sources":["../../../src/components/VirtualWalkthrough/useRestartOnVr.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAa,MAAM,OAAO,CAAC;AAG7C,OAAO,EAAE,MAAM,EAAa,MAAM,YAAY,CAAC;AAE/C;;;;;;;;;;GAUG;AACH,eAAO,MAAM,cAAc,GAAI,WAAW,SAAS,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,SAAS,OAAO,SAwBnF,CAAC;AAEF,eAAe,cAAc,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
|
+
import { useApp } from '@playcanvas/react/hooks';
|
|
3
|
+
import { XRTYPE_VR } from 'playcanvas';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Replays a one-shot script effect at the start of every VR session.
|
|
7
|
+
*
|
|
8
|
+
* `GSplatShaderEffect` zeroes its timeline on the script's `enable` event and switches itself off
|
|
9
|
+
* once the animation has played out, so re-enabling a finished effect runs it again. The `enabled`
|
|
10
|
+
* setter only fires the event on a change of state, which is why an effect still mid-play is taken
|
|
11
|
+
* back through disabled first.
|
|
12
|
+
*
|
|
13
|
+
* @param scriptRef - The script instance to replay, as handed back by `Script`'s ref.
|
|
14
|
+
* @param enabled - Whether the effect is in use at all. No session is watched while false.
|
|
15
|
+
*/
|
|
16
|
+
const useRestartOnVr = (scriptRef, enabled) => {
|
|
17
|
+
const app = useApp();
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
const xr = app?.xr;
|
|
20
|
+
if (!xr || !enabled) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
const restart = () => {
|
|
24
|
+
const script = scriptRef.current;
|
|
25
|
+
if (!script || xr.type !== XRTYPE_VR) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
script.enabled = false;
|
|
29
|
+
script.enabled = true;
|
|
30
|
+
};
|
|
31
|
+
xr.on('start', restart);
|
|
32
|
+
return () => {
|
|
33
|
+
xr.off('start', restart);
|
|
34
|
+
};
|
|
35
|
+
}, [app, enabled, scriptRef]);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export { useRestartOnVr as default, useRestartOnVr };
|
|
@@ -3,4 +3,69 @@
|
|
|
3
3
|
* each candidate line. A single word wider than `maxWidth` still gets its own line.
|
|
4
4
|
*/
|
|
5
5
|
export declare const wrapText: (text: string, maxWidth: number, measure: (segment: string) => number) => string[];
|
|
6
|
+
export declare const PANEL_CANVAS_WIDTH = 1024;
|
|
7
|
+
/** Tallest the panel can grow; also the canvas the content is painted into. */
|
|
8
|
+
export declare const PANEL_MAX_HEIGHT = 768;
|
|
9
|
+
/** Shortest the panel can shrink, so a bare title still reads as a panel rather than a sliver. */
|
|
10
|
+
export declare const PANEL_MIN_HEIGHT = 200;
|
|
11
|
+
export declare const PANEL_PAD_X = 48;
|
|
12
|
+
export declare const PANEL_PAD_Y = 40;
|
|
13
|
+
export declare const IMAGE_BAND_HEIGHT = 360;
|
|
14
|
+
export declare const CHIP_HEIGHT = 44;
|
|
15
|
+
export declare const TITLE_LINE_HEIGHT = 56;
|
|
16
|
+
export declare const BODY_LINE_HEIGHT = 40;
|
|
17
|
+
/**
|
|
18
|
+
* Tallest canvas the body text is rasterised into. The body scrolls as a window onto this canvas,
|
|
19
|
+
* so it bounds how much text an annotation can show — far more than the panel itself holds.
|
|
20
|
+
*/
|
|
21
|
+
export declare const TEXT_CANVAS_MAX_HEIGHT = 2048;
|
|
22
|
+
export declare const PANEL_FONTS: {
|
|
23
|
+
readonly label: "600 28px sans-serif";
|
|
24
|
+
readonly title: "700 48px sans-serif";
|
|
25
|
+
readonly body: "400 32px sans-serif";
|
|
26
|
+
};
|
|
27
|
+
export type PanelTextStyle = keyof typeof PANEL_FONTS;
|
|
28
|
+
export interface PanelLayoutInput {
|
|
29
|
+
title: string;
|
|
30
|
+
label?: string;
|
|
31
|
+
bodyText?: string;
|
|
32
|
+
imageCount: number;
|
|
33
|
+
contentWidth: number;
|
|
34
|
+
measure: (text: string, style: PanelTextStyle) => number;
|
|
35
|
+
}
|
|
36
|
+
export interface PanelLayout {
|
|
37
|
+
/** Painted height of the panel, between the min and max bounds. */
|
|
38
|
+
height: number;
|
|
39
|
+
image: {
|
|
40
|
+
y: number;
|
|
41
|
+
height: number;
|
|
42
|
+
} | null;
|
|
43
|
+
dotsY: number | null;
|
|
44
|
+
chip: {
|
|
45
|
+
y: number;
|
|
46
|
+
width: number;
|
|
47
|
+
height: number;
|
|
48
|
+
} | null;
|
|
49
|
+
title: {
|
|
50
|
+
y: number;
|
|
51
|
+
lines: string[];
|
|
52
|
+
};
|
|
53
|
+
body: {
|
|
54
|
+
/** Top of the body viewport, in panel coordinates. */
|
|
55
|
+
y: number;
|
|
56
|
+
lines: string[];
|
|
57
|
+
/** Height of all the lines together, which is also the text canvas height. */
|
|
58
|
+
contentHeight: number;
|
|
59
|
+
/** Height of the visible window onto that canvas. */
|
|
60
|
+
viewportHeight: number;
|
|
61
|
+
scrollable: boolean;
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Lays the panel's blocks out top-to-bottom and reports the height they need, so the quad
|
|
66
|
+
* can be sized to the content instead of a fixed aspect. Everything above the body is a header
|
|
67
|
+
* that has to fit within `PANEL_MAX_HEIGHT`; the body instead scrolls within whatever height
|
|
68
|
+
* the header leaves it.
|
|
69
|
+
*/
|
|
70
|
+
export declare const layoutPanel: ({ title, label, bodyText, imageCount, contentWidth, measure, }: PanelLayoutInput) => PanelLayout;
|
|
6
71
|
//# sourceMappingURL=vr-annotation-panel-layout.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vr-annotation-panel-layout.d.ts","sourceRoot":"","sources":["../../../src/components/VirtualWalkthrough/vr-annotation-panel-layout.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,eAAO,MAAM,QAAQ,GAAI,MAAM,MAAM,EAAE,UAAU,MAAM,EAAE,SAAS,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,KAAG,MAAM,EAoBrG,CAAC"}
|
|
1
|
+
{"version":3,"file":"vr-annotation-panel-layout.d.ts","sourceRoot":"","sources":["../../../src/components/VirtualWalkthrough/vr-annotation-panel-layout.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,eAAO,MAAM,QAAQ,GAAI,MAAM,MAAM,EAAE,UAAU,MAAM,EAAE,SAAS,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,KAAG,MAAM,EAoBrG,CAAC;AAEF,eAAO,MAAM,kBAAkB,OAAO,CAAC;AACvC,+EAA+E;AAC/E,eAAO,MAAM,gBAAgB,MAAM,CAAC;AACpC,kGAAkG;AAClG,eAAO,MAAM,gBAAgB,MAAM,CAAC;AAEpC,eAAO,MAAM,WAAW,KAAK,CAAC;AAC9B,eAAO,MAAM,WAAW,KAAK,CAAC;AAE9B,eAAO,MAAM,iBAAiB,MAAM,CAAC;AAKrC,eAAO,MAAM,WAAW,KAAK,CAAC;AAI9B,eAAO,MAAM,iBAAiB,KAAK,CAAC;AAEpC,eAAO,MAAM,gBAAgB,KAAK,CAAC;AAEnC;;;GAGG;AACH,eAAO,MAAM,sBAAsB,OAAO,CAAC;AAE3C,eAAO,MAAM,WAAW;;;;CAId,CAAC;AAEX,MAAM,MAAM,cAAc,GAAG,MAAM,OAAO,WAAW,CAAC;AAEtD,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,KAAK,MAAM,CAAC;CAC1D;AAED,MAAM,WAAW,WAAW;IAC1B,mEAAmE;IACnE,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAC5C,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,IAAI,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAC1D,KAAK,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IACtC,IAAI,EAAE;QACJ,sDAAsD;QACtD,CAAC,EAAE,MAAM,CAAC;QACV,KAAK,EAAE,MAAM,EAAE,CAAC;QAChB,8EAA8E;QAC9E,aAAa,EAAE,MAAM,CAAC;QACtB,qDAAqD;QACrD,cAAc,EAAE,MAAM,CAAC;QACvB,UAAU,EAAE,OAAO,CAAC;KACrB,CAAC;CACH;AASD;;;;;GAKG;AACH,eAAO,MAAM,WAAW,GAAI,gEAOzB,gBAAgB,KAAG,WA8CrB,CAAC"}
|
|
@@ -20,5 +20,103 @@ const wrapText = (text, maxWidth, measure) => {
|
|
|
20
20
|
}
|
|
21
21
|
return lines;
|
|
22
22
|
};
|
|
23
|
+
const PANEL_CANVAS_WIDTH = 1024;
|
|
24
|
+
/** Tallest the panel can grow; also the canvas the content is painted into. */
|
|
25
|
+
const PANEL_MAX_HEIGHT = 768;
|
|
26
|
+
/** Shortest the panel can shrink, so a bare title still reads as a panel rather than a sliver. */
|
|
27
|
+
const PANEL_MIN_HEIGHT = 200;
|
|
28
|
+
const PANEL_PAD_X = 48;
|
|
29
|
+
const PANEL_PAD_Y = 40;
|
|
30
|
+
const IMAGE_BAND_HEIGHT = 360;
|
|
31
|
+
const IMAGE_GAP = 16;
|
|
32
|
+
const DOTS_BLOCK_HEIGHT = 32;
|
|
33
|
+
const DOTS_CENTER_OFFSET = 4;
|
|
34
|
+
const CHIP_HEIGHT = 44;
|
|
35
|
+
const CHIP_PAD_X = 16;
|
|
36
|
+
const CHIP_GAP = 24;
|
|
37
|
+
const TITLE_LINE_HEIGHT = 56;
|
|
38
|
+
const TITLE_GAP = 16;
|
|
39
|
+
const BODY_LINE_HEIGHT = 40;
|
|
23
40
|
|
|
24
|
-
|
|
41
|
+
/**
|
|
42
|
+
* Tallest canvas the body text is rasterised into. The body scrolls as a window onto this canvas,
|
|
43
|
+
* so it bounds how much text an annotation can show — far more than the panel itself holds.
|
|
44
|
+
*/
|
|
45
|
+
const TEXT_CANVAS_MAX_HEIGHT = 2048;
|
|
46
|
+
const PANEL_FONTS = {
|
|
47
|
+
label: '600 28px sans-serif',
|
|
48
|
+
title: '700 48px sans-serif',
|
|
49
|
+
body: '400 32px sans-serif'
|
|
50
|
+
};
|
|
51
|
+
/** Keeps the lines whose full line box fits above the bottom padding. */
|
|
52
|
+
const fitLines = (lines, y, lineHeight) => {
|
|
53
|
+
const room = Math.max(0, Math.floor((PANEL_MAX_HEIGHT - PANEL_PAD_Y - y) / lineHeight));
|
|
54
|
+
return lines.slice(0, room);
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Lays the panel's blocks out top-to-bottom and reports the height they need, so the quad
|
|
59
|
+
* can be sized to the content instead of a fixed aspect. Everything above the body is a header
|
|
60
|
+
* that has to fit within `PANEL_MAX_HEIGHT`; the body instead scrolls within whatever height
|
|
61
|
+
* the header leaves it.
|
|
62
|
+
*/
|
|
63
|
+
const layoutPanel = ({
|
|
64
|
+
title,
|
|
65
|
+
label,
|
|
66
|
+
bodyText,
|
|
67
|
+
imageCount,
|
|
68
|
+
contentWidth,
|
|
69
|
+
measure
|
|
70
|
+
}) => {
|
|
71
|
+
let y = PANEL_PAD_Y;
|
|
72
|
+
const image = imageCount > 0 ? {
|
|
73
|
+
y,
|
|
74
|
+
height: IMAGE_BAND_HEIGHT
|
|
75
|
+
} : null;
|
|
76
|
+
let dotsY = null;
|
|
77
|
+
if (image) {
|
|
78
|
+
y += IMAGE_BAND_HEIGHT + IMAGE_GAP;
|
|
79
|
+
if (imageCount > 1) {
|
|
80
|
+
dotsY = y + DOTS_CENTER_OFFSET;
|
|
81
|
+
y += DOTS_BLOCK_HEIGHT;
|
|
82
|
+
}
|
|
83
|
+
y += IMAGE_GAP;
|
|
84
|
+
}
|
|
85
|
+
const chip = label ? {
|
|
86
|
+
y,
|
|
87
|
+
width: measure(label, 'label') + CHIP_PAD_X * 2,
|
|
88
|
+
height: CHIP_HEIGHT
|
|
89
|
+
} : null;
|
|
90
|
+
if (chip) {
|
|
91
|
+
y += CHIP_HEIGHT + CHIP_GAP;
|
|
92
|
+
}
|
|
93
|
+
const titleLines = wrapText(title, contentWidth, segment => measure(segment, 'title'));
|
|
94
|
+
const fittedTitle = fitLines(titleLines, y, TITLE_LINE_HEIGHT);
|
|
95
|
+
const titleY = y;
|
|
96
|
+
y += fittedTitle.length * TITLE_LINE_HEIGHT + TITLE_GAP;
|
|
97
|
+
const bodyLines = bodyText ? wrapText(bodyText, contentWidth, segment => measure(segment, 'body')) : [];
|
|
98
|
+
const fittedBody = bodyLines.slice(0, Math.floor(TEXT_CANVAS_MAX_HEIGHT / BODY_LINE_HEIGHT));
|
|
99
|
+
const bodyY = y;
|
|
100
|
+
const contentHeight = fittedBody.length * BODY_LINE_HEIGHT;
|
|
101
|
+
const viewportHeight = Math.min(contentHeight, Math.max(0, PANEL_MAX_HEIGHT - PANEL_PAD_Y - bodyY));
|
|
102
|
+
const height = Math.max(PANEL_MIN_HEIGHT, Math.min(PANEL_MAX_HEIGHT, bodyY + viewportHeight + PANEL_PAD_Y));
|
|
103
|
+
return {
|
|
104
|
+
height,
|
|
105
|
+
image,
|
|
106
|
+
dotsY,
|
|
107
|
+
chip,
|
|
108
|
+
title: {
|
|
109
|
+
y: titleY,
|
|
110
|
+
lines: fittedTitle
|
|
111
|
+
},
|
|
112
|
+
body: {
|
|
113
|
+
y: bodyY,
|
|
114
|
+
lines: fittedBody,
|
|
115
|
+
contentHeight,
|
|
116
|
+
viewportHeight,
|
|
117
|
+
scrollable: viewportHeight > 0 && contentHeight > viewportHeight
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
export { BODY_LINE_HEIGHT, CHIP_HEIGHT, IMAGE_BAND_HEIGHT, PANEL_CANVAS_WIDTH, PANEL_FONTS, PANEL_MAX_HEIGHT, PANEL_MIN_HEIGHT, PANEL_PAD_X, PANEL_PAD_Y, TEXT_CANVAS_MAX_HEIGHT, TITLE_LINE_HEIGHT, layoutPanel, wrapText };
|
|
@@ -6,13 +6,28 @@ export declare class VrAnnotationPanel extends Script {
|
|
|
6
6
|
bodyText?: string;
|
|
7
7
|
imageUrls?: string[];
|
|
8
8
|
annotationIndex: number;
|
|
9
|
-
private
|
|
10
|
-
private
|
|
11
|
-
private
|
|
12
|
-
private
|
|
9
|
+
private _chromeMaterial?;
|
|
10
|
+
private _chromeTexture?;
|
|
11
|
+
private _chromeMesh?;
|
|
12
|
+
private _chromeCanvas?;
|
|
13
|
+
private _textMaterial?;
|
|
14
|
+
private _textTexture?;
|
|
15
|
+
private _textMesh?;
|
|
16
|
+
private _textCanvas?;
|
|
17
|
+
private _textInstance?;
|
|
18
|
+
private _thumbMaterial?;
|
|
19
|
+
private _thumbMesh?;
|
|
20
|
+
private _thumbInstance?;
|
|
13
21
|
private _drawnSignature;
|
|
14
22
|
private _currentImage;
|
|
15
23
|
private _loadToken;
|
|
24
|
+
private _loadedImage?;
|
|
25
|
+
private _layout?;
|
|
26
|
+
private _scrollY;
|
|
27
|
+
private _maxScroll;
|
|
28
|
+
/** Height (canvas px) the chrome occupies; the rest of its canvas is unused. */
|
|
29
|
+
private _canvasHeight;
|
|
30
|
+
private _panelHeight;
|
|
16
31
|
private _headRotation;
|
|
17
32
|
private _anchor;
|
|
18
33
|
private _scratchDir;
|
|
@@ -22,17 +37,35 @@ export declare class VrAnnotationPanel extends Script {
|
|
|
22
37
|
private _scratchScale;
|
|
23
38
|
private _onSelect;
|
|
24
39
|
initialize(): void;
|
|
25
|
-
private
|
|
40
|
+
private _createTexture;
|
|
41
|
+
private _createTexturedMaterial;
|
|
42
|
+
private _createQuadMesh;
|
|
43
|
+
/** Rewrites the quad's four corners and the slice of its texture they map to. */
|
|
44
|
+
private _writeQuad;
|
|
45
|
+
/** Converts a y in panel canvas coordinates to the entity's local space. */
|
|
46
|
+
private _localY;
|
|
47
|
+
private _writeChromeQuad;
|
|
48
|
+
/** Maps the visible window of the (much taller) text texture onto the body quad. */
|
|
49
|
+
private _writeTextQuad;
|
|
50
|
+
private _writeThumbQuad;
|
|
26
51
|
private _roundRect;
|
|
27
52
|
private _drawArrow;
|
|
28
53
|
private _drawDots;
|
|
29
|
-
|
|
54
|
+
/** Re-measures the content, resizes the panel, and repaints both canvases. */
|
|
55
|
+
private _rebuild;
|
|
56
|
+
private _drawChrome;
|
|
57
|
+
/**
|
|
58
|
+
* Paints every body line onto its own canvas, tall enough to hold all of them. Scrolling then
|
|
59
|
+
* only moves the quad's UV window over this texture, so no repaint or upload happens per frame.
|
|
60
|
+
*/
|
|
61
|
+
private _drawText;
|
|
30
62
|
private _setImage;
|
|
31
63
|
private _loadCurrentImage;
|
|
32
64
|
private _rayQuadHit;
|
|
33
65
|
/** True when the ray points at the panel quad (used to suppress the empty-select dismiss). */
|
|
34
66
|
rayHitsPanel(origin: Vec3, direction: Vec3): boolean;
|
|
35
|
-
update(): void;
|
|
67
|
+
update(dt: number): void;
|
|
68
|
+
private _scroll;
|
|
36
69
|
private _trackHead;
|
|
37
70
|
private _teardown;
|
|
38
71
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vr-annotation-panel.d.ts","sourceRoot":"","sources":["../../../src/components/VirtualWalkthrough/vr-annotation-panel.ts"],"names":[],"mappings":"AAAA,OAAO,EAUL,MAAM,EAGN,IAAI,EAEL,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"vr-annotation-panel.d.ts","sourceRoot":"","sources":["../../../src/components/VirtualWalkthrough/vr-annotation-panel.ts"],"names":[],"mappings":"AAAA,OAAO,EAUL,MAAM,EAGN,IAAI,EAEL,MAAM,YAAY,CAAC;AA2CpB,qBAAa,iBAAkB,SAAQ,MAAM;IAC3C,MAAM,CAAC,UAAU,SAAuB;IAGxC,KAAK,SAAM;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,eAAe,SAAM;IAErB,OAAO,CAAC,eAAe,CAAC,CAAmB;IAC3C,OAAO,CAAC,cAAc,CAAC,CAAU;IACjC,OAAO,CAAC,WAAW,CAAC,CAAO;IAC3B,OAAO,CAAC,aAAa,CAAC,CAAoB;IAE1C,OAAO,CAAC,aAAa,CAAC,CAAmB;IACzC,OAAO,CAAC,YAAY,CAAC,CAAU;IAC/B,OAAO,CAAC,SAAS,CAAC,CAAO;IACzB,OAAO,CAAC,WAAW,CAAC,CAAoB;IACxC,OAAO,CAAC,aAAa,CAAC,CAAe;IAErC,OAAO,CAAC,cAAc,CAAC,CAAmB;IAC1C,OAAO,CAAC,UAAU,CAAC,CAAO;IAC1B,OAAO,CAAC,cAAc,CAAC,CAAe;IAEtC,OAAO,CAAC,eAAe,CAAuB;IAC9C,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,UAAU,CAAK;IACvB,OAAO,CAAC,YAAY,CAAC,CAAmB;IACxC,OAAO,CAAC,OAAO,CAAC,CAAc;IAC9B,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,UAAU,CAAK;IACvB,gFAAgF;IAChF,OAAO,CAAC,aAAa,CAAoB;IACzC,OAAO,CAAC,YAAY,CAA8B;IAElD,OAAO,CAAC,aAAa,CAAc;IACnC,OAAO,CAAC,OAAO,CAAc;IAC7B,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,QAAQ,CAAc;IAC9B,OAAO,CAAC,UAAU,CAAc;IAChC,OAAO,CAAC,OAAO,CAAc;IAC7B,OAAO,CAAC,aAAa,CAAc;IAEnC,OAAO,CAAC,SAAS,CAwBf;IAEF,UAAU;IA0CV,OAAO,CAAC,cAAc;IAgBtB,OAAO,CAAC,uBAAuB;IAgB/B,OAAO,CAAC,eAAe;IASvB,iFAAiF;IACjF,OAAO,CAAC,UAAU;IAelB,4EAA4E;IAC5E,OAAO,CAAC,OAAO;IAIf,OAAO,CAAC,gBAAgB;IASxB,oFAAoF;IACpF,OAAO,CAAC,cAAc;IAiBtB,OAAO,CAAC,eAAe;IA0BvB,OAAO,CAAC,UAAU;IAUlB,OAAO,CAAC,UAAU;IAkBlB,OAAO,CAAC,SAAS;IAWjB,8EAA8E;IAC9E,OAAO,CAAC,QAAQ;IAuChB,OAAO,CAAC,WAAW;IAoEnB;;;OAGG;IACH,OAAO,CAAC,SAAS;IAmCjB,OAAO,CAAC,SAAS;IAOjB,OAAO,CAAC,iBAAiB;IAmBzB,OAAO,CAAC,WAAW;IAmBnB,8FAA8F;IAC9F,YAAY,CAAC,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,GAAG,OAAO;IAIpD,MAAM,CAAC,EAAE,EAAE,MAAM;IAkCjB,OAAO,CAAC,OAAO;IAsBf,OAAO,CAAC,UAAU;IAQlB,OAAO,CAAC,SAAS;CAsBlB"}
|