partforge 0.53.0 → 0.54.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/docs/AUTHORING-PARTS.md +12 -2
- package/package.json +1 -1
- package/src/framework/app.css +27 -2
- package/src/framework/cutaway-controls.js +4 -27
- package/src/framework/cutaway-gizmo-scene.js +3 -1
- package/src/framework/measure/dim3-place.js +455 -0
- package/src/framework/measure/dim3-scene.js +438 -0
- package/src/framework/measure/feature-dims.js +258 -0
- package/src/framework/measure/measure-controls.js +110 -0
- package/src/framework/measure/measure-mode.js +546 -0
- package/src/framework/measure/param-link.js +32 -0
- package/src/framework/measure/pins.js +36 -0
- package/src/framework/mount.js +58 -5
- package/src/framework/panel/render.js +41 -0
- package/src/framework/selection/drag-tracker.js +39 -0
- package/src/framework/selection/feature-highlight.js +99 -0
- package/src/framework/selection/hover.js +14 -90
- package/src/framework/selection/index.js +1 -0
- package/src/framework/selection/pick.js +11 -37
- package/src/framework/teardown.js +29 -0
- package/src/framework/viewer.js +25 -1
- package/types/index.d.ts +16 -0
package/src/framework/viewer.js
CHANGED
|
@@ -39,13 +39,15 @@ export function srgbEncodeInPlace(data) {
|
|
|
39
39
|
// `renderer.renderOffscreen(pose)` does the GL work (temp camera → offscreen
|
|
40
40
|
// target → readback → JPEG data URL); injected so this is unit-testable without
|
|
41
41
|
// a GL context. The grid is hidden for the whole synchronous pass and restored.
|
|
42
|
-
export function captureViewsFromScene(viewNames, { renderer, liveCamera, grid, bounds }) {
|
|
42
|
+
export function captureViewsFromScene(viewNames, { renderer, liveCamera, grid, bounds, hidden = [] }) {
|
|
43
43
|
const views = (viewNames?.length ? viewNames : ["iso", "front", "top"])
|
|
44
44
|
.filter((v) => CANONICAL_VIEWS.includes(v))
|
|
45
45
|
.slice(0, CANONICAL_VIEWS.length);
|
|
46
46
|
const before = liveCamera.position.clone();
|
|
47
47
|
const gridWasVisible = grid?.visible;
|
|
48
48
|
if (grid) grid.visible = false;
|
|
49
|
+
const hiddenWas = hidden.map((o) => o.visible);
|
|
50
|
+
for (const o of hidden) o.visible = false;
|
|
49
51
|
try {
|
|
50
52
|
return views.map((view) => ({
|
|
51
53
|
view,
|
|
@@ -53,6 +55,7 @@ export function captureViewsFromScene(viewNames, { renderer, liveCamera, grid, b
|
|
|
53
55
|
}));
|
|
54
56
|
} finally {
|
|
55
57
|
if (grid) grid.visible = gridWasVisible;
|
|
58
|
+
hidden.forEach((o, i) => { o.visible = hiddenWas[i]; });
|
|
56
59
|
liveCamera.position.copy(before); // belt-and-suspenders: never leak camera state
|
|
57
60
|
}
|
|
58
61
|
}
|
|
@@ -139,6 +142,10 @@ export function createViewer(container, part) {
|
|
|
139
142
|
};
|
|
140
143
|
scene.background = new THREE.Color(THEME.dark.bg);
|
|
141
144
|
|
|
145
|
+
let currentTheme = "dark";
|
|
146
|
+
const themeListeners = new Set();
|
|
147
|
+
function onThemeChange(cb) { themeListeners.add(cb); return () => themeListeners.delete(cb); }
|
|
148
|
+
|
|
142
149
|
const camera = new THREE.PerspectiveCamera(45, 1, 0.1, 1000);
|
|
143
150
|
camera.position.set(18, 12, 18);
|
|
144
151
|
|
|
@@ -536,6 +543,8 @@ export function createViewer(container, part) {
|
|
|
536
543
|
for (const m of fadeLineMats.values()) m.color.set(t.line); // clones follow the theme
|
|
537
544
|
cutaway.setTheme(mode, t.line);
|
|
538
545
|
reassertLiveFades(); // setTheme re-clones every section's materials and reassigns them
|
|
546
|
+
currentTheme = THEME[mode] ? mode : "dark";
|
|
547
|
+
for (const cb of [...themeListeners]) cb(currentTheme);
|
|
539
548
|
}
|
|
540
549
|
|
|
541
550
|
function hideAssembly() {
|
|
@@ -640,6 +649,15 @@ export function createViewer(container, part) {
|
|
|
640
649
|
return canvas.toDataURL("image/jpeg", quality);
|
|
641
650
|
}
|
|
642
651
|
|
|
652
|
+
// Objects excluded from CANONICAL captures only (agent renders must stay
|
|
653
|
+
// dimension-free); captureCurrent — the user-framed showcase capture —
|
|
654
|
+
// deliberately does NOT consult this set.
|
|
655
|
+
const canonicalCaptureHidden = new Set();
|
|
656
|
+
function registerCanonicalCaptureHidden(obj) {
|
|
657
|
+
canonicalCaptureHidden.add(obj);
|
|
658
|
+
return () => canonicalCaptureHidden.delete(obj);
|
|
659
|
+
}
|
|
660
|
+
|
|
643
661
|
// Render the canonical camera angles offscreen, framed to whatever is visible,
|
|
644
662
|
// without disturbing the user's live view. Returns [{ view, dataUrl }].
|
|
645
663
|
function captureCanonicalViews(viewNames) {
|
|
@@ -653,6 +671,7 @@ export function createViewer(container, part) {
|
|
|
653
671
|
renderer: { renderOffscreen },
|
|
654
672
|
liveCamera: camera,
|
|
655
673
|
grid,
|
|
674
|
+
hidden: [...canonicalCaptureHidden],
|
|
656
675
|
bounds: { center, radius },
|
|
657
676
|
});
|
|
658
677
|
}
|
|
@@ -877,6 +896,8 @@ export function createViewer(container, part) {
|
|
|
877
896
|
controls.removeEventListener("start", onControlsStart);
|
|
878
897
|
cameraStartListeners.clear();
|
|
879
898
|
frameListeners.clear();
|
|
899
|
+
themeListeners.clear();
|
|
900
|
+
canonicalCaptureHidden.clear();
|
|
880
901
|
camTween.cancel();
|
|
881
902
|
controls.dispose();
|
|
882
903
|
for (const t of flashTimers) clearTimeout(t);
|
|
@@ -930,6 +951,8 @@ export function createViewer(container, part) {
|
|
|
930
951
|
setActive,
|
|
931
952
|
onContextLost,
|
|
932
953
|
setTheme,
|
|
954
|
+
onThemeChange,
|
|
955
|
+
getTheme: () => currentTheme,
|
|
933
956
|
getCameraState,
|
|
934
957
|
setCameraState,
|
|
935
958
|
onCameraEnd,
|
|
@@ -946,6 +969,7 @@ export function createViewer(container, part) {
|
|
|
946
969
|
resetCutaway: cutaway.reset,
|
|
947
970
|
isWorldPointVisible: cutaway.isPointVisible,
|
|
948
971
|
registerCutawayMaterial: cutaway.registerClippableMaterial,
|
|
972
|
+
registerCanonicalCaptureHidden,
|
|
949
973
|
onCutawayHandleHover: cutaway.onHandleHoverChange,
|
|
950
974
|
dispose,
|
|
951
975
|
};
|
package/types/index.d.ts
CHANGED
|
@@ -87,6 +87,7 @@ export interface MountElements {
|
|
|
87
87
|
reframe?: HTMLElement | null;
|
|
88
88
|
theme?: HTMLElement | null;
|
|
89
89
|
cutaway?: HTMLElement | null;
|
|
90
|
+
measure?: HTMLElement | null;
|
|
90
91
|
railToggle?: HTMLElement | null;
|
|
91
92
|
};
|
|
92
93
|
}
|
|
@@ -142,6 +143,19 @@ export interface CaptureViewOptions {
|
|
|
142
143
|
angle?: CanonicalView | string;
|
|
143
144
|
}
|
|
144
145
|
|
|
146
|
+
/**
|
|
147
|
+
* Measurement mode runtime controls. Dimensioned captures come straight from
|
|
148
|
+
* `captureCurrent()` while the mode is enabled — in-scene dims render into
|
|
149
|
+
* the frame natively (canonical-view captures and thumbnails never include
|
|
150
|
+
* them).
|
|
151
|
+
*/
|
|
152
|
+
export interface MeasureRuntime {
|
|
153
|
+
isEnabled(): boolean;
|
|
154
|
+
setEnabled(on: boolean): void;
|
|
155
|
+
clearPins(): void;
|
|
156
|
+
pinCount(): number;
|
|
157
|
+
}
|
|
158
|
+
|
|
145
159
|
/** Where playback is: idle, swinging the camera to an intro cue, playing, or paused. */
|
|
146
160
|
export type AnimationStatus = "idle" | "intro" | "playing" | "paused";
|
|
147
161
|
|
|
@@ -253,6 +267,8 @@ export interface PartRuntime {
|
|
|
253
267
|
* active view has none, where `state().animation` reads `null`.
|
|
254
268
|
*/
|
|
255
269
|
animation: AnimationRuntime | null;
|
|
270
|
+
/** Measurement mode's runtime-controls API — mode on/off, unit, and pin state; dimensioned captures come from `captureCurrent()` while enabled. Always present (a no-op stand-in outside `makeHandle` tests). */
|
|
271
|
+
measure: MeasureRuntime;
|
|
256
272
|
}
|
|
257
273
|
|
|
258
274
|
/** Mount a full parametric-part app from a `PartDefinition`. */
|