@geometra/renderer-three 0.1.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,7 +2,9 @@
2
2
 
3
3
  **Three.js helpers alongside Geometra** — not a replacement for `@geometra/renderer-canvas`. Geometra’s WebSocket geometry protocol still drives the 2D canvas; Three.js owns WebGL for meshes, scenes, and cameras.
4
4
 
5
- This package ships the **split-view host** pattern (Three.js pane + Geometra pane) as one bootstrap, plus small WebGL sizing utilities.
5
+ This package ships the **split-view host** pattern (Three.js pane + Geometra pane) and a **stacked HUD** host (full-viewport WebGL + corner Geometra overlay) as bootstrap helpers, plus small WebGL sizing utilities.
6
+
7
+ For the long-term goal — Three.js and Geometra in one **native**, protocol-first, DOM-free space — see [docs/GEOMETRA_NATIVE_SPACE.md](docs/GEOMETRA_NATIVE_SPACE.md). To drive the Cursor Agent CLI in a loop (like the main Geometra repo), use [`scripts/cursor-agent-loop.sh`](scripts/cursor-agent-loop.sh) after installing the [`agent` CLI](https://cursor.com/install); each iteration should pass `npm run release:gate`.
6
8
 
7
9
  ## Install
8
10
 
@@ -39,17 +41,45 @@ const host = createThreeGeometraSplitHost({
39
41
  host.destroy()
40
42
  ```
41
43
 
42
- `createThreeGeometraSplitHost` forwards all [`createBrowserCanvasClient`](https://github.com/razroo/geometra/tree/main/packages/renderer-canvas) options except `canvas` (it creates the Geometra canvas for you).
44
+ ### Stacked HUD (WebGL + overlay)
45
+
46
+ ```ts
47
+ import * as THREE from 'three'
48
+ import { createThreeGeometraStackedHost } from '@geometra/renderer-three'
49
+
50
+ const stacked = createThreeGeometraStackedHost({
51
+ container: document.getElementById('app')!,
52
+ geometraHudWidth: 400,
53
+ geometraHudHeight: 280,
54
+ geometraHudPlacement: 'bottom-right',
55
+ geometraHudMargin: 16,
56
+ url: 'ws://localhost:8080/geometra-ws',
57
+ binaryFraming: true,
58
+ onThreeReady: ({ scene, camera }) => {
59
+ scene.add(new THREE.AmbientLight(0xffffff, 0.55))
60
+ camera.position.set(0, 1.5, 6)
61
+ },
62
+ })
63
+ stacked.destroy()
64
+ ```
65
+
66
+ Use `geometraHudPointerEvents` (CSS `pointer-events`, default `'auto'`) when you want the HUD to ignore input — for example `'none'` so pointer events reach the full-viewport Three.js canvas underneath.
67
+
68
+ `createThreeGeometraSplitHost` and `createThreeGeometraStackedHost` each forward all [`createBrowserCanvasClient`](https://github.com/razroo/geometra/tree/main/packages/renderer-canvas) options except `canvas` (the host creates the Geometra canvas for you).
43
69
 
44
70
  ### Geometra resize
45
71
 
46
72
  The Geometra thin client listens to `window` resize by default. When only the Geometra column changes size, this host dispatches a synthetic `window` `resize` after layout so server layout width/height stay in sync.
47
73
 
74
+ ### Shared scene / camera defaults (headless-friendly)
75
+
76
+ `createGeometraThreeSceneBasics` builds a `Scene`, `PerspectiveCamera`, and `Clock` with the same defaults as the split and stacked hosts. Pair it with your own `WebGLRenderer` (or a headless GL context) and `resizeGeometraThreePerspectiveView` so buffer size and camera aspect match those hosts, or use `setWebGLDrawingBufferSize` alone when you already manage aspect and `setPixelRatio` yourself.
77
+
48
78
  ## Roadmap (not in v0)
49
79
 
50
80
  - Optional **scene-graph extension** to the GEOM protocol (headless + WebGL from the same JSON).
51
- - **Stacked** overlay (full-viewport WebGL + partial Geometra HUD) with explicit pointer routing.
52
- - **Node / headless Three** helpers for parity with “AI talks the same protocol” stories.
81
+ - **Stacked** overlay: v0 ships a corner HUD layout; richer stacking (custom regions, explicit `pointer-events` policies) may follow.
82
+ - **Node / headless Three** helpers for parity with “AI talks the same protocol” stories (`createGeometraThreeSceneBasics` is a small shared baseline; richer helpers may follow).
53
83
 
54
84
  ## Releasing (GitHub + npm)
55
85
 
package/dist/index.d.ts CHANGED
@@ -1,3 +1,7 @@
1
1
  export { createThreeGeometraSplitHost, type ThreeGeometraSplitHostHandle, type ThreeGeometraSplitHostOptions, type ThreeFrameContext, type ThreeRuntimeContext, } from './split-host.js';
2
- export { setWebGLDrawingBufferSize } from './utils.js';
2
+ export { createThreeGeometraStackedHost, type GeometraHudPlacement, type ThreeGeometraStackedHostHandle, type ThreeGeometraStackedHostOptions, } from './stacked-host.js';
3
+ export { resizeGeometraThreePerspectiveView, setWebGLDrawingBufferSize } from './utils.js';
4
+ export { createGeometraThreeSceneBasics, type GeometraThreeSceneBasicsOptions, } from './three-scene-basics.js';
5
+ /** Re-export for hybrid apps using {@link createThreeGeometraSplitHost} with `onData`. */
6
+ export { GEOM_DATA_CHANNEL_TRACKER_SNAPSHOT } from '@geometra/client';
3
7
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,4BAA4B,EAC5B,KAAK,4BAA4B,EACjC,KAAK,6BAA6B,EAClC,KAAK,iBAAiB,EACtB,KAAK,mBAAmB,GACzB,MAAM,iBAAiB,CAAA;AACxB,OAAO,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,4BAA4B,EAC5B,KAAK,4BAA4B,EACjC,KAAK,6BAA6B,EAClC,KAAK,iBAAiB,EACtB,KAAK,mBAAmB,GACzB,MAAM,iBAAiB,CAAA;AACxB,OAAO,EACL,8BAA8B,EAC9B,KAAK,oBAAoB,EACzB,KAAK,8BAA8B,EACnC,KAAK,+BAA+B,GACrC,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EAAE,kCAAkC,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAA;AAC1F,OAAO,EACL,8BAA8B,EAC9B,KAAK,+BAA+B,GACrC,MAAM,yBAAyB,CAAA;AAEhC,0FAA0F;AAC1F,OAAO,EAAE,kCAAkC,EAAE,MAAM,kBAAkB,CAAA"}
package/dist/index.js CHANGED
@@ -1,3 +1,7 @@
1
1
  export { createThreeGeometraSplitHost, } from './split-host.js';
2
- export { setWebGLDrawingBufferSize } from './utils.js';
2
+ export { createThreeGeometraStackedHost, } from './stacked-host.js';
3
+ export { resizeGeometraThreePerspectiveView, setWebGLDrawingBufferSize } from './utils.js';
4
+ export { createGeometraThreeSceneBasics, } from './three-scene-basics.js';
5
+ /** Re-export for hybrid apps using {@link createThreeGeometraSplitHost} with `onData`. */
6
+ export { GEOM_DATA_CHANNEL_TRACKER_SNAPSHOT } from '@geometra/client';
3
7
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,4BAA4B,GAK7B,MAAM,iBAAiB,CAAA;AACxB,OAAO,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,4BAA4B,GAK7B,MAAM,iBAAiB,CAAA;AACxB,OAAO,EACL,8BAA8B,GAI/B,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EAAE,kCAAkC,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAA;AAC1F,OAAO,EACL,8BAA8B,GAE/B,MAAM,yBAAyB,CAAA;AAEhC,0FAA0F;AAC1F,OAAO,EAAE,kCAAkC,EAAE,MAAM,kBAAkB,CAAA"}
@@ -1,25 +1,17 @@
1
1
  import * as THREE from 'three';
2
2
  import { type BrowserCanvasClientHandle, type BrowserCanvasClientOptions } from '@geometra/renderer-canvas';
3
- export interface ThreeGeometraSplitHostOptions extends Omit<BrowserCanvasClientOptions, 'canvas'> {
3
+ import { type GeometraThreeSceneBasicsOptions } from './three-scene-basics.js';
4
+ export interface ThreeGeometraSplitHostOptions extends Omit<BrowserCanvasClientOptions, 'canvas'>, GeometraThreeSceneBasicsOptions {
4
5
  /** Host element; a flex row is appended as a child (existing children are left untouched). */
5
6
  container: HTMLElement;
6
7
  /** Geometra column width in CSS pixels. Default: 420. */
7
8
  geometraWidth?: number;
8
9
  /** When true, Geometra panel is on the left. Default: false (Three.js left, Geometra right). */
9
10
  geometraOnLeft?: boolean;
10
- /** Clear color for the Three.js scene. Default: `0x000000`. */
11
- threeBackground?: THREE.ColorRepresentation;
12
- /** Perspective camera FOV in degrees. Default: 50. */
13
- cameraFov?: number;
14
- /** Near plane. Default: 0.1. */
15
- cameraNear?: number;
16
- /** Far plane. Default: 2000. */
17
- cameraFar?: number;
18
- /** Initial camera position. Default: `(0, 0, 5)`. */
19
- cameraPosition?: THREE.Vector3Tuple;
20
11
  /**
21
12
  * Called once after scene, camera, and renderer are created.
22
- * Add meshes, lights, controls, etc.
13
+ * Add meshes, lights, controls, etc. Call `ctx.destroy()` to tear down immediately; the render loop
14
+ * will not start if the host is already destroyed.
23
15
  */
24
16
  onThreeReady?: (ctx: ThreeRuntimeContext) => void;
25
17
  /**
@@ -33,6 +25,8 @@ export interface ThreeRuntimeContext {
33
25
  scene: THREE.Scene;
34
26
  camera: THREE.PerspectiveCamera;
35
27
  threeCanvas: HTMLCanvasElement;
28
+ /** Same as `ThreeGeometraSplitHostHandle.destroy` — idempotent full teardown. */
29
+ destroy(): void;
36
30
  }
37
31
  export interface ThreeFrameContext extends ThreeRuntimeContext {
38
32
  clock: THREE.Clock;
@@ -58,7 +52,15 @@ export interface ThreeGeometraSplitHostHandle {
58
52
  *
59
53
  * This is the recommended **hybrid** layout: 3D stays in Three; chrome and data panes stay in Geometra’s protocol.
60
54
  * Geometra’s client still uses `resizeTarget: window` by default; when only the Geometra column changes size,
61
- * a `ResizeObserver` dispatches a synthetic `resize` on `window` so layout width/height track the panel.
55
+ * a `ResizeObserver` dispatches a synthetic `resize` on `window` so layout width/height track the panel
56
+ * (coalesced to at most once per animation frame when both panes notify in the same frame).
57
+ *
58
+ * The Three.js pane listens to `window` `resize` as well so `devicePixelRatio` updates (zoom / display changes)
59
+ * refresh the WebGL drawing buffer without relying on panel `ResizeObserver` alone.
60
+ *
61
+ * Pass through {@link BrowserCanvasClientOptions} from `@geometra/renderer-canvas` / `@geometra/client`
62
+ * (for example `binaryFraming`, `onError`, `onFrameMetrics`, `onData` for JSON side-channels on the same
63
+ * socket as layout; channel names are defined by your app and the Geometra server).
62
64
  */
63
65
  export declare function createThreeGeometraSplitHost(options: ThreeGeometraSplitHostOptions): ThreeGeometraSplitHostHandle;
64
66
  //# sourceMappingURL=split-host.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"split-host.d.ts","sourceRoot":"","sources":["../src/split-host.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,EAEL,KAAK,yBAAyB,EAC9B,KAAK,0BAA0B,EAChC,MAAM,2BAA2B,CAAA;AAElC,MAAM,WAAW,6BAA8B,SAAQ,IAAI,CAAC,0BAA0B,EAAE,QAAQ,CAAC;IAC/F,8FAA8F;IAC9F,SAAS,EAAE,WAAW,CAAA;IACtB,yDAAyD;IACzD,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,gGAAgG;IAChG,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,+DAA+D;IAC/D,eAAe,CAAC,EAAE,KAAK,CAAC,mBAAmB,CAAA;IAC3C,sDAAsD;IACtD,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,gCAAgC;IAChC,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,gCAAgC;IAChC,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,qDAAqD;IACrD,cAAc,CAAC,EAAE,KAAK,CAAC,YAAY,CAAA;IACnC;;;OAGG;IACH,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,mBAAmB,KAAK,IAAI,CAAA;IACjD;;;OAGG;IACH,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,iBAAiB,KAAK,IAAI,CAAA;CAChD;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,KAAK,CAAC,aAAa,CAAA;IAC7B,KAAK,EAAE,KAAK,CAAC,KAAK,CAAA;IAClB,MAAM,EAAE,KAAK,CAAC,iBAAiB,CAAA;IAC/B,WAAW,EAAE,iBAAiB,CAAA;CAC/B;AAED,MAAM,WAAW,iBAAkB,SAAQ,mBAAmB;IAC5D,KAAK,EAAE,KAAK,CAAC,KAAK,CAAA;IAClB,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,4BAA4B;IAC3C,IAAI,EAAE,cAAc,CAAA;IACpB,UAAU,EAAE,cAAc,CAAA;IAC1B,aAAa,EAAE,cAAc,CAAA;IAC7B,WAAW,EAAE,iBAAiB,CAAA;IAC9B,cAAc,EAAE,iBAAiB,CAAA;IACjC,QAAQ,EAAE,KAAK,CAAC,aAAa,CAAA;IAC7B,KAAK,EAAE,KAAK,CAAC,KAAK,CAAA;IAClB,MAAM,EAAE,KAAK,CAAC,iBAAiB,CAAA;IAC/B,KAAK,EAAE,KAAK,CAAC,KAAK,CAAA;IAClB,QAAQ,EAAE,yBAAyB,CAAA;IACnC,wGAAwG;IACxG,OAAO,IAAI,IAAI,CAAA;CAChB;AAgBD;;;;;;GAMG;AACH,wBAAgB,4BAA4B,CAC1C,OAAO,EAAE,6BAA6B,GACrC,4BAA4B,CAgJ9B"}
1
+ {"version":3,"file":"split-host.d.ts","sourceRoot":"","sources":["../src/split-host.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,EAEL,KAAK,yBAAyB,EAC9B,KAAK,0BAA0B,EAChC,MAAM,2BAA2B,CAAA;AAClC,OAAO,EAAkC,KAAK,+BAA+B,EAAE,MAAM,yBAAyB,CAAA;AAG9G,MAAM,WAAW,6BACf,SAAQ,IAAI,CAAC,0BAA0B,EAAE,QAAQ,CAAC,EAChD,+BAA+B;IACjC,8FAA8F;IAC9F,SAAS,EAAE,WAAW,CAAA;IACtB,yDAAyD;IACzD,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,gGAAgG;IAChG,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB;;;;OAIG;IACH,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,mBAAmB,KAAK,IAAI,CAAA;IACjD;;;OAGG;IACH,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,iBAAiB,KAAK,IAAI,CAAA;CAChD;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,KAAK,CAAC,aAAa,CAAA;IAC7B,KAAK,EAAE,KAAK,CAAC,KAAK,CAAA;IAClB,MAAM,EAAE,KAAK,CAAC,iBAAiB,CAAA;IAC/B,WAAW,EAAE,iBAAiB,CAAA;IAC9B,iFAAiF;IACjF,OAAO,IAAI,IAAI,CAAA;CAChB;AAED,MAAM,WAAW,iBAAkB,SAAQ,mBAAmB;IAC5D,KAAK,EAAE,KAAK,CAAC,KAAK,CAAA;IAClB,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,4BAA4B;IAC3C,IAAI,EAAE,cAAc,CAAA;IACpB,UAAU,EAAE,cAAc,CAAA;IAC1B,aAAa,EAAE,cAAc,CAAA;IAC7B,WAAW,EAAE,iBAAiB,CAAA;IAC9B,cAAc,EAAE,iBAAiB,CAAA;IACjC,QAAQ,EAAE,KAAK,CAAC,aAAa,CAAA;IAC7B,KAAK,EAAE,KAAK,CAAC,KAAK,CAAA;IAClB,MAAM,EAAE,KAAK,CAAC,iBAAiB,CAAA;IAC/B,KAAK,EAAE,KAAK,CAAC,KAAK,CAAA;IAClB,QAAQ,EAAE,yBAAyB,CAAA;IACnC,wGAAwG;IACxG,OAAO,IAAI,IAAI,CAAA;CAChB;AAgBD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,4BAA4B,CAC1C,OAAO,EAAE,6BAA6B,GACrC,4BAA4B,CAkK9B"}
@@ -1,5 +1,7 @@
1
1
  import * as THREE from 'three';
2
2
  import { createBrowserCanvasClient, } from '@geometra/renderer-canvas';
3
+ import { createGeometraThreeSceneBasics } from './three-scene-basics.js';
4
+ import { resizeGeometraThreePerspectiveView } from './utils.js';
3
5
  function panelStyle(el, flex) {
4
6
  el.style.flex = flex;
5
7
  el.style.minWidth = '0';
@@ -17,7 +19,15 @@ function fullSizeCanvas(canvas) {
17
19
  *
18
20
  * This is the recommended **hybrid** layout: 3D stays in Three; chrome and data panes stay in Geometra’s protocol.
19
21
  * Geometra’s client still uses `resizeTarget: window` by default; when only the Geometra column changes size,
20
- * a `ResizeObserver` dispatches a synthetic `resize` on `window` so layout width/height track the panel.
22
+ * a `ResizeObserver` dispatches a synthetic `resize` on `window` so layout width/height track the panel
23
+ * (coalesced to at most once per animation frame when both panes notify in the same frame).
24
+ *
25
+ * The Three.js pane listens to `window` `resize` as well so `devicePixelRatio` updates (zoom / display changes)
26
+ * refresh the WebGL drawing buffer without relying on panel `ResizeObserver` alone.
27
+ *
28
+ * Pass through {@link BrowserCanvasClientOptions} from `@geometra/renderer-canvas` / `@geometra/client`
29
+ * (for example `binaryFraming`, `onError`, `onFrameMetrics`, `onData` for JSON side-channels on the same
30
+ * socket as layout; channel names are defined by your app and the Geometra server).
21
31
  */
22
32
  export function createThreeGeometraSplitHost(options) {
23
33
  const { container, geometraWidth = 420, geometraOnLeft = false, threeBackground = 0x000000, cameraFov = 50, cameraNear = 0.1, cameraFar = 2000, cameraPosition = [0, 0, 5], onThreeReady, onThreeFrame, window: providedWindow, ...browserOptions } = options;
@@ -57,27 +67,32 @@ export function createThreeGeometraSplitHost(options) {
57
67
  antialias: true,
58
68
  alpha: false,
59
69
  });
60
- glRenderer.setPixelRatio(win.devicePixelRatio || 1);
61
- const scene = new THREE.Scene();
62
- scene.background = new THREE.Color(threeBackground);
63
- const camera = new THREE.PerspectiveCamera(cameraFov, 1, cameraNear, cameraFar);
64
- camera.position.set(cameraPosition[0], cameraPosition[1], cameraPosition[2]);
65
- const clock = new THREE.Clock();
70
+ const { scene, camera, clock } = createGeometraThreeSceneBasics({
71
+ threeBackground,
72
+ cameraFov,
73
+ cameraNear,
74
+ cameraFar,
75
+ cameraPosition,
76
+ });
66
77
  const resizeThree = () => {
67
- const w = Math.max(1, Math.floor(threePanel.clientWidth));
68
- const h = Math.max(1, Math.floor(threePanel.clientHeight));
69
- camera.aspect = w / h;
70
- camera.updateProjectionMatrix();
71
- glRenderer.setSize(w, h, false);
78
+ resizeGeometraThreePerspectiveView(glRenderer, camera, threePanel.clientWidth, threePanel.clientHeight, win.devicePixelRatio || 1);
79
+ };
80
+ const onWindowResize = () => {
81
+ resizeThree();
72
82
  };
83
+ win.addEventListener('resize', onWindowResize, { passive: true });
73
84
  resizeThree();
74
85
  const geometraHandle = createBrowserCanvasClient({
75
86
  ...browserOptions,
76
87
  canvas: geometraCanvas,
77
88
  window: win,
78
89
  });
90
+ let geometraResizeRafId;
79
91
  const triggerGeometraResize = () => {
80
- win.requestAnimationFrame(() => {
92
+ if (geometraResizeRafId !== undefined)
93
+ return;
94
+ geometraResizeRafId = win.requestAnimationFrame(() => {
95
+ geometraResizeRafId = undefined;
81
96
  win.dispatchEvent(new Event('resize'));
82
97
  });
83
98
  };
@@ -87,37 +102,46 @@ export function createThreeGeometraSplitHost(options) {
87
102
  });
88
103
  roContainer.observe(threePanel);
89
104
  roContainer.observe(geometraPanel);
105
+ let rafId;
106
+ let destroyed = false;
107
+ const destroy = () => {
108
+ if (destroyed)
109
+ return;
110
+ destroyed = true;
111
+ if (rafId !== undefined) {
112
+ win.cancelAnimationFrame(rafId);
113
+ rafId = undefined;
114
+ }
115
+ if (geometraResizeRafId !== undefined) {
116
+ win.cancelAnimationFrame(geometraResizeRafId);
117
+ geometraResizeRafId = undefined;
118
+ }
119
+ win.removeEventListener('resize', onWindowResize);
120
+ roContainer.disconnect();
121
+ geometraHandle.destroy();
122
+ glRenderer.dispose();
123
+ root.remove();
124
+ };
90
125
  const ctxBase = {
91
126
  renderer: glRenderer,
92
127
  scene,
93
128
  camera,
94
129
  threeCanvas,
130
+ destroy,
95
131
  };
96
132
  onThreeReady?.(ctxBase);
97
- let raf = 0;
98
- let destroyed = false;
99
133
  const loop = () => {
100
134
  if (destroyed)
101
135
  return;
102
- raf = win.requestAnimationFrame(loop);
136
+ rafId = win.requestAnimationFrame(loop);
103
137
  const delta = clock.getDelta();
104
138
  const elapsed = clock.elapsedTime;
105
139
  onThreeFrame?.({ ...ctxBase, clock, delta, elapsed });
106
140
  glRenderer.render(scene, camera);
107
141
  };
108
- raf = win.requestAnimationFrame(loop);
109
- const destroy = () => {
110
- if (destroyed)
111
- return;
112
- destroyed = true;
113
- win.cancelAnimationFrame(raf);
114
- roContainer.disconnect();
115
- geometraHandle.destroy();
116
- glRenderer.dispose();
117
- if (root.parentNode === container) {
118
- container.removeChild(root);
119
- }
120
- };
142
+ if (!destroyed) {
143
+ rafId = win.requestAnimationFrame(loop);
144
+ }
121
145
  return {
122
146
  root,
123
147
  threePanel,
@@ -1 +1 @@
1
- {"version":3,"file":"split-host.js","sourceRoot":"","sources":["../src/split-host.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,EACL,yBAAyB,GAG1B,MAAM,2BAA2B,CAAA;AA2DlC,SAAS,UAAU,CAAC,EAAe,EAAE,IAAY;IAC/C,EAAE,CAAC,KAAK,CAAC,IAAI,GAAG,IAAI,CAAA;IACpB,EAAE,CAAC,KAAK,CAAC,QAAQ,GAAG,GAAG,CAAA;IACvB,EAAE,CAAC,KAAK,CAAC,SAAS,GAAG,GAAG,CAAA;IACxB,EAAE,CAAC,KAAK,CAAC,QAAQ,GAAG,UAAU,CAAA;IAC9B,EAAE,CAAC,KAAK,CAAC,QAAQ,GAAG,QAAQ,CAAA;AAC9B,CAAC;AAED,SAAS,cAAc,CAAC,MAAyB;IAC/C,MAAM,CAAC,KAAK,CAAC,OAAO,GAAG,OAAO,CAAA;IAC9B,MAAM,CAAC,KAAK,CAAC,KAAK,GAAG,MAAM,CAAA;IAC3B,MAAM,CAAC,KAAK,CAAC,MAAM,GAAG,MAAM,CAAA;AAC9B,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,4BAA4B,CAC1C,OAAsC;IAEtC,MAAM,EACJ,SAAS,EACT,aAAa,GAAG,GAAG,EACnB,cAAc,GAAG,KAAK,EACtB,eAAe,GAAG,QAAQ,EAC1B,SAAS,GAAG,EAAE,EACd,UAAU,GAAG,GAAG,EAChB,SAAS,GAAG,IAAI,EAChB,cAAc,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAC1B,YAAY,EACZ,YAAY,EACZ,MAAM,EAAE,cAAc,EACtB,GAAG,cAAc,EAClB,GAAG,OAAO,CAAA;IAEX,MAAM,GAAG,GAAG,SAAS,CAAC,aAAa,CAAA;IACnC,MAAM,GAAG,GAAG,cAAc,IAAI,GAAG,CAAC,WAAW,CAAA;IAC7C,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,IAAI,KAAK,CAAC,wDAAwD,CAAC,CAAA;IAC3E,CAAC;IAED,MAAM,IAAI,GAAG,GAAG,CAAC,aAAa,CAAC,KAAK,CAAC,CAAA;IACrC,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,MAAM,CAAA;IAC3B,IAAI,CAAC,KAAK,CAAC,aAAa,GAAG,KAAK,CAAA;IAChC,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,MAAM,CAAA;IACzB,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,MAAM,CAAA;IAC1B,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,GAAG,CAAA;IAC1B,IAAI,CAAC,KAAK,CAAC,QAAQ,GAAG,GAAG,CAAA;IACzB,SAAS,CAAC,WAAW,CAAC,IAAI,CAAC,CAAA;IAE3B,MAAM,UAAU,GAAG,GAAG,CAAC,aAAa,CAAC,KAAK,CAAC,CAAA;IAC3C,UAAU,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAA;IAEhC,MAAM,aAAa,GAAG,GAAG,CAAC,aAAa,CAAC,KAAK,CAAC,CAAA;IAC9C,UAAU,CAAC,aAAa,EAAE,UAAU,CAAC,CAAA;IACrC,aAAa,CAAC,KAAK,CAAC,KAAK,GAAG,GAAG,aAAa,IAAI,CAAA;IAChD,aAAa,CAAC,KAAK,CAAC,UAAU,GAAG,GAAG,CAAA;IAEpC,IAAI,cAAc,EAAE,CAAC;QACnB,IAAI,CAAC,MAAM,CAAC,aAAa,EAAE,UAAU,CAAC,CAAA;IACxC,CAAC;SAAM,CAAC;QACN,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,aAAa,CAAC,CAAA;IACxC,CAAC;IAED,MAAM,WAAW,GAAG,GAAG,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAA;IAC/C,cAAc,CAAC,WAAW,CAAC,CAAA;IAC3B,UAAU,CAAC,WAAW,CAAC,WAAW,CAAC,CAAA;IAEnC,MAAM,cAAc,GAAG,GAAG,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAA;IAClD,cAAc,CAAC,cAAc,CAAC,CAAA;IAC9B,aAAa,CAAC,WAAW,CAAC,cAAc,CAAC,CAAA;IAEzC,MAAM,UAAU,GAAG,IAAI,KAAK,CAAC,aAAa,CAAC;QACzC,MAAM,EAAE,WAAW;QACnB,SAAS,EAAE,IAAI;QACf,KAAK,EAAE,KAAK;KACb,CAAC,CAAA;IACF,UAAU,CAAC,aAAa,CAAC,GAAG,CAAC,gBAAgB,IAAI,CAAC,CAAC,CAAA;IAEnD,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,KAAK,EAAE,CAAA;IAC/B,KAAK,CAAC,UAAU,GAAG,IAAI,KAAK,CAAC,KAAK,CAAC,eAAe,CAAC,CAAA;IAEnD,MAAM,MAAM,GAAG,IAAI,KAAK,CAAC,iBAAiB,CAAC,SAAS,EAAE,CAAC,EAAE,UAAU,EAAE,SAAS,CAAC,CAAA;IAC/E,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,CAAE,EAAE,cAAc,CAAC,CAAC,CAAE,EAAE,cAAc,CAAC,CAAC,CAAE,CAAC,CAAA;IAE/E,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,KAAK,EAAE,CAAA;IAE/B,MAAM,WAAW,GAAG,GAAG,EAAE;QACvB,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC,CAAA;QACzD,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC,CAAA;QAC1D,MAAM,CAAC,MAAM,GAAG,CAAC,GAAG,CAAC,CAAA;QACrB,MAAM,CAAC,sBAAsB,EAAE,CAAA;QAC/B,UAAU,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC,CAAA;IACjC,CAAC,CAAA;IAED,WAAW,EAAE,CAAA;IAEb,MAAM,cAAc,GAAG,yBAAyB,CAAC;QAC/C,GAAG,cAAc;QACjB,MAAM,EAAE,cAAc;QACtB,MAAM,EAAE,GAAG;KACZ,CAAC,CAAA;IAEF,MAAM,qBAAqB,GAAG,GAAG,EAAE;QACjC,GAAG,CAAC,qBAAqB,CAAC,GAAG,EAAE;YAC7B,GAAG,CAAC,aAAa,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAA;QACxC,CAAC,CAAC,CAAA;IACJ,CAAC,CAAA;IAED,MAAM,WAAW,GAAG,IAAI,cAAc,CAAC,GAAG,EAAE;QAC1C,WAAW,EAAE,CAAA;QACb,qBAAqB,EAAE,CAAA;IACzB,CAAC,CAAC,CAAA;IACF,WAAW,CAAC,OAAO,CAAC,UAAU,CAAC,CAAA;IAC/B,WAAW,CAAC,OAAO,CAAC,aAAa,CAAC,CAAA;IAElC,MAAM,OAAO,GAAwB;QACnC,QAAQ,EAAE,UAAU;QACpB,KAAK;QACL,MAAM;QACN,WAAW;KACZ,CAAA;IAED,YAAY,EAAE,CAAC,OAAO,CAAC,CAAA;IAEvB,IAAI,GAAG,GAAG,CAAC,CAAA;IACX,IAAI,SAAS,GAAG,KAAK,CAAA;IAErB,MAAM,IAAI,GAAG,GAAG,EAAE;QAChB,IAAI,SAAS;YAAE,OAAM;QACrB,GAAG,GAAG,GAAG,CAAC,qBAAqB,CAAC,IAAI,CAAC,CAAA;QACrC,MAAM,KAAK,GAAG,KAAK,CAAC,QAAQ,EAAE,CAAA;QAC9B,MAAM,OAAO,GAAG,KAAK,CAAC,WAAW,CAAA;QACjC,YAAY,EAAE,CAAC,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAA;QACrD,UAAU,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,CAAA;IAClC,CAAC,CAAA;IACD,GAAG,GAAG,GAAG,CAAC,qBAAqB,CAAC,IAAI,CAAC,CAAA;IAErC,MAAM,OAAO,GAAG,GAAG,EAAE;QACnB,IAAI,SAAS;YAAE,OAAM;QACrB,SAAS,GAAG,IAAI,CAAA;QAChB,GAAG,CAAC,oBAAoB,CAAC,GAAG,CAAC,CAAA;QAC7B,WAAW,CAAC,UAAU,EAAE,CAAA;QACxB,cAAc,CAAC,OAAO,EAAE,CAAA;QACxB,UAAU,CAAC,OAAO,EAAE,CAAA;QACpB,IAAI,IAAI,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;YAClC,SAAS,CAAC,WAAW,CAAC,IAAI,CAAC,CAAA;QAC7B,CAAC;IACH,CAAC,CAAA;IAED,OAAO;QACL,IAAI;QACJ,UAAU;QACV,aAAa;QACb,WAAW;QACX,cAAc;QACd,QAAQ,EAAE,UAAU;QACpB,KAAK;QACL,MAAM;QACN,KAAK;QACL,QAAQ,EAAE,cAAc;QACxB,OAAO;KACR,CAAA;AACH,CAAC"}
1
+ {"version":3,"file":"split-host.js","sourceRoot":"","sources":["../src/split-host.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,59 @@
1
+ import * as THREE from 'three';
2
+ import { type BrowserCanvasClientHandle, type BrowserCanvasClientOptions } from '@geometra/renderer-canvas';
3
+ import type { ThreeFrameContext, ThreeRuntimeContext } from './split-host.js';
4
+ import { type GeometraThreeSceneBasicsOptions } from './three-scene-basics.js';
5
+ /** Corner anchor for the Geometra HUD overlay (CSS `position: absolute` on the host). */
6
+ export type GeometraHudPlacement = 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';
7
+ export interface ThreeGeometraStackedHostOptions extends Omit<BrowserCanvasClientOptions, 'canvas'>, GeometraThreeSceneBasicsOptions {
8
+ /** Host element; a full-size stacking context is appended (existing children are left untouched). */
9
+ container: HTMLElement;
10
+ /** HUD width in CSS pixels. Default: 420. */
11
+ geometraHudWidth?: number;
12
+ /** HUD height in CSS pixels. Default: 320. */
13
+ geometraHudHeight?: number;
14
+ /** HUD corner. Default: `bottom-right`. */
15
+ geometraHudPlacement?: GeometraHudPlacement;
16
+ /** Inset from the chosen corner in CSS pixels. Default: 12. */
17
+ geometraHudMargin?: number;
18
+ /**
19
+ * CSS `pointer-events` on the HUD wrapper (e.g. `'none'` so input falls through to the WebGL canvas).
20
+ * Default: `'auto'`.
21
+ */
22
+ geometraHudPointerEvents?: string;
23
+ /**
24
+ * Called once after scene, camera, and renderer are created.
25
+ * Call `ctx.destroy()` to tear down immediately; the render loop will not start if the host is already destroyed.
26
+ */
27
+ onThreeReady?: (ctx: ThreeRuntimeContext) => void;
28
+ /**
29
+ * Called every frame before `renderer.render`.
30
+ */
31
+ onThreeFrame?: (ctx: ThreeFrameContext) => void;
32
+ }
33
+ export interface ThreeGeometraStackedHostHandle {
34
+ root: HTMLDivElement;
35
+ /** Absolutely positioned wrapper around the Geometra canvas (stacked HUD). */
36
+ geometraHud: HTMLDivElement;
37
+ threeCanvas: HTMLCanvasElement;
38
+ geometraCanvas: HTMLCanvasElement;
39
+ renderer: THREE.WebGLRenderer;
40
+ scene: THREE.Scene;
41
+ camera: THREE.PerspectiveCamera;
42
+ clock: THREE.Clock;
43
+ geometra: BrowserCanvasClientHandle;
44
+ destroy(): void;
45
+ }
46
+ /**
47
+ * Stacked host: full-viewport Three.js `WebGLRenderer` with a positioned Geometra canvas **HUD** on top.
48
+ *
49
+ * Pointer routing follows normal hit-testing: events hit the Geometra canvas where it overlaps the WebGL layer
50
+ * (`z-index` above the Three canvas); elsewhere, the Three canvas receives input. Override with
51
+ * {@link ThreeGeometraStackedHostOptions.geometraHudPointerEvents} (e.g. `'none'` for a click-through HUD).
52
+ *
53
+ * Geometra’s client still uses `resizeTarget: window` by default; when only the HUD box changes size,
54
+ * a coalesced synthetic `resize` is dispatched on `window` (same pattern as {@link createThreeGeometraSplitHost}).
55
+ * The Three.js layer listens to `window` `resize` for `devicePixelRatio` changes and uses the host `root` size
56
+ * for the drawing buffer.
57
+ */
58
+ export declare function createThreeGeometraStackedHost(options: ThreeGeometraStackedHostOptions): ThreeGeometraStackedHostHandle;
59
+ //# sourceMappingURL=stacked-host.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stacked-host.d.ts","sourceRoot":"","sources":["../src/stacked-host.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,EAEL,KAAK,yBAAyB,EAC9B,KAAK,0BAA0B,EAChC,MAAM,2BAA2B,CAAA;AAClC,OAAO,KAAK,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAA;AAC7E,OAAO,EAAkC,KAAK,+BAA+B,EAAE,MAAM,yBAAyB,CAAA;AAG9G,yFAAyF;AACzF,MAAM,MAAM,oBAAoB,GAAG,cAAc,GAAG,aAAa,GAAG,WAAW,GAAG,UAAU,CAAA;AAE5F,MAAM,WAAW,+BACf,SAAQ,IAAI,CAAC,0BAA0B,EAAE,QAAQ,CAAC,EAChD,+BAA+B;IACjC,qGAAqG;IACrG,SAAS,EAAE,WAAW,CAAA;IACtB,6CAA6C;IAC7C,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,8CAA8C;IAC9C,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,2CAA2C;IAC3C,oBAAoB,CAAC,EAAE,oBAAoB,CAAA;IAC3C,+DAA+D;IAC/D,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B;;;OAGG;IACH,wBAAwB,CAAC,EAAE,MAAM,CAAA;IACjC;;;OAGG;IACH,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,mBAAmB,KAAK,IAAI,CAAA;IACjD;;OAEG;IACH,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,iBAAiB,KAAK,IAAI,CAAA;CAChD;AAED,MAAM,WAAW,8BAA8B;IAC7C,IAAI,EAAE,cAAc,CAAA;IACpB,8EAA8E;IAC9E,WAAW,EAAE,cAAc,CAAA;IAC3B,WAAW,EAAE,iBAAiB,CAAA;IAC9B,cAAc,EAAE,iBAAiB,CAAA;IACjC,QAAQ,EAAE,KAAK,CAAC,aAAa,CAAA;IAC7B,KAAK,EAAE,KAAK,CAAC,KAAK,CAAA;IAClB,MAAM,EAAE,KAAK,CAAC,iBAAiB,CAAA;IAC/B,KAAK,EAAE,KAAK,CAAC,KAAK,CAAA;IAClB,QAAQ,EAAE,yBAAyB,CAAA;IACnC,OAAO,IAAI,IAAI,CAAA;CAChB;AAsCD;;;;;;;;;;;GAWG;AACH,wBAAgB,8BAA8B,CAC5C,OAAO,EAAE,+BAA+B,GACvC,8BAA8B,CA6KhC"}
@@ -0,0 +1,182 @@
1
+ import * as THREE from 'three';
2
+ import { createBrowserCanvasClient, } from '@geometra/renderer-canvas';
3
+ import { createGeometraThreeSceneBasics } from './three-scene-basics.js';
4
+ import { resizeGeometraThreePerspectiveView } from './utils.js';
5
+ function fullSizeCanvas(canvas) {
6
+ canvas.style.display = 'block';
7
+ canvas.style.width = '100%';
8
+ canvas.style.height = '100%';
9
+ }
10
+ function applyHudPlacement(wrap, placement, marginPx) {
11
+ const m = `${marginPx}px`;
12
+ wrap.style.left = '';
13
+ wrap.style.right = '';
14
+ wrap.style.top = '';
15
+ wrap.style.bottom = '';
16
+ switch (placement) {
17
+ case 'bottom-right':
18
+ wrap.style.right = m;
19
+ wrap.style.bottom = m;
20
+ break;
21
+ case 'bottom-left':
22
+ wrap.style.left = m;
23
+ wrap.style.bottom = m;
24
+ break;
25
+ case 'top-right':
26
+ wrap.style.right = m;
27
+ wrap.style.top = m;
28
+ break;
29
+ case 'top-left':
30
+ wrap.style.left = m;
31
+ wrap.style.top = m;
32
+ break;
33
+ }
34
+ }
35
+ /**
36
+ * Stacked host: full-viewport Three.js `WebGLRenderer` with a positioned Geometra canvas **HUD** on top.
37
+ *
38
+ * Pointer routing follows normal hit-testing: events hit the Geometra canvas where it overlaps the WebGL layer
39
+ * (`z-index` above the Three canvas); elsewhere, the Three canvas receives input. Override with
40
+ * {@link ThreeGeometraStackedHostOptions.geometraHudPointerEvents} (e.g. `'none'` for a click-through HUD).
41
+ *
42
+ * Geometra’s client still uses `resizeTarget: window` by default; when only the HUD box changes size,
43
+ * a coalesced synthetic `resize` is dispatched on `window` (same pattern as {@link createThreeGeometraSplitHost}).
44
+ * The Three.js layer listens to `window` `resize` for `devicePixelRatio` changes and uses the host `root` size
45
+ * for the drawing buffer.
46
+ */
47
+ export function createThreeGeometraStackedHost(options) {
48
+ const { container, geometraHudWidth = 420, geometraHudHeight = 320, geometraHudPlacement = 'bottom-right', geometraHudMargin = 12, geometraHudPointerEvents = 'auto', threeBackground = 0x000000, cameraFov = 50, cameraNear = 0.1, cameraFar = 2000, cameraPosition = [0, 0, 5], onThreeReady, onThreeFrame, window: providedWindow, ...browserOptions } = options;
49
+ const doc = container.ownerDocument;
50
+ const win = providedWindow ?? doc.defaultView;
51
+ if (!win) {
52
+ throw new Error('createThreeGeometraStackedHost requires a browser window');
53
+ }
54
+ const root = doc.createElement('div');
55
+ root.style.position = 'relative';
56
+ root.style.width = '100%';
57
+ root.style.height = '100%';
58
+ root.style.minHeight = '0';
59
+ root.style.minWidth = '0';
60
+ root.style.overflow = 'hidden';
61
+ container.appendChild(root);
62
+ const threeCanvas = doc.createElement('canvas');
63
+ fullSizeCanvas(threeCanvas);
64
+ threeCanvas.style.position = 'absolute';
65
+ threeCanvas.style.left = '0';
66
+ threeCanvas.style.top = '0';
67
+ threeCanvas.style.width = '100%';
68
+ threeCanvas.style.height = '100%';
69
+ threeCanvas.style.zIndex = '0';
70
+ root.appendChild(threeCanvas);
71
+ const geometraHud = doc.createElement('div');
72
+ geometraHud.style.position = 'absolute';
73
+ geometraHud.style.zIndex = '1';
74
+ geometraHud.style.width = `${geometraHudWidth}px`;
75
+ geometraHud.style.height = `${geometraHudHeight}px`;
76
+ geometraHud.style.minWidth = '0';
77
+ geometraHud.style.minHeight = '0';
78
+ geometraHud.style.overflow = 'hidden';
79
+ geometraHud.style.pointerEvents = geometraHudPointerEvents;
80
+ applyHudPlacement(geometraHud, geometraHudPlacement, geometraHudMargin);
81
+ root.appendChild(geometraHud);
82
+ const geometraCanvas = doc.createElement('canvas');
83
+ fullSizeCanvas(geometraCanvas);
84
+ geometraHud.appendChild(geometraCanvas);
85
+ const glRenderer = new THREE.WebGLRenderer({
86
+ canvas: threeCanvas,
87
+ antialias: true,
88
+ alpha: false,
89
+ });
90
+ const { scene, camera, clock } = createGeometraThreeSceneBasics({
91
+ threeBackground,
92
+ cameraFov,
93
+ cameraNear,
94
+ cameraFar,
95
+ cameraPosition,
96
+ });
97
+ const resizeThree = () => {
98
+ resizeGeometraThreePerspectiveView(glRenderer, camera, root.clientWidth, root.clientHeight, win.devicePixelRatio || 1);
99
+ };
100
+ const onWindowResize = () => {
101
+ resizeThree();
102
+ };
103
+ win.addEventListener('resize', onWindowResize, { passive: true });
104
+ resizeThree();
105
+ const geometraHandle = createBrowserCanvasClient({
106
+ ...browserOptions,
107
+ canvas: geometraCanvas,
108
+ window: win,
109
+ });
110
+ let geometraResizeRafId;
111
+ const triggerGeometraResize = () => {
112
+ if (geometraResizeRafId !== undefined)
113
+ return;
114
+ geometraResizeRafId = win.requestAnimationFrame(() => {
115
+ geometraResizeRafId = undefined;
116
+ win.dispatchEvent(new Event('resize'));
117
+ });
118
+ };
119
+ const roRoot = new ResizeObserver(() => {
120
+ resizeThree();
121
+ triggerGeometraResize();
122
+ });
123
+ roRoot.observe(root);
124
+ const roHud = new ResizeObserver(() => {
125
+ triggerGeometraResize();
126
+ });
127
+ roHud.observe(geometraHud);
128
+ let rafId;
129
+ let destroyed = false;
130
+ const destroy = () => {
131
+ if (destroyed)
132
+ return;
133
+ destroyed = true;
134
+ if (rafId !== undefined) {
135
+ win.cancelAnimationFrame(rafId);
136
+ rafId = undefined;
137
+ }
138
+ if (geometraResizeRafId !== undefined) {
139
+ win.cancelAnimationFrame(geometraResizeRafId);
140
+ geometraResizeRafId = undefined;
141
+ }
142
+ win.removeEventListener('resize', onWindowResize);
143
+ roRoot.disconnect();
144
+ roHud.disconnect();
145
+ geometraHandle.destroy();
146
+ glRenderer.dispose();
147
+ root.remove();
148
+ };
149
+ const ctxBase = {
150
+ renderer: glRenderer,
151
+ scene,
152
+ camera,
153
+ threeCanvas,
154
+ destroy,
155
+ };
156
+ onThreeReady?.(ctxBase);
157
+ const loop = () => {
158
+ if (destroyed)
159
+ return;
160
+ rafId = win.requestAnimationFrame(loop);
161
+ const delta = clock.getDelta();
162
+ const elapsed = clock.elapsedTime;
163
+ onThreeFrame?.({ ...ctxBase, clock, delta, elapsed });
164
+ glRenderer.render(scene, camera);
165
+ };
166
+ if (!destroyed) {
167
+ rafId = win.requestAnimationFrame(loop);
168
+ }
169
+ return {
170
+ root,
171
+ geometraHud,
172
+ threeCanvas,
173
+ geometraCanvas,
174
+ renderer: glRenderer,
175
+ scene,
176
+ camera,
177
+ clock,
178
+ geometra: geometraHandle,
179
+ destroy,
180
+ };
181
+ }
182
+ //# sourceMappingURL=stacked-host.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stacked-host.js","sourceRoot":"","sources":["../src/stacked-host.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ import * as THREE from 'three';
2
+ /** Options shared by split/stacked hosts and {@link createGeometraThreeSceneBasics}. */
3
+ export interface GeometraThreeSceneBasicsOptions {
4
+ /** Clear color for the Three.js scene. Default: `0x000000`. */
5
+ threeBackground?: THREE.ColorRepresentation;
6
+ /** Perspective camera FOV in degrees. Default: 50. */
7
+ cameraFov?: number;
8
+ /** Near plane. Default: 0.1. */
9
+ cameraNear?: number;
10
+ /** Far plane. Default: 2000. */
11
+ cameraFar?: number;
12
+ /** Initial camera position. Default: `(0, 0, 5)`. */
13
+ cameraPosition?: THREE.Vector3Tuple;
14
+ }
15
+ /**
16
+ * Create a scene, perspective camera, and clock with the same defaults as
17
+ * {@link createThreeGeometraSplitHost} and {@link createThreeGeometraStackedHost}.
18
+ *
19
+ * Use this when you want Three.js state aligned with those hosts but manage your own
20
+ * `WebGLRenderer` (for example headless GL, offscreen canvas, or custom render targets).
21
+ */
22
+ export declare function createGeometraThreeSceneBasics(options?: GeometraThreeSceneBasicsOptions): {
23
+ scene: THREE.Scene;
24
+ camera: THREE.PerspectiveCamera;
25
+ clock: THREE.Clock;
26
+ };
27
+ //# sourceMappingURL=three-scene-basics.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"three-scene-basics.d.ts","sourceRoot":"","sources":["../src/three-scene-basics.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,wFAAwF;AACxF,MAAM,WAAW,+BAA+B;IAC9C,+DAA+D;IAC/D,eAAe,CAAC,EAAE,KAAK,CAAC,mBAAmB,CAAA;IAC3C,sDAAsD;IACtD,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,gCAAgC;IAChC,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,gCAAgC;IAChC,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,qDAAqD;IACrD,cAAc,CAAC,EAAE,KAAK,CAAC,YAAY,CAAA;CACpC;AAED;;;;;;GAMG;AACH,wBAAgB,8BAA8B,CAC5C,OAAO,GAAE,+BAAoC,GAC5C;IACD,KAAK,EAAE,KAAK,CAAC,KAAK,CAAA;IAClB,MAAM,EAAE,KAAK,CAAC,iBAAiB,CAAA;IAC/B,KAAK,EAAE,KAAK,CAAC,KAAK,CAAA;CACnB,CAkBA"}
@@ -0,0 +1,18 @@
1
+ import * as THREE from 'three';
2
+ /**
3
+ * Create a scene, perspective camera, and clock with the same defaults as
4
+ * {@link createThreeGeometraSplitHost} and {@link createThreeGeometraStackedHost}.
5
+ *
6
+ * Use this when you want Three.js state aligned with those hosts but manage your own
7
+ * `WebGLRenderer` (for example headless GL, offscreen canvas, or custom render targets).
8
+ */
9
+ export function createGeometraThreeSceneBasics(options = {}) {
10
+ const { threeBackground = 0x000000, cameraFov = 50, cameraNear = 0.1, cameraFar = 2000, cameraPosition = [0, 0, 5], } = options;
11
+ const scene = new THREE.Scene();
12
+ scene.background = new THREE.Color(threeBackground);
13
+ const camera = new THREE.PerspectiveCamera(cameraFov, 1, cameraNear, cameraFar);
14
+ camera.position.set(cameraPosition[0], cameraPosition[1], cameraPosition[2]);
15
+ const clock = new THREE.Clock();
16
+ return { scene, camera, clock };
17
+ }
18
+ //# sourceMappingURL=three-scene-basics.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"three-scene-basics.js","sourceRoot":"","sources":["../src/three-scene-basics.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAgB9B;;;;;;GAMG;AACH,MAAM,UAAU,8BAA8B,CAC5C,UAA2C,EAAE;IAM7C,MAAM,EACJ,eAAe,GAAG,QAAQ,EAC1B,SAAS,GAAG,EAAE,EACd,UAAU,GAAG,GAAG,EAChB,SAAS,GAAG,IAAI,EAChB,cAAc,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GAC3B,GAAG,OAAO,CAAA;IAEX,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,KAAK,EAAE,CAAA;IAC/B,KAAK,CAAC,UAAU,GAAG,IAAI,KAAK,CAAC,KAAK,CAAC,eAAe,CAAC,CAAA;IAEnD,MAAM,MAAM,GAAG,IAAI,KAAK,CAAC,iBAAiB,CAAC,SAAS,EAAE,CAAC,EAAE,UAAU,EAAE,SAAS,CAAC,CAAA;IAC/E,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,CAAE,EAAE,cAAc,CAAC,CAAC,CAAE,EAAE,cAAc,CAAC,CAAC,CAAE,CAAC,CAAA;IAE/E,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,KAAK,EAAE,CAAA;IAE/B,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,CAAA;AACjC,CAAC"}
package/dist/utils.d.ts CHANGED
@@ -1,7 +1,15 @@
1
- import type { WebGLRenderer } from 'three';
1
+ import type { PerspectiveCamera, WebGLRenderer } from 'three';
2
2
  /**
3
3
  * Resize drawing buffer to match CSS pixel size × device pixel ratio.
4
4
  * Use when you manage your own canvas layout (no `renderer.setSize`).
5
5
  */
6
6
  export declare function setWebGLDrawingBufferSize(renderer: WebGLRenderer, cssWidth: number, cssHeight: number, pixelRatio?: number): void;
7
+ /**
8
+ * Apply the same CSS-size → aspect ratio → WebGL buffer sizing path as
9
+ * {@link createThreeGeometraSplitHost} and {@link createThreeGeometraStackedHost}.
10
+ *
11
+ * Use with {@link createGeometraThreeSceneBasics} when you own the `WebGLRenderer` (headless GL,
12
+ * offscreen canvas, tests) but want buffer dimensions and projection to stay aligned with those hosts.
13
+ */
14
+ export declare function resizeGeometraThreePerspectiveView(renderer: WebGLRenderer, camera: PerspectiveCamera, cssWidth: number, cssHeight: number, pixelRatio: number): void;
7
15
  //# sourceMappingURL=utils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAA;AAE1C;;;GAGG;AACH,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,aAAa,EACvB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,UAAU,CAAC,EAAE,MAAM,GAClB,IAAI,CAKN"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,OAAO,CAAA;AAE7D;;;GAGG;AACH,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,aAAa,EACvB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,UAAU,CAAC,EAAE,MAAM,GAClB,IAAI,CAKN;AAED;;;;;;GAMG;AACH,wBAAgB,kCAAkC,CAChD,QAAQ,EAAE,aAAa,EACvB,MAAM,EAAE,iBAAiB,EACzB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,GACjB,IAAI,CAON"}
package/dist/utils.js CHANGED
@@ -8,4 +8,19 @@ export function setWebGLDrawingBufferSize(renderer, cssWidth, cssHeight, pixelRa
8
8
  const h = Math.max(1, Math.floor(cssHeight * pr));
9
9
  renderer.setDrawingBufferSize(w, h, pr);
10
10
  }
11
+ /**
12
+ * Apply the same CSS-size → aspect ratio → WebGL buffer sizing path as
13
+ * {@link createThreeGeometraSplitHost} and {@link createThreeGeometraStackedHost}.
14
+ *
15
+ * Use with {@link createGeometraThreeSceneBasics} when you own the `WebGLRenderer` (headless GL,
16
+ * offscreen canvas, tests) but want buffer dimensions and projection to stay aligned with those hosts.
17
+ */
18
+ export function resizeGeometraThreePerspectiveView(renderer, camera, cssWidth, cssHeight, pixelRatio) {
19
+ renderer.setPixelRatio(pixelRatio);
20
+ const w = Math.max(1, Math.floor(cssWidth));
21
+ const h = Math.max(1, Math.floor(cssHeight));
22
+ camera.aspect = w / h;
23
+ camera.updateProjectionMatrix();
24
+ renderer.setSize(w, h, false);
25
+ }
11
26
  //# sourceMappingURL=utils.js.map
package/dist/utils.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,UAAU,yBAAyB,CACvC,QAAuB,EACvB,QAAgB,EAChB,SAAiB,EACjB,UAAmB;IAEnB,MAAM,EAAE,GAAG,UAAU,IAAI,CAAC,OAAO,MAAM,KAAK,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,gBAAgB,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IAC3F,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,QAAQ,GAAG,EAAE,CAAC,CAAC,CAAA;IAChD,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,EAAE,CAAC,CAAC,CAAA;IACjD,QAAQ,CAAC,oBAAoB,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAA;AACzC,CAAC"}
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,UAAU,yBAAyB,CACvC,QAAuB,EACvB,QAAgB,EAChB,SAAiB,EACjB,UAAmB;IAEnB,MAAM,EAAE,GAAG,UAAU,IAAI,CAAC,OAAO,MAAM,KAAK,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,gBAAgB,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IAC3F,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,QAAQ,GAAG,EAAE,CAAC,CAAC,CAAA;IAChD,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,EAAE,CAAC,CAAC,CAAA;IACjD,QAAQ,CAAC,oBAAoB,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAA;AACzC,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,kCAAkC,CAChD,QAAuB,EACvB,MAAyB,EACzB,QAAgB,EAChB,SAAiB,EACjB,UAAkB;IAElB,QAAQ,CAAC,aAAa,CAAC,UAAU,CAAC,CAAA;IAClC,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAA;IAC3C,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAA;IAC5C,MAAM,CAAC,MAAM,GAAG,CAAC,GAAG,CAAC,CAAA;IACrB,MAAM,CAAC,sBAAsB,EAAE,CAAA;IAC/B,QAAQ,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC,CAAA;AAC/B,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geometra/renderer-three",
3
- "version": "0.1.2",
3
+ "version": "0.2.0",
4
4
  "description": "Three.js render target helpers for Geometra — stack WebGL with the geometry-streamed canvas client",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -33,24 +33,21 @@
33
33
  "scripts": {
34
34
  "build": "rm -rf dist && tsc -p tsconfig.build.json",
35
35
  "check": "tsc --noEmit -p tsconfig.json",
36
- "release:gate": "npm run check && npm run build",
36
+ "verify:exports": "node scripts/verify-exports.mjs",
37
+ "verify:utils": "node scripts/verify-utils.mjs",
38
+ "release:gate": "npm run check && npm run build && npm run verify:exports && npm run verify:utils",
37
39
  "prepare": "npm run build",
38
40
  "prepublishOnly": "npm run build"
39
41
  },
40
42
  "peerDependencies": {
41
- "@geometra/renderer-canvas": "^1.2.1",
43
+ "@geometra/renderer-canvas": "^1.2.2",
42
44
  "three": ">=0.160.0 <0.180.0"
43
45
  },
44
46
  "devDependencies": {
45
- "@geometra/client": "^1.2.1",
46
- "@geometra/renderer-canvas": "^1.2.1",
47
+ "@geometra/client": "^1.2.2",
48
+ "@geometra/renderer-canvas": "^1.2.2",
47
49
  "@types/three": "^0.170.0",
48
50
  "three": "^0.170.0",
49
51
  "typescript": "^6.0.2"
50
- },
51
- "overrides": {
52
- "@geometra/renderer-canvas": {
53
- "@geometra/client": "^1.2.1"
54
- }
55
52
  }
56
53
  }