partforge 0.91.0 → 0.93.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.
Files changed (38) hide show
  1. package/bin/cli.js +6 -3
  2. package/docs/AUTHORING-PARTS.md +184 -1
  3. package/docs/ERROR-PATTERNS.md +97 -0
  4. package/docs/KERNEL-CONTRACT.md +1 -0
  5. package/docs/VECTOR-FORMAT.md +737 -0
  6. package/package.json +9 -1
  7. package/src/app-emblem.js +15 -0
  8. package/src/emblem-worker.js +3 -0
  9. package/src/framework/asset-resolve.js +5 -4
  10. package/src/framework/geometry/arc-fit.js +146 -0
  11. package/src/framework/geometry/contour-offset.js +5 -0
  12. package/src/framework/geometry/curve-fill.js +57 -7
  13. package/src/framework/geometry/kernel-front.js +46 -0
  14. package/src/framework/geometry/kernel.js +1 -1
  15. package/src/framework/geometry/probe.js +1 -1
  16. package/src/framework/geometry/stroke-outline.js +119 -0
  17. package/src/framework/geometry/vector-format.js +334 -0
  18. package/src/framework/geometry/vector2d.js +96 -0
  19. package/src/framework/ingest/svg-ingest.js +212 -0
  20. package/src/framework/jobs.js +11 -0
  21. package/src/framework/lint/index.js +28 -3
  22. package/src/framework/lint/rules-vector.js +112 -0
  23. package/src/framework/mount.js +30 -2
  24. package/src/framework/pick-flash.js +31 -0
  25. package/src/framework/selection/pick.js +16 -1
  26. package/src/framework/vectors.js +170 -0
  27. package/src/framework/viewer.js +106 -7
  28. package/src/framework/worker.js +46 -1
  29. package/src/ingest.js +8 -0
  30. package/src/parts/assets/emblem.svg +10 -0
  31. package/src/parts/assets/emblem.vector.json +110 -0
  32. package/src/parts/assets/plate.vector.json +27 -0
  33. package/src/parts/emblem.js +102 -0
  34. package/src/testing/manifold.js +3 -1
  35. package/src/testing/occt.js +3 -1
  36. package/types/index.d.ts +22 -0
  37. package/types/ingest.d.ts +118 -0
  38. package/types/kernel.d.ts +28 -0
