partforge 0.138.2 → 0.139.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
CHANGED
|
@@ -3013,8 +3013,9 @@ instead of (or in addition to) the built-in `#part` bar:
|
|
|
3013
3013
|
Resolves `null` on failure rather than throwing (a build error, a part with no sub-parts
|
|
3014
3014
|
in that view, a disposed runtime). The render happens in a throwaway scene, so it takes
|
|
3015
3015
|
no colour from the viewer's light/dark theme. The default `style` is `"thumbnail"`, the
|
|
3016
|
-
product shot: a fixed light background, soft lights, lighter edges, a contact shadow
|
|
3017
|
-
|
|
3016
|
+
product shot: a fixed light background, soft lights, lighter edges, a contact shadow,
|
|
3017
|
+
4:3 framing (`size` is the width: 640 → 640×480, the shape of the cards it is shown in;
|
|
3018
|
+
`cad` stays square) and fit framing — a thumbnail is captured once and then displayed under host chrome
|
|
3018
3019
|
partforge cannot see. `style: "cad"` gives the agent-render look instead. `style.view` is
|
|
3019
3020
|
only a default angle; an explicit `angle` wins. Pass `background` (any
|
|
3020
3021
|
`THREE.Color`-compatible value) to override the style's, or `background: null` for no
|
package/package.json
CHANGED
|
@@ -45,7 +45,11 @@ const STYLES = {
|
|
|
45
45
|
view: null,
|
|
46
46
|
},
|
|
47
47
|
thumbnail: {
|
|
48
|
-
|
|
48
|
+
// aspect (width / height) is the shape of the card the product shot is
|
|
49
|
+
// shown in: 4:3 across the cloud's dashboard and library, so the shape-fitted
|
|
50
|
+
// framing fills the card exactly instead of being cropped by object-cover.
|
|
51
|
+
// A style without one (cad) is square.
|
|
52
|
+
camera: { projection: "perspective", fov: 45, framing: "fit", fill: 0.88, aspect: 4 / 3 },
|
|
49
53
|
lights: {
|
|
50
54
|
hemisphere: { ...CAD_LIGHTS.hemisphere, intensity: 1.65 },
|
|
51
55
|
key: { ...CAD_LIGHTS.key, intensity: 1.25 },
|
|
@@ -71,6 +75,7 @@ export function validateRenderStyle(s) {
|
|
|
71
75
|
check(inRange(s.camera.fov, 1, 120), "camera.fov");
|
|
72
76
|
check(s.camera.framing === "canonical" || s.camera.framing === "fit", "camera.framing");
|
|
73
77
|
if (s.camera.framing === "fit") check(inRange(s.camera.fill, 0.05, 1), "camera.fill");
|
|
78
|
+
if (s.camera.aspect !== undefined) check(inRange(s.camera.aspect, 0.25, 4), "camera.aspect");
|
|
74
79
|
const h = s.lights?.hemisphere;
|
|
75
80
|
check(isHex(h?.sky) && isHex(h?.ground) && inRange(h?.intensity, 0, 20), "lights.hemisphere");
|
|
76
81
|
for (const k of ["key", "fill"]) {
|
package/src/framework/viewer.js
CHANGED
|
@@ -2228,7 +2228,11 @@ export function createViewer(container, part) {
|
|
|
2228
2228
|
const worldPositions = payloads.map((p) => modelToWorld(p.positions));
|
|
2229
2229
|
const points = new Float32Array(worldPositions.reduce((n, a) => n + a.length, 0));
|
|
2230
2230
|
worldPositions.reduce((o, a) => { points.set(a, o); return o + a.length; }, 0);
|
|
2231
|
-
|
|
2231
|
+
// `size` is the long (width) edge; the style's aspect (the thumbnail's 4:3
|
|
2232
|
+
// card shape) sets the height, and the fit frames for that same aspect.
|
|
2233
|
+
const width = size;
|
|
2234
|
+
const height = Math.max(1, Math.round(size / (st.camera.aspect ?? 1)));
|
|
2235
|
+
const { pose, fov, sceneBounds } = stylePose(st, angle, box, { aspect: width / height, points });
|
|
2232
2236
|
|
|
2233
2237
|
// Light the throwaway scene ourselves: renderOffscreen's own key/fill (and the
|
|
2234
2238
|
// persistent hemisphere) live in the LIVE scene, which is never rendered here — so
|
|
@@ -2272,7 +2276,7 @@ export function createViewer(container, part) {
|
|
|
2272
2276
|
pose,
|
|
2273
2277
|
// The throwaway scene holds these meshes and nothing else — no grid, no
|
|
2274
2278
|
// gizmo — so its own bounds are the whole of what the planes must hold.
|
|
2275
|
-
{ width
|
|
2279
|
+
{ width, height, fov, quality, sceneBounds },
|
|
2276
2280
|
tmpScene,
|
|
2277
2281
|
);
|
|
2278
2282
|
} finally {
|
package/src/testing/render.js
CHANGED
|
@@ -50,11 +50,15 @@ export function renderAngle(name) {
|
|
|
50
50
|
// part still occludes what is behind it (real transparency needs a depth sort
|
|
51
51
|
// this renderer does not do; stills only need to read as faded).
|
|
52
52
|
export async function renderViewImages(kernel, part, view = Object.keys(part.views)[0], {
|
|
53
|
-
views = ["iso", "front", "top"], size
|
|
53
|
+
views = ["iso", "front", "top"], size, edges = true, params = {}, opacity = {},
|
|
54
54
|
style = "cad", supersample = 2,
|
|
55
55
|
} = {}) {
|
|
56
56
|
const { PNG } = await import("pngjs");
|
|
57
|
-
getRenderStyle(style); // a bad style fails here, before the kernel build
|
|
57
|
+
const resolved = getRenderStyle(style); // a bad style fails here, before the kernel build
|
|
58
|
+
// No size given: a style with its own aspect (the thumbnail's 4:3) sets the
|
|
59
|
+
// shape — 640 wide, the viewer's thumbnail size — else the 800×600 default.
|
|
60
|
+
// An explicit size is the caller's choice and is honoured as given.
|
|
61
|
+
if (!size) size = resolved.camera.aspect ? [640, Math.round(640 / resolved.camera.aspect)] : [800, 600];
|
|
58
62
|
for (const angle of views) {
|
|
59
63
|
if (!renderAngle(angle)) throw new Error(`unknown angle "${angle}" (use: ${RENDER_VIEWS.join(", ")}, or a view-cube orientation such as top-front-left)`);
|
|
60
64
|
}
|
package/types/index.d.ts
CHANGED
|
@@ -420,7 +420,7 @@ export interface CaptureCurrentOptions {
|
|
|
420
420
|
}
|
|
421
421
|
|
|
422
422
|
export interface CaptureViewOptions {
|
|
423
|
-
/**
|
|
423
|
+
/** Render width in px (the long edge). The style's aspect sets the height: `"thumbnail"` is 4:3 (640 → 640×480), `"cad"` is square. Default 640. */
|
|
424
424
|
size?: number;
|
|
425
425
|
/** JPEG quality, 0..1. Default 0.8. */
|
|
426
426
|
quality?: number;
|
package/types/testing.d.ts
CHANGED
|
@@ -678,6 +678,7 @@ export function renderViews(
|
|
|
678
678
|
opts?: {
|
|
679
679
|
views?: string[];
|
|
680
680
|
out?: string;
|
|
681
|
+
/** [width, height] px. Default: 800×600, or 640 wide at the style's aspect when it has one (thumbnail → 640×480). */
|
|
681
682
|
size?: [number, number];
|
|
682
683
|
edges?: boolean;
|
|
683
684
|
params?: ResolvedParams;
|
|
@@ -708,6 +709,7 @@ export function renderViewImages(
|
|
|
708
709
|
view?: string,
|
|
709
710
|
opts?: {
|
|
710
711
|
views?: string[];
|
|
712
|
+
/** [width, height] px. Default: 800×600, or 640 wide at the style's aspect when it has one (thumbnail → 640×480). */
|
|
711
713
|
size?: [number, number];
|
|
712
714
|
edges?: boolean;
|
|
713
715
|
params?: ResolvedParams;
|