@nodetool-ai/video-nodes 0.8.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 +58 -0
- package/dist/.nodetool-build-stamp +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/nodes/ffmpeg-helpers.d.ts +77 -0
- package/dist/nodes/ffmpeg-helpers.d.ts.map +1 -0
- package/dist/nodes/ffmpeg-helpers.js +220 -0
- package/dist/nodes/ffmpeg-helpers.js.map +1 -0
- package/dist/nodes/lib-video-download.d.ts +29 -0
- package/dist/nodes/lib-video-download.d.ts.map +1 -0
- package/dist/nodes/lib-video-download.js +353 -0
- package/dist/nodes/lib-video-download.js.map +1 -0
- package/dist/nodes/model3d/analysis.d.ts +16 -0
- package/dist/nodes/model3d/analysis.d.ts.map +1 -0
- package/dist/nodes/model3d/analysis.js +38 -0
- package/dist/nodes/model3d/analysis.js.map +1 -0
- package/dist/nodes/model3d/base.d.ts +28 -0
- package/dist/nodes/model3d/base.d.ts.map +1 -0
- package/dist/nodes/model3d/base.js +20 -0
- package/dist/nodes/model3d/base.js.map +1 -0
- package/dist/nodes/model3d/boolean-ops.d.ts +2 -0
- package/dist/nodes/model3d/boolean-ops.d.ts.map +1 -0
- package/dist/nodes/model3d/boolean-ops.js +172 -0
- package/dist/nodes/model3d/boolean-ops.js.map +1 -0
- package/dist/nodes/model3d/cleanup.d.ts +56 -0
- package/dist/nodes/model3d/cleanup.d.ts.map +1 -0
- package/dist/nodes/model3d/cleanup.js +144 -0
- package/dist/nodes/model3d/cleanup.js.map +1 -0
- package/dist/nodes/model3d/convert.d.ts +27 -0
- package/dist/nodes/model3d/convert.d.ts.map +1 -0
- package/dist/nodes/model3d/convert.js +66 -0
- package/dist/nodes/model3d/convert.js.map +1 -0
- package/dist/nodes/model3d/defaults.d.ts +34 -0
- package/dist/nodes/model3d/defaults.d.ts.map +1 -0
- package/dist/nodes/model3d/defaults.js +28 -0
- package/dist/nodes/model3d/defaults.js.map +1 -0
- package/dist/nodes/model3d/document-ops.d.ts +4 -0
- package/dist/nodes/model3d/document-ops.d.ts.map +1 -0
- package/dist/nodes/model3d/document-ops.js +40 -0
- package/dist/nodes/model3d/document-ops.js.map +1 -0
- package/dist/nodes/model3d/generation.d.ts +56 -0
- package/dist/nodes/model3d/generation.d.ts.map +1 -0
- package/dist/nodes/model3d/generation.js +203 -0
- package/dist/nodes/model3d/generation.js.map +1 -0
- package/dist/nodes/model3d/glb.d.ts +29 -0
- package/dist/nodes/model3d/glb.d.ts.map +1 -0
- package/dist/nodes/model3d/glb.js +255 -0
- package/dist/nodes/model3d/glb.js.map +1 -0
- package/dist/nodes/model3d/index.d.ts +22 -0
- package/dist/nodes/model3d/index.d.ts.map +1 -0
- package/dist/nodes/model3d/index.js +41 -0
- package/dist/nodes/model3d/index.js.map +1 -0
- package/dist/nodes/model3d/io.d.ts +81 -0
- package/dist/nodes/model3d/io.d.ts.map +1 -0
- package/dist/nodes/model3d/io.js +162 -0
- package/dist/nodes/model3d/io.js.map +1 -0
- package/dist/nodes/model3d/mesh-ops.d.ts +40 -0
- package/dist/nodes/model3d/mesh-ops.d.ts.map +1 -0
- package/dist/nodes/model3d/mesh-ops.js +775 -0
- package/dist/nodes/model3d/mesh-ops.js.map +1 -0
- package/dist/nodes/model3d/nodes.d.ts +2 -0
- package/dist/nodes/model3d/nodes.d.ts.map +1 -0
- package/dist/nodes/model3d/nodes.js +2 -0
- package/dist/nodes/model3d/nodes.js.map +1 -0
- package/dist/nodes/model3d/ops.d.ts +44 -0
- package/dist/nodes/model3d/ops.d.ts.map +1 -0
- package/dist/nodes/model3d/ops.js +142 -0
- package/dist/nodes/model3d/ops.js.map +1 -0
- package/dist/nodes/model3d/render.d.ts +51 -0
- package/dist/nodes/model3d/render.d.ts.map +1 -0
- package/dist/nodes/model3d/render.js +124 -0
- package/dist/nodes/model3d/render.js.map +1 -0
- package/dist/nodes/model3d/render3d-core.d.ts +177 -0
- package/dist/nodes/model3d/render3d-core.d.ts.map +1 -0
- package/dist/nodes/model3d/render3d-core.js +366 -0
- package/dist/nodes/model3d/render3d-core.js.map +1 -0
- package/dist/nodes/model3d/render3d-headless.d.ts +75 -0
- package/dist/nodes/model3d/render3d-headless.d.ts.map +1 -0
- package/dist/nodes/model3d/render3d-headless.js +190 -0
- package/dist/nodes/model3d/render3d-headless.js.map +1 -0
- package/dist/nodes/model3d/render3d-page.d.ts +16 -0
- package/dist/nodes/model3d/render3d-page.d.ts.map +1 -0
- package/dist/nodes/model3d/render3d-page.js +51 -0
- package/dist/nodes/model3d/render3d-page.js.map +1 -0
- package/dist/nodes/model3d/transforms.d.ts +56 -0
- package/dist/nodes/model3d/transforms.d.ts.map +1 -0
- package/dist/nodes/model3d/transforms.js +148 -0
- package/dist/nodes/model3d/transforms.js.map +1 -0
- package/dist/nodes/model3d/types.d.ts +82 -0
- package/dist/nodes/model3d/types.d.ts.map +1 -0
- package/dist/nodes/model3d/types.js +2 -0
- package/dist/nodes/model3d/types.js.map +1 -0
- package/dist/nodes/model3d/utils.d.ts +46 -0
- package/dist/nodes/model3d/utils.d.ts.map +1 -0
- package/dist/nodes/model3d/utils.js +190 -0
- package/dist/nodes/model3d/utils.js.map +1 -0
- package/dist/nodes/model3d.d.ts +2 -0
- package/dist/nodes/model3d.d.ts.map +1 -0
- package/dist/nodes/model3d.js +2 -0
- package/dist/nodes/model3d.js.map +1 -0
- package/dist/nodes/placeholder-values.d.ts +10 -0
- package/dist/nodes/placeholder-values.d.ts.map +1 -0
- package/dist/nodes/placeholder-values.js +19 -0
- package/dist/nodes/placeholder-values.js.map +1 -0
- package/dist/nodes/script.d.ts +161 -0
- package/dist/nodes/script.d.ts.map +1 -0
- package/dist/nodes/script.js +673 -0
- package/dist/nodes/script.js.map +1 -0
- package/dist/nodes/storyboard.d.ts +269 -0
- package/dist/nodes/storyboard.d.ts.map +1 -0
- package/dist/nodes/storyboard.js +1017 -0
- package/dist/nodes/storyboard.js.map +1 -0
- package/dist/nodes/timeline/assetFiles.d.ts +19 -0
- package/dist/nodes/timeline/assetFiles.d.ts.map +1 -0
- package/dist/nodes/timeline/assetFiles.js +52 -0
- package/dist/nodes/timeline/assetFiles.js.map +1 -0
- package/dist/nodes/timeline/compositeRender.d.ts +60 -0
- package/dist/nodes/timeline/compositeRender.d.ts.map +1 -0
- package/dist/nodes/timeline/compositeRender.js +508 -0
- package/dist/nodes/timeline/compositeRender.js.map +1 -0
- package/dist/nodes/timeline/contactSheet.d.ts +43 -0
- package/dist/nodes/timeline/contactSheet.d.ts.map +1 -0
- package/dist/nodes/timeline/contactSheet.js +78 -0
- package/dist/nodes/timeline/contactSheet.js.map +1 -0
- package/dist/nodes/timeline/outputFormats.d.ts +69 -0
- package/dist/nodes/timeline/outputFormats.d.ts.map +1 -0
- package/dist/nodes/timeline/outputFormats.js +173 -0
- package/dist/nodes/timeline/outputFormats.js.map +1 -0
- package/dist/nodes/timeline/pngSequence.d.ts +44 -0
- package/dist/nodes/timeline/pngSequence.d.ts.map +1 -0
- package/dist/nodes/timeline/pngSequence.js +172 -0
- package/dist/nodes/timeline/pngSequence.js.map +1 -0
- package/dist/nodes/timeline/rasterizers.d.ts +47 -0
- package/dist/nodes/timeline/rasterizers.d.ts.map +1 -0
- package/dist/nodes/timeline/rasterizers.js +103 -0
- package/dist/nodes/timeline/rasterizers.js.map +1 -0
- package/dist/nodes/timeline/rawFrames.d.ts +149 -0
- package/dist/nodes/timeline/rawFrames.d.ts.map +1 -0
- package/dist/nodes/timeline/rawFrames.js +598 -0
- package/dist/nodes/timeline/rawFrames.js.map +1 -0
- package/dist/nodes/timeline.d.ts +206 -0
- package/dist/nodes/timeline.d.ts.map +1 -0
- package/dist/nodes/timeline.js +1395 -0
- package/dist/nodes/timeline.js.map +1 -0
- package/dist/nodes/video.d.ts +705 -0
- package/dist/nodes/video.d.ts.map +1 -0
- package/dist/nodes/video.js +2672 -0
- package/dist/nodes/video.js.map +1 -0
- package/dist/render3d-page.js +4118 -0
- package/package.json +95 -0
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared three.js render core for `nodetool.model3d.RenderToImage` and for
|
|
3
|
+
* the timeline's `model3d` clips.
|
|
4
|
+
*
|
|
5
|
+
* A **session** loads a GLB once and draws many frames from it
|
|
6
|
+
* (`createModel3DRenderSession`); `renderGlbToPng` is the one-frame case.
|
|
7
|
+
* Turning bytes into pixels needs a WebGL context on an `OffscreenCanvas`,
|
|
8
|
+
* so the same module runs in two hosts:
|
|
9
|
+
*
|
|
10
|
+
* - the in-browser workflow runner and the timeline preview (main thread or
|
|
11
|
+
* worker), importing this file directly;
|
|
12
|
+
* - a headless Chromium page on the Node backend, via the esbuild bundle
|
|
13
|
+
* `dist/render3d-page.js` (entry: `render3d-page.ts`).
|
|
14
|
+
*
|
|
15
|
+
* Editor-only clutter (grid, axes, gizmos) never exists here — the scene is
|
|
16
|
+
* built fresh from the model plus the requested lights and nothing else.
|
|
17
|
+
*/
|
|
18
|
+
export type LightingPreset = "studio" | "soft" | "flat";
|
|
19
|
+
/**
|
|
20
|
+
* The 3D half of a timeline clip's style.
|
|
21
|
+
*
|
|
22
|
+
* These three interfaces are declared here, not imported, because
|
|
23
|
+
* `@nodetool-ai/timeline` is pure document logic and this file is the GPU
|
|
24
|
+
* side: the bundle that ships to a headless Chromium page must not pull the
|
|
25
|
+
* document package in. They are structurally identical to the
|
|
26
|
+
* `ClipModel3DCamera` / `ClipModel3DAnimation` / `ClipModel3DStyle` of
|
|
27
|
+
* `packages/timeline/src/types.ts`, so a caller assigns one to the other
|
|
28
|
+
* without a conversion.
|
|
29
|
+
*/
|
|
30
|
+
export type Model3DCameraMode = "orbit" | "scene";
|
|
31
|
+
export interface ClipModel3DCamera {
|
|
32
|
+
/** `orbit` frames the model's bounding sphere; `scene` uses a glTF camera. */
|
|
33
|
+
mode: Model3DCameraMode;
|
|
34
|
+
/** Horizontal orbit angle around the model, degrees. 0 looks down -Z. */
|
|
35
|
+
azimuthDeg: number;
|
|
36
|
+
/** Vertical angle above the horizon, degrees. */
|
|
37
|
+
elevationDeg: number;
|
|
38
|
+
/** Vertical field of view, degrees. */
|
|
39
|
+
fovDeg: number;
|
|
40
|
+
/** Distance multiplier on the auto-framed fit: >1 closer. */
|
|
41
|
+
zoom: number;
|
|
42
|
+
/** Look-at offset from the bounding-sphere center, world units. */
|
|
43
|
+
targetOffset?: [number, number, number];
|
|
44
|
+
/** `scene` mode: the glTF camera's name; the first camera when absent. */
|
|
45
|
+
sceneCameraName?: string;
|
|
46
|
+
}
|
|
47
|
+
export interface ClipModel3DAnimation {
|
|
48
|
+
/** glTF animation name; every animation plays when absent. */
|
|
49
|
+
clipName?: string;
|
|
50
|
+
loop: boolean;
|
|
51
|
+
/** Playback multiplier on top of the clip's own speed / time remap. */
|
|
52
|
+
speed: number;
|
|
53
|
+
}
|
|
54
|
+
export interface ClipModel3DStyle {
|
|
55
|
+
camera: ClipModel3DCamera;
|
|
56
|
+
animation: ClipModel3DAnimation;
|
|
57
|
+
lighting: LightingPreset;
|
|
58
|
+
lightIntensity: number;
|
|
59
|
+
background: {
|
|
60
|
+
transparent: true;
|
|
61
|
+
} | {
|
|
62
|
+
transparent: false;
|
|
63
|
+
color: string;
|
|
64
|
+
};
|
|
65
|
+
/** A Blender render of this clip at a given style. */
|
|
66
|
+
bake?: {
|
|
67
|
+
assetId: string;
|
|
68
|
+
dependencyHash: string;
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
export interface Render3DOptions {
|
|
72
|
+
/** Output image width in pixels. */
|
|
73
|
+
width: number;
|
|
74
|
+
/** Output image height in pixels. */
|
|
75
|
+
height: number;
|
|
76
|
+
/** Horizontal orbit angle around the model, degrees. 0 looks down -Z. */
|
|
77
|
+
azimuthDeg: number;
|
|
78
|
+
/** Vertical angle above the horizon, degrees. 90 is straight down. */
|
|
79
|
+
elevationDeg: number;
|
|
80
|
+
/** Vertical field of view, degrees. */
|
|
81
|
+
fovDeg: number;
|
|
82
|
+
/** Camera distance multiplier on the auto-framed fit: >1 closer, <1 farther. */
|
|
83
|
+
zoom: number;
|
|
84
|
+
lighting: LightingPreset;
|
|
85
|
+
/** Multiplier applied to every light in the preset. */
|
|
86
|
+
lightIntensity: number;
|
|
87
|
+
/** CSS color for the background; ignored when `transparent` is true. */
|
|
88
|
+
backgroundColor: string;
|
|
89
|
+
transparent: boolean;
|
|
90
|
+
}
|
|
91
|
+
export interface Model3DRenderFrame {
|
|
92
|
+
timeSec: number;
|
|
93
|
+
/** Already folded with the animated camera channels. */
|
|
94
|
+
camera: ClipModel3DCamera;
|
|
95
|
+
width: number;
|
|
96
|
+
height: number;
|
|
97
|
+
}
|
|
98
|
+
export interface Model3DRenderSession {
|
|
99
|
+
/** Names of the glTF's animations and cameras, for the inspector and the validator. */
|
|
100
|
+
readonly animations: readonly string[];
|
|
101
|
+
readonly cameras: readonly string[];
|
|
102
|
+
/** Draws one frame into the session's canvas and returns it. */
|
|
103
|
+
render(frame: Model3DRenderFrame): OffscreenCanvas;
|
|
104
|
+
dispose(): void;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Everything a session fixes at creation. Two layers share a session only
|
|
108
|
+
* when these are equal, so the key of every pool and every headless group is
|
|
109
|
+
* `assetId` plus this object, never `assetId` alone.
|
|
110
|
+
*/
|
|
111
|
+
export interface Model3DSessionOptions {
|
|
112
|
+
lighting: ClipModel3DStyle["lighting"];
|
|
113
|
+
lightIntensity: number;
|
|
114
|
+
background: ClipModel3DStyle["background"];
|
|
115
|
+
animation: ClipModel3DAnimation;
|
|
116
|
+
}
|
|
117
|
+
interface CameraFraming {
|
|
118
|
+
/** Distance from the bounding-sphere center to the camera. */
|
|
119
|
+
distance: number;
|
|
120
|
+
near: number;
|
|
121
|
+
far: number;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Distance that fits a bounding sphere of `radius` fully into a camera with
|
|
125
|
+
* the given vertical fov and aspect, then applies `zoom` (>1 moves closer).
|
|
126
|
+
* Pure math, exported for tests.
|
|
127
|
+
*/
|
|
128
|
+
export declare function computeFraming(radius: number, fovDeg: number, aspect: number, zoom: number): CameraFraming;
|
|
129
|
+
/** Spherical → cartesian offset for the orbit camera. Exported for tests. */
|
|
130
|
+
export declare function orbitOffset(azimuthDeg: number, elevationDeg: number, distance: number): {
|
|
131
|
+
x: number;
|
|
132
|
+
y: number;
|
|
133
|
+
z: number;
|
|
134
|
+
};
|
|
135
|
+
/**
|
|
136
|
+
* Map a clip-local animation time onto the mixer's time.
|
|
137
|
+
*
|
|
138
|
+
* With `loop` on the time wraps at `duration`; off, it clamps to the last
|
|
139
|
+
* frame. A model with no animation (`duration` 0, or a non-finite duration)
|
|
140
|
+
* has one pose, so every time maps to 0. Pure, exported for tests.
|
|
141
|
+
*/
|
|
142
|
+
export declare function mixerTimeFor(t: number, duration: number, loop: boolean): number;
|
|
143
|
+
/**
|
|
144
|
+
* The animation clips `animation.clipName` selects: the named one, or every
|
|
145
|
+
* clip when the name is absent. Pure, exported for tests.
|
|
146
|
+
*/
|
|
147
|
+
export declare function selectAnimationClips<T extends {
|
|
148
|
+
name: string;
|
|
149
|
+
}>(clips: readonly T[], clipName?: string): T[];
|
|
150
|
+
/**
|
|
151
|
+
* The glTF camera `camera.sceneCameraName` selects, or the first one when the
|
|
152
|
+
* name is absent. Pure, exported for tests.
|
|
153
|
+
*/
|
|
154
|
+
export declare function selectSceneCamera<T extends {
|
|
155
|
+
name: string;
|
|
156
|
+
}>(cameras: readonly T[], sceneCameraName?: string): T;
|
|
157
|
+
/** The session options a one-shot `Render3DOptions` describes. */
|
|
158
|
+
export declare function sessionOptionsFromRender3DOptions(options: Render3DOptions): Model3DSessionOptions;
|
|
159
|
+
/** The single frame a one-shot `Render3DOptions` describes. */
|
|
160
|
+
export declare function renderFrameFromRender3DOptions(options: Render3DOptions): Model3DRenderFrame;
|
|
161
|
+
/**
|
|
162
|
+
* Load a model once and draw frames from it. Must run in a browser context
|
|
163
|
+
* (window or worker) with WebGL and `OffscreenCanvas`.
|
|
164
|
+
*
|
|
165
|
+
* Every option here is fixed for the session's life: a style edit that
|
|
166
|
+
* touches one of them disposes the session and creates a new one, because a
|
|
167
|
+
* mixer whose actions were rebuilt mid-session is the same picture as a fresh
|
|
168
|
+
* one and harder to prove.
|
|
169
|
+
*/
|
|
170
|
+
export declare function createModel3DRenderSession(glb: Uint8Array, options: Model3DSessionOptions): Promise<Model3DRenderSession>;
|
|
171
|
+
/**
|
|
172
|
+
* Render GLB (or embedded-buffer glTF JSON) bytes to PNG bytes. Must run in a
|
|
173
|
+
* browser context (window or worker) with WebGL and `OffscreenCanvas`.
|
|
174
|
+
*/
|
|
175
|
+
export declare function renderGlbToPng(glb: Uint8Array, options: Render3DOptions): Promise<Uint8Array>;
|
|
176
|
+
export {};
|
|
177
|
+
//# sourceMappingURL=render3d-core.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render3d-core.d.ts","sourceRoot":"","sources":["../../../src/nodes/model3d/render3d-core.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
/// <reference lib="dom" />
|
|
2
|
+
/**
|
|
3
|
+
* Shared three.js render core for `nodetool.model3d.RenderToImage` and for
|
|
4
|
+
* the timeline's `model3d` clips.
|
|
5
|
+
*
|
|
6
|
+
* A **session** loads a GLB once and draws many frames from it
|
|
7
|
+
* (`createModel3DRenderSession`); `renderGlbToPng` is the one-frame case.
|
|
8
|
+
* Turning bytes into pixels needs a WebGL context on an `OffscreenCanvas`,
|
|
9
|
+
* so the same module runs in two hosts:
|
|
10
|
+
*
|
|
11
|
+
* - the in-browser workflow runner and the timeline preview (main thread or
|
|
12
|
+
* worker), importing this file directly;
|
|
13
|
+
* - a headless Chromium page on the Node backend, via the esbuild bundle
|
|
14
|
+
* `dist/render3d-page.js` (entry: `render3d-page.ts`).
|
|
15
|
+
*
|
|
16
|
+
* Editor-only clutter (grid, axes, gizmos) never exists here — the scene is
|
|
17
|
+
* built fresh from the model plus the requested lights and nothing else.
|
|
18
|
+
*/
|
|
19
|
+
import * as THREE from "three";
|
|
20
|
+
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
|
|
21
|
+
import { MeshoptDecoder } from "meshoptimizer";
|
|
22
|
+
/**
|
|
23
|
+
* Distance that fits a bounding sphere of `radius` fully into a camera with
|
|
24
|
+
* the given vertical fov and aspect, then applies `zoom` (>1 moves closer).
|
|
25
|
+
* Pure math, exported for tests.
|
|
26
|
+
*/
|
|
27
|
+
export function computeFraming(radius, fovDeg, aspect, zoom) {
|
|
28
|
+
const safeRadius = Math.max(radius, 1e-6);
|
|
29
|
+
const vFov = (Math.max(fovDeg, 1) * Math.PI) / 180;
|
|
30
|
+
const hFov = 2 * Math.atan(Math.tan(vFov / 2) * Math.max(aspect, 1e-6));
|
|
31
|
+
const fitV = safeRadius / Math.sin(vFov / 2);
|
|
32
|
+
const fitH = safeRadius / Math.sin(hFov / 2);
|
|
33
|
+
const distance = Math.max(fitV, fitH) / Math.max(zoom, 1e-3);
|
|
34
|
+
return {
|
|
35
|
+
distance,
|
|
36
|
+
near: Math.max(distance - safeRadius * 4, distance / 100, 1e-4),
|
|
37
|
+
far: distance + safeRadius * 10
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/** Spherical → cartesian offset for the orbit camera. Exported for tests. */
|
|
41
|
+
export function orbitOffset(azimuthDeg, elevationDeg, distance) {
|
|
42
|
+
const az = (azimuthDeg * Math.PI) / 180;
|
|
43
|
+
const el = (Math.min(Math.max(elevationDeg, -89.9), 89.9) * Math.PI) / 180;
|
|
44
|
+
return {
|
|
45
|
+
x: distance * Math.cos(el) * Math.sin(az),
|
|
46
|
+
y: distance * Math.sin(el),
|
|
47
|
+
z: distance * Math.cos(el) * Math.cos(az)
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Map a clip-local animation time onto the mixer's time.
|
|
52
|
+
*
|
|
53
|
+
* With `loop` on the time wraps at `duration`; off, it clamps to the last
|
|
54
|
+
* frame. A model with no animation (`duration` 0, or a non-finite duration)
|
|
55
|
+
* has one pose, so every time maps to 0. Pure, exported for tests.
|
|
56
|
+
*/
|
|
57
|
+
export function mixerTimeFor(t, duration, loop) {
|
|
58
|
+
if (!Number.isFinite(t))
|
|
59
|
+
return 0;
|
|
60
|
+
if (!Number.isFinite(duration) || duration <= 0)
|
|
61
|
+
return 0;
|
|
62
|
+
if (!loop)
|
|
63
|
+
return Math.min(Math.max(t, 0), duration);
|
|
64
|
+
return ((t % duration) + duration) % duration;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* The animation clips `animation.clipName` selects: the named one, or every
|
|
68
|
+
* clip when the name is absent. Pure, exported for tests.
|
|
69
|
+
*/
|
|
70
|
+
export function selectAnimationClips(clips, clipName) {
|
|
71
|
+
if (clipName === undefined || clipName === "")
|
|
72
|
+
return [...clips];
|
|
73
|
+
const found = clips.filter((clip) => clip.name === clipName);
|
|
74
|
+
if (found.length > 0)
|
|
75
|
+
return found;
|
|
76
|
+
throw new Error(clips.length === 0
|
|
77
|
+
? `Model3D render: animation "${clipName}" is not in this model — it has no animations`
|
|
78
|
+
: `Model3D render: animation "${clipName}" is not in this model — available animations: ${clips
|
|
79
|
+
.map((clip) => clip.name)
|
|
80
|
+
.join(", ")}`);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* The glTF camera `camera.sceneCameraName` selects, or the first one when the
|
|
84
|
+
* name is absent. Pure, exported for tests.
|
|
85
|
+
*/
|
|
86
|
+
export function selectSceneCamera(cameras, sceneCameraName) {
|
|
87
|
+
if (cameras.length === 0) {
|
|
88
|
+
throw new Error("Model3D render: camera mode is \"scene\" but this model has no cameras — use orbit mode");
|
|
89
|
+
}
|
|
90
|
+
if (sceneCameraName === undefined || sceneCameraName === "") {
|
|
91
|
+
return cameras[0];
|
|
92
|
+
}
|
|
93
|
+
const found = cameras.find((camera) => camera.name === sceneCameraName);
|
|
94
|
+
if (found)
|
|
95
|
+
return found;
|
|
96
|
+
throw new Error(`Model3D render: camera "${sceneCameraName}" is not in this model — available cameras: ${cameras
|
|
97
|
+
.map((camera) => camera.name)
|
|
98
|
+
.join(", ")}`);
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Build the preset's lights into `scene`. Key/fill/rim ride along with the
|
|
102
|
+
* camera azimuth so the visible side of the model is always lit, whatever
|
|
103
|
+
* angle the user picked — `aim` is what puts them there.
|
|
104
|
+
*/
|
|
105
|
+
function createLightRig(scene, preset, intensity) {
|
|
106
|
+
const scaled = (base) => base * intensity;
|
|
107
|
+
const directionals = [];
|
|
108
|
+
let hemisphere = null;
|
|
109
|
+
const placeDirectional = (color, lightIntensity, direction) => {
|
|
110
|
+
const light = new THREE.DirectionalLight(color, lightIntensity);
|
|
111
|
+
scene.add(light);
|
|
112
|
+
scene.add(light.target);
|
|
113
|
+
directionals.push({ light, direction });
|
|
114
|
+
};
|
|
115
|
+
switch (preset) {
|
|
116
|
+
case "studio": {
|
|
117
|
+
scene.add(new THREE.AmbientLight(0xffffff, scaled(0.5)));
|
|
118
|
+
placeDirectional(0xffffff, scaled(2.2), (toCamera, side, up) => toCamera
|
|
119
|
+
.clone()
|
|
120
|
+
.add(side.clone().multiplyScalar(0.8))
|
|
121
|
+
.add(up.clone().multiplyScalar(0.9)));
|
|
122
|
+
placeDirectional(0xffffff, scaled(0.8), (toCamera, side, up) => toCamera
|
|
123
|
+
.clone()
|
|
124
|
+
.sub(side.clone().multiplyScalar(1.1))
|
|
125
|
+
.add(up.clone().multiplyScalar(0.2)));
|
|
126
|
+
placeDirectional(0xffffff, scaled(1.4), (toCamera, _side, up) => toCamera.clone().negate().add(up.clone().multiplyScalar(1.2)));
|
|
127
|
+
break;
|
|
128
|
+
}
|
|
129
|
+
case "soft": {
|
|
130
|
+
hemisphere = new THREE.HemisphereLight(0xffffff, 0x555566, scaled(1.6));
|
|
131
|
+
scene.add(hemisphere);
|
|
132
|
+
placeDirectional(0xffffff, scaled(0.9), (toCamera, _side, up) => toCamera.clone().add(up));
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
135
|
+
case "flat":
|
|
136
|
+
scene.add(new THREE.AmbientLight(0xffffff, scaled(3)));
|
|
137
|
+
break;
|
|
138
|
+
}
|
|
139
|
+
return {
|
|
140
|
+
aim(cameraPosition, target) {
|
|
141
|
+
const toCamera = cameraPosition.clone().sub(target).normalize();
|
|
142
|
+
const up = new THREE.Vector3(0, 1, 0);
|
|
143
|
+
const side = new THREE.Vector3().crossVectors(up, toCamera).normalize();
|
|
144
|
+
if (side.lengthSq() < 1e-6)
|
|
145
|
+
side.set(1, 0, 0);
|
|
146
|
+
for (const entry of directionals) {
|
|
147
|
+
entry.light.position
|
|
148
|
+
.copy(target)
|
|
149
|
+
.add(entry.direction(toCamera, side, up));
|
|
150
|
+
entry.light.target.position.copy(target);
|
|
151
|
+
}
|
|
152
|
+
if (hemisphere)
|
|
153
|
+
hemisphere.position.copy(target).add(up);
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
function disposeMaterial(material) {
|
|
158
|
+
for (const value of Object.values(material)) {
|
|
159
|
+
if (value instanceof THREE.Texture)
|
|
160
|
+
value.dispose();
|
|
161
|
+
}
|
|
162
|
+
material.dispose();
|
|
163
|
+
}
|
|
164
|
+
function disposeScene(scene) {
|
|
165
|
+
scene.traverse((object) => {
|
|
166
|
+
const mesh = object;
|
|
167
|
+
if (mesh.geometry)
|
|
168
|
+
mesh.geometry.dispose();
|
|
169
|
+
const material = mesh.material;
|
|
170
|
+
if (Array.isArray(material)) {
|
|
171
|
+
material.forEach(disposeMaterial);
|
|
172
|
+
}
|
|
173
|
+
else if (material) {
|
|
174
|
+
disposeMaterial(material);
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
function toArrayBuffer(bytes) {
|
|
179
|
+
// GLTFLoader wants an ArrayBuffer that starts at offset 0.
|
|
180
|
+
return bytes.buffer instanceof ArrayBuffer &&
|
|
181
|
+
bytes.byteOffset === 0 &&
|
|
182
|
+
bytes.byteLength === bytes.buffer.byteLength
|
|
183
|
+
? bytes.buffer
|
|
184
|
+
: bytes.slice().buffer;
|
|
185
|
+
}
|
|
186
|
+
/** The session options a one-shot `Render3DOptions` describes. */
|
|
187
|
+
export function sessionOptionsFromRender3DOptions(options) {
|
|
188
|
+
return {
|
|
189
|
+
lighting: options.lighting,
|
|
190
|
+
lightIntensity: options.lightIntensity,
|
|
191
|
+
background: options.transparent
|
|
192
|
+
? { transparent: true }
|
|
193
|
+
: { transparent: false, color: options.backgroundColor },
|
|
194
|
+
animation: { loop: false, speed: 1 }
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
/** The single frame a one-shot `Render3DOptions` describes. */
|
|
198
|
+
export function renderFrameFromRender3DOptions(options) {
|
|
199
|
+
return {
|
|
200
|
+
timeSec: 0,
|
|
201
|
+
width: options.width,
|
|
202
|
+
height: options.height,
|
|
203
|
+
camera: {
|
|
204
|
+
mode: "orbit",
|
|
205
|
+
azimuthDeg: options.azimuthDeg,
|
|
206
|
+
elevationDeg: options.elevationDeg,
|
|
207
|
+
fovDeg: options.fovDeg,
|
|
208
|
+
zoom: options.zoom
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Load a model once and draw frames from it. Must run in a browser context
|
|
214
|
+
* (window or worker) with WebGL and `OffscreenCanvas`.
|
|
215
|
+
*
|
|
216
|
+
* Every option here is fixed for the session's life: a style edit that
|
|
217
|
+
* touches one of them disposes the session and creates a new one, because a
|
|
218
|
+
* mixer whose actions were rebuilt mid-session is the same picture as a fresh
|
|
219
|
+
* one and harder to prove.
|
|
220
|
+
*/
|
|
221
|
+
export async function createModel3DRenderSession(glb, options) {
|
|
222
|
+
if (typeof OffscreenCanvas === "undefined") {
|
|
223
|
+
throw new Error("RenderToImage needs a browser context with OffscreenCanvas support");
|
|
224
|
+
}
|
|
225
|
+
const loader = new GLTFLoader();
|
|
226
|
+
await MeshoptDecoder.ready;
|
|
227
|
+
loader.setMeshoptDecoder(MeshoptDecoder);
|
|
228
|
+
const gltf = await loader.parseAsync(toArrayBuffer(glb), "");
|
|
229
|
+
const scene = new THREE.Scene();
|
|
230
|
+
scene.add(gltf.scene);
|
|
231
|
+
// Resolve the world matrix before measuring, or the box is stale.
|
|
232
|
+
scene.updateMatrixWorld(true);
|
|
233
|
+
const box = new THREE.Box3().setFromObject(gltf.scene);
|
|
234
|
+
if (box.isEmpty()) {
|
|
235
|
+
disposeScene(scene);
|
|
236
|
+
throw new Error("RenderToImage: the model contains no visible geometry");
|
|
237
|
+
}
|
|
238
|
+
// The framing sphere is the model's rest pose, so a camera does not drift
|
|
239
|
+
// as the animation moves the geometry.
|
|
240
|
+
const sphere = box.getBoundingSphere(new THREE.Sphere());
|
|
241
|
+
const sceneCameras = gltf.cameras;
|
|
242
|
+
const selected = selectAnimationClips(gltf.animations, options.animation.clipName);
|
|
243
|
+
const duration = selected.reduce((longest, clip) => Math.max(longest, clip.duration), 0);
|
|
244
|
+
const loop = options.animation.loop;
|
|
245
|
+
const mixer = selected.length > 0 ? new THREE.AnimationMixer(gltf.scene) : null;
|
|
246
|
+
const actions = [];
|
|
247
|
+
if (mixer) {
|
|
248
|
+
for (const clip of selected) {
|
|
249
|
+
const action = mixer.clipAction(clip);
|
|
250
|
+
if (loop) {
|
|
251
|
+
action.setLoop(THREE.LoopRepeat, Infinity);
|
|
252
|
+
}
|
|
253
|
+
else {
|
|
254
|
+
// LoopOnce holds the last frame at exactly `duration`; LoopRepeat
|
|
255
|
+
// would wrap it back to 0 there.
|
|
256
|
+
action.setLoop(THREE.LoopOnce, 1);
|
|
257
|
+
action.clampWhenFinished = true;
|
|
258
|
+
}
|
|
259
|
+
action.play();
|
|
260
|
+
actions.push(action);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
const rig = createLightRig(scene, options.lighting, options.lightIntensity);
|
|
264
|
+
if (!options.background.transparent) {
|
|
265
|
+
scene.background = new THREE.Color(options.background.color);
|
|
266
|
+
}
|
|
267
|
+
const canvas = new OffscreenCanvas(1, 1);
|
|
268
|
+
const renderer = new THREE.WebGLRenderer({
|
|
269
|
+
canvas,
|
|
270
|
+
antialias: true,
|
|
271
|
+
alpha: true,
|
|
272
|
+
preserveDrawingBuffer: true
|
|
273
|
+
});
|
|
274
|
+
renderer.toneMapping = THREE.NeutralToneMapping;
|
|
275
|
+
renderer.outputColorSpace = THREE.SRGBColorSpace;
|
|
276
|
+
if (options.background.transparent) {
|
|
277
|
+
renderer.setClearColor(0x000000, 0);
|
|
278
|
+
}
|
|
279
|
+
const orbitCamera = new THREE.PerspectiveCamera();
|
|
280
|
+
const target = new THREE.Vector3();
|
|
281
|
+
const eye = new THREE.Vector3();
|
|
282
|
+
let disposed = false;
|
|
283
|
+
return {
|
|
284
|
+
animations: gltf.animations.map((clip) => clip.name),
|
|
285
|
+
cameras: sceneCameras.map((camera) => camera.name),
|
|
286
|
+
render(frame) {
|
|
287
|
+
if (disposed) {
|
|
288
|
+
throw new Error("Model3D render: this session is already disposed");
|
|
289
|
+
}
|
|
290
|
+
const width = Math.max(1, Math.round(frame.width));
|
|
291
|
+
const height = Math.max(1, Math.round(frame.height));
|
|
292
|
+
const aspect = width / height;
|
|
293
|
+
if (mixer) {
|
|
294
|
+
// Reset before every frame so a render depends only on its own time:
|
|
295
|
+
// a scrub backwards, or one past a non-looping clip's end, draws the
|
|
296
|
+
// same picture whatever was drawn before it.
|
|
297
|
+
for (const action of actions)
|
|
298
|
+
action.reset();
|
|
299
|
+
mixer.setTime(mixerTimeFor(frame.timeSec, duration, loop));
|
|
300
|
+
}
|
|
301
|
+
scene.updateMatrixWorld(true);
|
|
302
|
+
target.copy(sphere.center);
|
|
303
|
+
if (frame.camera.targetOffset) {
|
|
304
|
+
target.x += frame.camera.targetOffset[0];
|
|
305
|
+
target.y += frame.camera.targetOffset[1];
|
|
306
|
+
target.z += frame.camera.targetOffset[2];
|
|
307
|
+
}
|
|
308
|
+
let camera;
|
|
309
|
+
if (frame.camera.mode === "scene") {
|
|
310
|
+
const sceneCamera = selectSceneCamera(sceneCameras, frame.camera.sceneCameraName);
|
|
311
|
+
if (sceneCamera instanceof THREE.PerspectiveCamera) {
|
|
312
|
+
sceneCamera.aspect = aspect;
|
|
313
|
+
sceneCamera.updateProjectionMatrix();
|
|
314
|
+
}
|
|
315
|
+
sceneCamera.getWorldPosition(eye);
|
|
316
|
+
camera = sceneCamera;
|
|
317
|
+
}
|
|
318
|
+
else {
|
|
319
|
+
const framing = computeFraming(sphere.radius, frame.camera.fovDeg, aspect, frame.camera.zoom);
|
|
320
|
+
const offset = orbitOffset(frame.camera.azimuthDeg, frame.camera.elevationDeg, framing.distance);
|
|
321
|
+
orbitCamera.fov = frame.camera.fovDeg;
|
|
322
|
+
orbitCamera.aspect = aspect;
|
|
323
|
+
orbitCamera.near = framing.near;
|
|
324
|
+
orbitCamera.far = framing.far;
|
|
325
|
+
orbitCamera.updateProjectionMatrix();
|
|
326
|
+
orbitCamera.position.set(target.x + offset.x, target.y + offset.y, target.z + offset.z);
|
|
327
|
+
orbitCamera.lookAt(target);
|
|
328
|
+
orbitCamera.updateMatrixWorld(true);
|
|
329
|
+
eye.copy(orbitCamera.position);
|
|
330
|
+
camera = orbitCamera;
|
|
331
|
+
}
|
|
332
|
+
rig.aim(eye, target);
|
|
333
|
+
renderer.setSize(width, height, false);
|
|
334
|
+
renderer.render(scene, camera);
|
|
335
|
+
return canvas;
|
|
336
|
+
},
|
|
337
|
+
dispose() {
|
|
338
|
+
if (disposed)
|
|
339
|
+
return;
|
|
340
|
+
disposed = true;
|
|
341
|
+
if (mixer) {
|
|
342
|
+
mixer.stopAllAction();
|
|
343
|
+
mixer.uncacheRoot(gltf.scene);
|
|
344
|
+
}
|
|
345
|
+
disposeScene(scene);
|
|
346
|
+
renderer.dispose();
|
|
347
|
+
renderer.forceContextLoss();
|
|
348
|
+
}
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* Render GLB (or embedded-buffer glTF JSON) bytes to PNG bytes. Must run in a
|
|
353
|
+
* browser context (window or worker) with WebGL and `OffscreenCanvas`.
|
|
354
|
+
*/
|
|
355
|
+
export async function renderGlbToPng(glb, options) {
|
|
356
|
+
const session = await createModel3DRenderSession(glb, sessionOptionsFromRender3DOptions(options));
|
|
357
|
+
try {
|
|
358
|
+
const canvas = session.render(renderFrameFromRender3DOptions(options));
|
|
359
|
+
const blob = await canvas.convertToBlob({ type: "image/png" });
|
|
360
|
+
return new Uint8Array(await blob.arrayBuffer());
|
|
361
|
+
}
|
|
362
|
+
finally {
|
|
363
|
+
session.dispose();
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
//# sourceMappingURL=render3d-core.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render3d-core.js","sourceRoot":"","sources":["../../../src/nodes/model3d/render3d-core.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Node-side driver for `nodetool.model3d.RenderToImage`.
|
|
3
|
+
*
|
|
4
|
+
* The backend has no WebGL, so it launches a headless Chromium, injects the
|
|
5
|
+
* self-contained render bundle (`dist/render3d-page.js`, built by
|
|
6
|
+
* `scripts/bundle-render3d-page.mjs`), and calls `__nodetoolRenderGlbFrames`
|
|
7
|
+
* over CDP. SwiftShader flags keep WebGL working on GPU-less servers.
|
|
8
|
+
*
|
|
9
|
+
* Chrome discovery follows the automation nodes: `CHROME_PATH` when set,
|
|
10
|
+
* otherwise chrome-launcher's platform search. Everything here is
|
|
11
|
+
* lazy-imported so merely registering the node never requires Chrome.
|
|
12
|
+
*/
|
|
13
|
+
import { type Model3DRenderFrame, type Model3DSessionOptions, type Render3DOptions } from "./render3d-core.js";
|
|
14
|
+
interface RuntimeEvaluateResult {
|
|
15
|
+
result?: {
|
|
16
|
+
type?: string;
|
|
17
|
+
value?: unknown;
|
|
18
|
+
};
|
|
19
|
+
exceptionDetails?: {
|
|
20
|
+
text?: string;
|
|
21
|
+
exception?: {
|
|
22
|
+
description?: string;
|
|
23
|
+
value?: unknown;
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
interface CdpClient {
|
|
28
|
+
Runtime: {
|
|
29
|
+
/** CDP commands with no return parameters resolve to an empty object. */
|
|
30
|
+
enable(): Promise<object>;
|
|
31
|
+
evaluate(params: {
|
|
32
|
+
expression: string;
|
|
33
|
+
awaitPromise?: boolean;
|
|
34
|
+
returnByValue?: boolean;
|
|
35
|
+
}): Promise<RuntimeEvaluateResult>;
|
|
36
|
+
};
|
|
37
|
+
close(): Promise<void>;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Attach to a freshly launched Chrome's debug port, retrying while it refuses.
|
|
41
|
+
*
|
|
42
|
+
* `chrome-launcher` resolves as soon as the port accepts a TCP connection, but
|
|
43
|
+
* `chrome-remote-interface` then issues an HTTP request to `/json/list` that a
|
|
44
|
+
* still-initializing Chrome refuses outright. A single attempt loses that race
|
|
45
|
+
* on a loaded machine — it turned up as an `ECONNREFUSED` on CI while several
|
|
46
|
+
* other Chrome-driving suites were running — so poll until the endpoint is
|
|
47
|
+
* actually serving.
|
|
48
|
+
*
|
|
49
|
+
* Exported for tests.
|
|
50
|
+
*/
|
|
51
|
+
export declare function connectCdp(port: number, { timeoutMs, retryMs }?: {
|
|
52
|
+
retryMs?: number | undefined;
|
|
53
|
+
timeoutMs?: number | undefined;
|
|
54
|
+
}): Promise<CdpClient>;
|
|
55
|
+
/**
|
|
56
|
+
* Launch Chrome, retrying once after a beat. `chrome-launcher` rejects with
|
|
57
|
+
* its port poll's raw error (an `ECONNREFUSED`) when Chromium is too slow to
|
|
58
|
+
* open its debug port on a loaded machine — the same transient race the CDP
|
|
59
|
+
* attach retry above covers, one step earlier. A machine that cannot run
|
|
60
|
+
* Chrome at all fails the same way twice and the second error propagates.
|
|
61
|
+
*
|
|
62
|
+
* Exported for tests.
|
|
63
|
+
*/
|
|
64
|
+
export declare function launchChromeWithRetry<T>(launch: () => Promise<T>, retryDelayMs?: number): Promise<T>;
|
|
65
|
+
/**
|
|
66
|
+
* Render one session's worth of frames in a fresh headless Chromium: one
|
|
67
|
+
* launch, one model load, one PNG per frame. One Chrome per call keeps the
|
|
68
|
+
* node stateless; launch cost (~1s) is negligible next to a typical
|
|
69
|
+
* workflow's model-generation steps.
|
|
70
|
+
*/
|
|
71
|
+
export declare function renderGlbFramesHeadless(glb: Uint8Array, options: Model3DSessionOptions, frames: readonly Model3DRenderFrame[]): Promise<Uint8Array[]>;
|
|
72
|
+
/** Render GLB bytes to PNG bytes: the one-frame case of the above. */
|
|
73
|
+
export declare function renderGlbHeadless(glb: Uint8Array, options: Render3DOptions): Promise<Uint8Array>;
|
|
74
|
+
export {};
|
|
75
|
+
//# sourceMappingURL=render3d-headless.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render3d-headless.d.ts","sourceRoot":"","sources":["../../../src/nodes/model3d/render3d-headless.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,OAAO,EAGL,KAAK,kBAAkB,EACvB,KAAK,qBAAqB,EAC1B,KAAK,eAAe,EACrB,MAAM,oBAAoB,CAAC;AA0B5B,UAAU,qBAAqB;IAC7B,MAAM,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IAC5C,gBAAgB,CAAC,EAAE;QACjB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,SAAS,CAAC,EAAE;YAAE,WAAW,CAAC,EAAE,MAAM,CAAC;YAAC,KAAK,CAAC,EAAE,OAAO,CAAA;SAAE,CAAC;KACvD,CAAC;CACH;AAED,UAAU,SAAS;IACjB,OAAO,EAAE;QACP,yEAAyE;QACzE,MAAM,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;QAC1B,QAAQ,CAAC,MAAM,EAAE;YACf,UAAU,EAAE,MAAM,CAAC;YACnB,YAAY,CAAC,EAAE,OAAO,CAAC;YACvB,aAAa,CAAC,EAAE,OAAO,CAAC;SACzB,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAAC;KACpC,CAAC;IACF,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAyDD;;;;;;;;;;;GAWG;AACH,wBAAsB,UAAU,CAC9B,IAAI,EAAE,MAAM,EACZ,EAAE,SAAkC,EAAE,OAA8B,EAAE;;;CAAK,GAC1E,OAAO,CAAC,SAAS,CAAC,CA2BpB;AAED;;;;;;;;GAQG;AACH,wBAAsB,qBAAqB,CAAC,CAAC,EAC3C,MAAM,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACxB,YAAY,SAAQ,GACnB,OAAO,CAAC,CAAC,CAAC,CASZ;AAED;;;;;GAKG;AACH,wBAAsB,uBAAuB,CAC3C,GAAG,EAAE,UAAU,EACf,OAAO,EAAE,qBAAqB,EAC9B,MAAM,EAAE,SAAS,kBAAkB,EAAE,GACpC,OAAO,CAAC,UAAU,EAAE,CAAC,CAgEvB;AAED,sEAAsE;AACtE,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,UAAU,EACf,OAAO,EAAE,eAAe,GACvB,OAAO,CAAC,UAAU,CAAC,CAOrB"}
|