@selvajs/visualization 1.0.0-beta.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/LICENSE +21 -0
- package/README.md +113 -0
- package/dist/chunk-5XGN7UAV.js +2 -0
- package/dist/chunk-5XGN7UAV.js.map +1 -0
- package/dist/chunk-AQJPVUH3.cjs +2 -0
- package/dist/chunk-AQJPVUH3.cjs.map +1 -0
- package/dist/chunk-BYLIBOAU.cjs +2 -0
- package/dist/chunk-BYLIBOAU.cjs.map +1 -0
- package/dist/chunk-EXAI6IC5.js +2 -0
- package/dist/chunk-EXAI6IC5.js.map +1 -0
- package/dist/index.cjs +1 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -0
- package/dist/parse.cjs +10 -0
- package/dist/parse.cjs.map +1 -0
- package/dist/parse.d.cts +235 -0
- package/dist/parse.d.ts +235 -0
- package/dist/parse.js +10 -0
- package/dist/parse.js.map +1 -0
- package/dist/render.cjs +110 -0
- package/dist/render.cjs.map +1 -0
- package/dist/render.d.cts +430 -0
- package/dist/render.d.ts +430 -0
- package/dist/render.js +110 -0
- package/dist/render.js.map +1 -0
- package/dist/scene.cjs +2 -0
- package/dist/scene.cjs.map +1 -0
- package/dist/scene.d.cts +110 -0
- package/dist/scene.d.ts +110 -0
- package/dist/scene.js +2 -0
- package/dist/scene.js.map +1 -0
- package/dist/types-CdF9R3qA.d.cts +41 -0
- package/dist/types-CdF9R3qA.d.ts +41 -0
- package/package.json +115 -0
package/dist/parse.d.cts
ADDED
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import * as THREE from 'three';
|
|
2
|
+
import { RhinoModule } from 'rhino3dm';
|
|
3
|
+
import { M as MaterialAppearanceOptions } from './types-CdF9R3qA.cjs';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Structural, not nominal: satisfies `@selvajs/solve/client`'s `MeshPolicy<THREE.Object3D>`
|
|
7
|
+
* without this package depending on solve.
|
|
8
|
+
*/
|
|
9
|
+
declare const meshPolicy: {
|
|
10
|
+
clone(meshes: THREE.Object3D[]): THREE.Object3D[];
|
|
11
|
+
release(meshes: THREE.Object3D[]): void;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Declared structurally rather than imported from `@selvajs/compute` — this package converts
|
|
16
|
+
* meshes, it doesn't need a Rhino.Compute client dependency to describe the shape a caller hands
|
|
17
|
+
* it. `@selvajs/compute`'s `GrasshopperComputeResponse` is a superset and stays assignable to
|
|
18
|
+
* this, so {@link getThreeMeshesFromComputeResponse} keeps accepting one unchanged.
|
|
19
|
+
*/
|
|
20
|
+
interface DisplayDataItem {
|
|
21
|
+
/** Namespaced wire type token, e.g. `Selva.GH.Features.Display.Services.DisplayBatch`. */
|
|
22
|
+
type: string;
|
|
23
|
+
data: string;
|
|
24
|
+
}
|
|
25
|
+
interface DisplayResponseValue {
|
|
26
|
+
InnerTree: {
|
|
27
|
+
[branchPath: string]: DisplayDataItem[];
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
interface DisplayComputeResponse {
|
|
31
|
+
values: DisplayResponseValue[];
|
|
32
|
+
/** Rhino `UnitSystem` name driving unit scaling (see `SCALE_FACTORS`); unrecognized/absent scales 1. */
|
|
33
|
+
modelunits: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Shared by meshes and items so pick/filter/label code treats them uniformly. No color/material — that's per-kind. */
|
|
37
|
+
interface DisplayIdentity {
|
|
38
|
+
/** Stable pick key: `${sourceComponentId}:${originalIndex}`. */
|
|
39
|
+
id: string;
|
|
40
|
+
/** Distinct from {@link id} — renaming must not change identity. */
|
|
41
|
+
name: string;
|
|
42
|
+
layer: string;
|
|
43
|
+
/** Arbitrary key-value pairs from the GH Metadata input. */
|
|
44
|
+
metadata?: Record<string, string>;
|
|
45
|
+
}
|
|
46
|
+
interface DisplayItemBase extends DisplayIdentity {
|
|
47
|
+
/** Hex/rgb/named color string, parsed by `parseColor`. Falls back to a viewer default. */
|
|
48
|
+
color?: string;
|
|
49
|
+
/** 0–1. Omitted means fully opaque. */
|
|
50
|
+
opacity?: number;
|
|
51
|
+
}
|
|
52
|
+
/** Rhino's Z-up frame and `{X,Y,Z}` casing. */
|
|
53
|
+
interface DisplayPosition {
|
|
54
|
+
X: number;
|
|
55
|
+
Y: number;
|
|
56
|
+
Z: number;
|
|
57
|
+
}
|
|
58
|
+
/** Rhino-native JSON (`curve.ToNurbsCurve().ToJSON()`), tessellated to a fat `Line2` on decode. */
|
|
59
|
+
interface DisplayCurve extends DisplayItemBase {
|
|
60
|
+
kind: 'curve';
|
|
61
|
+
json: string;
|
|
62
|
+
/** Screen-space CSS px, constant regardless of zoom. Omitted → viewer default. */
|
|
63
|
+
width?: number;
|
|
64
|
+
}
|
|
65
|
+
interface DisplayPoint extends DisplayItemBase {
|
|
66
|
+
kind: 'point';
|
|
67
|
+
position: DisplayPosition;
|
|
68
|
+
}
|
|
69
|
+
type DisplayItem = DisplayCurve | DisplayPoint;
|
|
70
|
+
|
|
71
|
+
interface SerializableMaterial {
|
|
72
|
+
color: string;
|
|
73
|
+
metalness: number;
|
|
74
|
+
roughness: number;
|
|
75
|
+
opacity: number;
|
|
76
|
+
transparent: boolean;
|
|
77
|
+
/**
|
|
78
|
+
* Optional color-map texture reference: http(s) URL, data URI, or plugin asset URL
|
|
79
|
+
* (`http://localhost:{port}/assets/{hash}`). Omitted entirely (not just empty) when untextured.
|
|
80
|
+
* When set, the mesh blob also carries per-vertex UVs (FLAG_HAS_UVS) for meshes using it.
|
|
81
|
+
*/
|
|
82
|
+
map?: string;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* `vertexStart`/`vertexCount` and `indexStart`/`indexCount` are in **element units** of the
|
|
86
|
+
* decoded typed arrays, not bytes — vertex component offset is `vertexStart * 3` (component count
|
|
87
|
+
* `vertexCount * 3`); index element offset is `indexStart` (count `indexCount`). For byte offsets,
|
|
88
|
+
* multiply by element size: indices are 2 bytes (`FLAG_UINT16_INDICES`) or 4 bytes (uint32);
|
|
89
|
+
* vertex components are 2 bytes (int16 quantized) or 4 bytes (`FLAG_FLOAT32`).
|
|
90
|
+
*/
|
|
91
|
+
interface MeshMetadata {
|
|
92
|
+
name: string;
|
|
93
|
+
/** Layer path for grouping in the scene manager, e.g. 'Structure/Walls'. */
|
|
94
|
+
layer: string;
|
|
95
|
+
/** Index in the GH input tree before material grouping; combined with sourceComponentId
|
|
96
|
+
* uniquely identifies the GH source geometry. */
|
|
97
|
+
originalIndex: number;
|
|
98
|
+
vertexCount: number;
|
|
99
|
+
/** 3 per triangle. */
|
|
100
|
+
indexCount: number;
|
|
101
|
+
vertexStart: number;
|
|
102
|
+
indexStart: number;
|
|
103
|
+
/** Arbitrary key-value pairs from the GH Metadata input. */
|
|
104
|
+
metadata?: Record<string, string>;
|
|
105
|
+
}
|
|
106
|
+
interface MaterialGroup {
|
|
107
|
+
/** Index into the batch's materials array. */
|
|
108
|
+
materialId: number;
|
|
109
|
+
meshes: MeshMetadata[];
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* One Display component's payload, ready for Three.js rendering.
|
|
113
|
+
*
|
|
114
|
+
* `compressedData` is the binary "SLVA" blob, base64-encoded for transit inside the values JSON
|
|
115
|
+
* envelope; opaque to the outer JSON so a future binary WebSocket frame can drop the base64 step
|
|
116
|
+
* without changing this shape.
|
|
117
|
+
*
|
|
118
|
+
* Named `DisplayBatch` rather than `MeshBatch` because it's the seam non-mesh display items
|
|
119
|
+
* (curves, points, later labels/icons) also travel through — those ride as JSON alongside the mesh
|
|
120
|
+
* blob (`items`), not inside it.
|
|
121
|
+
*/
|
|
122
|
+
interface DisplayBatch {
|
|
123
|
+
materials: SerializableMaterial[];
|
|
124
|
+
groups: MaterialGroup[];
|
|
125
|
+
compressedData: string;
|
|
126
|
+
/** InstanceGuid of the WebDisplay GH component that produced this batch; combined with
|
|
127
|
+
* MeshMetadata.originalIndex to backtrack any mesh to its GH source. */
|
|
128
|
+
sourceComponentId?: string;
|
|
129
|
+
/** Non-mesh display items — see {@link DisplayItem}. Parsed by the separate `display-items`
|
|
130
|
+
* path, not the SLVA mesh parser. Omitted when there are none. */
|
|
131
|
+
items?: DisplayItem[];
|
|
132
|
+
}
|
|
133
|
+
interface MeshBatchParsingOptions {
|
|
134
|
+
/** Merge meshes with same material into single geometry. Defaults to true. */
|
|
135
|
+
mergeByMaterial?: boolean;
|
|
136
|
+
debug?: boolean;
|
|
137
|
+
/**
|
|
138
|
+
* Appearance dials applied to every material built from this batch — set once at parse time
|
|
139
|
+
* (materials are rebuilt per solve). Runtime restyling of an already-built scene lives in the
|
|
140
|
+
* viewer's `setLook`.
|
|
141
|
+
*/
|
|
142
|
+
material?: MaterialAppearanceOptions;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
interface MeshExtractionOptions {
|
|
146
|
+
parsing?: MeshBatchParsingOptions;
|
|
147
|
+
/** Apply scaling based on model units. Defaults to true. */
|
|
148
|
+
allowScaling?: boolean;
|
|
149
|
+
/**
|
|
150
|
+
* Drop geometry so its lowest point sits on the ground plane. **Defaults to `false`** — content
|
|
151
|
+
* renders at its true Rhino coordinates, matching the Grasshopper definition. Setting `true`
|
|
152
|
+
* shifts objects, so anything read back out of the scene (bounds, measured/picked positions) no
|
|
153
|
+
* longer corresponds to Rhino coordinates.
|
|
154
|
+
*/
|
|
155
|
+
allowAutoPosition?: boolean;
|
|
156
|
+
/** Up axis for `allowAutoPosition` grounding. Defaults to `'z'` (Rhino's frame) — only set this
|
|
157
|
+
* if the viewer is configured with a non-default `sceneUp`. */
|
|
158
|
+
groundAxis?: 'x' | 'y' | 'z';
|
|
159
|
+
/** rhino3dm instance for decoding curve display items — selva-compute doesn't own the WASM
|
|
160
|
+
* instance, the host threads it in. Omit to skip curves (points still render). */
|
|
161
|
+
rhino?: RhinoModule;
|
|
162
|
+
debug?: boolean;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Metres per model unit, keyed by Rhino `UnitSystem` name (the `modelunits` string on the compute
|
|
167
|
+
* response). Imperial factors are the exact international definitions (1 in = 0.0254 m,
|
|
168
|
+
* 1 ft = 0.3048 m, 1 mi = 1609.344 m). Units not in this table scale 1 and log a one-time warning
|
|
169
|
+
* — see {@link getScaleFactor}.
|
|
170
|
+
*/
|
|
171
|
+
declare const SCALE_FACTORS: Record<string, number>;
|
|
172
|
+
/**
|
|
173
|
+
* Extracts display meshes and items from a Grasshopper WebDisplay compute response: decompresses,
|
|
174
|
+
* scales to meters, and optionally grounds them. Requires the VektorNode Rhino.Compute fork (see
|
|
175
|
+
* root CLAUDE.md).
|
|
176
|
+
*
|
|
177
|
+
* Synchronous internally (large batches block the UI for their duration); `async` only so the
|
|
178
|
+
* shape can stay stable if parsing moves off-thread later.
|
|
179
|
+
*
|
|
180
|
+
* @throws Rethrows unexpected errors after attempting to dispose any created meshes.
|
|
181
|
+
*/
|
|
182
|
+
declare function getThreeMeshesFromComputeResponse(data: DisplayComputeResponse, options?: MeshExtractionOptions): Promise<THREE.Object3D[]>;
|
|
183
|
+
|
|
184
|
+
/** Internal telemetry only (not exposed in public options). */
|
|
185
|
+
interface ParseTelemetry {
|
|
186
|
+
parseTime?: number;
|
|
187
|
+
perfStart?: number;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Parses a DisplayBatch object and creates Three.js meshes from its mesh blob.
|
|
191
|
+
*
|
|
192
|
+
* Synchronous internally — `parseBinaryMeshBatch` does no IO, just typed-array views over the
|
|
193
|
+
* blob. Stays `async` so callers don't have to change shape if parsing moves into a worker later.
|
|
194
|
+
*
|
|
195
|
+
* @throws {VisualizationError} On a corrupt/truncated/unsupported mesh blob or malformed group metadata.
|
|
196
|
+
*/
|
|
197
|
+
declare function parseMeshBatchObject(batch: DisplayBatch, options?: MeshBatchParsingOptions,
|
|
198
|
+
/** @internal Timings threaded from an outer entry point; not a caller option. */
|
|
199
|
+
telemetry?: ParseTelemetry): Promise<THREE.Mesh[]>;
|
|
200
|
+
/**
|
|
201
|
+
* Parses a raw binary mesh batch blob (SLVA wire format) and creates Three.js meshes.
|
|
202
|
+
*
|
|
203
|
+
* Use this entry point when the blob arrives as a binary WebSocket frame rather than inside a JSON
|
|
204
|
+
* envelope. The blob is self-describing — materials, groups, and `sourceComponentId` come from its
|
|
205
|
+
* embedded metadata header.
|
|
206
|
+
*
|
|
207
|
+
* @throws {VisualizationError} On a corrupt/truncated/unsupported mesh blob or malformed group metadata.
|
|
208
|
+
*/
|
|
209
|
+
declare function parseMeshBatchBlob(blob: ArrayBuffer | Uint8Array, options?: MeshBatchParsingOptions): Promise<THREE.Mesh[]>;
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Subscribed to the renderer's own report below, so no host wiring is needed; still exported for a
|
|
213
|
+
* host embedding a foreign renderer that wants to set it directly.
|
|
214
|
+
*/
|
|
215
|
+
declare function setTextureAnisotropy(value: number): void;
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Free every cross-solve GPU cache — cached geometries, textures and edge segments.
|
|
219
|
+
*
|
|
220
|
+
* **You do not normally need this.** These caches register themselves for teardown, so the viewer's
|
|
221
|
+
* `dispose()` already frees them once the last live viewer goes away. It is exported for the cases
|
|
222
|
+
* that sit outside a viewer lifecycle: reclaiming memory under pressure, or a test isolating
|
|
223
|
+
* module-level state.
|
|
224
|
+
*
|
|
225
|
+
* Safe to call repeatedly and with a viewer running — the caches simply repopulate on the next solve.
|
|
226
|
+
*/
|
|
227
|
+
declare function releaseParseCaches(): void;
|
|
228
|
+
|
|
229
|
+
interface DisplayItemParseOptions {
|
|
230
|
+
/** Omit to skip curves; points still render. */
|
|
231
|
+
rhino?: RhinoModule;
|
|
232
|
+
}
|
|
233
|
+
declare function parseDisplayItems(items: DisplayItem[] | undefined, options?: DisplayItemParseOptions): THREE.Object3D[];
|
|
234
|
+
|
|
235
|
+
export { type DisplayBatch, type DisplayComputeResponse, type DisplayCurve, type DisplayDataItem, type DisplayIdentity, type DisplayItem, type DisplayItemBase, type DisplayItemParseOptions, type DisplayPoint, type DisplayPosition, type DisplayResponseValue, type MaterialGroup, type MeshBatchParsingOptions, type MeshExtractionOptions, type MeshMetadata, SCALE_FACTORS, type SerializableMaterial, getThreeMeshesFromComputeResponse, meshPolicy, parseDisplayItems, parseMeshBatchBlob, parseMeshBatchObject, releaseParseCaches, setTextureAnisotropy };
|
package/dist/parse.d.ts
ADDED
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import * as THREE from 'three';
|
|
2
|
+
import { RhinoModule } from 'rhino3dm';
|
|
3
|
+
import { M as MaterialAppearanceOptions } from './types-CdF9R3qA.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Structural, not nominal: satisfies `@selvajs/solve/client`'s `MeshPolicy<THREE.Object3D>`
|
|
7
|
+
* without this package depending on solve.
|
|
8
|
+
*/
|
|
9
|
+
declare const meshPolicy: {
|
|
10
|
+
clone(meshes: THREE.Object3D[]): THREE.Object3D[];
|
|
11
|
+
release(meshes: THREE.Object3D[]): void;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Declared structurally rather than imported from `@selvajs/compute` — this package converts
|
|
16
|
+
* meshes, it doesn't need a Rhino.Compute client dependency to describe the shape a caller hands
|
|
17
|
+
* it. `@selvajs/compute`'s `GrasshopperComputeResponse` is a superset and stays assignable to
|
|
18
|
+
* this, so {@link getThreeMeshesFromComputeResponse} keeps accepting one unchanged.
|
|
19
|
+
*/
|
|
20
|
+
interface DisplayDataItem {
|
|
21
|
+
/** Namespaced wire type token, e.g. `Selva.GH.Features.Display.Services.DisplayBatch`. */
|
|
22
|
+
type: string;
|
|
23
|
+
data: string;
|
|
24
|
+
}
|
|
25
|
+
interface DisplayResponseValue {
|
|
26
|
+
InnerTree: {
|
|
27
|
+
[branchPath: string]: DisplayDataItem[];
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
interface DisplayComputeResponse {
|
|
31
|
+
values: DisplayResponseValue[];
|
|
32
|
+
/** Rhino `UnitSystem` name driving unit scaling (see `SCALE_FACTORS`); unrecognized/absent scales 1. */
|
|
33
|
+
modelunits: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Shared by meshes and items so pick/filter/label code treats them uniformly. No color/material — that's per-kind. */
|
|
37
|
+
interface DisplayIdentity {
|
|
38
|
+
/** Stable pick key: `${sourceComponentId}:${originalIndex}`. */
|
|
39
|
+
id: string;
|
|
40
|
+
/** Distinct from {@link id} — renaming must not change identity. */
|
|
41
|
+
name: string;
|
|
42
|
+
layer: string;
|
|
43
|
+
/** Arbitrary key-value pairs from the GH Metadata input. */
|
|
44
|
+
metadata?: Record<string, string>;
|
|
45
|
+
}
|
|
46
|
+
interface DisplayItemBase extends DisplayIdentity {
|
|
47
|
+
/** Hex/rgb/named color string, parsed by `parseColor`. Falls back to a viewer default. */
|
|
48
|
+
color?: string;
|
|
49
|
+
/** 0–1. Omitted means fully opaque. */
|
|
50
|
+
opacity?: number;
|
|
51
|
+
}
|
|
52
|
+
/** Rhino's Z-up frame and `{X,Y,Z}` casing. */
|
|
53
|
+
interface DisplayPosition {
|
|
54
|
+
X: number;
|
|
55
|
+
Y: number;
|
|
56
|
+
Z: number;
|
|
57
|
+
}
|
|
58
|
+
/** Rhino-native JSON (`curve.ToNurbsCurve().ToJSON()`), tessellated to a fat `Line2` on decode. */
|
|
59
|
+
interface DisplayCurve extends DisplayItemBase {
|
|
60
|
+
kind: 'curve';
|
|
61
|
+
json: string;
|
|
62
|
+
/** Screen-space CSS px, constant regardless of zoom. Omitted → viewer default. */
|
|
63
|
+
width?: number;
|
|
64
|
+
}
|
|
65
|
+
interface DisplayPoint extends DisplayItemBase {
|
|
66
|
+
kind: 'point';
|
|
67
|
+
position: DisplayPosition;
|
|
68
|
+
}
|
|
69
|
+
type DisplayItem = DisplayCurve | DisplayPoint;
|
|
70
|
+
|
|
71
|
+
interface SerializableMaterial {
|
|
72
|
+
color: string;
|
|
73
|
+
metalness: number;
|
|
74
|
+
roughness: number;
|
|
75
|
+
opacity: number;
|
|
76
|
+
transparent: boolean;
|
|
77
|
+
/**
|
|
78
|
+
* Optional color-map texture reference: http(s) URL, data URI, or plugin asset URL
|
|
79
|
+
* (`http://localhost:{port}/assets/{hash}`). Omitted entirely (not just empty) when untextured.
|
|
80
|
+
* When set, the mesh blob also carries per-vertex UVs (FLAG_HAS_UVS) for meshes using it.
|
|
81
|
+
*/
|
|
82
|
+
map?: string;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* `vertexStart`/`vertexCount` and `indexStart`/`indexCount` are in **element units** of the
|
|
86
|
+
* decoded typed arrays, not bytes — vertex component offset is `vertexStart * 3` (component count
|
|
87
|
+
* `vertexCount * 3`); index element offset is `indexStart` (count `indexCount`). For byte offsets,
|
|
88
|
+
* multiply by element size: indices are 2 bytes (`FLAG_UINT16_INDICES`) or 4 bytes (uint32);
|
|
89
|
+
* vertex components are 2 bytes (int16 quantized) or 4 bytes (`FLAG_FLOAT32`).
|
|
90
|
+
*/
|
|
91
|
+
interface MeshMetadata {
|
|
92
|
+
name: string;
|
|
93
|
+
/** Layer path for grouping in the scene manager, e.g. 'Structure/Walls'. */
|
|
94
|
+
layer: string;
|
|
95
|
+
/** Index in the GH input tree before material grouping; combined with sourceComponentId
|
|
96
|
+
* uniquely identifies the GH source geometry. */
|
|
97
|
+
originalIndex: number;
|
|
98
|
+
vertexCount: number;
|
|
99
|
+
/** 3 per triangle. */
|
|
100
|
+
indexCount: number;
|
|
101
|
+
vertexStart: number;
|
|
102
|
+
indexStart: number;
|
|
103
|
+
/** Arbitrary key-value pairs from the GH Metadata input. */
|
|
104
|
+
metadata?: Record<string, string>;
|
|
105
|
+
}
|
|
106
|
+
interface MaterialGroup {
|
|
107
|
+
/** Index into the batch's materials array. */
|
|
108
|
+
materialId: number;
|
|
109
|
+
meshes: MeshMetadata[];
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* One Display component's payload, ready for Three.js rendering.
|
|
113
|
+
*
|
|
114
|
+
* `compressedData` is the binary "SLVA" blob, base64-encoded for transit inside the values JSON
|
|
115
|
+
* envelope; opaque to the outer JSON so a future binary WebSocket frame can drop the base64 step
|
|
116
|
+
* without changing this shape.
|
|
117
|
+
*
|
|
118
|
+
* Named `DisplayBatch` rather than `MeshBatch` because it's the seam non-mesh display items
|
|
119
|
+
* (curves, points, later labels/icons) also travel through — those ride as JSON alongside the mesh
|
|
120
|
+
* blob (`items`), not inside it.
|
|
121
|
+
*/
|
|
122
|
+
interface DisplayBatch {
|
|
123
|
+
materials: SerializableMaterial[];
|
|
124
|
+
groups: MaterialGroup[];
|
|
125
|
+
compressedData: string;
|
|
126
|
+
/** InstanceGuid of the WebDisplay GH component that produced this batch; combined with
|
|
127
|
+
* MeshMetadata.originalIndex to backtrack any mesh to its GH source. */
|
|
128
|
+
sourceComponentId?: string;
|
|
129
|
+
/** Non-mesh display items — see {@link DisplayItem}. Parsed by the separate `display-items`
|
|
130
|
+
* path, not the SLVA mesh parser. Omitted when there are none. */
|
|
131
|
+
items?: DisplayItem[];
|
|
132
|
+
}
|
|
133
|
+
interface MeshBatchParsingOptions {
|
|
134
|
+
/** Merge meshes with same material into single geometry. Defaults to true. */
|
|
135
|
+
mergeByMaterial?: boolean;
|
|
136
|
+
debug?: boolean;
|
|
137
|
+
/**
|
|
138
|
+
* Appearance dials applied to every material built from this batch — set once at parse time
|
|
139
|
+
* (materials are rebuilt per solve). Runtime restyling of an already-built scene lives in the
|
|
140
|
+
* viewer's `setLook`.
|
|
141
|
+
*/
|
|
142
|
+
material?: MaterialAppearanceOptions;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
interface MeshExtractionOptions {
|
|
146
|
+
parsing?: MeshBatchParsingOptions;
|
|
147
|
+
/** Apply scaling based on model units. Defaults to true. */
|
|
148
|
+
allowScaling?: boolean;
|
|
149
|
+
/**
|
|
150
|
+
* Drop geometry so its lowest point sits on the ground plane. **Defaults to `false`** — content
|
|
151
|
+
* renders at its true Rhino coordinates, matching the Grasshopper definition. Setting `true`
|
|
152
|
+
* shifts objects, so anything read back out of the scene (bounds, measured/picked positions) no
|
|
153
|
+
* longer corresponds to Rhino coordinates.
|
|
154
|
+
*/
|
|
155
|
+
allowAutoPosition?: boolean;
|
|
156
|
+
/** Up axis for `allowAutoPosition` grounding. Defaults to `'z'` (Rhino's frame) — only set this
|
|
157
|
+
* if the viewer is configured with a non-default `sceneUp`. */
|
|
158
|
+
groundAxis?: 'x' | 'y' | 'z';
|
|
159
|
+
/** rhino3dm instance for decoding curve display items — selva-compute doesn't own the WASM
|
|
160
|
+
* instance, the host threads it in. Omit to skip curves (points still render). */
|
|
161
|
+
rhino?: RhinoModule;
|
|
162
|
+
debug?: boolean;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Metres per model unit, keyed by Rhino `UnitSystem` name (the `modelunits` string on the compute
|
|
167
|
+
* response). Imperial factors are the exact international definitions (1 in = 0.0254 m,
|
|
168
|
+
* 1 ft = 0.3048 m, 1 mi = 1609.344 m). Units not in this table scale 1 and log a one-time warning
|
|
169
|
+
* — see {@link getScaleFactor}.
|
|
170
|
+
*/
|
|
171
|
+
declare const SCALE_FACTORS: Record<string, number>;
|
|
172
|
+
/**
|
|
173
|
+
* Extracts display meshes and items from a Grasshopper WebDisplay compute response: decompresses,
|
|
174
|
+
* scales to meters, and optionally grounds them. Requires the VektorNode Rhino.Compute fork (see
|
|
175
|
+
* root CLAUDE.md).
|
|
176
|
+
*
|
|
177
|
+
* Synchronous internally (large batches block the UI for their duration); `async` only so the
|
|
178
|
+
* shape can stay stable if parsing moves off-thread later.
|
|
179
|
+
*
|
|
180
|
+
* @throws Rethrows unexpected errors after attempting to dispose any created meshes.
|
|
181
|
+
*/
|
|
182
|
+
declare function getThreeMeshesFromComputeResponse(data: DisplayComputeResponse, options?: MeshExtractionOptions): Promise<THREE.Object3D[]>;
|
|
183
|
+
|
|
184
|
+
/** Internal telemetry only (not exposed in public options). */
|
|
185
|
+
interface ParseTelemetry {
|
|
186
|
+
parseTime?: number;
|
|
187
|
+
perfStart?: number;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Parses a DisplayBatch object and creates Three.js meshes from its mesh blob.
|
|
191
|
+
*
|
|
192
|
+
* Synchronous internally — `parseBinaryMeshBatch` does no IO, just typed-array views over the
|
|
193
|
+
* blob. Stays `async` so callers don't have to change shape if parsing moves into a worker later.
|
|
194
|
+
*
|
|
195
|
+
* @throws {VisualizationError} On a corrupt/truncated/unsupported mesh blob or malformed group metadata.
|
|
196
|
+
*/
|
|
197
|
+
declare function parseMeshBatchObject(batch: DisplayBatch, options?: MeshBatchParsingOptions,
|
|
198
|
+
/** @internal Timings threaded from an outer entry point; not a caller option. */
|
|
199
|
+
telemetry?: ParseTelemetry): Promise<THREE.Mesh[]>;
|
|
200
|
+
/**
|
|
201
|
+
* Parses a raw binary mesh batch blob (SLVA wire format) and creates Three.js meshes.
|
|
202
|
+
*
|
|
203
|
+
* Use this entry point when the blob arrives as a binary WebSocket frame rather than inside a JSON
|
|
204
|
+
* envelope. The blob is self-describing — materials, groups, and `sourceComponentId` come from its
|
|
205
|
+
* embedded metadata header.
|
|
206
|
+
*
|
|
207
|
+
* @throws {VisualizationError} On a corrupt/truncated/unsupported mesh blob or malformed group metadata.
|
|
208
|
+
*/
|
|
209
|
+
declare function parseMeshBatchBlob(blob: ArrayBuffer | Uint8Array, options?: MeshBatchParsingOptions): Promise<THREE.Mesh[]>;
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Subscribed to the renderer's own report below, so no host wiring is needed; still exported for a
|
|
213
|
+
* host embedding a foreign renderer that wants to set it directly.
|
|
214
|
+
*/
|
|
215
|
+
declare function setTextureAnisotropy(value: number): void;
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Free every cross-solve GPU cache — cached geometries, textures and edge segments.
|
|
219
|
+
*
|
|
220
|
+
* **You do not normally need this.** These caches register themselves for teardown, so the viewer's
|
|
221
|
+
* `dispose()` already frees them once the last live viewer goes away. It is exported for the cases
|
|
222
|
+
* that sit outside a viewer lifecycle: reclaiming memory under pressure, or a test isolating
|
|
223
|
+
* module-level state.
|
|
224
|
+
*
|
|
225
|
+
* Safe to call repeatedly and with a viewer running — the caches simply repopulate on the next solve.
|
|
226
|
+
*/
|
|
227
|
+
declare function releaseParseCaches(): void;
|
|
228
|
+
|
|
229
|
+
interface DisplayItemParseOptions {
|
|
230
|
+
/** Omit to skip curves; points still render. */
|
|
231
|
+
rhino?: RhinoModule;
|
|
232
|
+
}
|
|
233
|
+
declare function parseDisplayItems(items: DisplayItem[] | undefined, options?: DisplayItemParseOptions): THREE.Object3D[];
|
|
234
|
+
|
|
235
|
+
export { type DisplayBatch, type DisplayComputeResponse, type DisplayCurve, type DisplayDataItem, type DisplayIdentity, type DisplayItem, type DisplayItemBase, type DisplayItemParseOptions, type DisplayPoint, type DisplayPosition, type DisplayResponseValue, type MaterialGroup, type MeshBatchParsingOptions, type MeshExtractionOptions, type MeshMetadata, SCALE_FACTORS, type SerializableMaterial, getThreeMeshesFromComputeResponse, meshPolicy, parseDisplayItems, parseMeshBatchBlob, parseMeshBatchObject, releaseParseCaches, setTextureAnisotropy };
|
package/dist/parse.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import{a as G,b as N,c as A,f as Be,k as Ie,l as De,m as Oe,n as $,o as Q,q as ee,r as Ue,u as He,v as Le}from"./chunk-EXAI6IC5.js";import"./chunk-5XGN7UAV.js";function bt(e){return e.map(t=>{let r=t.clone(!0),n=[];t.traverse(a=>n.push(a));let o=0;return r.traverse(a=>{let s=n[o++],u=a;if(!s.geometry)return;let i=s.geometry.clone();i.userData={...i.userData},delete i.userData[$],u.geometry=i}),r})}function Et(e){e.forEach(t=>Le(t,{materials:!1}))}var xt={clone:bt,release:Et};import*as de from"three";import{Line2 as Mt}from"three/addons/lines/Line2.js";import{LineGeometry as wt}from"three/addons/lines/LineGeometry.js";import{LineMaterial as Tt}from"three/addons/lines/LineMaterial.js";import*as _e from"three";var At="#ffffff";function te(e,t){let r=t??1;return{color:new _e.Color(e??At),transparent:r<1,opacity:r}}var Fe=12,Rt=4e-4,vt=12,St=.05,Ct=2;function Pe(e,t){if(!t)return A().warn("No rhino3dm instance provided; skipping curve display item."),null;let r=Bt(e.json,t);if(!r)return null;let n;try{n=It(r)}catch(l){return A().warn("Failed to tessellate curve display item; skipping.",l),null}finally{Y(r)}if(n.length<2)return null;let o=[];for(let l of n)o.push(l.x,l.y,l.z);let a=new wt;a.setPositions(o);let s=te(e.color,e.opacity),u=new Tt({color:s.color}),i=u;i.linewidth=e.width??Ct,i.transparent=s.transparent,i.opacity=s.opacity;let f=new Mt(a,u);return f.computeLineDistances(),f.name=e.name,f.userData={source:"compute",id:e.id,layer:e.layer,kind:"curve",metadata:e.metadata},f}function Y(e){e?.delete?.()}function Bt(e,t){try{let r=JSON.parse(e),n=t.CommonObject.decode(r);return n&&typeof n.pointAt=="function"?n:(Y(n),A().warn("Decoded display-item JSON is not a curve; skipping."),null)}catch(r){return A().warn("Failed to decode curve display item JSON:",r),null}}function It(e){let t=Dt(e);return t||Ot(e)}function Dt(e){if(!e.isPolyline())return null;let t=e.tryGetPolyline(),r=Array.isArray(t)?t[1]:t;if(!r||typeof r.count!="number"||r.count<2)return Y(r),null;let n=[];for(let o=0;o<r.count;o++){let a=r.get(o);n.push(new de.Vector3(a[0],a[1],a[2]))}return Y(r),n}function Ot(e){let t=e.domain,r=t[0],o=t[1]-r,a=l=>{let y=e.pointAt(l);return new de.Vector3(y[0],y[1],y[2])},s=Ut(e),u=r,i=a(r),f=[i];for(let l=0;l<Fe;l++){let y=r+o*(l+1)/Fe,b=a(y);me(u,i,y,b,a,s,vt,f),f.push(b),u=y,i=b}return f}function me(e,t,r,n,o,a,s,u){if(s<=0)return;let i=(e+r)/2,f=o(i),l=Lt(f,t,n),y=Ht(t,f,n);l<=a&&y<=St||(me(e,t,i,f,o,a,s-1,u),u.push(f),me(i,f,r,n,o,a,s-1,u))}function Ut(e){let t=e.getBoundingBox(),r=t.min,n=t.max;Y(t);let o=Math.hypot(n[0]-r[0],n[1]-r[1],n[2]-r[2]);return Math.max(o*Rt,1e-6)}function Ht(e,t,r){let n=t.x-e.x,o=t.y-e.y,a=t.z-e.z,s=r.x-t.x,u=r.y-t.y,i=r.z-t.z,f=Math.sqrt(n*n+o*o+a*a),l=Math.sqrt(s*s+u*u+i*i);if(f===0||l===0)return 0;let y=n*s+o*u+a*i,b=Math.max(-1,Math.min(1,y/(f*l)));return Math.acos(b)}function Lt(e,t,r){let n=r.x-t.x,o=r.y-t.y,a=r.z-t.z,s=n*n+o*o+a*a;if(s===0)return e.distanceTo(t);let u=e.x-t.x,i=e.y-t.y,f=e.z-t.z,l=Math.max(0,Math.min(1,(u*n+i*o+f*a)/s)),y=u-n*l,b=i-o*l,w=f-a*l;return Math.sqrt(y*y+b*b+w*w)}import*as P from"three";function Ve(e){let{position:t}=e;if(!t||typeof t.X!="number"||!Number.isFinite(t.X)||typeof t.Y!="number"||!Number.isFinite(t.Y)||typeof t.Z!="number"||!Number.isFinite(t.Z))return A().warn(`Skipping point display item with missing or non-finite position (id: ${String(e.id)}).`),null;let r=new P.BufferGeometry;r.setAttribute("position",new P.Float32BufferAttribute([t.X,t.Y,t.Z],3));let n=new P.PointsMaterial({...te(e.color,e.opacity),size:6,sizeAttenuation:!1}),o=new P.Points(r,n);return o.name=e.name,o.userData={source:"compute",id:e.id,layer:e.layer,kind:"point",metadata:e.metadata},o}function pe(e,t={}){if(!e||e.length===0)return[];let{rhino:r}=t,n=[];for(let o of e)switch(o.kind){case"curve":{let a=Pe(o,r);a&&n.push(a);break}case"point":{let a=Ve(o);a&&n.push(a);break}default:{let s=o;A().warn(`Skipping unknown display item kind: ${String(s.kind)}`);break}}return n}import*as V from"three";var ke=new Uint16Array(new Uint8Array([1,0]).buffer)[0]===1;import{inflateSync as _t}from"fflate";function Ge(e){return typeof e=="string"?Be(e):e instanceof Uint8Array?e:new Uint8Array(e)}function Ne(e){if(e.byteLength<8)return e;let t=new DataView(e.buffer,e.byteOffset,e.byteLength);if(t.getUint32(0,!0)!==1515605075)return e;let r=t.getUint32(4,!0),n=e.subarray(8),o=Math.max(n.byteLength*1032+1024,1<<20);if(r>o)throw I("SLVZ header declares an implausible uncompressed length",{uncompressedLen:r,deflatedBytes:n.byteLength,maxPlausibleLen:o});let a;try{a=_t(n,{out:new Uint8Array(r+1)})}catch(s){throw I(`Failed to inflate SLVZ blob: ${s instanceof Error?s.message:String(s)}`,{uncompressedLen:r,deflatedBytes:n.byteLength})}if(a.byteLength!==r)throw I("SLVZ payload inflated to a different size than the header declares.",{declaredLen:r,actualLen:a.byteLength,deflatedBytes:n.byteLength});return a}function je(e){if(typeof TextDecoder<"u")return new TextDecoder("utf-8").decode(e);if(typeof globalThis.Buffer<"u")return globalThis.Buffer.from(e).toString("utf-8");throw new N("No UTF-8 decoder available in this environment.",G.INVALID_STATE)}function $e(e,t,r){if(r===0)return new Int16Array(0);if(t%2===0)return new Int16Array(e,t,r);let n=new Uint8Array(r*2);return n.set(new Uint8Array(e,t,r*2)),new Int16Array(n.buffer)}function re(e,t,r){if(r===0)return new Float32Array(0);if(t%4===0)return new Float32Array(e,t,r);let n=new Uint8Array(r*4);return n.set(new Uint8Array(e,t,r*4)),new Float32Array(n.buffer)}function W(e,t,r){if(r===0)return new Uint16Array(0);if(t%2===0)return new Uint16Array(e,t,r);let n=new Uint8Array(r*2);return n.set(new Uint8Array(e,t,r*2)),new Uint16Array(n.buffer)}function ze(e,t,r){if(r===0)return new Uint32Array(0);if(t%4===0)return new Uint32Array(e,t,r);let n=new Uint8Array(r*4);return n.set(new Uint8Array(e,t,r*4)),new Uint32Array(n.buffer)}function Ye(e,t){if(e.length!==0&&!(e instanceof Uint16Array&&t>65535)){for(let r=0;r<e.length;r++)if(e[r]>=t)throw I("Index out of range of vertexCount.",{indexPosition:r,indexValue:e[r],vertexCount:t})}}function L(e){return e>>>1^-(e&1)}function We(e){let t=new Int16Array(e.length),r=0,n=0,o=0;for(let a=0;a<e.length;a+=3)r=r+L(e[a])<<16>>16,n=n+L(e[a+1])<<16>>16,o=o+L(e[a+2])<<16>>16,t[a]=r,t[a+1]=n,t[a+2]=o;return t}function Je(e){let t=new Uint16Array(e.length),r=0;for(let n=0;n<e.length;n++)r=r+L(e[n])&65535,t[n]=r;return t}function Xe(e){let t=new Uint32Array(e.length),r=0;for(let n=0;n<e.length;n++)r=r+L(e[n])>>>0,t[n]=r;return t}function I(e,t){return new N(e,G.VALIDATION_ERROR,{context:t})}var Ke=36;function qe(e,t,r,n,o){if(r+Ke>e.byteLength)throw I("Insufficient data to read UV chunk header.",{expectedBytes:Ke,availableBytes:e.byteLength-r,offset:r});let a=t.getUint32(r,!0);r+=4;let s=t.getFloat64(r,!0);r+=8;let u=t.getFloat64(r,!0);r+=8;let i=t.getFloat64(r,!0);r+=8;let f=t.getFloat64(r,!0);r+=8;let l=n*2,y=a===1,b=l*(y?4:2);if(r+b>e.byteLength)throw I("Insufficient data to read UV chunk.",{expectedBytes:b,availableBytes:e.byteLength-r,offset:r,uvFormat:a,vertexCount:n});let w=e.byteOffset+r,v;if(y)v=re(e.buffer,w,l).slice();else{let C=W(e.buffer,w,l);v=new Float32Array(l);let c=0,d=0;for(let p=0;p<l;p+=2)o?(c=c+L(C[p])&65535,d=d+L(C[p+1])&65535):(c=C[p],d=C[p+1]),v[p]=s+c*i,v[p+1]=u+d*f}return{uvs:v,offset:r+b}}function Ze(e,t,r,n){let o=r*3;if(t+o>e.byteLength)throw I("Insufficient data to read vertex-color chunk.",{expectedBytes:o,availableBytes:e.byteLength-t,offset:t,vertexCount:r});let a=e.subarray(t,t+o);if(!n)return a.slice();let s=new Uint8Array(o),u=0,i=0,f=0;for(let l=0;l<o;l+=3)u=u+L(a[l])&255,i=i+L(a[l+1])&255,f=f+L(a[l+2])&255,s[l]=u,s[l+1]=i,s[l+2]=f;return s}function he(e){let t=ge(e),r;t.isFloat32?r=t.vertexData:t.deltaEncoded?r=We(t.vertexData):r=t.vertexData;let n=t.indexData;return t.deltaEncoded&&(n=n instanceof Uint16Array?Je(n):Xe(n)),Ye(n,t.vertexCount),{metadata:t.metadata,flags:t.flags,vertices:r,indices:n,origin:t.origin,scale:t.scale,uvs:t.uvs,colors:t.colors}}function ge(e){if(!ke)throw new N("SLVA parsing requires a little-endian host: the zero-copy geometry readers view the wire bytes in host byte order.",G.ENVIRONMENT_ERROR);let t=Ne(Ge(e)),r=new DataView(t.buffer,t.byteOffset,t.byteLength);if(t.byteLength<12)throw I("Blob too small to contain SLVA header.",{expectedBytes:12,availableBytes:t.byteLength});let n=0,o=r.getUint32(n,!0);if(n+=4,o!==1096174675)throw I(`Invalid SLVA magic: 0x${o.toString(16)}`,{expectedMagic:`0x${1096174675 .toString(16)}`,actualMagic:`0x${o.toString(16)}`});let a=r.getUint32(n,!0);if(n+=4,a<1||a>3)throw I(`Unsupported SLVA version: ${a}`,{minSupportedVersion:1,maxSupportedVersion:3,actualVersion:a});let s=r.getUint32(n,!0);if(n+=4,n+s>t.byteLength)throw I("Insufficient data to read metadata JSON.",{expectedBytes:s,availableBytes:t.byteLength-n,offset:n});let u=t.subarray(n,n+s);n+=s;let i;try{i=JSON.parse(je(u))}catch(U){throw I(`Failed to parse metadata JSON: ${U instanceof Error?U.message:String(U)}`,{metadataLen:s})}if(n+56>t.byteLength)throw I("Insufficient data to read geometry header.",{expectedBytes:56,availableBytes:t.byteLength-n,offset:n});let f=r.getUint32(n,!0);n+=4;let l=r.getFloat64(n,!0);n+=8;let y=r.getFloat64(n,!0);n+=8;let b=r.getFloat64(n,!0);n+=8;let w=r.getFloat64(n,!0);n+=8;let v=r.getFloat64(n,!0);n+=8;let C=r.getFloat64(n,!0);n+=8;let c=r.getUint32(n,!0);n+=4;let d=(f&1)!==0,p=(f&4)!==0,m=c*3,S=m*(d?4:2);if(n+S>t.byteLength)throw I("Insufficient data to read vertices.",{expectedBytes:S,availableBytes:t.byteLength-n,offset:n,useFloat32:d,vertexCount:c});let T=t.byteOffset+n,g;if(d?g=re(t.buffer,T,m):p?g=W(t.buffer,T,m):g=$e(t.buffer,T,m),n+=S,n+4>t.byteLength)throw I("Insufficient data to read index count.",{expectedBytes:4,availableBytes:t.byteLength-n,offset:n});let x=r.getUint32(n,!0);n+=4;let R=(f&2)!==0,M=x*(R?2:4);if(n+M>t.byteLength)throw I("Insufficient data to read indices.",{expectedBytes:M,availableBytes:t.byteLength-n,offset:n,indexCount:x,useUint16Indices:R});let B=R?W(t.buffer,t.byteOffset+n,x):ze(t.buffer,t.byteOffset+n,x);n+=M;let D=null;if((f&8)!==0){let U=qe(t,r,n,c,p);D=U.uvs,n=U.offset}let H=null;return(f&16)!==0&&(H=Ze(t,n,c,p)),{metadata:i,flags:f,vertexData:g,indexData:B,isFloat32:d,deltaEncoded:p,vertexCount:c,origin:[l,y,b],scale:[w,v,C],uvs:D,colors:H}}var ot=256*1024*1024,ne=1024,_=new Map,oe=0;function at(e,t){let r=2166136261,n=o=>{r^=o,r=Math.imul(r,16777619)};for(let o=0;o<t.length;o++)n(t.charCodeAt(o));for(let o of e){if(!o){n(57005);continue}let a=o.byteLength;if(n(a),(o.byteOffset&3)===0&&(a&3)===0){let s=new Uint32Array(o.buffer,o.byteOffset,a>>2),u=Math.min(ne,s.length);for(let i=0;i<u;i++)n(s[i]);for(let i=Math.max(u,s.length-ne);i<s.length;i++)n(s[i])}else{let s=new Uint8Array(o.buffer,o.byteOffset,a),u=Math.min(ne*4,s.length);for(let i=0;i<u;i++)n(s[i]);for(let i=Math.max(u,s.length-ne*4);i<s.length;i++)n(s[i])}}return`${(r>>>0).toString(36)}:${e.length}`}function Gt(e){let t=e.index?.array.byteLength??0;for(let r of Object.values(e.attributes))t+=r.array.byteLength;return t}function J(e){let t=_.get(e);if(t)return _.delete(e),_.set(e,t),t.geometry}function X(e,t){let r=Gt(t);if(!(r>ot)&&!_.has(e))for(t.userData[$]=!0,_.set(e,{geometry:t,bytes:r}),oe+=r;oe>ot&&_.size>1;){let n=_.keys().next().value,o=_.get(n);_.delete(n),oe-=o.bytes,delete o.geometry.userData[$],o.geometry.dispose()}}ee(()=>Nt());function Nt(){for(let e of _.values())delete e.geometry.userData[$],e.geometry.dispose();_.clear(),oe=0}function jt(e){let{isFloat32:t,deltaEncoded:r,origin:n,scale:o,uvs:a,colors:s,jobs:u}=e,i=c=>c>>>1^-(c&1),f;if(t)f=e.vertexData;else{let c;if(r){let g=e.vertexData;c=new Int16Array(g.length);let x=0,R=0,E=0;for(let M=0;M<g.length;M+=3)x=x+i(g[M])<<16>>16,R=R+i(g[M+1])<<16>>16,E=E+i(g[M+2])<<16>>16,c[M]=x,c[M+1]=R,c[M+2]=E}else c=e.vertexData;f=new Float32Array(c.length);let d=n[0],p=n[1],m=n[2],h=o[0],S=o[1],T=o[2];for(let g=0;g<c.length;g+=3)f[g]=d+(c[g]+32767)*h,f[g+1]=p+(c[g+1]+32767)*S,f[g+2]=m+(c[g+2]+32767)*T}let l;if(r){let c=e.indexData;if(c instanceof Uint16Array){let d=new Uint16Array(c.length),p=0;for(let m=0;m<c.length;m++)p=p+i(c[m])&65535,d[m]=p;l=d}else{let d=new Uint32Array(c.length),p=0;for(let m=0;m<c.length;m++)p=p+i(c[m])>>>0,d[m]=p;l=d}}else l=e.indexData;let y=f.length/3;for(let c=0;c<l.length;c++)if(l[c]>=y)throw new Error(`Index ${l[c]} out of range of vertexCount ${y}`);let b=1024,w=(c,d)=>{let p=2166136261,m=h=>{p^=h,p=Math.imul(p,16777619)};for(let h=0;h<d.length;h++)m(d.charCodeAt(h));for(let h of c){if(!h){m(57005);continue}let S=h.byteLength;if(m(S),(h.byteOffset&3)===0&&(S&3)===0){let T=new Uint32Array(h.buffer,h.byteOffset,S>>2),g=Math.min(b,T.length);for(let x=0;x<g;x++)m(T[x]);for(let x=Math.max(g,T.length-b);x<T.length;x++)m(T[x])}else{let T=new Uint8Array(h.buffer,h.byteOffset,S),g=Math.min(b*4,T.length);for(let x=0;x<g;x++)m(T[x]);for(let x=Math.max(g,T.length-b*4);x<T.length;x++)m(T[x])}}return`${(p>>>0).toString(36)}:${c.length}`},v=(c,d)=>{let p=[],m=c;for(let h of d){m+=`|${h.vertexStart},${h.vertexCount},${h.indexStart},${h.indexCount}`;let S=h.vertexStart*3,T=S+h.vertexCount*3;p.push(f.subarray(S,T)),p.push(l.subarray(h.indexStart,h.indexStart+h.indexCount)),p.push(a?a.subarray(h.vertexStart*2,(h.vertexStart+h.vertexCount)*2):null),p.push(s?s.subarray(S,T):null)}return w(p,m)},C=[];for(let c of u){let d=0,p=0;for(let E of c.windows)d+=E.vertexCount,p+=E.indexCount;let m=new Float32Array(d*3),h=new Uint32Array(p),S=a?new Float32Array(d*2):null,T=s?new Uint8Array(d*3):null,g=0,x=0;for(let E of c.windows){let M=E.vertexStart*3;m.set(f.subarray(M,M+E.vertexCount*3),g*3),S&&a&&S.set(a.subarray(E.vertexStart*2,(E.vertexStart+E.vertexCount)*2),g*2),T&&s&&T.set(s.subarray(M,M+E.vertexCount*3),g*3);let B=E.vertexStart,D=E.vertexStart+E.vertexCount,H=g-E.vertexStart;for(let U=0;U<E.indexCount;U++){let k=l[E.indexStart+U];if(k<B||k>=D)throw new Error(`Index ${k} outside vertex window [${B}, ${D})`);h[x+U]=k+H}g+=E.vertexCount,x+=E.indexCount}let R=new Float32Array(d*3);for(let E=0;E<h.length;E+=3){let M=h[E]*3,B=h[E+1]*3,D=h[E+2]*3,H=m[D]-m[B],U=m[D+1]-m[B+1],k=m[D+2]-m[B+2],ve=m[M]-m[B],Se=m[M+1]-m[B+1],Ce=m[M+2]-m[B+2],le=U*Ce-k*Se,ue=k*ve-H*Ce,fe=H*Se-U*ve;R[M]+=le,R[M+1]+=ue,R[M+2]+=fe,R[B]+=le,R[B+1]+=ue,R[B+2]+=fe,R[D]+=le,R[D+1]+=ue,R[D+2]+=fe}for(let E=0;E<R.length;E+=3){let M=R[E],B=R[E+1],D=R[E+2],H=Math.sqrt(M*M+B*B+D*D)||1;R[E]=M/H,R[E+1]=B/H,R[E+2]=D/H}C.push({key:v(c.kind,c.windows),positions:m,normals:R,indices:h,uvs:S,colors:T})}return C}function st(){return[`const assemble = ${jt.toString()};`,"self.onmessage = (event) => {"," const { id, input } = event.data;"," try {"," const geometries = assemble(input);"," const transfer = [];"," for (const g of geometries) {"," transfer.push(g.positions.buffer, g.normals.buffer, g.indices.buffer);"," if (g.uvs) transfer.push(g.uvs.buffer);"," if (g.colors) transfer.push(g.colors.buffer);"," }"," self.postMessage({ id, geometries }, transfer);"," } catch (error) {"," self.postMessage({ id, error: String((error && error.message) || error) });"," }","};"].join(`
|
|
2
|
+
`)}var it=5e4,j,K=new Map,$t=1;function ct(){if(j!==void 0)return j;if(typeof Worker>"u"||typeof Blob>"u"||typeof URL>"u"||typeof URL.createObjectURL!="function")return j=null,null;try{let e=URL.createObjectURL(new Blob([st()],{type:"text/javascript"})),t=new Worker(e);t.onmessage=r=>{let{id:n,geometries:o,error:a}=r.data,s=K.get(n);s&&(K.delete(n),o?s.resolve(o):s.reject(new Error(a??"mesh assembly failed in worker")))},t.onerror=()=>{for(let r of K.values())r.reject(new Error("mesh assembly worker crashed"));K.clear(),t.terminate(),j=null},j=t}catch{j=null}return j}function lt(e,t,r){return new Promise((n,o)=>{let a=$t++;K.set(a,{resolve:n,reject:o}),e.postMessage({id:a,input:t},r)})}import*as z from"three";import*as ce from"three";var zt=64,Yt=256,F=new Map,q=new Map,ae=0,ie=class extends Error{constructor(t){super(`Texture load for ${t} resolved after clearTextureCache(); texture disposed.`),this.name="StaleTextureLoadError"}},se=1;function ut(e){se=Math.max(1,e);for(let t of F.values())t.anisotropy!==se&&(t.anisotropy=se,t.needsUpdate=!0)}He(ut);function ft(e,t){let r=Jt(t),n=F.get(r);if(n){F.delete(r),F.set(r,n),e.map=n,e.needsUpdate=!0;return}typeof document>"u"||qt(t,r).then(o=>{e.map=o,e.needsUpdate=!0}).catch(o=>{o instanceof ie||A().warn(`Failed to load material texture ${t}:`,o)})}ee(()=>Wt());function Wt(){ae++;for(let e of F.values())delete e.userData[Q],e.dispose();F.clear(),q.clear()}function Jt(e){return e.length<=Yt?e:`data-uri:${Xt(e).toString(16)}:${e.length}`}function Xt(e){let t=2166136261;for(let r=0;r<e.length;r++)t^=e.charCodeAt(r),t=Math.imul(t,16777619);return t>>>0}function Kt(e,t){for(t.userData[Q]=!0,F.set(e,t);F.size>zt;){let r=F.keys().next().value,n=F.get(r);F.delete(r),delete n.userData[Q],n.dispose()}}function qt(e,t){let r=q.get(t);if(!r){let n=ae;r=new Promise((o,a)=>{new ce.TextureLoader().load(e,s=>{if(n!==ae){s.dispose(),a(new ie(e));return}s.colorSpace=ce.SRGBColorSpace,s.anisotropy=se,Kt(t,s),q.delete(t),o(s)},void 0,s=>{n===ae&&q.delete(t),a(s)})}),q.set(t,r)}return r}var Zt=.5,Qt=.5,er=.3;function be(e,t){let r=Ie(e.color),n=t?.vertexColors??!1,o=t?.appearance,a=new z.MeshPhysicalMaterial({color:r,metalness:e.metalness,roughness:e.roughness,opacity:e.opacity,transparent:e.transparent,vertexColors:n,side:o?.cullBackfaces?z.FrontSide:z.DoubleSide,polygonOffset:!0,polygonOffsetFactor:.5,polygonOffsetUnits:.5,depthWrite:!0,depthTest:!0});return o?.envMapIntensity!=null&&(a.envMapIntensity=o.envMapIntensity),e.metalness>Zt&&(a.clearcoat=Qt,a.clearcoatRoughness=er),n&&tr(a),e.map&&ft(a,e.map),a}function tr(e){e.onBeforeCompile=t=>{t.vertexShader=t.vertexShader.replace("#include <color_vertex>",`#include <color_vertex>
|
|
3
|
+
#if defined( USE_COLOR ) || defined( USE_COLOR_ALPHA )
|
|
4
|
+
vColor.rgb = mix(
|
|
5
|
+
vColor.rgb / 12.92,
|
|
6
|
+
pow( ( vColor.rgb + 0.055 ) / 1.055, vec3( 2.4 ) ),
|
|
7
|
+
step( vec3( 0.04045 ), vColor.rgb )
|
|
8
|
+
);
|
|
9
|
+
#endif`)}}import*as O from"three";function Z(e,t){return new N(e,G.VALIDATION_ERROR,{context:t})}function Ee(e,t,r,n){for(let o of e){if(!Number.isInteger(o.materialId)||o.materialId<0||o.materialId>=t)throw Z("Group materialId out of range of the materials array.",{materialId:o.materialId,materialCount:t});for(let a of o.meshes){let s={vertexStart:a.vertexStart,vertexCount:a.vertexCount,indexStart:a.indexStart,indexCount:a.indexCount};for(let[u,i]of Object.entries(s))if(!Number.isInteger(i)||i<0)throw Z(`Mesh metadata field "${u}" must be a non-negative integer.`,{meshName:a.name,field:u,value:i});if(a.vertexStart+a.vertexCount>r)throw Z("Mesh vertex window exceeds the batch vertex buffer.",{meshName:a.name,vertexStart:a.vertexStart,vertexCount:a.vertexCount,totalVertexCount:r});if(a.indexStart+a.indexCount>n)throw Z("Mesh index window exceeds the batch index buffer.",{meshName:a.name,indexStart:a.indexStart,indexCount:a.indexCount,totalIndexCount:n})}}}function xe(e,t){return Z("Index references a vertex outside its mesh's vertex window.",{meshName:t.name,indexValue:e,vertexStart:t.vertexStart,vertexCount:t.vertexCount})}function Ae(e,t,r,n,o,a){let s=[],u=e;for(let i of t){u+=`|${i.vertexStart},${i.vertexCount},${i.indexStart},${i.indexCount}`;let f=i.vertexStart*3,l=f+i.vertexCount*3;s.push(r.subarray(f,l)),s.push(n.subarray(i.indexStart,i.indexStart+i.indexCount)),s.push(o?o.subarray(i.vertexStart*2,(i.vertexStart+i.vertexCount)*2):null),s.push(a?a.subarray(f,l):null)}return at(s,u)}function mt(e,t,r){let n=new Float32Array(e.length),o=t[0],a=t[1],s=t[2],u=r[0],i=r[1],f=r[2];for(let l=0;l<e.length;l+=3)n[l]=o+(e[l]+32767)*u,n[l+1]=a+(e[l+1]+32767)*i,n[l+2]=s+(e[l+2]+32767)*f;return n}function dt(e,t,r,n,o=null,a=null){let s=Ae("merged",e.meshes,t,r,o,a),u=J(s);if(!u){let i=0,f=0;for(let c of e.meshes)i+=c.vertexCount,f+=c.indexCount;let l=new Float32Array(i*3),y=new Uint32Array(f),b=o?new Float32Array(i*2):null,w=a?new Uint8Array(i*3):null,v=0,C=0;for(let c of e.meshes){let d=c.vertexStart*3,p=c.vertexCount*3;if(l.set(t.subarray(d,d+p),v*3),b&&o){let g=c.vertexStart*2;b.set(o.subarray(g,g+c.vertexCount*2),v*2)}w&&a&&w.set(a.subarray(d,d+p),v*3);let m=r.subarray(c.indexStart,c.indexStart+c.indexCount),h=v-c.vertexStart,S=c.vertexStart,T=c.vertexStart+c.vertexCount;for(let g=0;g<m.length;g++){let x=m[g];if(x<S||x>=T)throw xe(x,c);y[C+g]=x+h}v+=c.vertexCount,C+=c.indexCount}u=new O.BufferGeometry,u.setAttribute("position",new O.BufferAttribute(l,3)),u.setIndex(new O.BufferAttribute(y,1)),b&&u.setAttribute("uv",new O.BufferAttribute(b,2)),w&&u.setAttribute("color",new O.BufferAttribute(w,3,!0)),u.computeVertexNormals(),X(s,u)}return Me(u,e,n)}function Me(e,t,r){let n=new O.Mesh(e,r[t.materialId]),o=t.meshes[0],a=t.meshes.map(s=>s.name).filter(s=>s&&s.length>0);return n.name=a.length>0?a[0]:`merged_material_${t.materialId}`,n.castShadow=!0,n.receiveShadow=!0,n.userData={source:"compute",name:n.name,layer:o?.layer??"",originalIndex:o?.originalIndex??0,metadata:o?.metadata??{},mergedFrom:t.meshes.slice(1).map(s=>({name:s.name,layer:s.layer,originalIndex:s.originalIndex}))},n}function pt(e,t,r,n,o=null,a=null){let s=[];for(let u of e.meshes){let i=u.vertexStart*3,f=u.vertexCount*3,l=Ae("single",[u],t,r,o,a),y=J(l);if(!y){let b=t.slice(i,i+f),w=r.subarray(u.indexStart,u.indexStart+u.indexCount),v=new Uint32Array(w.length),C=u.vertexStart,c=u.vertexStart+u.vertexCount;for(let d=0;d<w.length;d++){let p=w[d];if(p<C||p>=c)throw xe(p,u);v[d]=p-C}if(y=new O.BufferGeometry,y.setAttribute("position",new O.BufferAttribute(b,3)),y.setIndex(new O.BufferAttribute(v,1)),o){let d=u.vertexStart*2,p=o.slice(d,d+u.vertexCount*2);y.setAttribute("uv",new O.BufferAttribute(p,2))}if(a){let d=a.slice(i,i+f);y.setAttribute("color",new O.BufferAttribute(d,3,!0))}y.computeVertexNormals(),X(l,y)}s.push(we(y,u,e,n))}return s}function we(e,t,r,n){let o=new O.Mesh(e,n[r.materialId]);return o.name=t.name,o.userData={source:"compute",name:t.name,layer:t.layer??"",originalIndex:t.originalIndex,metadata:t.metadata??{}},o.castShadow=!0,o.receiveShadow=!0,o}async function Te(e,t,r){let{mergeByMaterial:n=!0,debug:o=!1,material:a}=t??{},{parseTime:s=0,perfStart:u=o?performance.now():0}=r??{};if(!e.compressedData)return[];let i=await ht(e.compressedData,{mergeByMaterial:n,debug:o,material:a,fallback:{materials:e.materials,groups:e.groups,sourceComponentId:e.sourceComponentId}});if(i)return i;let f=performance.now(),l=he(e.compressedData),y=performance.now()-f,b=o?nr(e.compressedData):0;return yt(l,{mergeByMaterial:n,debug:o,material:a,parseTime:s,decodeTime:y,perfStart:u,blobBytes:b,fallback:{materials:e.materials,groups:e.groups,sourceComponentId:e.sourceComponentId}})}async function rr(e,t){let{mergeByMaterial:r=!0,debug:n=!1,material:o}=t??{},a=n?performance.now():0,s=await ht(e,{mergeByMaterial:r,debug:n,material:o});if(s)return s;let u=performance.now(),i=he(e),f=performance.now()-u,l=e.byteLength;return yt(i,{mergeByMaterial:r,debug:n,material:o,parseTime:0,decodeTime:f,perfStart:a,blobBytes:l})}function yt(e,t){let{mergeByMaterial:r,debug:n,material:o,parseTime:a,decodeTime:s,perfStart:u,blobBytes:i,fallback:f}=t,l=e.metadata.materials??f?.materials??[],y=e.metadata.groups??f?.groups??[],b=f?.sourceComponentId??e.metadata.sourceComponentId,w=(e.flags&1)!==0;Ee(y,l.length,e.vertices.length/3,e.indices.length);let v=w?e.vertices:mt(e.vertices,e.origin,e.scale);if(n){let m=e.vertices.byteLength+e.indices.byteLength;A().debug("Mesh Batch Stats:"),A().debug(` Materials: ${l.length} | Groups: ${y.length}`),A().debug(` Vertices: ${e.vertices.length/3} | Indices: ${e.indices.length}`),A().debug(` Format: ${w?"float32":"int16 quantized"}`),A().debug(` Blob: ${(i/1024/1024).toFixed(2)} MB | Geometry on wire: ${(m/1024/1024).toFixed(2)} MB`)}let C=performance.now(),c=l.map(m=>be(m,{vertexColors:e.colors!=null,appearance:o})),d=[];for(let m of y)if(r&&m.meshes.length>1){let h=dt(m,v,e.indices,c,e.uvs,e.colors);h.userData.sourceComponentId=b??null,d.push(h)}else{let h=pt(m,v,e.indices,c,e.uvs,e.colors);for(let S of h)S.userData.sourceComponentId=b??null;d.push(...h)}let p=performance.now()-C;if(n){let m=performance.now()-u;A().debug("Performance:"),a>0&&A().debug(` Parse JSON: ${a.toFixed(2)}ms`),A().debug(` Decode binary: ${s.toFixed(2)}ms`),A().debug(` Create Meshes: ${p.toFixed(2)}ms`),A().debug(` Total: ${m.toFixed(2)}ms`)}return Promise.resolve(d)}async function ht(e,t){if(typeof Worker>"u")return null;let r=ge(e);if(r.indexData.length/3<it)return null;let n=ct();if(!n)return null;let o=r.metadata.materials??t.fallback?.materials??[],a=r.metadata.groups??t.fallback?.groups??[],s=t.fallback?.sourceComponentId??r.metadata.sourceComponentId;Ee(a,o.length,r.vertexCount,r.indexData.length);let u=c=>({vertexStart:c.vertexStart,vertexCount:c.vertexCount,indexStart:c.indexStart,indexCount:c.indexCount}),i=[],f=[];for(let c of a)if(t.mergeByMaterial&&c.meshes.length>1)i.push({kind:"merged",windows:c.meshes.map(u)}),f.push({kind:"merged",group:c});else for(let d of c.meshes)i.push({kind:"single",windows:[u(d)]}),f.push({kind:"single",group:c,meshMeta:d});let l=r.vertexData.slice(),y=r.indexData.slice(),b=[l.buffer,y.buffer];r.uvs&&b.push(r.uvs.buffer),r.colors&&b.push(r.colors.buffer);let w;try{w=await lt(n,{vertexData:l,isFloat32:r.isFloat32,deltaEncoded:r.deltaEncoded,origin:r.origin,scale:r.scale,indexData:y,uvs:r.uvs,colors:r.colors,jobs:i},b)}catch(c){return A().warn("Mesh assembly worker failed; falling back to main-thread parse.",c),null}if(w.length!==i.length)return null;let v=o.map(c=>be(c,{vertexColors:r.colors!=null,appearance:t.material})),C=[];for(let c=0;c<w.length;c++){let d=w[c],p=f[c],m=J(d.key);m||(m=new V.BufferGeometry,m.setAttribute("position",new V.BufferAttribute(d.positions,3)),m.setAttribute("normal",new V.BufferAttribute(d.normals,3)),m.setIndex(new V.BufferAttribute(d.indices,1)),d.uvs&&m.setAttribute("uv",new V.BufferAttribute(d.uvs,2)),d.colors&&m.setAttribute("color",new V.BufferAttribute(d.colors,3,!0)),X(d.key,m));let h=p.kind==="merged"?Me(m,p.group,v):we(m,p.meshMeta,p.group,v);h.userData.sourceComponentId=s??null,C.push(h)}return t.debug&&A().debug(`Mesh batch assembled off-thread: ${C.length} meshes, ${r.indexData.length/3} triangles`),C}function nr(e){return Math.floor(e.length*3/4)}var Re={Angstroms:1e-10,Nanometers:1e-9,Microns:1e-6,Millimeters:.001,Centimeters:.01,Decimeters:.1,Meters:1,Dekameters:10,Hectometers:100,Kilometers:1e3,Megameters:1e6,Gigameters:1e9,Microinches:254e-10,Mils:254e-7,Inches:.0254,Feet:.3048,Yards:.9144,Miles:1609.344,NauticalMiles:1852},or="Display",ar="DisplayBatch";function sr(e){let t=e.split(".");return t.includes(or)||t.includes(ar)}var gt=new Set;async function ir(e,t){let r=performance.now(),n=[],{allowScaling:o=!0,allowAutoPosition:a=!1,groundAxis:s="z",rhino:u,debug:i=!1,parsing:f={}}=t??{};try{let l=o?cr(e.modelunits):1;return await lr(e,n,l,f,u,i),a&&dr(n,s),n}catch(l){throw pr(l,n),l}finally{i&&hr(r)}}function cr(e){let t=Re[e];return t!==void 0?t:(gt.has(e)||(gt.add(e),A().warn(`Unknown Rhino model unit "${e}" \u2014 geometry will not be scaled (factor 1). Known units: ${Object.keys(Re).join(", ")}.`)),1)}async function lr(e,t,r,n,o,a){for(let s of e.values){let u=s.InnerTree;for(let i in u){let f=u[i];f&&await ur(f,t,r,n,o,a)}}}async function ur(e,t,r,n,o,a){for(let s of e){if(!sr(s.type))continue;let u={mergeByMaterial:!0,debug:!1,...n},i=fr(s.data);if(!i){A().error("Error parsing display batch envelope: invalid JSON");continue}let f=await Te(i,u),l=pe(i.items,{rhino:o}),y=[...f,...l];if(r!==1)for(let b of y)b.scale.set(r,r,r);t.push(...y),a&&A().debug(`Extracted ${f.length} meshes and ${l.length} items from batch`)}}function fr(e){return typeof e=="string"?mr(e):e}function mr(e){try{return JSON.parse(e)}catch{return}}function dr(e,t){if(e.length===0)return;let r=Oe(e);De(e,r.min[t],t)}function pr(e,t){A().error("An unexpected error occurred:",e),yr(t)}function yr(e){for(let t of e){let r=t;r.geometry&&r.geometry.dispose(),r.material&&(Array.isArray(r.material)?r.material.forEach(n=>n.dispose()):r.material.dispose())}}function hr(e){let t=performance.now()-e;A().info("Time to process meshes:",`${t.toFixed(2)}ms`)}function gr(){Ue()}export{Re as SCALE_FACTORS,ir as getThreeMeshesFromComputeResponse,xt as meshPolicy,pe as parseDisplayItems,rr as parseMeshBatchBlob,Te as parseMeshBatchObject,gr as releaseParseCaches,ut as setTextureAnisotropy};
|
|
10
|
+
//# sourceMappingURL=parse.js.map
|