@chestnutlabs/gcode-model-renderer 0.20.0 → 0.21.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/NOTICE.md +46 -0
- package/README.md +7 -0
- package/dist/model-content.d.ts +3 -0
- package/dist/model-content.d.ts.map +1 -1
- package/dist/model-content.js +4 -1
- package/dist/model-preview.d.ts +2 -0
- package/dist/model-preview.d.ts.map +1 -1
- package/dist/model-preview.js +38 -0
- package/dist/model-viewer.d.ts +13 -2
- package/dist/model-viewer.d.ts.map +1 -1
- package/dist/model-viewer.js +28 -13
- package/dist/stl.d.ts +6 -0
- package/dist/stl.d.ts.map +1 -1
- package/dist/stl.js +42 -1
- package/package.json +7 -5
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2017-2025 Remco Veldkamp
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/NOTICE.md
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# NOTICE — Attribution & Provenance
|
|
2
|
+
|
|
3
|
+
This file summarizes attribution and provenance for **ChestnutLabs/gcode-preview**. It complements,
|
|
4
|
+
and does not replace, the [`LICENSE`](LICENSE) file and the full
|
|
5
|
+
[Upstream, Fork, License & Contribution Policy](docs/03_UPSTREAM_FORK_LICENSE_AND_CONTRIBUTION_POLICY.md).
|
|
6
|
+
|
|
7
|
+
## Upstream origin
|
|
8
|
+
|
|
9
|
+
- **This repository is a public GitHub fork of** [`xyz-tools/gcode-preview`](https://github.com/xyz-tools/gcode-preview)
|
|
10
|
+
(project identity `remcoder/gcode-preview`; npm package `gcode-preview`).
|
|
11
|
+
- **Chestnut founding baseline:** branch `develop` at commit
|
|
12
|
+
`15375e56129f5a57aebc099ed7bfb5a092dcfb9e` (2026-06-08).
|
|
13
|
+
- The inherited Git history and the upstream fork relationship are preserved. Chestnut Labs maintains
|
|
14
|
+
its own roadmap, governance, release cadence, and package APIs.
|
|
15
|
+
|
|
16
|
+
## Copyright
|
|
17
|
+
|
|
18
|
+
- Inherited code: **Copyright (c) 2017–2025 Remco Veldkamp** and the `xyz-tools/gcode-preview`
|
|
19
|
+
contributors, licensed under the MIT License (see [`LICENSE`](LICENSE)).
|
|
20
|
+
- Chestnut Labs additions and modifications: **Copyright (c) 2026 Chestnut Labs**, also under the MIT
|
|
21
|
+
License.
|
|
22
|
+
|
|
23
|
+
The upstream copyright notice and MIT permission notice are retained in [`LICENSE`](LICENSE) and must
|
|
24
|
+
not be removed. Chestnut notices are added **alongside** — never in place of — upstream notices.
|
|
25
|
+
|
|
26
|
+
## License
|
|
27
|
+
|
|
28
|
+
MIT for both the inherited fork and Chestnut Labs additions, unless a later, explicit
|
|
29
|
+
legal/governance decision changes the license for an independently separable new package. See the
|
|
30
|
+
fork/license policy for the full rationale, including how an MIT package may be consumed by a
|
|
31
|
+
GPL-3.0 application (e.g., AnyBridge) without altering the package's MIT notice obligations.
|
|
32
|
+
|
|
33
|
+
## Derived vs. independently replaced code
|
|
34
|
+
|
|
35
|
+
Chestnut Labs does **not** claim a clean-room implementation. Subsystem provenance (inherited /
|
|
36
|
+
modified / replaced / original) is tracked in
|
|
37
|
+
[`docs/UPSTREAM_PROVENANCE.md`](docs/UPSTREAM_PROVENANCE.md). Replacing every line of an inherited
|
|
38
|
+
file does not by itself establish clean-room status; the ledger exists for honesty, maintenance, and
|
|
39
|
+
license review.
|
|
40
|
+
|
|
41
|
+
## Third-party code
|
|
42
|
+
|
|
43
|
+
Bundled and dependency notices are tracked in [`THIRD_PARTY_NOTICES.md`](THIRD_PARTY_NOTICES.md).
|
|
44
|
+
Reference implementations studied for behavior (Sindarius/Mainsail, Fluidd, PrusaSlicer/libvgcode,
|
|
45
|
+
BambuBuddy, and others) are **not** incorporated as source unless their license is compatible and the
|
|
46
|
+
incorporation is recorded per policy.
|
package/README.md
CHANGED
|
@@ -88,6 +88,13 @@ restore a pose (the same serializable `CameraState` as the toolpath renderer), `
|
|
|
88
88
|
source formats become viewable by registering a `ModelLoader` for a new `kind`, with no change to the
|
|
89
89
|
viewer's API (design: [DD-021](../../docs/design/DD-021-interactive-model-viewer.md)).
|
|
90
90
|
|
|
91
|
+
The first frame and `frame()` (a "Reset view" button) fit the model's bounding box to the canvas: a
|
|
92
|
+
wide, flat part fills a wide canvas and still fits a narrow 375 px panel, centered with a small margin.
|
|
93
|
+
Until the user orbits, zooms, or pans, each `resize(w, h)` re-fits the model, so a sidebar collapse
|
|
94
|
+
or a window resize never leaves it cropped. After the user moves the camera, `resize` keeps their
|
|
95
|
+
view, and `frame()` hands framing back to the viewer. Pass `autoFrameOnResize: false` to keep the
|
|
96
|
+
first framing across resizes. The `camera-changed` event fires only when a user gesture settles.
|
|
97
|
+
|
|
91
98
|
Prefer to build your own scene? `parseStl` / `parse3mf` return a three-free `ModelScene`, and both the
|
|
92
99
|
still and the viewer accept a pre-built `ModelScene` directly.
|
|
93
100
|
|
package/dist/model-content.d.ts
CHANGED
|
@@ -15,6 +15,9 @@ import type { ModelScene, RGB } from './scene-model.js';
|
|
|
15
15
|
export interface ModelFraming {
|
|
16
16
|
center: Vector3;
|
|
17
17
|
radius: number;
|
|
18
|
+
/** The model bounds as a scene-space (Y-up) box, for an aspect-aware fit. */
|
|
19
|
+
sceneMin: Vector3;
|
|
20
|
+
sceneMax: Vector3;
|
|
18
21
|
}
|
|
19
22
|
/**
|
|
20
23
|
* Adds the model root + studio rig to a provided `Scene` and builds meshes into it. The root carries the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model-content.d.ts","sourceRoot":"","sources":["../src/model-content.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAWL,KAAK,EACL,OAAO,EACR,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EAAe,UAAU,EAAE,GAAG,EAAE,MAAM,kBAAkB,CAAC;AAKrE,yGAAyG;AACzG,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"model-content.d.ts","sourceRoot":"","sources":["../src/model-content.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAWL,KAAK,EACL,OAAO,EACR,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EAAe,UAAU,EAAE,GAAG,EAAE,MAAM,kBAAkB,CAAC;AAKrE,yGAAyG;AACzG,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,6EAA6E;IAC7E,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED;;;;;GAKG;AACH,qBAAa,YAAY;IAKX,OAAO,CAAC,QAAQ,CAAC,KAAK;IAJlC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAe;IACpC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA6B;IACzD,OAAO,CAAC,WAAW,CAA6B;gBAEnB,KAAK,EAAE,KAAK;IAezC;;;;OAIG;IACH,QAAQ,CAAC,KAAK,EAAE,UAAU,EAAE,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI;IAiBpE,oFAAoF;IACpF,IAAI,OAAO,IAAI,YAAY,GAAG,IAAI,CAEjC;IAED,OAAO,CAAC,SAAS;IAoCjB,OAAO,CAAC,WAAW;IAMnB,kGAAkG;IAClG,OAAO,IAAI,IAAI;CAGhB;AAED,2FAA2F;AAC3F,eAAO,MAAM,sBAAsB,EAAE,GAAmB,CAAC"}
|
package/dist/model-content.js
CHANGED
|
@@ -52,7 +52,10 @@ export class ModelContent {
|
|
|
52
52
|
const [maxX, maxY, maxZ] = scene.bounds.max;
|
|
53
53
|
const center = new Vector3((minX + maxX) / 2, (minY + maxY) / 2, (minZ + maxZ) / 2);
|
|
54
54
|
const radius = Math.max(1e-3, center.distanceTo(new Vector3(minX, minY, minZ)));
|
|
55
|
-
|
|
55
|
+
// Model Z-up → scene Y-up: (x, y, z) → (x, z, −y), matching the root rotation.
|
|
56
|
+
const sceneMin = new Vector3(minX, minZ, -maxY);
|
|
57
|
+
const sceneMax = new Vector3(maxX, maxZ, -minY);
|
|
58
|
+
this.framedModel = { center, radius, sceneMin, sceneMax };
|
|
56
59
|
}
|
|
57
60
|
/** The framing of the last-set scene (`null` before the first {@link setScene}). */
|
|
58
61
|
get framing() {
|
package/dist/model-preview.d.ts
CHANGED
|
@@ -46,6 +46,8 @@ export interface ModelPreviewControllerOptions {
|
|
|
46
46
|
createRenderer?: (canvas: RenderTargetCanvas) => GLRendererLike;
|
|
47
47
|
/** Injected orbit controls (tests). */
|
|
48
48
|
createControls?: NonNullable<InteractiveStageOptions['createControls']>;
|
|
49
|
+
/** Re-fit the model on canvas resize until the user moves the camera (default `true`). */
|
|
50
|
+
autoFrameOnResize?: boolean;
|
|
49
51
|
}
|
|
50
52
|
/** Reactive snapshot of the model viewer, derived from engine events. Immutable — every change replaces it. */
|
|
51
53
|
export interface ModelPreviewState {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model-preview.d.ts","sourceRoot":"","sources":["../src/model-preview.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,OAAO,KAAK,EACV,WAAW,EACX,UAAU,EACV,cAAc,EACd,cAAc,EACd,uBAAuB,EACvB,YAAY,EACZ,kBAAkB,EACnB,MAAM,oCAAoC,CAAC;AAC5C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,EAAqB,KAAK,WAAW,EAAE,KAAK,gBAAgB,EAAE,KAAK,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACpH,OAAO,KAAK,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAClE,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAC7E,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C;;;GAGG;AACH,MAAM,WAAW,6BAA6B;IAC5C,OAAO,CAAC,EAAE,SAAS,WAAW,EAAE,CAAC;IACjC,UAAU,CAAC,EAAE,eAAe,CAAC;IAC7B,kBAAkB,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IACpC,UAAU,CAAC,EAAE,uBAAuB,CAAC,YAAY,CAAC,CAAC;IACnD,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,eAAe,CAAC,EAAE,SAAS,CAAC,MAAM,GAAG,SAAS,CAAC,EAAE,CAAC;IAClD,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,6EAA6E;IAC7E,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,YAAY,KAAK,IAAI,CAAC;IAC9C,0CAA0C;IAC1C,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,kBAAkB,KAAK,cAAc,CAAC;IAChE,uCAAuC;IACvC,cAAc,CAAC,EAAE,WAAW,CAAC,uBAAuB,CAAC,gBAAgB,CAAC,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"model-preview.d.ts","sourceRoot":"","sources":["../src/model-preview.ts"],"names":[],"mappings":"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"}
|
package/dist/model-preview.js
CHANGED
|
@@ -47,6 +47,7 @@ export function createModelPreviewController(options = {}) {
|
|
|
47
47
|
...(options.renderScope ? { renderScope: options.renderScope } : {}),
|
|
48
48
|
...(options.createRenderer ? { createRenderer: options.createRenderer } : {}),
|
|
49
49
|
...(options.createControls ? { createControls: options.createControls } : {}),
|
|
50
|
+
...(options.autoFrameOnResize !== undefined ? { autoFrameOnResize: options.autoFrameOnResize } : {}),
|
|
50
51
|
// Fold progress into state, then hand it to the consumer's callback.
|
|
51
52
|
onProgress: (p) => {
|
|
52
53
|
mutate({ progress: p, loading: p.stage !== 'ready' });
|
|
@@ -88,9 +89,43 @@ export function createModelPreviewController(options = {}) {
|
|
|
88
89
|
emit(e);
|
|
89
90
|
});
|
|
90
91
|
};
|
|
92
|
+
/** Detaches the bound canvas's size tracking (ResizeObserver + window-resize fallback). */
|
|
93
|
+
let unsizeCanvas = null;
|
|
94
|
+
/**
|
|
95
|
+
* Keep the engine sized to a DOM canvas's CSS box, mirroring the toolpath controller: an initial fit,
|
|
96
|
+
* a ResizeObserver, and a window-resize fallback (observers may never fire in embedded panes). A
|
|
97
|
+
* zero-sized (hidden) canvas is skipped. OffscreenCanvas hosts size themselves via `controls.resize`.
|
|
98
|
+
*/
|
|
99
|
+
const trackCanvasSize = (canvas, v) => {
|
|
100
|
+
const el = canvas;
|
|
101
|
+
if (typeof el.getBoundingClientRect !== 'function')
|
|
102
|
+
return;
|
|
103
|
+
const fit = () => {
|
|
104
|
+
const w = el.clientWidth ?? 0;
|
|
105
|
+
const h = el.clientHeight ?? 0;
|
|
106
|
+
if (w > 0 && h > 0)
|
|
107
|
+
v.resize(w, h);
|
|
108
|
+
};
|
|
109
|
+
fit();
|
|
110
|
+
let ro = null;
|
|
111
|
+
if (typeof ResizeObserver !== 'undefined') {
|
|
112
|
+
ro = new ResizeObserver(fit);
|
|
113
|
+
ro.observe(canvas);
|
|
114
|
+
}
|
|
115
|
+
const hasWindow = typeof window !== 'undefined';
|
|
116
|
+
if (hasWindow)
|
|
117
|
+
window.addEventListener('resize', fit);
|
|
118
|
+
unsizeCanvas = () => {
|
|
119
|
+
ro?.disconnect();
|
|
120
|
+
if (hasWindow)
|
|
121
|
+
window.removeEventListener('resize', fit);
|
|
122
|
+
};
|
|
123
|
+
};
|
|
91
124
|
function bindCanvas(canvas) {
|
|
92
125
|
if (disposed || canvas === boundCanvas)
|
|
93
126
|
return;
|
|
127
|
+
unsizeCanvas?.();
|
|
128
|
+
unsizeCanvas = null;
|
|
94
129
|
// Tear down the engine bound to the previous canvas.
|
|
95
130
|
if (engine !== null) {
|
|
96
131
|
unsubEngine?.();
|
|
@@ -104,6 +139,7 @@ export function createModelPreviewController(options = {}) {
|
|
|
104
139
|
const v = createModelViewer(canvas, engineOptions);
|
|
105
140
|
engine = v;
|
|
106
141
|
wire(v);
|
|
142
|
+
trackCanvasSize(canvas, v);
|
|
107
143
|
for (const op of pendingOps)
|
|
108
144
|
op(v);
|
|
109
145
|
pendingOps.length = 0;
|
|
@@ -187,6 +223,8 @@ export function createModelPreviewController(options = {}) {
|
|
|
187
223
|
if (disposed)
|
|
188
224
|
return;
|
|
189
225
|
disposed = true;
|
|
226
|
+
unsizeCanvas?.();
|
|
227
|
+
unsizeCanvas = null;
|
|
190
228
|
unsubEngine?.();
|
|
191
229
|
unsubEngine = null;
|
|
192
230
|
engine?.dispose();
|
package/dist/model-viewer.d.ts
CHANGED
|
@@ -29,6 +29,12 @@ export interface ModelViewerOptions {
|
|
|
29
29
|
* consumer owns all wording/i18n. Events from a superseded load are not emitted.
|
|
30
30
|
*/
|
|
31
31
|
onProgress?: (progress: LoadProgress) => void;
|
|
32
|
+
/**
|
|
33
|
+
* Re-fit the camera to the model when the canvas is resized (`resize()`), until the user moves the
|
|
34
|
+
* camera (orbit/zoom/pan or `setCameraState`); after that their view is kept. `frame()` (Reset view),
|
|
35
|
+
* a new source, or a render-scope change resumes auto-framing. Default `true`.
|
|
36
|
+
*/
|
|
37
|
+
autoFrameOnResize?: boolean;
|
|
32
38
|
}
|
|
33
39
|
/** What the source actually carried, surfaced on `ready` (never fabricated — DD-001). */
|
|
34
40
|
export interface ModelReadyInfo {
|
|
@@ -59,7 +65,9 @@ export interface ModelReadyInfo {
|
|
|
59
65
|
export type ModelViewerEvent = {
|
|
60
66
|
type: 'ready';
|
|
61
67
|
info: ModelReadyInfo;
|
|
62
|
-
}
|
|
68
|
+
}
|
|
69
|
+
/** Fires once per user camera gesture (orbit/zoom/pan) after it settles — never for programmatic moves. */
|
|
70
|
+
| {
|
|
63
71
|
type: 'camera-changed';
|
|
64
72
|
state: CameraState;
|
|
65
73
|
} | {
|
|
@@ -86,7 +94,10 @@ export interface ModelViewer {
|
|
|
86
94
|
setBackground(bg: ModelBackground): void;
|
|
87
95
|
setInteractionQuality(mode: 'off' | 'auto'): void;
|
|
88
96
|
resize(width: number, height: number): void;
|
|
89
|
-
/**
|
|
97
|
+
/**
|
|
98
|
+
* Re-fit the camera to the current model bounds (the default 3/4 view), fitting the bounding box to
|
|
99
|
+
* the canvas aspect and field of view. Also resumes auto-framing on resize.
|
|
100
|
+
*/
|
|
90
101
|
frame(): void;
|
|
91
102
|
/**
|
|
92
103
|
* Render only a subset of the current source (DD-030 D2): `{ plateId }`, `{ objectIds }`, or an
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model-viewer.d.ts","sourceRoot":"","sources":["../src/model-viewer.ts"],"names":[],"mappings":"AAeA,OAAO,EAIL,KAAK,UAAU,EACf,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,uBAAuB,EAC5B,KAAK,YAAY,EACjB,KAAK,kBAAkB,EACxB,MAAM,oCAAoC,CAAC;AAC5C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAE9D,OAAO,EAA4C,KAAK,WAAW,EAAE,KAAK,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACjH,OAAO,EAAmB,KAAK,WAAW,EAAE,MAAM,aAAa,CAAC;AAChE,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAC7E,OAAO,EAGL,KAAK,WAAW,EAChB,KAAK,iBAAiB,EAEtB,KAAK,WAAW,EACjB,MAAM,kBAAkB,CAAC;AAE1B,MAAM,WAAW,kBAAkB;IACjC,4EAA4E;IAC5E,OAAO,CAAC,EAAE,SAAS,WAAW,EAAE,CAAC;IACjC,oEAAoE;IACpE,UAAU,CAAC,EAAE,eAAe,CAAC;IAC7B,oFAAoF;IACpF,kBAAkB,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IACpC,0DAA0D;IAC1D,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,gEAAgE;IAChE,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,2GAA2G;IAC3G,eAAe,CAAC,EAAE,SAAS,CAAC,MAAM,GAAG,SAAS,CAAC,EAAE,CAAC;IAClD,2FAA2F;IAC3F,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,kBAAkB,KAAK,cAAc,CAAC;IAChE,qFAAqF;IACrF,cAAc,CAAC,EAAE,WAAW,CAAC,uBAAuB,CAAC,gBAAgB,CAAC,CAAC,CAAC;IACxE,iGAAiG;IACjG,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B;;;;OAIG;IACH,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,YAAY,KAAK,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"model-viewer.d.ts","sourceRoot":"","sources":["../src/model-viewer.ts"],"names":[],"mappings":"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"}
|
package/dist/model-viewer.js
CHANGED
|
@@ -55,7 +55,17 @@ export function createModelViewer(canvas, options = {}) {
|
|
|
55
55
|
const scopedScene = () => currentScene === null ? null : currentScope ? applyRenderScope(currentScene, currentScope) : currentScene;
|
|
56
56
|
// Monotonic token for last-wins overlapping setSource (stale async results are discarded).
|
|
57
57
|
let sourceToken = 0;
|
|
58
|
+
const autoFrameOnResize = options.autoFrameOnResize ?? true;
|
|
59
|
+
// True while the camera is library-framed; a user gesture (or setCameraState) hands it to the user.
|
|
60
|
+
let autoFraming = true;
|
|
58
61
|
let stage = null;
|
|
62
|
+
/** Fit the current model's box to the viewport (aspect + FOV aware); `keepDirection` keeps the view angle. */
|
|
63
|
+
const fit = (keepDirection = false) => {
|
|
64
|
+
const f = content.framing;
|
|
65
|
+
if (f === null || stage === null)
|
|
66
|
+
return;
|
|
67
|
+
stage.fitBox(f.sceneMin, f.sceneMax, keepDirection ? { direction: stage.viewDirection() } : {});
|
|
68
|
+
};
|
|
59
69
|
try {
|
|
60
70
|
stage = new InteractiveStage({
|
|
61
71
|
canvas,
|
|
@@ -65,6 +75,9 @@ export function createModelViewer(canvas, options = {}) {
|
|
|
65
75
|
createRenderer: options.createRenderer ?? ((c) => createDefaultGLRenderer(c, { alpha: true })),
|
|
66
76
|
...(options.createControls ? { createControls: options.createControls } : {}),
|
|
67
77
|
onCameraChanged: (state) => emit({ type: 'camera-changed', state }),
|
|
78
|
+
onInteractionStart: () => {
|
|
79
|
+
autoFraming = false;
|
|
80
|
+
},
|
|
68
81
|
onContextLost: () => emit({ type: 'context-lost' }),
|
|
69
82
|
onContextRestored: () => {
|
|
70
83
|
// GPU resources are gone; rebuild meshes from the retained scene and reframe, mirroring the
|
|
@@ -72,9 +85,8 @@ export function createModelViewer(canvas, options = {}) {
|
|
|
72
85
|
const shown = scopedScene();
|
|
73
86
|
if (shown !== null && stage !== null) {
|
|
74
87
|
content.setScene(shown);
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
stage.frameTo(f.center, f.radius);
|
|
88
|
+
if (autoFraming)
|
|
89
|
+
fit(true);
|
|
78
90
|
}
|
|
79
91
|
emit({ type: 'context-restored' });
|
|
80
92
|
}
|
|
@@ -124,9 +136,8 @@ export function createModelViewer(canvas, options = {}) {
|
|
|
124
136
|
const total = shown.objects.length;
|
|
125
137
|
progress({ stage: 'building-geometry', done: 0, total, unit: 'objects' });
|
|
126
138
|
content.setScene(shown, (done) => progress({ stage: 'building-geometry', done, total, unit: 'objects' }));
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
stage.frameTo(f.center, f.radius);
|
|
139
|
+
autoFraming = true;
|
|
140
|
+
fit();
|
|
130
141
|
progress({ stage: 'ready' });
|
|
131
142
|
emit({ type: 'ready', info });
|
|
132
143
|
return info;
|
|
@@ -144,11 +155,15 @@ export function createModelViewer(canvas, options = {}) {
|
|
|
144
155
|
setSource,
|
|
145
156
|
setView(view) {
|
|
146
157
|
stage?.setView(view);
|
|
158
|
+
// While library-framed, a preset view is fitted too (and stays fitted across resizes).
|
|
159
|
+
if (autoFraming)
|
|
160
|
+
fit(true);
|
|
147
161
|
},
|
|
148
162
|
getCameraState() {
|
|
149
163
|
return stage ? stage.getCameraState() : null;
|
|
150
164
|
},
|
|
151
165
|
setCameraState(state) {
|
|
166
|
+
autoFraming = false;
|
|
152
167
|
stage?.setCameraState(state);
|
|
153
168
|
},
|
|
154
169
|
setBackground(bg) {
|
|
@@ -160,11 +175,13 @@ export function createModelViewer(canvas, options = {}) {
|
|
|
160
175
|
},
|
|
161
176
|
resize(width, height) {
|
|
162
177
|
stage?.resize(width, height);
|
|
178
|
+
// A hidden (0×0) canvas has no aspect to fit; keep the pose and re-fit on the next real size.
|
|
179
|
+
if (autoFrameOnResize && autoFraming && width > 0 && height > 0)
|
|
180
|
+
fit(true);
|
|
163
181
|
},
|
|
164
182
|
frame() {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
stage?.frameTo(f.center, f.radius);
|
|
183
|
+
autoFraming = true;
|
|
184
|
+
fit();
|
|
168
185
|
},
|
|
169
186
|
setRenderScope(scope) {
|
|
170
187
|
currentScope = scope;
|
|
@@ -172,10 +189,8 @@ export function createModelViewer(canvas, options = {}) {
|
|
|
172
189
|
if (shown === null || stage === null)
|
|
173
190
|
return; // no source yet — applied on the next setSource
|
|
174
191
|
content.setScene(shown);
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
stage.frameTo(f.center, f.radius);
|
|
178
|
-
stage.render();
|
|
192
|
+
autoFraming = true;
|
|
193
|
+
fit();
|
|
179
194
|
},
|
|
180
195
|
capture(opts) {
|
|
181
196
|
if (stage === null) {
|
package/dist/stl.d.ts
CHANGED
|
@@ -2,4 +2,10 @@ import { type ModelScene } from './scene-model.js';
|
|
|
2
2
|
import { type ModelLimits } from './limits.js';
|
|
3
3
|
/** Parse STL bytes into a single-object {@link ModelScene}. Throws {@link ModelParseError} on failure. */
|
|
4
4
|
export declare function parseStl(source: Uint8Array | ArrayBuffer, limits?: ModelLimits): ModelScene;
|
|
5
|
+
/**
|
|
6
|
+
* STL facet normals are advisory and many exporters write `0 0 0` (or garbage). A zero/non-finite normal
|
|
7
|
+
* lights the facet black, so replace each degenerate one with the face normal from its winding; valid
|
|
8
|
+
* file normals are kept. A triangle with no area keeps a zero normal (it covers no pixels).
|
|
9
|
+
*/
|
|
10
|
+
export declare function repairFacetNormals(positions: Float32Array, normals: Float32Array): Float32Array;
|
|
5
11
|
//# sourceMappingURL=stl.d.ts.map
|
package/dist/stl.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stl.d.ts","sourceRoot":"","sources":["../src/stl.ts"],"names":[],"mappings":"AAQA,OAAO,EAAsD,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACvG,OAAO,EAAkC,KAAK,WAAW,EAAE,MAAM,aAAa,CAAC;AAgB/E,0GAA0G;AAC1G,wBAAgB,QAAQ,CAAC,MAAM,EAAE,UAAU,GAAG,WAAW,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,UAAU,
|
|
1
|
+
{"version":3,"file":"stl.d.ts","sourceRoot":"","sources":["../src/stl.ts"],"names":[],"mappings":"AAQA,OAAO,EAAsD,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACvG,OAAO,EAAkC,KAAK,WAAW,EAAE,MAAM,aAAa,CAAC;AAgB/E,0GAA0G;AAC1G,wBAAgB,QAAQ,CAAC,MAAM,EAAE,UAAU,GAAG,WAAW,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,UAAU,CA4D3F;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,YAAY,EAAE,OAAO,EAAE,YAAY,GAAG,YAAY,CA+B/F"}
|
package/dist/stl.js
CHANGED
|
@@ -54,7 +54,9 @@ export function parseStl(source, limits) {
|
|
|
54
54
|
}
|
|
55
55
|
const positions = new Float32Array(posAttr.array);
|
|
56
56
|
const normAttr = geom.getAttribute('normal');
|
|
57
|
-
const normals = normAttr !== undefined
|
|
57
|
+
const normals = normAttr !== undefined
|
|
58
|
+
? repairFacetNormals(positions, new Float32Array(normAttr.array))
|
|
59
|
+
: undefined;
|
|
58
60
|
const bounds = boundsOf(positions);
|
|
59
61
|
geom.dispose();
|
|
60
62
|
const geometry = normals !== undefined ? { positions, normals } : { positions };
|
|
@@ -70,6 +72,45 @@ export function parseStl(source, limits) {
|
|
|
70
72
|
}
|
|
71
73
|
};
|
|
72
74
|
}
|
|
75
|
+
/**
|
|
76
|
+
* STL facet normals are advisory and many exporters write `0 0 0` (or garbage). A zero/non-finite normal
|
|
77
|
+
* lights the facet black, so replace each degenerate one with the face normal from its winding; valid
|
|
78
|
+
* file normals are kept. A triangle with no area keeps a zero normal (it covers no pixels).
|
|
79
|
+
*/
|
|
80
|
+
export function repairFacetNormals(positions, normals) {
|
|
81
|
+
for (let t = 0; t + 8 < normals.length && t + 8 < positions.length; t += 9) {
|
|
82
|
+
const nx = normals[t];
|
|
83
|
+
const ny = normals[t + 1];
|
|
84
|
+
const nz = normals[t + 2];
|
|
85
|
+
const len = Math.hypot(nx, ny, nz);
|
|
86
|
+
if (Number.isFinite(len) && len > 1e-6)
|
|
87
|
+
continue;
|
|
88
|
+
const ax = positions[t + 3] - positions[t];
|
|
89
|
+
const ay = positions[t + 4] - positions[t + 1];
|
|
90
|
+
const az = positions[t + 5] - positions[t + 2];
|
|
91
|
+
const bx = positions[t + 6] - positions[t];
|
|
92
|
+
const by = positions[t + 7] - positions[t + 1];
|
|
93
|
+
const bz = positions[t + 8] - positions[t + 2];
|
|
94
|
+
let cx = ay * bz - az * by;
|
|
95
|
+
let cy = az * bx - ax * bz;
|
|
96
|
+
let cz = ax * by - ay * bx;
|
|
97
|
+
const cl = Math.hypot(cx, cy, cz);
|
|
98
|
+
if (cl > 0) {
|
|
99
|
+
cx /= cl;
|
|
100
|
+
cy /= cl;
|
|
101
|
+
cz /= cl;
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
cx = cy = cz = 0;
|
|
105
|
+
}
|
|
106
|
+
for (let v = 0; v < 9; v += 3) {
|
|
107
|
+
normals[t + v] = cx;
|
|
108
|
+
normals[t + v + 1] = cy;
|
|
109
|
+
normals[t + v + 2] = cz;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return normals;
|
|
113
|
+
}
|
|
73
114
|
function boundsOf(positions) {
|
|
74
115
|
let minX = Infinity;
|
|
75
116
|
let minY = Infinity;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chestnutlabs/gcode-model-renderer",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.0",
|
|
4
4
|
"description": "Three.js presentation renderer for source models (STL/3MF) — clean thumbnails/cards, distinct from toolpath inspection (DD-018).",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"3d-printing",
|
|
@@ -44,7 +44,9 @@
|
|
|
44
44
|
}
|
|
45
45
|
},
|
|
46
46
|
"files": [
|
|
47
|
-
"dist"
|
|
47
|
+
"dist",
|
|
48
|
+
"LICENSE",
|
|
49
|
+
"NOTICE.md"
|
|
48
50
|
],
|
|
49
51
|
"publishConfig": {
|
|
50
52
|
"access": "public"
|
|
@@ -55,9 +57,9 @@
|
|
|
55
57
|
"test": "vitest run"
|
|
56
58
|
},
|
|
57
59
|
"dependencies": {
|
|
58
|
-
"@chestnutlabs/gcode-containers": "0.
|
|
59
|
-
"@chestnutlabs/gcode-renderer-three": "0.
|
|
60
|
-
"@chestnutlabs/toolpath-core": "0.
|
|
60
|
+
"@chestnutlabs/gcode-containers": "0.21.0",
|
|
61
|
+
"@chestnutlabs/gcode-renderer-three": "0.21.0",
|
|
62
|
+
"@chestnutlabs/toolpath-core": "0.21.0"
|
|
61
63
|
},
|
|
62
64
|
"devDependencies": {
|
|
63
65
|
"@types/three": "0.178.0"
|