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.
- package/LICENSE +191 -0
- package/README.md +415 -0
- package/dist/allowanceFns.d.ts +21 -0
- package/dist/api.d.ts +128 -0
- package/dist/authorFns.d.ts +100 -0
- package/dist/bendTableFns.d.ts +58 -0
- package/dist/brepjs-sheetmetal.cjs +7321 -0
- package/dist/brepjs-sheetmetal.js +7233 -0
- package/dist/contourFlangeFns.d.ts +74 -0
- package/dist/cutoutFns.d.ts +52 -0
- package/dist/dxfFns.d.ts +38 -0
- package/dist/facade.d.ts +175 -0
- package/dist/featureTreeFns.d.ts +55 -0
- package/dist/foldFns.d.ts +68 -0
- package/dist/foreignUnfoldFns.d.ts +54 -0
- package/dist/formFns.d.ts +43 -0
- package/dist/hemFns.d.ts +12 -0
- package/dist/index.d.ts +35 -0
- package/dist/internal.d.ts +17 -0
- package/dist/jogFns.d.ts +13 -0
- package/dist/loftedFlangeFns.d.ts +18 -0
- package/dist/materials.d.ts +6 -0
- package/dist/miterFns.d.ts +20 -0
- package/dist/nestFns.d.ts +95 -0
- package/dist/polygonFns.d.ts +48 -0
- package/dist/reliefFns.d.ts +28 -0
- package/dist/reportFns.d.ts +19 -0
- package/dist/tabFns.d.ts +33 -0
- package/dist/types.d.ts +608 -0
- package/dist/unfoldFns.d.ts +80 -0
- package/dist/validateFns.d.ts +14 -0
- package/package.json +61 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Result } from 'brepjs';
|
|
2
|
+
import { LoftedFlangeSpec, SheetMetalPart } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Author a lofted / ruled transition flange between two parallel OPEN profiles.
|
|
5
|
+
* The two profiles are lofted (ruled, straight generators) into a transition surface
|
|
6
|
+
* and thickened to a valid solid; the result is fused onto the part. The developed
|
|
7
|
+
* pattern is produced by TRIANGULATION (the standard transition development): each
|
|
8
|
+
* quad between consecutive vertex pairs is split into two triangles laid flat
|
|
9
|
+
* edge-length-preserving and accumulated into a developed boundary.
|
|
10
|
+
*
|
|
11
|
+
* The triangulated development is EXACT (to tolerance) when the ruled surface is
|
|
12
|
+
* developable — every quad is planar, so the two triangles share their diagonal in
|
|
13
|
+
* 3D and the flat layout preserves all lengths and angles. When a quad is non-planar
|
|
14
|
+
* (twisted ruling) the surface is not developable and the flat layout is an
|
|
15
|
+
* approximation; {@link approximate} is set and the unfold emits a
|
|
16
|
+
* `DEVELOPMENT_APPROXIMATE` warning.
|
|
17
|
+
*/
|
|
18
|
+
export declare function authorLoftedFlange(part: SheetMetalPart, spec: LoftedFlangeSpec): Result<SheetMetalPart>;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { MaterialSpec } from './types.js';
|
|
2
|
+
export declare const DEFAULT_K_FACTOR = 0.44;
|
|
3
|
+
export declare const STEEL_GAUGES: Readonly<Record<string, MaterialSpec>>;
|
|
4
|
+
export declare const ALUMINUM_GAUGES: Readonly<Record<string, MaterialSpec>>;
|
|
5
|
+
export declare const MATERIALS: Readonly<Record<string, MaterialSpec>>;
|
|
6
|
+
export declare function getMaterial(name: string): MaterialSpec | undefined;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Result, Vec3 } from 'brepjs';
|
|
2
|
+
import { SheetMetalPart } from './types.js';
|
|
3
|
+
/** An oriented cutting plane: material on the `+normal` side is removed. */
|
|
4
|
+
export interface MiterPlane {
|
|
5
|
+
origin: Vec3;
|
|
6
|
+
normal: Vec3;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* General miter primitive: cut a sheet-metal part by a plane, removing the
|
|
10
|
+
* material on the `+normal` side. The tool is a half-space block sized to the
|
|
11
|
+
* part's bounding box, so the cut is exact regardless of part extent.
|
|
12
|
+
*/
|
|
13
|
+
export declare function miterCut(part: SheetMetalPart, plane: MiterPlane): Result<SheetMetalPart>;
|
|
14
|
+
/**
|
|
15
|
+
* Auto corner-miter between two adjacent flanges. The miter plane bisects the two
|
|
16
|
+
* flanges' fold-up directions at their shared corner and is offset by half the gap,
|
|
17
|
+
* so the cut falls on the flat/flange regions and never crosses a bend patch. The
|
|
18
|
+
* single bisector cut trims both flanges to a clean mitered corner.
|
|
19
|
+
*/
|
|
20
|
+
export declare function autoMiterCorner(part: SheetMetalPart, flangeIdA: string, flangeIdB: string, gap?: number): Result<SheetMetalPart>;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { Result } from 'brepjs';
|
|
2
|
+
import { FlatPattern, SheetMetalWarning } from './types.js';
|
|
3
|
+
import { DxfOptions } from './dxfFns.js';
|
|
4
|
+
/** Stock sheet the parts are packed onto (the gross blank, before margin). */
|
|
5
|
+
export interface SheetSpec {
|
|
6
|
+
width: number;
|
|
7
|
+
height: number;
|
|
8
|
+
}
|
|
9
|
+
export interface NestOptions {
|
|
10
|
+
sheet: SheetSpec;
|
|
11
|
+
/** Clear border kept empty around the sheet edge (per side). Default `0`. */
|
|
12
|
+
margin?: number | undefined;
|
|
13
|
+
/** Minimum clear gap between any two placed parts (and the bbox padding used
|
|
14
|
+
* for the non-overlap test). Default `0`. */
|
|
15
|
+
spacing?: number | undefined;
|
|
16
|
+
/** Try a 90° rotation per part and keep whichever orientation fits/packs better. */
|
|
17
|
+
allowRotation?: boolean | undefined;
|
|
18
|
+
/**
|
|
19
|
+
* Packing strategy. Default `"bbox"`: each part is packed as its axis-aligned
|
|
20
|
+
* bounding box (the original {@link nest} behavior — fast, parts never interlock).
|
|
21
|
+
* `"nfp"` is true-shape / no-fit-polygon nesting: the actual outline polygons are
|
|
22
|
+
* packed so concave (L-shaped) parts interlock for higher material utilization.
|
|
23
|
+
* The NFP packer is a HEURISTIC (bottom-left-fill, largest-first) — not provably
|
|
24
|
+
* optimal — but it never overlaps parts and never drops a part silently.
|
|
25
|
+
*/
|
|
26
|
+
strategy?: 'bbox' | 'nfp' | undefined;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Where a single pattern lands on a sheet. For the bbox strategy `rotationDeg` is
|
|
30
|
+
* `0` or `90`; the true-shape (nfp) strategy may also emit `180` or `270`. `(x, y)`
|
|
31
|
+
* is the lower-left of the part's transformed bounding box in sheet coordinates.
|
|
32
|
+
*/
|
|
33
|
+
export interface Placement {
|
|
34
|
+
/** Index into the `patterns` array passed to {@link nest}. */
|
|
35
|
+
patternIndex: number;
|
|
36
|
+
/** Lower-left x of the part's (rotated) bounding box, in sheet coordinates. */
|
|
37
|
+
x: number;
|
|
38
|
+
/** Lower-left y of the part's (rotated) bounding box, in sheet coordinates. */
|
|
39
|
+
y: number;
|
|
40
|
+
rotationDeg: number;
|
|
41
|
+
}
|
|
42
|
+
export interface NestSheet {
|
|
43
|
+
placements: Placement[];
|
|
44
|
+
/**
|
|
45
|
+
* Σ placed part areas ÷ usable-sheet area, in `(0, 1]`. "Usable" =
|
|
46
|
+
* `(width − 2·margin) × (height − 2·margin)`. For the `"bbox"` strategy the part
|
|
47
|
+
* area is its BOUNDING-BOX area (intra-bbox waste of a non-rectangular part is not
|
|
48
|
+
* credited — a conservative measure). For the `"nfp"` true-shape strategy it is the
|
|
49
|
+
* actual OUTLINE-POLYGON area, so an L-shaped part credits only its true material,
|
|
50
|
+
* making the two strategies' utilizations directly comparable on the same parts.
|
|
51
|
+
*/
|
|
52
|
+
utilization: number;
|
|
53
|
+
}
|
|
54
|
+
export interface NestResult {
|
|
55
|
+
sheets: NestSheet[];
|
|
56
|
+
/** Indices of patterns too large for the usable sheet even rotated. */
|
|
57
|
+
unplaced: number[];
|
|
58
|
+
warnings: SheetMetalWarning[];
|
|
59
|
+
}
|
|
60
|
+
/** Axis-aligned bounding box of one pattern outline, in developed-plane coords. */
|
|
61
|
+
export interface Bbox {
|
|
62
|
+
minX: number;
|
|
63
|
+
minY: number;
|
|
64
|
+
width: number;
|
|
65
|
+
height: number;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Bounding-box nest: arrange developed flat patterns onto stock sheets to reduce
|
|
69
|
+
* waste. This is BOUNDING-BOX nesting — each part is packed as its axis-aligned
|
|
70
|
+
* outline bounding box, so parts never interlock and concave parts leave their
|
|
71
|
+
* bounding-box waste unused. True-shape (no-fit-polygon) nesting is a follow-up.
|
|
72
|
+
*
|
|
73
|
+
* Algorithm: a shelf (level) packing heuristic. Parts are sorted largest-first by
|
|
74
|
+
* bbox height then area, then placed left-to-right into horizontal shelves; a new
|
|
75
|
+
* shelf opens below when the current row is full, and a NEW sheet opens when the
|
|
76
|
+
* next shelf would overflow the usable height. With `allowRotation`, each part is
|
|
77
|
+
* tried both at 0° and 90° and the orientation that fits the current shelf (or,
|
|
78
|
+
* failing that, fits at all) is kept. A part larger than the usable sheet even
|
|
79
|
+
* rotated is reported in `unplaced` with a warning — never dropped silently, never
|
|
80
|
+
* retried in an infinite loop.
|
|
81
|
+
*/
|
|
82
|
+
export declare function nest(patterns: FlatPattern[], options: NestOptions): Result<NestResult>;
|
|
83
|
+
/**
|
|
84
|
+
* Axis-aligned bounding box of a pattern's OUTER outline wire, read from the wire
|
|
85
|
+
* vertices (each edge start point) — the same 2D-coordinate read the DXF/SVG writers
|
|
86
|
+
* use. Only the outer outline is packed; holes ride inside it.
|
|
87
|
+
*/
|
|
88
|
+
export declare function patternBbox(pattern: FlatPattern): Result<Bbox>;
|
|
89
|
+
/**
|
|
90
|
+
* Emit one fabrication-ready DXF for a single nested sheet: every pattern placed on
|
|
91
|
+
* `result.sheets[sheetIndex]` is translated and rotated onto the sheet (via the same
|
|
92
|
+
* bounding boxes the packer used) and written through {@link multiPatternToDXF}.
|
|
93
|
+
* `patterns` must be the SAME array passed to {@link nest} (placements index into it).
|
|
94
|
+
*/
|
|
95
|
+
export declare function nestToDXF(result: NestResult, patterns: FlatPattern[], sheetIndex: number, options?: DxfOptions): Result<string>;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { Result, Wire } from 'brepjs';
|
|
2
|
+
export type Pt2 = [number, number];
|
|
3
|
+
/** A simple (non-self-intersecting) polygon as an ordered, non-closing vertex loop. */
|
|
4
|
+
export type Polygon = Pt2[];
|
|
5
|
+
/**
|
|
6
|
+
* Ordered vertex loop of a closed outline wire — one vertex per edge start point, the
|
|
7
|
+
* same 2D read the DXF writer (`outlinePoints`) and the bbox nester (`patternBbox`)
|
|
8
|
+
* use. The loop is NOT closed (the last vertex is not repeated); edges are taken as
|
|
9
|
+
* `v[i] → v[(i+1) % n]`.
|
|
10
|
+
*/
|
|
11
|
+
export declare function wireToPolygon(outline: Wire): Result<Polygon>;
|
|
12
|
+
/** Signed area (CCW positive) — used to normalise orientation and reject degenerates. */
|
|
13
|
+
export declare function signedArea(poly: Polygon): number;
|
|
14
|
+
/** Axis-aligned bounds `[minX, minY, maxX, maxY]` of a polygon. */
|
|
15
|
+
export declare function polygonBounds(poly: Polygon): [number, number, number, number];
|
|
16
|
+
/** Rotate every vertex (CCW, degrees, about the origin), then translate by (dx, dy). */
|
|
17
|
+
export declare function transformPolygon(poly: Polygon, dx: number, dy: number, rotationDeg: number): Polygon;
|
|
18
|
+
/** Ray-cast point-in-polygon: points strictly inside return true; behaviour for a
|
|
19
|
+
* point exactly on the boundary is undefined (the overlap predicate handles edge
|
|
20
|
+
* contact separately via segment-intersection). */
|
|
21
|
+
export declare function pointInPolygon(poly: Polygon, x: number, y: number): boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Proper-or-improper segment intersection (`p1p2` vs `p3p4`). Returns true when the
|
|
24
|
+
* segments cross OR touch (collinear overlap or a shared endpoint), so it is
|
|
25
|
+
* conservative for the overlap test — never a false negative.
|
|
26
|
+
*/
|
|
27
|
+
export declare function segmentsIntersect(p1: Pt2, p2: Pt2, p3: Pt2, p4: Pt2): boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Do two simple polygons overlap? True iff any edge of `a` intersects any edge of
|
|
30
|
+
* `b`, or one polygon fully contains a vertex of the other. This pair of conditions
|
|
31
|
+
* is exhaustive for simple polygons: a disjoint pair has no crossing edges and no
|
|
32
|
+
* mutually-contained vertex; a touching-but-non-overlapping pair (shared edge/vertex,
|
|
33
|
+
* zero interior overlap) is caught by `segmentsIntersect` and so is reported as
|
|
34
|
+
* overlapping — callers wanting a clearance gap should inflate via {@link polygonsOverlapWithClearance}.
|
|
35
|
+
*
|
|
36
|
+
* Centroid containment is NOT sufficient on its own (a concave part's centroid can
|
|
37
|
+
* lie outside it), so a representative INTERIOR vertex of each polygon is tested for
|
|
38
|
+
* containment in the other — combined with the edge-crossing test this covers the
|
|
39
|
+
* nested-without-crossing case.
|
|
40
|
+
*/
|
|
41
|
+
export declare function polygonsOverlap(a: Polygon, b: Polygon): boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Overlap test honoring a clearance gap: the two polygons must stay at least
|
|
44
|
+
* `clearance` apart. Implemented by testing raw overlap and, when disjoint, the
|
|
45
|
+
* minimum edge-to-edge distance against the clearance. `clearance <= 0` is the plain
|
|
46
|
+
* {@link polygonsOverlap}.
|
|
47
|
+
*/
|
|
48
|
+
export declare function polygonsOverlapWithClearance(a: Polygon, b: Polygon, clearance: number): boolean;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Result } from 'brepjs';
|
|
2
|
+
import { ReliefSpec, SheetMetalPart } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Add a bend relief to a flange: a small slot cut into the PARENT flat at each end
|
|
5
|
+
* of the bend line that does not reach the parent edge endpoint (a partial/offset
|
|
6
|
+
* flange). Without it the parent material tears at the corner where the bend
|
|
7
|
+
* terminates mid-edge. Each slot is `width` (≈ thickness) along the parent edge by
|
|
8
|
+
* `depth` (≈ developed length + clearance) into the parent flat, cut from the 3D
|
|
9
|
+
* solid and recorded so {@link unfold} replays the 2D notch.
|
|
10
|
+
*
|
|
11
|
+
* A relief is a recorded feature replayed by unfold — exactly the pattern
|
|
12
|
+
* {@link autoMiterCorner} establishes for corner miters.
|
|
13
|
+
*/
|
|
14
|
+
export declare function addBendRelief(part: SheetMetalPart, flangeId: string, spec?: ReliefSpec): Result<SheetMetalPart>;
|
|
15
|
+
/**
|
|
16
|
+
* Add a bend relief to every partial-span bend (a flange that does not span its
|
|
17
|
+
* full parent edge). Full-span flanges are skipped — they have no mid-edge bend
|
|
18
|
+
* terminus to relieve. Convenience over calling {@link addBendRelief} per flange.
|
|
19
|
+
*/
|
|
20
|
+
export declare function autoBendReliefs(part: SheetMetalPart, spec?: ReliefSpec): Result<SheetMetalPart>;
|
|
21
|
+
/**
|
|
22
|
+
* Corner relief between two adjacent flanges: a notch cut at their shared corner so
|
|
23
|
+
* the two upright flanges clear each other once folded — the alternative to a 45°
|
|
24
|
+
* miter for the same corner {@link autoMiterCorner} handles. The notch is cut from
|
|
25
|
+
* the solid and recorded as a {@link ReliefFeature}; the corner is also recorded in
|
|
26
|
+
* `miters` (gap 0) so the collision check treats the interference as resolved.
|
|
27
|
+
*/
|
|
28
|
+
export declare function cornerRelief(part: SheetMetalPart, flangeIdA: string, flangeIdB: string, spec?: ReliefSpec): Result<SheetMetalPart>;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Result } from 'brepjs';
|
|
2
|
+
import { BendReport, SheetMetalPart, UnfoldResult } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Build the bend-table JSON report directly from an authored part. Walks the
|
|
5
|
+
* same tree layout the unfold consumes so the per-bend values and the total flat
|
|
6
|
+
* size agree with the flat pattern. `allowance` is the consumed neutral-axis arc
|
|
7
|
+
* length (the bend allowance); `flatLength` is the straight flange leg past the
|
|
8
|
+
* bend — distinct values a downstream nesting/cut-list tool needs separately.
|
|
9
|
+
*/
|
|
10
|
+
export declare function buildReport(part: SheetMetalPart): Result<BendReport>;
|
|
11
|
+
/**
|
|
12
|
+
* Project the report already computed by `unfold` into the standalone
|
|
13
|
+
* `BendReport` shape, defensively re-validating its numeric fields. This keeps
|
|
14
|
+
* the report a pure function of a previously-unfolded result without re-walking
|
|
15
|
+
* the tree.
|
|
16
|
+
*/
|
|
17
|
+
export declare function reportFromUnfold(result: UnfoldResult): Result<BendReport>;
|
|
18
|
+
/** Serialize a `BendReport` to a stable, pretty-printed JSON string. */
|
|
19
|
+
export declare function reportToJSON(report: BendReport): string;
|
package/dist/tabFns.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Result } from 'brepjs';
|
|
2
|
+
import { TabSpec, SheetMetalPart } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Fuse a rectangular tab onto a named flat region's edge — additive material (the
|
|
5
|
+
* counterpart of a cutout). The tab is built as a region-local rectangle just past
|
|
6
|
+
* the chosen `side`, placed onto the correct folded face via the region's world
|
|
7
|
+
* {@link FlatFrame}, extruded through the sheet thickness and fused to the solid. The
|
|
8
|
+
* same local rectangle, mapped through the region's developed {@link Frame2}, is
|
|
9
|
+
* recorded as a {@link TabFeature} so {@link unfold} extends the OUTER outline by the
|
|
10
|
+
* protrusion (tabs add material, so the developed area grows). Guards a valid,
|
|
11
|
+
* single-bodied solid.
|
|
12
|
+
*/
|
|
13
|
+
export declare function addTab(part: SheetMetalPart, spec: TabSpec): Result<SheetMetalPart>;
|
|
14
|
+
/** The mating-slot placement for a {@link tabAndSlot} joint. */
|
|
15
|
+
export interface SlotPlacement {
|
|
16
|
+
region: string;
|
|
17
|
+
x: number;
|
|
18
|
+
y: number;
|
|
19
|
+
clearance?: number | undefined;
|
|
20
|
+
/** In-plane rotation of the slot (deg, CCW) in the mating region's local frame.
|
|
21
|
+
* Default 0 = slot length along the region's +x (bend-axis) direction; set this
|
|
22
|
+
* when the tab meets the slot region at a non-default orientation. */
|
|
23
|
+
angleDeg?: number | undefined;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Self-fixturing tab-and-slot joint: fuse a tab on one region and punch a matching
|
|
27
|
+
* SLOT CUTOUT on the mating region, sized so the tab's cross-section
|
|
28
|
+
* (`width × thickness`) inserts into the slot. The slot is `tab.width + clearance`
|
|
29
|
+
* long by `thickness + clearance` wide, centred at the mating region's local
|
|
30
|
+
* `(x, y)`. The slot is always strictly larger than the tab cross-section, so the
|
|
31
|
+
* joint mates (verified numerically by callers). Clearance defaults to `0.1` mm.
|
|
32
|
+
*/
|
|
33
|
+
export declare function tabAndSlot(part: SheetMetalPart, tab: TabSpec, slot: SlotPlacement): Result<SheetMetalPart>;
|