@@ -0,0 +1,118 @@
1
+ // partforge/ingest — SVG -> the partforge-vector JSON format (docs/VECTOR-FORMAT.md).
2
+ //
3
+ // DOM-dependent and main-thread-only: this entry is deliberately NOT reachable
4
+ // from the geometry worker, and is never re-exported from the main entry or
5
+ // from `partforge/geometry`. A host runs it once per artwork, in a browser,
6
+ // and stores the result — the same division of labour as `fontCatalog`.
7
+ // partforge does not write files.
8
+ //
9
+ // These declarations describe the FORMAT, not just what `ingestSvg` happens to
10
+ // emit — the same documents are hand-authored, so `bbox` and `source` are
11
+ // optional here even though ingest always writes both.
12
+
13
+ /** Coordinate meaning. `"mm"` places as authored; `"artwork"` requires a size at every call site. */
14
+ export type VectorUnits = "mm" | "artwork";
15
+
16
+ /** Whether a shape adds material to the composed result or is cut from it. `"add"` is the default. */
17
+ export type VectorRole = "add" | "subtract";
18
+
19
+ /** The document's tight bounding box. A cache, not an authority — placement recomputes it. */
20
+ export interface VectorBbox {
21
+ minX: number;
22
+ minY: number;
23
+ maxX: number;
24
+ maxY: number;
25
+ }
26
+
27
+ /** A parsed `partforge-vector` JSON document — see docs/VECTOR-FORMAT.md. */
28
+ export interface VectorDocument {
29
+ format: "partforge-vector";
30
+ version: number;
31
+ units: VectorUnits;
32
+ /** Free text; ignored on load. Ingest writes the format's own one-paragraph summary. */
33
+ note?: string;
34
+ /** Provenance only — typically the original filename. Not validated or used at load/build time. */
35
+ source?: string | null;
36
+ /** Optional: an author need not compute analytic curve extrema, but a stale value is a named error. */
37
+ bbox?: VectorBbox;
38
+ /** Name -> shape. At least one shape, and at least one of them must have role `"add"`. */
39
+ shapes: Record<string, VectorShape>;
40
+ }
41
+
42
+ /**
43
+ * A named shape: either a bare region list (role `"add"`) or `{ role, regions }`.
44
+ * Both forms exist because `"add"` is an honest default — a painted region adds
45
+ * material, which is what every file written before roles existed already meant.
46
+ */
47
+ export type VectorShape = VectorRegion[] | { role?: VectorRole; regions: VectorRegion[] };
48
+
49
+ /** One filled region: an `outer` boundary with `holes` subtracted from it. */
50
+ export interface VectorRegion {
51
+ outer: VectorContour;
52
+ holes?: VectorContour[];
53
+ }
54
+
55
+ /** One closed contour: the explicit `"path"` form, or one of the three primitives. */
56
+ export type VectorContour = VectorPath | VectorCircle | VectorRect | VectorPolygon;
57
+
58
+ /**
59
+ * Segments run head-to-tail from `start`, and the contour closes IMPLICITLY from
60
+ * the last segment's `to` back to `start`. At least one segment, and at least two
61
+ * if they are all `"line"` — a single straight edge and its closure are the same
62
+ * line, so they bound nothing, while a single `"arc"` or `"cubic"` bounds area
63
+ * against the closing chord.
64
+ */
65
+ export interface VectorPath {
66
+ kind: "path";
67
+ start: [number, number];
68
+ segments: VectorSegment[];
69
+ }
70
+
71
+ /** Expands to two 180-degree arcs, counter-clockwise. */
72
+ export interface VectorCircle {
73
+ kind: "circle";
74
+ center: [number, number];
75
+ r: number;
76
+ }
77
+
78
+ /** Axis-aligned, counter-clockwise. `radius` rounds all four corners; at most half the shorter side. */
79
+ export interface VectorRect {
80
+ kind: "rect";
81
+ center: [number, number];
82
+ width: number;
83
+ height: number;
84
+ radius?: number;
85
+ }
86
+
87
+ /** At least 3 points, joined by straight edges in the author's own order. */
88
+ export interface VectorPolygon {
89
+ kind: "polygon";
90
+ points: Array<[number, number]>;
91
+ }
92
+
93
+ export type VectorSegment =
94
+ | { kind: "line"; to: [number, number] }
95
+ | { kind: "arc"; to: [number, number]; through: [number, number] }
96
+ | { kind: "cubic"; to: [number, number]; c1: [number, number]; c2: [number, number] };
97
+
98
+ export interface IngestSvgOptions {
99
+ /**
100
+ * `"outline"` (default) turns strokes into filled geometry; `"ignore"` drops
101
+ * stroke geometry entirely and keeps only fills. There is no equivalent
102
+ * option on `k.vector2d` — once ingested, there is no stroke left to ignore.
103
+ */
104
+ strokes?: "outline" | "ignore";
105
+ /** Provenance only — typically the original filename. Stored verbatim as `source`; not validated or used at load/build time. */
106
+ source?: string | null;
107
+ }
108
+
109
+ /**
110
+ * Convert an SVG document (as text) into the `partforge-vector` JSON format a
111
+ * part's `k.vector2d()` calls can place. DOM-required — call this in a browser,
112
+ * store the returned document (e.g. as `<name>.vector.json` beside the part),
113
+ * and reference it from the part's `vectors` field. The result is always one
114
+ * shape named `"artwork"` in `"artwork"` units, with `bbox` and `source` written.
115
+ * Throws if the SVG can't be parsed, or if it contains no painted geometry (every
116
+ * element is `fill="none"` with no stroke, hidden, or empty).
117
+ */
118
+ export function ingestSvg(svgText: string, opts?: IngestSvgOptions): VectorDocument;
package/types/kernel.d.ts CHANGED
@@ -455,6 +455,29 @@ export interface Text2dOptions {
455
455
  kerning?: boolean;
456
456
  }
457
457
 
458
+ /** Horizontal alignment of a `vector2d` placement. */
459
+ export type Vector2dAlign = "center" | "left" | "right";
460
+ /** Vertical alignment of a `vector2d` placement. */
461
+ export type Vector2dValign = "middle" | "top" | "bottom";
462
+
463
+ export interface Vector2dOptions {
464
+ /**
465
+ * Name of one shape in the file, returned whatever its `role`. Omit for the
466
+ * composed result: every `"add"` shape unioned, minus every `"subtract"` one.
467
+ */
468
+ shape?: string;
469
+ /** Target width in mm. At most one of `width`/`height`/`fit`; required for `units: "artwork"`. */
470
+ width?: number;
471
+ /** Target height in mm. At most one of `width`/`height`/`fit`; required for `units: "artwork"`. */
472
+ height?: number;
473
+ /** Target size in mm for the larger extent. At most one of `width`/`height`/`fit`; required for `units: "artwork"`. */
474
+ fit?: number;
475
+ /** Defaults to `"center"` for `units: "artwork"`, and to no horizontal translate for `units: "mm"`. */
476
+ align?: Vector2dAlign;
477
+ /** Defaults to `"middle"` for `units: "artwork"`, and to no vertical translate for `units: "mm"`. */
478
+ valign?: Vector2dValign;
479
+ }
480
+
458
481
  /** Anything `k.hull`/`k.hullChain` accepts as one input. */
459
482
  export type HullInput = Shape2D | Contour;
460
483
 
@@ -501,6 +524,11 @@ export interface GeometryKernel {
501
524
  shape2d(profile: ProfileInput): Shape2D;
502
525
  /** Render outline-font text as a `Shape2D`. */
503
526
  text2d(string: string, opts?: Text2dOptions): Shape2D;
527
+ /**
528
+ * Place a declared vector file as a `Shape2D`. `name` is a key in the part's
529
+ * `vectors` field (`partforge-vector` JSON, not raw `.svg`).
530
+ */
531
+ vector2d(name: string, opts?: Vector2dOptions): Shape2D;
504
532
  /** Convex hull of all inputs → a convex (faceted) `Shape2D`. */
505
533
  hull(inputs: HullInput[]): Shape2D;
506
534
  /** Swept hull over an ordered sequence (>= 2 inputs). */