partforge 0.25.0 → 0.26.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "partforge",
3
- "version": "0.25.0",
3
+ "version": "0.26.1",
4
4
  "description": "Turn a declarative part definition into a parametric-CAD web app (three.js + Manifold/Replicad). Requires a Vite-based consumer.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -12,7 +12,16 @@
12
12
  // capability (Manifold can't do toSTEP; both backends now define shape2d, so
13
13
  // that stub is dead in practice — kept as a safety net for a future backend).
14
14
  // The per-Solid twin of this layer is addSugar() in solid-sugar.js.
15
- import * as opentype from "opentype.js";
15
+ // opentype.js 2.x ships no `exports` map: bundlers take its `module` field (real
16
+ // ESM, named `parse`), while Node ESM takes `main` (a UMD/CJS bundle, whose named
17
+ // exports Node cannot statically detect — the namespace holds only `default`).
18
+ // So `import * as opentype` gives a working `.parse` in the browser and `undefined`
19
+ // under Node, which broke every headless text2d build (`opentype.parse is not a
20
+ // function`) while the browser stayed green. Normalize both interop shapes here.
21
+ import * as opentypeNamespace from "opentype.js";
22
+ const opentype = typeof opentypeNamespace.parse === "function"
23
+ ? opentypeNamespace
24
+ : (opentypeNamespace.default ?? opentypeNamespace);
16
25
  import { KernelCapabilityError } from "./errors.js";
17
26
  import { isPlainOptions, KERNEL_OP_SPECS } from "./op-options.js";
18
27
  import { textGlyphs } from "./text2d.js";
@@ -5,11 +5,22 @@ import { bounds } from "./mesh.js";
5
5
 
6
6
  // Canonical view directions in MODEL space (Z-up). `dir` is the direction from
7
7
  // the part centre toward the camera; `up` is the camera up vector.
8
- const ANGLES = {
9
- iso: { dir: [1, 1, 1], up: [0, 0, 1] },
10
- front: { dir: [0, -1, 0], up: [0, 0, 1] },
11
- top: { dir: [0, 0, 1], up: [0, 1, 0] },
8
+ //
9
+ // The same seven cameras as the viewer's framework/view-angles.js, which states
10
+ // them in the viewer's Y-up WORLD space (the pivot rotates the Z-up model into
11
+ // it). The two tables are related by model (x, y, z) → world (x, z, -y), and
12
+ // test/render-angles.test.js holds them to that — an agent asking for `left`
13
+ // through the CLI and through the browser must be shown the same face.
14
+ export const RENDER_ANGLES = {
15
+ iso: { dir: [1, -1, 1], up: [0, 0, 1] },
16
+ front: { dir: [0, -1, 0], up: [0, 0, 1] },
17
+ back: { dir: [0, 1, 0], up: [0, 0, 1] },
18
+ top: { dir: [0, 0, 1], up: [0, 1, 0] },
19
+ bottom: { dir: [0, 0, -1], up: [0, -1, 0] },
20
+ left: { dir: [-1, 0, 0], up: [0, 0, 1] },
21
+ right: { dir: [1, 0, 0], up: [0, 0, 1] },
12
22
  };
23
+ export const RENDER_VIEWS = Object.keys(RENDER_ANGLES);
13
24
 
14
25
  const slug = (s) => String(s).toLowerCase().replace(/\s+/g, "-");
15
26
  const sub = (a, b) => [a[0] - b[0], a[1] - b[1], a[2] - b[2]];
@@ -40,7 +51,6 @@ export async function renderViews(kernel, part, view = Object.keys(part.views)[0
40
51
  const radius = Math.max(hi[0] - lo[0], hi[1] - lo[1], hi[2] - lo[2]) / 2 || 5;
41
52
 
42
53
  const bg = [0x15, 0x18, 0x1d], base = [0x9f, 0xb4, 0xcc], edgeColor = [0x1c, 0x23, 0x2d];
43
- const light = norm([0.4, 0.5, 0.8]); // world-space key direction (toward the light)
44
54
  const ambient = 0.35, diffuse = 0.75;
45
55
  const bias = radius * 0.02; // edge depth bias so visible edges win ties
46
56
 
@@ -49,10 +59,16 @@ export async function renderViews(kernel, part, view = Object.keys(part.views)[0
49
59
  const written = [];
50
60
 
51
61
  for (const angle of views) {
52
- const a = ANGLES[angle];
53
- if (!a) throw new Error(`unknown angle "${angle}" (use: ${Object.keys(ANGLES).join(", ")})`);
62
+ const a = RENDER_ANGLES[angle];
63
+ if (!a) throw new Error(`unknown angle "${angle}" (use: ${RENDER_VIEWS.join(", ")})`);
54
64
  // orthographic camera basis: zc toward camera, xc right, yc up
55
65
  const zc = norm(a.dir), xc = norm(cross(a.up, zc)), yc = cross(zc, xc);
66
+ // Key direction (toward the light), placed over the viewer's shoulder — up and to
67
+ // the right of the view axis — so every angle is lit and shaded. A world-fixed key
68
+ // would leave whichever face the camera happens to be looking at in flat ambient:
69
+ // that is what made `bottom` a featureless disc, and the browser viewer's offscreen
70
+ // captures had the same bug (framework/viewer-lighting.js captureLightPoses).
71
+ const light = norm([0, 1, 2].map((i) => zc[i] + 0.45 * xc[i] + 0.75 * yc[i]));
56
72
  const ppu = Math.min(W, H) / (2 * radius * 1.25); // pixels per mm (uniform; margin)
57
73
  const project = (p) => {
58
74
  const r = sub(p, center);
package/src/testing.js CHANGED
@@ -12,7 +12,7 @@ export { bootOcctKernel } from "./testing/occt.js";
12
12
  export { meshVolume, bboxSize } from "./testing/mesh.js";
13
13
  export { buildView } from "./testing/build.js";
14
14
  export { measure } from "./testing/measure.js";
15
- export { renderViews } from "./testing/render.js";
15
+ export { renderViews, RENDER_VIEWS } from "./testing/render.js";
16
16
  export { verify } from "./testing/verify.js";
17
17
  export { buildBVH } from "./testing/bvh.js";
18
18
  export { minWall } from "./testing/min-wall.js";