brepjs-sheetmetal 0.1.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.
@@ -0,0 +1,74 @@
1
+ import { Result, Vec3, Solid } from 'brepjs';
2
+ import { ContourFlangeSpec, FlatSide, ProfileSegment, SheetMetalPart } from './types.js';
3
+ export interface SegmentFrame {
4
+ /** Origin of the −normal (bottom) surface at the start of this segment. */
5
+ origin: Vec3;
6
+ /** Outward run direction (the segment's local +X). */
7
+ run: Vec3;
8
+ /** Top-surface normal (+Z), n = bendAxis × run. */
9
+ n: Vec3;
10
+ /** Bend axis along the base edge (+Y), shared by every segment. */
11
+ axis: Vec3;
12
+ }
13
+ /**
14
+ * Author a contour flange: an OPEN 2D profile (alternating line/arc segments) swept
15
+ * along one straight edge of the base flat. Each arc is a cylindrical bend, each
16
+ * line a flat leg; consecutive segments chain frame-to-frame exactly as
17
+ * {@link authorPart} chains flanges, building one connected multi-bend cross-section
18
+ * (a return, a hat, a J). The recorded {@link ContourFlangeFeature} carries each
19
+ * segment's EXACT developed length (lines: `length`; arcs: the canonical
20
+ * {@link developedLength}) so the unfold lays the strip out straight with no error.
21
+ * Construction stays on the public, OCCT-WASM-safe API.
22
+ */
23
+ export declare function authorContourFlange(part: SheetMetalPart, spec: ContourFlangeSpec): Result<SheetMetalPart>;
24
+ export interface BuiltSegment {
25
+ solid: Solid;
26
+ frame: SegmentFrame;
27
+ }
28
+ export interface BuiltArc extends BuiltSegment {
29
+ axisOrigin: Vec3;
30
+ }
31
+ /**
32
+ * Add one straight leg of `legLength` extending from `frame.origin` along
33
+ * `frame.run`, full thickness along `frame.n`, across `span` along `frame.axis`.
34
+ * The next segment's frame starts at the leg's far end.
35
+ */
36
+ export declare function buildLineLeg(base: Solid, frame: SegmentFrame, span: number, thickness: number, legLength: number): Result<BuiltSegment>;
37
+ /**
38
+ * Add one cylindrical bend turning the running direction by `angleDeg`. Mirrors
39
+ * {@link authorPart}'s bend patch: a hollow tube (outer R+T, inner R) intersected
40
+ * with the fold wedge, in a canonical frame mapped onto the current segment frame.
41
+ * The returned frame's run/normal are rotated by the fold so the next leg continues
42
+ * tangentially.
43
+ */
44
+ export declare function buildArcBend(base: Solid, frame: SegmentFrame, span: number, thickness: number, seg: Extract<ProfileSegment, {
45
+ kind: 'arc';
46
+ }>): Result<BuiltArc>;
47
+ export interface BaseEdge {
48
+ dir: Vec3;
49
+ out: Vec3;
50
+ start: Vec3;
51
+ length: number;
52
+ }
53
+ /** Resolve one of the four edges of the base flat (the contour-flange root). */
54
+ export declare function baseEdge(part: SheetMetalPart, side: FlatSide): BaseEdge;
55
+ /**
56
+ * Resolve one of the four edges of an arbitrary flat region (its world top-surface
57
+ * frame), the region-aware generalization of {@link baseEdge}: the base is the
58
+ * special case where `origin = 0`, `u = +X`, `v = +Y`, `n = +Z`. Used by hems and
59
+ * jogs to chain their profile off a named flange (or the base) edge.
60
+ */
61
+ export declare function regionEdge(frame: {
62
+ origin: Vec3;
63
+ u: Vec3;
64
+ v: Vec3;
65
+ n: Vec3;
66
+ uLen: number;
67
+ vLen: number;
68
+ }, side: FlatSide): BaseEdge;
69
+ /**
70
+ * The starting {@link SegmentFrame} for a profile chained off `edge` at `offset`
71
+ * along it: contact at the edge, run pointing outward, normal = the region normal,
72
+ * bend axis along the edge. Shared by the contour-flange, hem and jog builders.
73
+ */
74
+ export declare function initialSegmentFrame(edge: BaseEdge, n: Vec3, offset: number): SegmentFrame;
@@ -0,0 +1,52 @@
1
+ import { Result } from 'brepjs';
2
+ import { CutoutSpec, SheetMetalPart } from './types.js';
3
+ import { FlatFrame } from './authorFns.js';
4
+ import { Frame2 } from './unfoldFns.js';
5
+ type Pt2 = [number, number];
6
+ /**
7
+ * Punch a cutout (hole / slot / polygon) through a named flat region's thickness.
8
+ * The 2D profile is built in the region's LOCAL frame, then placed onto the correct
9
+ * folded face via the region's world {@link FlatFrame} (origin/u/v/n) and extruded
10
+ * through the sheet, so a feature authored at local `(x, y)` lands on the matching
11
+ * face whether the region is the base or a folded flange. The same local profile is
12
+ * mapped through the region's developed {@link Frame2} and recorded as a
13
+ * {@link CutoutFeature}, so {@link unfold} emits the matching loop in the flat
14
+ * pattern and {@link fold} can replay it. Guards a valid, single-bodied solid.
15
+ */
16
+ export declare function addCutout(part: SheetMetalPart, spec: CutoutSpec): Result<SheetMetalPart>;
17
+ /** Punch a circular hole of `diameter` centred at region-local `(x, y)`. */
18
+ export declare function addHole(part: SheetMetalPart, region: string, x: number, y: number, diameter: number): Result<SheetMetalPart>;
19
+ /** Punch a slot centred at `(x, y)`: `length` along the slot axis by `width` across. */
20
+ export declare function addSlot(part: SheetMetalPart, region: string, opts: {
21
+ x: number;
22
+ y: number;
23
+ length: number;
24
+ width: number;
25
+ angleDeg?: number;
26
+ round?: boolean;
27
+ }): Result<SheetMetalPart>;
28
+ /** Punch an arbitrary polygon cutout from its region-local `points` (≥ 3). */
29
+ export declare function addPolygonCutout(part: SheetMetalPart, region: string, points: [number, number][]): Result<SheetMetalPart>;
30
+ /** `'base'` is an alias for the base region; everything else passes through. */
31
+ export declare function resolveRegionId(region: string): string;
32
+ /**
33
+ * Resolve a region's world {@link FlatFrame} and developed {@link Frame2} in one
34
+ * shared feature-tree walk — the lookup tabs/forms need to place geometry on the
35
+ * correct folded face and emit the matching developed-plane loops. Mirrors the
36
+ * frame resolution {@link addCutout} performs inline.
37
+ */
38
+ export declare function regionFrames(part: SheetMetalPart, region: string): Result<{
39
+ regionId: string;
40
+ world: FlatFrame;
41
+ dev: Frame2;
42
+ }>;
43
+ /** Developed-plane (u, v) extent of a region: base dims, or a flange frame's lengths. */
44
+ export declare function regionDevExtent(part: SheetMetalPart, regionId: string, world: FlatFrame): Extent;
45
+ /** Local-frame extent the cutout must fit within. */
46
+ export interface Extent {
47
+ uMax: number;
48
+ vMax: number;
49
+ }
50
+ /** Map a region-local `(x, y)` into developed-plane coordinates via its {@link Frame2}. */
51
+ export declare function mapToFrame2(p: Pt2, f: Frame2): Pt2;
52
+ export {};
@@ -0,0 +1,38 @@
1
+ import { Result } from 'brepjs';
2
+ import { FlatPattern } from './types.js';
3
+ export interface DxfOptions {
4
+ textHeight?: number | undefined;
5
+ }
6
+ type Pt2 = [number, number];
7
+ /**
8
+ * A 2D point transform applied to every emitted vertex. Used by the nesting writer
9
+ * to place each pattern at its `(x, y)` and rotation on the sheet; the default
10
+ * single-pattern writer uses the identity transform.
11
+ */
12
+ export type Transform2 = (p: Pt2) => Pt2;
13
+ /**
14
+ * Package-local DXF writer for sheet-metal flat patterns. The core public writer
15
+ * (`blueprintToDXF`) is R12 LINE/POLYLINE-only with no MTEXT, layer color, or
16
+ * INSUNITS, so it cannot carry the annotated multi-layer output required here.
17
+ *
18
+ * Emits a strict AC1015 (R2000) DXF: `INSUNITS=4` (mm), the outline polyline on
19
+ * layer OUTLINE, each bend line on BEND_UP / BEND_DOWN, and an MTEXT
20
+ * angle/direction annotation (e.g. "∠90° U") at each bend-line midpoint. Every
21
+ * table record and entity carries a unique handle and the AcDb subclass markers
22
+ * R13+ readers (AutoCAD AUDIT, ODA/Teigha) require to parse the file cleanly.
23
+ */
24
+ export declare function flatPatternToDXF(pattern: FlatPattern, options?: DxfOptions): Result<string>;
25
+ /** One pattern plus the sheet-placement transform applied to all its geometry. */
26
+ export interface PlacedPattern {
27
+ pattern: FlatPattern;
28
+ transform: Transform2;
29
+ }
30
+ /**
31
+ * Emit one DXF for any number of placed patterns (the nesting per-sheet writer).
32
+ * Each pattern's outline / bend lines / holes / forms are run through its own
33
+ * {@link Transform2} (translate + rotate onto the sheet) before being written, so
34
+ * the nested file is fabrication-ready. Layers/colors/handle scheme match the
35
+ * single-pattern writer exactly.
36
+ */
37
+ export declare function multiPatternToDXF(placed: PlacedPattern[], options?: DxfOptions): Result<string>;
38
+ export {};
@@ -0,0 +1,175 @@
1
+ import { BrepError, Solid } from 'brepjs';
2
+ import { AuthorSpec, BaseFlatSpec, FlangeSpec, SeamSpec } from './authorFns.js';
3
+ import { MiterPlane, DxfOptions, SlotPlacement } from './api.js';
4
+ import { SheetMetalPart, FlatPattern, FlatInput, BendReport, MaterialSpec, ReliefSpec, CutoutSpec, TabSpec, ContourFlangeSpec, LoftedFlangeSpec, HemSpec, JogSpec, SheetMetalWarning, UnfoldResult } from './types.js';
5
+ /** Thrown by the fluent facade when an underlying `Result<T>` is an `Err`. */
6
+ export declare class SheetMetalError extends Error {
7
+ readonly code: string;
8
+ readonly kind: string;
9
+ constructor(brepError: BrepError);
10
+ }
11
+ /** A built part already mitered/operated on, re-entering the fluent chain. */
12
+ declare class SheetMetalPartHandle {
13
+ readonly part: SheetMetalPart;
14
+ constructor(part: SheetMetalPart);
15
+ /** Cut by an oriented plane, removing material on the `+normal` side. */
16
+ miter(plane: MiterPlane): SheetMetalPartHandle;
17
+ /** Auto-miter the shared corner of two flanges with an optional gap. */
18
+ miterCorner(flangeIdA: string, flangeIdB: string, gap?: number): SheetMetalPartHandle;
19
+ /** Add a bend relief at each mid-edge end of a partial flange's bend line. */
20
+ bendRelief(flangeId: string, spec?: ReliefSpec): SheetMetalPartHandle;
21
+ /** Add a bend relief to every partial-span bend in the part. */
22
+ autoReliefs(spec?: ReliefSpec): SheetMetalPartHandle;
23
+ /** Cut a corner relief notch at the shared corner of two adjacent flanges. */
24
+ cornerRelief(flangeIdA: string, flangeIdB: string, spec?: ReliefSpec): SheetMetalPartHandle;
25
+ /** Punch a cutout (hole / slot / polygon) through a named flat region. */
26
+ cutout(spec: CutoutSpec): SheetMetalPartHandle;
27
+ /** Punch a circular hole of `diameter` centred at region-local `(x, y)`. */
28
+ hole(region: string, x: number, y: number, diameter: number): SheetMetalPartHandle;
29
+ /** Punch a slot (rectangular or obround) centred at region-local `(x, y)`. */
30
+ slot(region: string, opts: {
31
+ x: number;
32
+ y: number;
33
+ length: number;
34
+ width: number;
35
+ angleDeg?: number;
36
+ round?: boolean;
37
+ }): SheetMetalPartHandle;
38
+ /** Punch an arbitrary polygon cutout from its region-local `points`. */
39
+ polygonCutout(region: string, points: [number, number][]): SheetMetalPartHandle;
40
+ /** Fuse a rectangular tab (additive protrusion) onto a region's edge. */
41
+ tab(spec: TabSpec): SheetMetalPartHandle;
42
+ /** Self-fixturing tab-and-slot joint: a tab on one region + a matching slot on another. */
43
+ tabAndSlot(tab: TabSpec, slot: SlotPlacement): SheetMetalPartHandle;
44
+ /** Form a louver (vent flap) on a region. */
45
+ louver(opts: {
46
+ region: string;
47
+ x: number;
48
+ y: number;
49
+ length: number;
50
+ width: number;
51
+ height: number;
52
+ direction?: 'up' | 'down';
53
+ }): SheetMetalPartHandle;
54
+ /** Form a round emboss (raised) or dimple (recessed) on a region. */
55
+ emboss(opts: {
56
+ region: string;
57
+ x: number;
58
+ y: number;
59
+ diameter: number;
60
+ height: number;
61
+ kind: 'dimple' | 'emboss';
62
+ }): SheetMetalPartHandle;
63
+ /** Author a contour flange (open line/arc profile swept along a base edge). */
64
+ contourFlange(spec: ContourFlangeSpec): SheetMetalPartHandle;
65
+ /** Author a lofted / ruled transition flange between two parallel open profiles. */
66
+ loftedFlange(spec: LoftedFlangeSpec): SheetMetalPartHandle;
67
+ /** Fold a region edge back ~180°+ onto its parent as a hem (closed/open/teardrop/rolled). */
68
+ hem(spec: HemSpec): SheetMetalPartHandle;
69
+ /** Step a region's flat by `offsetHeight` with two opposite bends (a jog/joggle). */
70
+ jog(spec: JogSpec): SheetMetalPartHandle;
71
+ /** Flatten into a developed flat pattern + bend report + warnings. */
72
+ unfold(): UnfoldResult;
73
+ /** Just the flat pattern from the unfold. */
74
+ flatPattern(): FlatPattern;
75
+ /** Bend report built from the feature tree. */
76
+ report(): BendReport;
77
+ /** Annotated multi-layer DXF of the developed flat pattern. */
78
+ dxf(options?: DxfOptions): string;
79
+ /** Manufacturability warnings (advisory, never throws). */
80
+ validate(): SheetMetalWarning[];
81
+ /** The materialized part (escape hatch back to the functional API). */
82
+ get(): SheetMetalPart;
83
+ }
84
+ /**
85
+ * An imported foreign solid (no feature tree) re-entering the fluent chain: detect
86
+ * its geometry and unfold it. `kFactor` defaults to the mid-surface neutral axis.
87
+ */
88
+ declare class ForeignSolidHandle {
89
+ private readonly solid;
90
+ private readonly opts?;
91
+ constructor(solid: Solid, opts?: {
92
+ kFactor?: number;
93
+ } | undefined);
94
+ /** Override the neutral-axis K-factor (default 0.5, the mid-surface). */
95
+ kFactor(kFactor: number): ForeignSolidHandle;
96
+ /** Detect geometry and flatten into a developed flat pattern + report + warnings. */
97
+ unfold(): UnfoldResult;
98
+ /** Just the flat pattern from the detected unfold. */
99
+ flatPattern(): FlatPattern;
100
+ /** Annotated multi-layer DXF of the developed flat pattern. */
101
+ dxf(options?: DxfOptions): string;
102
+ }
103
+ /** Authoring builder — accumulates the base/flanges/material spec, then folds. */
104
+ declare class SheetMetalBuilder {
105
+ private readonly spec;
106
+ private built?;
107
+ constructor(spec: AuthorSpec);
108
+ /** Add a flange folded off its parent edge (the base by default). */
109
+ flange(flange: FlangeSpec): SheetMetalBuilder;
110
+ /** Add a seam closing a profile into a tube/box (left unfolded). */
111
+ seam(seam: SeamSpec): SheetMetalBuilder;
112
+ /** Set the part material (its thickness/default rule). */
113
+ material(material: MaterialSpec): SheetMetalBuilder;
114
+ /**
115
+ * Fold the accumulated spec into a 3D part, re-entering the fluent chain.
116
+ * Memoized so chaining multiple terminal shortcuts (e.g. `unfold()` then
117
+ * `report()`) authors the solid only once.
118
+ */
119
+ build(): SheetMetalPartHandle;
120
+ miter(plane: MiterPlane): SheetMetalPartHandle;
121
+ miterCorner(flangeIdA: string, flangeIdB: string, gap?: number): SheetMetalPartHandle;
122
+ bendRelief(flangeId: string, spec?: ReliefSpec): SheetMetalPartHandle;
123
+ autoReliefs(spec?: ReliefSpec): SheetMetalPartHandle;
124
+ cornerRelief(flangeIdA: string, flangeIdB: string, spec?: ReliefSpec): SheetMetalPartHandle;
125
+ cutout(spec: CutoutSpec): SheetMetalPartHandle;
126
+ hole(region: string, x: number, y: number, diameter: number): SheetMetalPartHandle;
127
+ slot(region: string, opts: {
128
+ x: number;
129
+ y: number;
130
+ length: number;
131
+ width: number;
132
+ angleDeg?: number;
133
+ round?: boolean;
134
+ }): SheetMetalPartHandle;
135
+ polygonCutout(region: string, points: [number, number][]): SheetMetalPartHandle;
136
+ tab(spec: TabSpec): SheetMetalPartHandle;
137
+ tabAndSlot(tab: TabSpec, slot: SlotPlacement): SheetMetalPartHandle;
138
+ louver(opts: {
139
+ region: string;
140
+ x: number;
141
+ y: number;
142
+ length: number;
143
+ width: number;
144
+ height: number;
145
+ direction?: 'up' | 'down';
146
+ }): SheetMetalPartHandle;
147
+ emboss(opts: {
148
+ region: string;
149
+ x: number;
150
+ y: number;
151
+ diameter: number;
152
+ height: number;
153
+ kind: 'dimple' | 'emboss';
154
+ }): SheetMetalPartHandle;
155
+ contourFlange(spec: ContourFlangeSpec): SheetMetalPartHandle;
156
+ loftedFlange(spec: LoftedFlangeSpec): SheetMetalPartHandle;
157
+ hem(spec: HemSpec): SheetMetalPartHandle;
158
+ jog(spec: JogSpec): SheetMetalPartHandle;
159
+ unfold(): UnfoldResult;
160
+ report(): BendReport;
161
+ dxf(options?: DxfOptions): string;
162
+ validate(): SheetMetalWarning[];
163
+ get(): SheetMetalPart;
164
+ }
165
+ /** Start a fluent sheet-metal chain from a base flat (`length × width`). */
166
+ export declare function sheetMetal(base: BaseFlatSpec, thickness: number): SheetMetalBuilder;
167
+ /** Re-enter the fluent chain from an already-authored part. */
168
+ export declare function fromPart(part: SheetMetalPart): SheetMetalPartHandle;
169
+ /** Fold a flat pattern up into a part and re-enter the fluent chain. */
170
+ export declare function foldFlat(input: FlatInput): SheetMetalPartHandle;
171
+ /** Detect and unfold an imported foreign sheet-metal solid (no feature tree). */
172
+ export declare function fromSolid(solid: Solid, opts?: {
173
+ kFactor?: number;
174
+ }): ForeignSolidHandle;
175
+ export { SheetMetalBuilder, SheetMetalPartHandle, ForeignSolidHandle };
@@ -0,0 +1,55 @@
1
+ import { Result } from 'brepjs';
2
+ import { BendFeature, FlangeFeature, SheetMetalPart, SheetMetalWarning } from './types.js';
3
+ export declare const ROOT_FLAT_ID = "root";
4
+ /** A flat region in the feature graph (the part's base plus every flange face). */
5
+ export interface FlatNode {
6
+ id: string;
7
+ isRoot: boolean;
8
+ flange?: FlangeFeature | undefined;
9
+ }
10
+ /** A bend connecting two flats. `parent`/`child` are flat ids; `bend` is the recorded feature. */
11
+ export interface BendEdge {
12
+ bend: BendFeature;
13
+ parent: string;
14
+ child: string;
15
+ }
16
+ /** Graph view of an authored part: flats = nodes, bends = edges. */
17
+ export interface FeatureGraph {
18
+ nodes: Map<string, FlatNode>;
19
+ edges: BendEdge[];
20
+ }
21
+ /** A bend that survives in the spanning tree, paired with its source/target flat. */
22
+ export interface TreeBend {
23
+ bend: BendFeature;
24
+ parent: string;
25
+ child: string;
26
+ }
27
+ /** A non-tree edge that must be cut to flatten a closed profile. */
28
+ export interface SeamCut {
29
+ bend: BendFeature;
30
+ between: [string, string];
31
+ }
32
+ export interface FeatureTree {
33
+ rootId: string;
34
+ /** Tree edges in breadth-first order (parents precede their children). */
35
+ bends: TreeBend[];
36
+ seams: SeamCut[];
37
+ nodes: Map<string, FlatNode>;
38
+ warnings: SheetMetalWarning[];
39
+ }
40
+ /**
41
+ * Build the feature graph for an authored part. Each flange contributes a flat
42
+ * node `flange.id`; bends connect a parent flat to a child flat. A bend whose id
43
+ * matches a flange id (the flange's own fold) is wired parent→flange; any other
44
+ * bend is matched to a flange by id suffix `<flange>::<n>` or defaults to a
45
+ * root-anchored edge so authored parts always produce a connected graph.
46
+ */
47
+ export declare function buildFeatureGraph(part: SheetMetalPart): Result<FeatureGraph>;
48
+ /**
49
+ * Spanning tree over the feature graph, rooted at the base flat. Tree edges are
50
+ * returned BFS-ordered (every parent appears before its children); edges that
51
+ * would close a cycle (box/closed profiles) become seam cuts with a warning.
52
+ */
53
+ export declare function buildFeatureTree(graph: FeatureGraph, rootId?: string): Result<FeatureTree>;
54
+ /** Convenience: graph + spanning tree in one call. */
55
+ export declare function featureTree(part: SheetMetalPart, rootId?: string): Result<FeatureTree>;
@@ -0,0 +1,68 @@
1
+ import { Result } from 'brepjs';
2
+ import { BendRule, FlatInput, FlatPattern, MaterialSpec, SheetMetalPart, SheetMetalWarning } from './types.js';
3
+ /** A folded part plus the warnings raised while folding (e.g. SEAM_CUT, MIN_RADIUS). */
4
+ export interface FoldResult {
5
+ part: SheetMetalPart;
6
+ warnings: SheetMetalWarning[];
7
+ }
8
+ /**
9
+ * Fold a flat pattern up into a 3D part. Each {@link FoldRegion} folds about its
10
+ * fold line off its parent region, exactly inverting {@link unfold}: folding by an
11
+ * angle is the same rigid construction the forward authoring path performs, so this
12
+ * reuses {@link authorPart}'s bend geometry wholesale rather than re-deriving it.
13
+ *
14
+ * The result carries a fully-populated {@link SheetMetalPart} (valid solid + the
15
+ * BendFeature/FlangeFeature tree consistent with `authorPart`), so
16
+ * `unfold(fold(input))` round-trips. SEAM_CUT-style and min-radius warnings ride
17
+ * inside the Ok payload.
18
+ */
19
+ export declare function fold(input: FlatInput): Result<SheetMetalPart>;
20
+ /** {@link fold} that also surfaces the fold warnings alongside the part. */
21
+ export declare function foldWithWarnings(input: FlatInput): Result<FoldResult>;
22
+ /** How to resolve the bend rule for a bend line read out of a 2D flat pattern. */
23
+ export interface PatternToFlatInputOptions {
24
+ thickness: number;
25
+ /**
26
+ * Rule for bend line `i` (0-based, in {@link FlatPattern.bendLines} order). A flat
27
+ * pattern is pure geometry and cannot encode K-factor/inner-radius, so the rule is
28
+ * a required *input*; only the regions/sides/offsets/spans/lengths/angles are
29
+ * recovered from the 2D geometry.
30
+ */
31
+ ruleFor: (bendIndex: number) => BendRule;
32
+ material?: MaterialSpec | undefined;
33
+ }
34
+ /**
35
+ * Reconstruct a {@link FlatInput} region-tree from the *2D flat-pattern geometry*
36
+ * alone — `pattern.outline` (a closed 2D wire) and `pattern.bendLines` (each a 2D
37
+ * segment + fold angle/direction). Nothing is read from a feature tree or a 3D
38
+ * solid: the regions, sides, offsets, spans and flat lengths are all recovered by
39
+ * reading real 2D coordinates back out of the wire/edges via the public brepjs
40
+ * geometry readers (`getEdges`, `curveStartPoint`, `curveEndPoint`).
41
+ *
42
+ * This is the non-circular round-trip bridge: feeding `unfold(part).pattern` through
43
+ * here and into {@link fold} exercises unfold's 2D placement, this parser, and the
44
+ * forward fold geometry — a bug in any of them breaks the round-trip.
45
+ *
46
+ * Algorithm (rectilinear families — PR1 develops only axis-aligned rectangles):
47
+ * every bend line is an axis-aligned segment lying on the shared edge between a
48
+ * parent region and a child's developed strip. The developed strip width is
49
+ * `developedLength(angle, thickness, ruleFor(i))` (the supplied rule). BFS outward
50
+ * from the base region (the one anchored at the origin): a bend line on a known
51
+ * region's edge spawns a child region whose far edge is the next outward bend line
52
+ * (a grandchild) or the outline boundary; `length = far-extent − dev`, `span` =
53
+ * bend-line length, `offset`/`side` are the bend line's position on the parent edge.
54
+ */
55
+ export declare function patternToFlatInput(pattern: FlatPattern, opts: PatternToFlatInputOptions): Result<FlatInput>;
56
+ /**
57
+ * Convert an authored {@link SheetMetalPart} into the {@link FlatInput} that folds
58
+ * back into it, going strictly through the 2D flat pattern: `unfold(part)` produces
59
+ * the developed wire + bend lines, and {@link patternToFlatInput} recovers the
60
+ * region tree from that 2D geometry. Only the bend *rule* is read off the part (by
61
+ * matching bend id), which a flat pattern legitimately cannot encode; every
62
+ * geometric attribute is parsed from the wire/edges. This makes the round-trip
63
+ * oracle non-circular — a bug in unfold's 2D placement or in the parser breaks it.
64
+ *
65
+ * Seam bends (closed profiles) are left unfolded by `unfold`, so the recovered part
66
+ * is the open spanning-tree shape, not the re-closed box.
67
+ */
68
+ export declare function partToFlatInput(part: SheetMetalPart): Result<FlatInput>;
@@ -0,0 +1,54 @@
1
+ import { Result, Vec3, Solid, Face } from 'brepjs';
2
+ import { UnfoldResult } from './types.js';
3
+ /** A fitted cylinder recovered numerically from a face's sampled points + normals. */
4
+ export interface FittedCylinder {
5
+ /** A point on the axis line. */
6
+ axisOrigin: Vec3;
7
+ /** Unit axis direction. */
8
+ axisDir: Vec3;
9
+ /** Fitted radius (mean distance from the axis line to the sampled points). */
10
+ radius: number;
11
+ /** Angular extent swept by the face about the axis, in radians. */
12
+ angleSpan: number;
13
+ /** Max relative residual of the fit (fraction of radius); small for a true cylinder. */
14
+ residual: number;
15
+ }
16
+ /**
17
+ * Numerically fit a cylinder to a face.
18
+ *
19
+ * Every normal of a cylinder is perpendicular to the axis, so the axis direction
20
+ * is the common perpendicular of the sampled normals: it is recovered as the
21
+ * sign-aligned average of the cross products of pairs of non-parallel sampled
22
+ * normals (each such cross product lies along ±axis). The axis LINE and RADIUS
23
+ * are then found by projecting the sample points onto the plane ⟂ axis and
24
+ * fitting a circle (algebraic least squares); the radius is the mean distance
25
+ * from the axis line to the points. The angular span is the extent of the
26
+ * projected points about the fitted centre. Returns `null` when the surface
27
+ * does not fit a cylinder within {@link FIT_RESIDUAL_TOL}.
28
+ */
29
+ export declare function fitCylinder(face: Face): FittedCylinder | null;
30
+ /**
31
+ * Unfold an ARBITRARY imported sheet-metal solid with no feature tree, by
32
+ * detecting its geometry numerically. The supported class is roughly-uniform-
33
+ * thickness solids whose panels are planar and whose bends are cylindrical:
34
+ *
35
+ * 1. Classify faces by surface type — planar faces are panel faces, cylindrical
36
+ * faces are bend faces. Any other surface type is reported `UNSUPPORTED_FACE`.
37
+ * 2. Pair the two large parallel planar faces of each flat panel (a thickness
38
+ * apart); pair the inner/outer cylindrical faces of each bend and fit its
39
+ * axis + inner radius + swept angle via {@link fitCylinder}.
40
+ * 3. Build a bend graph (flats = nodes; a bend connects the two flats it shares
41
+ * edges with), take a spanning tree from a root flat, and turn any non-tree
42
+ * bend into a seam cut (warned).
43
+ * 4. Walk the tree replacing each cylindrical bend region with a developed strip
44
+ * of length `developedLength(angle, thickness, { innerRadius, kFactor })`
45
+ * (`kFactor` defaults to the {@link DEFAULT_FOREIGN_K} mid-surface), laying
46
+ * each flat out into the plane. Bend direction (up/down) is read from the
47
+ * cylinder centre relative to the parent flat.
48
+ *
49
+ * Non-fatal warnings ride inside the Ok payload; the function fails only when the
50
+ * input is not a recognizable sheet-metal solid at all.
51
+ */
52
+ export declare function unfoldForeignSolid(solid: Solid, opts?: {
53
+ kFactor?: number | undefined;
54
+ }): Result<UnfoldResult>;
@@ -0,0 +1,43 @@
1
+ import { Result } from 'brepjs';
2
+ import { FormSpec, SheetMetalPart } from './types.js';
3
+ /**
4
+ * Add a form feature (louver or emboss/dimple) to a named flat region.
5
+ *
6
+ * 3D FIDELITY (simplified, by design — the public CSG-only API makes true forming
7
+ * impractical):
8
+ * - LOUVER: the vent opening (`length × width`) is cut fully through the sheet, and
9
+ * the formed flap is represented as a thin box hinged on one side and tilted up to
10
+ * `height`, fused so it stays connected to the body at the hinge. True forming
11
+ * keeps the flap continuous with the parent and bends it; the cut+tilted-flap
12
+ * representation captures the vent geometry while keeping a single valid solid.
13
+ * - EMBOSS: a short cylinder fused onto the formed face (raised by `height`).
14
+ * DIMPLE: a shallow cylindrical recess cut into the formed face (recessed by
15
+ * `height`, never through). A true spherical/conical form is approximated by the
16
+ * flat-topped round so the result stays a valid single body.
17
+ *
18
+ * FLAT PATTERN (the fabrication-critical representation): the louver emits its
19
+ * footprint as an OPEN three-side cut path (the fabricator cuts three of its four
20
+ * sides — all but the hinge) plus the hinge fold line; the emboss emits its footprint
21
+ * circle as a marker. Forming removes no net material, so the developed outline and
22
+ * area are unchanged. Guards a valid, single-bodied solid.
23
+ */
24
+ export declare function addForm(part: SheetMetalPart, spec: FormSpec): Result<SheetMetalPart>;
25
+ /** A louver vent on a region; see {@link addForm}. */
26
+ export declare function louver(part: SheetMetalPart, opts: {
27
+ region: string;
28
+ x: number;
29
+ y: number;
30
+ length: number;
31
+ width: number;
32
+ height: number;
33
+ direction?: 'up' | 'down';
34
+ }): Result<SheetMetalPart>;
35
+ /** An emboss (raised) or dimple (recessed) round form on a region; see {@link addForm}. */
36
+ export declare function emboss(part: SheetMetalPart, opts: {
37
+ region: string;
38
+ x: number;
39
+ y: number;
40
+ diameter: number;
41
+ height: number;
42
+ kind: 'dimple' | 'emboss';
43
+ }): Result<SheetMetalPart>;
@@ -0,0 +1,12 @@
1
+ import { Result } from 'brepjs';
2
+ import { HemSpec, SheetMetalPart } from './types.js';
3
+ /**
4
+ * Author a hem: fold a region edge back ~180°+ onto its parent flat, then run a
5
+ * short return leg. Built on the contour-flange segment chainer — the curl is one
6
+ * or more ≤120° sub-arcs (each a recorded `hem::<id>::<n>` bend) followed by a flat
7
+ * return — so the development is EXACT: Σ curl bend allowances (via the table-aware
8
+ * {@link developedLength}) + the return length, laid out straight past the edge.
9
+ * Four `type`s set the curl/gap geometry (see {@link HemSpec}). Construction stays
10
+ * on the public, OCCT-WASM-safe API; the result is guarded to a valid single solid.
11
+ */
12
+ export declare function hem(part: SheetMetalPart, spec: HemSpec): Result<SheetMetalPart>;
@@ -0,0 +1,35 @@
1
+ export { DEFAULT_K_FACTOR, STEEL_GAUGES, ALUMINUM_GAUGES, MATERIALS, getMaterial, } from './materials.js';
2
+ export { bendAllowance, developedLength, neutralRadius } from './allowanceFns.js';
3
+ export { registerBendTable, getBendTable, resolveBendAllowance } from './bendTableFns.js';
4
+ export type { BendTable, BendTableRow } from './bendTableFns.js';
5
+ export { ROOT_FLAT_ID, buildFeatureGraph, buildFeatureTree, featureTree, } from './featureTreeFns.js';
6
+ export type { FlatNode, BendEdge, FeatureGraph, TreeBend, SeamCut, FeatureTree, } from './featureTreeFns.js';
7
+ export { unfold } from './unfoldFns.js';
8
+ export { unfoldForeignSolid, fitCylinder } from './foreignUnfoldFns.js';
9
+ export type { FittedCylinder } from './foreignUnfoldFns.js';
10
+ export { fold, foldWithWarnings, patternToFlatInput, partToFlatInput } from './foldFns.js';
11
+ export type { FoldResult, PatternToFlatInputOptions } from './foldFns.js';
12
+ export { authorPart, baseEdgeRef } from './authorFns.js';
13
+ export type { AuthorSpec, BaseFlatSpec, FlangeSpec, FlangeSide, SeamSpec, } from './authorFns.js';
14
+ export { miterCut, autoMiterCorner } from './miterFns.js';
15
+ export type { MiterPlane } from './miterFns.js';
16
+ export { addBendRelief, autoBendReliefs, cornerRelief } from './reliefFns.js';
17
+ export { addCutout, addHole, addSlot, addPolygonCutout } from './cutoutFns.js';
18
+ export { addTab, tabAndSlot } from './tabFns.js';
19
+ export type { SlotPlacement } from './tabFns.js';
20
+ export { addForm, louver, emboss } from './formFns.js';
21
+ export { authorContourFlange } from './contourFlangeFns.js';
22
+ export { authorLoftedFlange } from './loftedFlangeFns.js';
23
+ export { hem } from './hemFns.js';
24
+ export { jog } from './jogFns.js';
25
+ export { flatPatternToDXF, multiPatternToDXF } from './dxfFns.js';
26
+ export type { DxfOptions, PlacedPattern, Transform2 } from './dxfFns.js';
27
+ export { patternBbox } from './nestFns.js';
28
+ export type { NestOptions, NestResult, NestSheet, Placement, SheetSpec, Bbox, } from './nestFns.js';
29
+ export { wireToPolygon, polygonsOverlap, polygonsOverlapWithClearance, segmentsIntersect, pointInPolygon, transformPolygon, polygonBounds, signedArea, } from './polygonFns.js';
30
+ export type { Polygon, Pt2 } from './polygonFns.js';
31
+ export { buildReport, reportFromUnfold, reportToJSON } from './reportFns.js';
32
+ export { validatePart } from './validateFns.js';
33
+ export { author, unfold as unfoldPart, unfoldSolid, fold as foldPart, miter, miterCorner, bendRelief, autoReliefs, relieveCorner, toDXF, report, reportFrom, reportJSON, validate, allowance, developed, addBendTable, bendTable, resolveAllowance, contourFlange, loftedFlange, nest, nestToDXF, } from './api.js';
34
+ export { sheetMetal, fromPart, foldFlat, fromSolid, SheetMetalError, SheetMetalBuilder, SheetMetalPartHandle, ForeignSolidHandle, } from './facade.js';
35
+ export type { EdgeRef, FlatSide, BendRule, MaterialSpec, MiterSpec, BendFeature, FlangeFeature, CornerMiter, ReliefSpec, ReliefFeature, CutoutSpec, CutoutFeature, TabSpec, TabFeature, FormSpec, FormFeature, ProfileSegment, ContourFlangeSpec, ContourFlangeFeature, LoftedFlangeSpec, LoftedFlangeFeature, HemSpec, HemFeature, JogSpec, JogFeature, SheetMetalPart, FlatPattern, FlatInput, FoldRegion, BendReport, SheetMetalWarning, UnfoldResult, } from './types.js';
@@ -0,0 +1,17 @@
1
+ import { Solid, Vec3 } from 'brepjs';
2
+ import { FlatSide } from './types.js';
3
+ /**
4
+ * OCCT boolean fuse returns a compound wrapping the unioned solid. Extract the
5
+ * single solid so a part's cached shape is a true `Solid` (clean STEP/GLB
6
+ * export); fall back to the compound if the union produced multiple bodies.
7
+ */
8
+ export declare function normalizeSolid(shape: Solid): Solid;
9
+ /**
10
+ * Fallback develop side for a hand-built flange whose `baseEdge.side` is omitted:
11
+ * derive it from the recorded bend axis. An axis along +Y (|axisDir.y|>0.5) is an
12
+ * east flange that develops in +X (`xmax`); an axis along +X is a north flange
13
+ * developing in +Y (`ymax`). This preserves the original axis-only unfold
14
+ * heuristic for external fixtures predating the explicit `side` field; parts from
15
+ * {@link authorPart} always set `side`, so this only kicks in for the legacy path.
16
+ */
17
+ export declare function sideFromAxisDir(axisDir: Vec3): FlatSide | undefined;
@@ -0,0 +1,13 @@
1
+ import { Result } from 'brepjs';
2
+ import { JogSpec, SheetMetalPart } from './types.js';
3
+ /**
4
+ * Author a jog (joggle): two opposite bends (`+θ` then `−θ`) that step a flat by
5
+ * `offsetHeight` perpendicular to its plane, then continue parallel past the step.
6
+ * Built on the contour-flange chainer: a flat position leg, a `+θ` up bend
7
+ * (`jog::<id>::0`), the connecting step run, a `−θ` down bend (`jog::<id>::1`), and
8
+ * a flat run-out leg. The development is EXACT (Σ legs + Σ bend allowances, via the
9
+ * table-aware {@link developedLength}). The connecting step run is
10
+ * `offsetHeight / sin(θ)` so the two bends realize the requested perpendicular step;
11
+ * the result is guarded to a valid single solid. OCCT-WASM-safe construction only.
12
+ */
13
+ export declare function jog(part: SheetMetalPart, spec: JogSpec): Result<SheetMetalPart>;