@faicad/sheetmetal 0.16.1 → 0.18.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/dist/allowanceFns.d.ts +1 -1
- package/dist/allowanceFns.d.ts.map +1 -1
- package/dist/allowanceFns.js +1 -1
- package/dist/allowanceFns.js.map +1 -1
- package/dist/api.d.ts +21 -20
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +20 -20
- package/dist/api.js.map +1 -1
- package/dist/authorFns.d.ts +2 -2
- package/dist/authorFns.d.ts.map +1 -1
- package/dist/authorFns.js +61 -51
- package/dist/authorFns.js.map +1 -1
- package/dist/bendTableFns.d.ts +1 -1
- package/dist/bendTableFns.d.ts.map +1 -1
- package/dist/bendTableFns.js +1 -1
- package/dist/bendTableFns.js.map +1 -1
- package/dist/contourFlangeFns.d.ts +5 -4
- package/dist/contourFlangeFns.d.ts.map +1 -1
- package/dist/contourFlangeFns.js +59 -44
- package/dist/contourFlangeFns.js.map +1 -1
- package/dist/cutoutFns.d.ts +5 -5
- package/dist/cutoutFns.d.ts.map +1 -1
- package/dist/cutoutFns.js +22 -14
- package/dist/cutoutFns.js.map +1 -1
- package/dist/dxfFns.d.ts +1 -1
- package/dist/dxfFns.d.ts.map +1 -1
- package/dist/dxfFns.js +1 -1
- package/dist/dxfFns.js.map +1 -1
- package/dist/facade.d.ts +43 -42
- package/dist/facade.d.ts.map +1 -1
- package/dist/facade.js +83 -83
- package/dist/facade.js.map +1 -1
- package/dist/featureTreeFns.d.ts +1 -1
- package/dist/featureTreeFns.d.ts.map +1 -1
- package/dist/featureTreeFns.js +1 -1
- package/dist/featureTreeFns.js.map +1 -1
- package/dist/foldFns.d.ts +3 -3
- package/dist/foldFns.d.ts.map +1 -1
- package/dist/foldFns.js +12 -12
- package/dist/foldFns.js.map +1 -1
- package/dist/foreignUnfoldFns.d.ts +2 -1
- package/dist/foreignUnfoldFns.d.ts.map +1 -1
- package/dist/foreignUnfoldFns.js +21 -10
- package/dist/foreignUnfoldFns.js.map +1 -1
- package/dist/formFns.d.ts +4 -4
- package/dist/formFns.d.ts.map +1 -1
- package/dist/formFns.js +91 -76
- package/dist/formFns.js.map +1 -1
- package/dist/geometryOps.d.ts +53 -0
- package/dist/geometryOps.d.ts.map +1 -0
- package/dist/geometryOps.js +104 -0
- package/dist/geometryOps.js.map +1 -0
- package/dist/hemFns.d.ts +2 -2
- package/dist/hemFns.d.ts.map +1 -1
- package/dist/hemFns.js +4 -4
- package/dist/hemFns.js.map +1 -1
- package/dist/internal.d.ts +2 -1
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +1 -1
- package/dist/internal.js.map +1 -1
- package/dist/jogFns.d.ts +2 -2
- package/dist/jogFns.d.ts.map +1 -1
- package/dist/jogFns.js +6 -6
- package/dist/jogFns.js.map +1 -1
- package/dist/loftedFlangeFns.d.ts +2 -2
- package/dist/loftedFlangeFns.d.ts.map +1 -1
- package/dist/loftedFlangeFns.js +35 -25
- package/dist/loftedFlangeFns.js.map +1 -1
- package/dist/miterFns.d.ts +3 -3
- package/dist/miterFns.d.ts.map +1 -1
- package/dist/miterFns.js +30 -19
- package/dist/miterFns.js.map +1 -1
- package/dist/nestFns.d.ts +1 -1
- package/dist/nestFns.d.ts.map +1 -1
- package/dist/nestFns.js +1 -1
- package/dist/nestFns.js.map +1 -1
- package/dist/polygonFns.d.ts +2 -2
- package/dist/polygonFns.d.ts.map +1 -1
- package/dist/polygonFns.js +1 -1
- package/dist/polygonFns.js.map +1 -1
- package/dist/reliefFns.d.ts +4 -4
- package/dist/reliefFns.d.ts.map +1 -1
- package/dist/reliefFns.js +39 -31
- package/dist/reliefFns.js.map +1 -1
- package/dist/reportFns.d.ts +1 -1
- package/dist/reportFns.d.ts.map +1 -1
- package/dist/reportFns.js +1 -1
- package/dist/reportFns.js.map +1 -1
- package/dist/tabFns.d.ts +3 -3
- package/dist/tabFns.d.ts.map +1 -1
- package/dist/tabFns.js +21 -13
- package/dist/tabFns.js.map +1 -1
- package/dist/types.d.ts +21 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/unfoldFns.d.ts +1 -1
- package/dist/unfoldFns.d.ts.map +1 -1
- package/dist/unfoldFns.js +2 -2
- package/dist/unfoldFns.js.map +1 -1
- package/dist/validateFns.d.ts.map +1 -1
- package/dist/validateFns.js +4 -6
- package/dist/validateFns.js.map +1 -1
- package/package.json +2 -2
package/dist/cutoutFns.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ok, err, validationError, line, wireLoop, face, extrude, cut, getSolids, isValid, isPlanarWire, vecAdd, vecScale
|
|
1
|
+
import { ok, err, kernelError, validationError, line, wireLoop, face, extrude, cut, getSolids, isValid, isPlanarWire, vecAdd, vecScale } from '@faicad/faijs/api';
|
|
2
2
|
import { normalizeSolid } from './internal.js';
|
|
3
3
|
import { worldFrames } from './authorFns.js';
|
|
4
4
|
import { layoutTree } from './unfoldFns.js';
|
|
@@ -22,7 +22,7 @@ const CIRCLE_SEGMENTS = 48;
|
|
|
22
22
|
* @param spec - the cutout specification (kind, region, geometry).
|
|
23
23
|
* @returns the updated part with the cutout feature recorded, or an error.
|
|
24
24
|
*/
|
|
25
|
-
export function addCutout(part, spec) {
|
|
25
|
+
export async function addCutout(part, spec) {
|
|
26
26
|
if (part.solid === undefined) {
|
|
27
27
|
return err(validationError('NO_SOLID', 'addCutout: part has no folded solid to cut'));
|
|
28
28
|
}
|
|
@@ -52,13 +52,16 @@ export function addCutout(part, spec) {
|
|
|
52
52
|
const dev = developedFrame(part, regionId, tree);
|
|
53
53
|
if (!dev.ok)
|
|
54
54
|
return dev;
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
55
|
+
let solid;
|
|
56
|
+
try {
|
|
57
|
+
const toolResult = await buildTool(local, worldFrame, part.thickness);
|
|
58
|
+
if (!toolResult.ok)
|
|
59
|
+
return toolResult;
|
|
60
|
+
solid = normalizeSolid((await cut(part.solid, toolResult.value)));
|
|
61
|
+
}
|
|
62
|
+
catch (e) {
|
|
63
|
+
return err(kernelError('CUT_FAILED', `cutout: ${e instanceof Error ? e.message : String(e)}`));
|
|
64
|
+
}
|
|
62
65
|
if (!isValid(solid) || getSolids(solid).length > 1) {
|
|
63
66
|
return err(validationError('CUTOUT_SEVERED_SOLID', `addCutout: ${spec.kind} on region '${spec.region}' severs the part into multiple bodies`));
|
|
64
67
|
}
|
|
@@ -80,7 +83,7 @@ export function addCutout(part, spec) {
|
|
|
80
83
|
* @param diameter - the hole diameter.
|
|
81
84
|
* @returns the updated part with the hole feature recorded, or an error.
|
|
82
85
|
*/
|
|
83
|
-
export function addHole(part, region, x, y, diameter) {
|
|
86
|
+
export async function addHole(part, region, x, y, diameter) {
|
|
84
87
|
return addCutout(part, { kind: 'hole', region, x, y, diameter });
|
|
85
88
|
}
|
|
86
89
|
/**
|
|
@@ -90,7 +93,7 @@ export function addHole(part, region, x, y, diameter) {
|
|
|
90
93
|
* @param opts - slot geometry (x, y, length, width, angleDeg?, round?).
|
|
91
94
|
* @returns the updated part with the slot feature recorded, or an error.
|
|
92
95
|
*/
|
|
93
|
-
export function addSlot(part, region, opts) {
|
|
96
|
+
export async function addSlot(part, region, opts) {
|
|
94
97
|
return addCutout(part, { kind: 'slot', region, ...opts });
|
|
95
98
|
}
|
|
96
99
|
/**
|
|
@@ -100,7 +103,7 @@ export function addSlot(part, region, opts) {
|
|
|
100
103
|
* @param points - the polygon vertices in region-local coordinates (≥ 3).
|
|
101
104
|
* @returns the updated part with the polygon cutout feature recorded, or an error.
|
|
102
105
|
*/
|
|
103
|
-
export function addPolygonCutout(part, region, points) {
|
|
106
|
+
export async function addPolygonCutout(part, region, points) {
|
|
104
107
|
return addCutout(part, { kind: 'polygon', region, points });
|
|
105
108
|
}
|
|
106
109
|
/**
|
|
@@ -235,7 +238,7 @@ function checkInBounds(local, ext) {
|
|
|
235
238
|
* sheet by `thickness + 2·HAIR` along +n. Using the world frame's `u`/`v`/`n` is
|
|
236
239
|
* what lands the cut on the correct folded face.
|
|
237
240
|
*/
|
|
238
|
-
function buildTool(local, f, thickness) {
|
|
241
|
+
async function buildTool(local, f, thickness) {
|
|
239
242
|
const base = vecAdd(f.origin, vecScale(f.n, -HAIR));
|
|
240
243
|
const worldPts = local.map(([x, y]) => vecAdd(vecAdd(base, vecScale(f.u, x)), vecScale(f.v, y)));
|
|
241
244
|
const edges = [];
|
|
@@ -257,7 +260,12 @@ function buildTool(local, f, thickness) {
|
|
|
257
260
|
if (!profile.ok)
|
|
258
261
|
return profile;
|
|
259
262
|
const depth = thickness + 2 * HAIR;
|
|
260
|
-
|
|
263
|
+
try {
|
|
264
|
+
return ok((await extrude(profile.value, [f.n[0] * depth, f.n[1] * depth, f.n[2] * depth])));
|
|
265
|
+
}
|
|
266
|
+
catch (e) {
|
|
267
|
+
return err(kernelError('CUTOUT_TOOL_FAILED', `extrude tool: ${e instanceof Error ? e.message : String(e)}`));
|
|
268
|
+
}
|
|
261
269
|
}
|
|
262
270
|
/** Developed-plane {@link Frame2} of a region (base or flange) via the unfold layout. */
|
|
263
271
|
function developedFrame(part, regionId, tree) {
|
package/dist/cutoutFns.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cutoutFns.js","sourceRoot":"","sources":["../src/cutoutFns.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n type Result,\n type Vec3,\n type Solid,\n ok,\n err,\n validationError,\n line,\n wireLoop,\n face,\n extrude,\n cut,\n getSolids,\n isValid,\n isPlanarWire,\n vecAdd,\n vecScale,\n} from '@faicad/faijs/brepjs-compat';\nimport type { CutoutSpec, CutoutFeature, SheetMetalPart } from './types.js';\nimport { normalizeSolid } from './internal.js';\nimport { worldFrames, type FlatFrame } from './authorFns.js';\nimport { layoutTree, type Frame2 } from './unfoldFns.js';\nimport { featureTree, ROOT_FLAT_ID, type FeatureTree } from './featureTreeFns.js';\n\nconst EPS = 1e-6;\n/** A hair of over-cut so the tool pokes through both faces cleanly. */\nconst HAIR = 0.05;\n/** Polyline segments approximating a circular boundary (hole / obround end cap). */\nconst CIRCLE_SEGMENTS = 48;\n\ntype Pt2 = [number, number];\n\n/**\n * Punch a cutout (hole / slot / polygon) through a named flat region's thickness.\n * The 2D profile is built in the region's LOCAL frame, then placed onto the correct\n * folded face via the region's world {@link FlatFrame} (origin/u/v/n) and extruded\n * through the sheet, so a feature authored at local `(x, y)` lands on the matching\n * face whether the region is the base or a folded flange. The same local profile is\n * mapped through the region's developed {@link Frame2} and recorded as a\n * {@link CutoutFeature}, so {@link unfold} emits the matching loop in the flat\n * pattern and {@link fold} can replay it. Guards a valid, single-bodied solid.\n *\n * @param part - the sheet metal part to cut.\n * @param spec - the cutout specification (kind, region, geometry).\n * @returns the updated part with the cutout feature recorded, or an error.\n */\nexport function addCutout(part: SheetMetalPart, spec: CutoutSpec): Result<SheetMetalPart> {\n if (part.solid === undefined) {\n return err(validationError('NO_SOLID', 'addCutout: part has no folded solid to cut'));\n }\n\n const regionId = resolveRegionId(spec.region);\n // Walk the feature tree once and thread it into both the world-frame and\n // developed-frame lookups (each would otherwise re-walk it independently).\n const treeResult = featureTree(part);\n if (!treeResult.ok) return treeResult;\n const tree = treeResult.value;\n const framesResult = worldFrames(part, tree);\n if (!framesResult.ok) return framesResult;\n const worldFrame = framesResult.value.get(regionId);\n if (worldFrame === undefined) {\n return err(validationError('UNKNOWN_REGION', `addCutout: region '${spec.region}' not found`));\n }\n\n const localResult = localLoop(spec);\n if (!localResult.ok) return localResult;\n const local = localResult.value;\n\n const bounds = regionExtent(part, regionId, worldFrame);\n const oob = checkInBounds(local, bounds);\n if (oob !== undefined) {\n return err(validationError('CUTOUT_OUT_OF_BOUNDS', oob));\n }\n\n const dev = developedFrame(part, regionId, tree);\n if (!dev.ok) return dev;\n\n const toolResult = buildTool(local, worldFrame, part.thickness);\n if (!toolResult.ok) return toolResult;\n\n const cutResult = cut(part.solid, toolResult.value);\n if (!cutResult.ok) return cutResult;\n const solid = normalizeSolid(cutResult.value);\n if (!isValid(solid) || getSolids(solid).length > 1) {\n return err(\n validationError(\n 'CUTOUT_SEVERED_SOLID',\n `addCutout: ${spec.kind} on region '${spec.region}' severs the part into multiple bodies`\n )\n );\n }\n\n const loop = local.map((p) => mapToFrame2(p, dev.value));\n const feature: CutoutFeature = {\n spec,\n region: regionId,\n loop,\n area: polygonArea(loop),\n };\n\n return ok({ ...part, solid, cutouts: [...(part.cutouts ?? []), feature] });\n}\n\n/**\n * Punch a circular hole of `diameter` centred at region-local `(x, y)`.\n * @param part - the sheet metal part to cut.\n * @param region - the region name to cut into.\n * @param x - the region-local x coordinate of the hole centre.\n * @param y - the region-local y coordinate of the hole centre.\n * @param diameter - the hole diameter.\n * @returns the updated part with the hole feature recorded, or an error.\n */\nexport function addHole(\n part: SheetMetalPart,\n region: string,\n x: number,\n y: number,\n diameter: number\n): Result<SheetMetalPart> {\n return addCutout(part, { kind: 'hole', region, x, y, diameter });\n}\n\n/**\n * Punch a slot centred at `(x, y)`: `length` along the slot axis by `width` across.\n * @param part - the sheet metal part to cut.\n * @param region - the region name to cut into.\n * @param opts - slot geometry (x, y, length, width, angleDeg?, round?).\n * @returns the updated part with the slot feature recorded, or an error.\n */\nexport function addSlot(\n part: SheetMetalPart,\n region: string,\n opts: { x: number; y: number; length: number; width: number; angleDeg?: number; round?: boolean }\n): Result<SheetMetalPart> {\n return addCutout(part, { kind: 'slot', region, ...opts });\n}\n\n/**\n * Punch an arbitrary polygon cutout from its region-local `points` (≥ 3).\n * @param part - the sheet metal part to cut.\n * @param region - the region name to cut into.\n * @param points - the polygon vertices in region-local coordinates (≥ 3).\n * @returns the updated part with the polygon cutout feature recorded, or an error.\n */\nexport function addPolygonCutout(\n part: SheetMetalPart,\n region: string,\n points: [number, number][]\n): Result<SheetMetalPart> {\n return addCutout(part, { kind: 'polygon', region, points });\n}\n\n/**\n * `'base'` is an alias for the base region; everything else passes through.\n * @param region - the region name to resolve.\n * @returns the canonical region ID.\n */\nexport function resolveRegionId(region: string): string {\n return region === 'base' || region === 'face-0' ? ROOT_FLAT_ID : region;\n}\n\n/**\n * Resolve a region's world {@link FlatFrame} and developed {@link Frame2} in one\n * shared feature-tree walk — the lookup tabs/forms need to place geometry on the\n * correct folded face and emit the matching developed-plane loops. Mirrors the\n * frame resolution {@link addCutout} performs inline.\n * @param part - the sheet metal part.\n * @param region - the region name.\n * @returns the region ID, world frame, and developed frame, or an error.\n */\nexport function regionFrames(\n part: SheetMetalPart,\n region: string\n): Result<{ regionId: string; world: FlatFrame; dev: Frame2 }> {\n const regionId = resolveRegionId(region);\n const treeResult = featureTree(part);\n if (!treeResult.ok) return treeResult;\n const tree = treeResult.value;\n const framesResult = worldFrames(part, tree);\n if (!framesResult.ok) return framesResult;\n const world = framesResult.value.get(regionId);\n if (world === undefined) {\n return err(validationError('UNKNOWN_REGION', `region '${region}' not found`));\n }\n const dev = developedFrame(part, regionId, tree);\n if (!dev.ok) return dev;\n return ok({ regionId, world, dev: dev.value });\n}\n\n/**\n * Developed-plane (u, v) extent of a region: base dims, or a flange frame's lengths.\n * @param part - the sheet metal part.\n * @param regionId - the canonical region ID.\n * @param world - the region's world frame.\n * @returns the developed-plane extent.\n */\nexport function regionDevExtent(part: SheetMetalPart, regionId: string, world: FlatFrame): Extent {\n return regionExtent(part, regionId, world);\n}\n\n/** Closed local-plane loop (CCW, no closing-point duplicate) for a cutout spec. */\nfunction localLoop(spec: CutoutSpec): Result<Pt2[]> {\n switch (spec.kind) {\n case 'hole': {\n if (!Number.isFinite(spec.diameter) || spec.diameter <= 0) {\n return err(validationError('INVALID_CUTOUT', `hole diameter must be positive, got ${spec.diameter}`));\n }\n return ok(circlePts([spec.x, spec.y], spec.diameter / 2));\n }\n case 'slot': {\n if (!Number.isFinite(spec.length) || spec.length <= 0 || !Number.isFinite(spec.width) || spec.width <= 0) {\n return err(validationError('INVALID_CUTOUT', `slot length/width must be positive`));\n }\n if (spec.round && spec.width > spec.length + EPS) {\n return err(validationError('INVALID_CUTOUT', `obround slot width must not exceed its length`));\n }\n return ok(slotPts(spec.x, spec.y, spec.length, spec.width, spec.angleDeg ?? 0, spec.round ?? false));\n }\n case 'polygon': {\n if (spec.points.length < 3) {\n return err(validationError('INVALID_CUTOUT', `polygon cutout needs ≥ 3 points, got ${spec.points.length}`));\n }\n return ok(spec.points.map((p) => [p[0], p[1]] as Pt2));\n }\n default:\n return spec satisfies never;\n }\n}\n\nfunction circlePts(center: Pt2, r: number): Pt2[] {\n const pts: Pt2[] = [];\n for (let i = 0; i < CIRCLE_SEGMENTS; i += 1) {\n const a = (2 * Math.PI * i) / CIRCLE_SEGMENTS;\n pts.push([center[0] + r * Math.cos(a), center[1] + r * Math.sin(a)]);\n }\n return pts;\n}\n\n/**\n * Slot loop centred at `(cx, cy)`: a `length × width` rectangle (square ends), or an\n * obround (semicircular ends, radius `width/2`) when `round`. Built axis-aligned\n * (length along local +x) then rotated by `angleDeg` about the centre.\n */\nfunction slotPts(cx: number, cy: number, length: number, width: number, angleDeg: number, round: boolean): Pt2[] {\n const hw = width / 2;\n const pts: Pt2[] = [];\n if (!round) {\n const hl = length / 2;\n pts.push([-hl, -hw], [hl, -hw], [hl, hw], [-hl, hw]);\n } else {\n const straight = length / 2 - hw;\n const half = Math.max(CIRCLE_SEGMENTS / 2, 2);\n // Right semicircle (from bottom to top), then left semicircle.\n for (let i = 0; i <= half; i += 1) {\n const a = -Math.PI / 2 + (Math.PI * i) / half;\n pts.push([straight + hw * Math.cos(a), hw * Math.sin(a)]);\n }\n for (let i = 0; i <= half; i += 1) {\n const a = Math.PI / 2 + (Math.PI * i) / half;\n pts.push([-straight + hw * Math.cos(a), hw * Math.sin(a)]);\n }\n }\n const rad = (angleDeg * Math.PI) / 180;\n const c = Math.cos(rad);\n const s = Math.sin(rad);\n return pts.map(([x, y]) => [cx + x * c - y * s, cy + x * s + y * c]);\n}\n\n/** Local-frame extent the cutout must fit within. */\nexport interface Extent {\n uMax: number;\n vMax: number;\n}\n\nfunction regionExtent(part: SheetMetalPart, regionId: string, worldFrame: FlatFrame): Extent {\n if (regionId === ROOT_FLAT_ID) return { uMax: part.baseLength, vMax: part.width };\n // A flange frame's uLen = span (along the bend axis), vLen = flat length.\n return { uMax: worldFrame.uLen, vMax: worldFrame.vLen };\n}\n\n/** First out-of-bounds message, or undefined if the whole loop lies inside the region. */\nfunction checkInBounds(local: Pt2[], ext: Extent): string | undefined {\n for (const [x, y] of local) {\n if (x < -EPS || x > ext.uMax + EPS || y < -EPS || y > ext.vMax + EPS) {\n return `cutout point (${x.toFixed(3)}, ${y.toFixed(3)}) lies outside region extent [0,${ext.uMax}]×[0,${ext.vMax}]`;\n }\n }\n return undefined;\n}\n\n/**\n * Build the 3D cut tool: the local loop placed onto the region's bottom surface\n * (`origin + x·u + y·v − HAIR·n`), turned into a face, and extruded through the\n * sheet by `thickness + 2·HAIR` along +n. Using the world frame's `u`/`v`/`n` is\n * what lands the cut on the correct folded face.\n */\nfunction buildTool(local: Pt2[], f: FlatFrame, thickness: number): Result<Solid> {\n const base = vecAdd(f.origin, vecScale(f.n, -HAIR));\n const worldPts: Vec3[] = local.map(([x, y]) =>\n vecAdd(vecAdd(base, vecScale(f.u, x)), vecScale(f.v, y))\n );\n const edges = [];\n for (let i = 0; i < worldPts.length; i += 1) {\n const a = worldPts[i];\n const b = worldPts[(i + 1) % worldPts.length];\n if (a === undefined || b === undefined) {\n return err(validationError('CUTOUT_TOOL_FAILED', 'failed to index cutout loop points'));\n }\n edges.push(line(a, b));\n }\n const wire = wireLoop(edges);\n if (!wire.ok) return wire;\n if (!isPlanarWire(wire.value)) {\n return err(validationError('CUTOUT_TOOL_FAILED', 'cutout profile wire is not planar'));\n }\n const profile = face(wire.value);\n if (!profile.ok) return profile;\n const depth = thickness + 2 * HAIR;\n return extrude(profile.value, [f.n[0] * depth, f.n[1] * depth, f.n[2] * depth]);\n}\n\n/** Developed-plane {@link Frame2} of a region (base or flange) via the unfold layout. */\nfunction developedFrame(part: SheetMetalPart, regionId: string, tree?: FeatureTree): Result<Frame2> {\n const treeResult = tree !== undefined ? ok(tree) : featureTree(part);\n if (!treeResult.ok) return treeResult;\n const layout = layoutTree(part, treeResult.value, part.baseLength, part.width);\n if (!layout.ok) return layout;\n const placed = layout.value.flats.find((p) => p.id === regionId);\n if (placed === undefined) {\n return err(validationError('UNKNOWN_REGION', `developedFrame: region '${regionId}' missing from layout`));\n }\n return ok(placed.frame);\n}\n\n/**\n * Map a region-local `(x, y)` into developed-plane coordinates via its {@link Frame2}.\n * @param p - the region-local point.\n * @param f - the developed frame.\n * @returns the developed-plane point.\n */\nexport function mapToFrame2(p: Pt2, f: Frame2): Pt2 {\n return [\n f.origin[0] + f.u[0] * p[0] + f.v[0] * p[1],\n f.origin[1] + f.u[1] * p[0] + f.v[1] * p[1],\n ];\n}\n\n/** Shoelace area (always positive) of a closed loop. */\nfunction polygonArea(loop: Pt2[]): number {\n let sum = 0;\n for (let i = 0; i < loop.length; i += 1) {\n const a = loop[i];\n const b = loop[(i + 1) % loop.length];\n if (a === undefined || b === undefined) continue;\n sum += a[0] * b[1] - b[0] * a[1];\n }\n return Math.abs(sum) / 2;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"cutoutFns.js","sourceRoot":"","sources":["../src/cutoutFns.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Solid } from './types.js';\nimport {type Result, type Vec3, ok, err, kernelError, validationError, line, wireLoop, face, extrude, cut, getSolids, isValid, isPlanarWire, vecAdd, vecScale} from '@faicad/faijs/api';\nimport type { CutoutSpec, CutoutFeature, SheetMetalPart } from './types.js';\nimport { normalizeSolid } from './internal.js';\nimport { worldFrames, type FlatFrame } from './authorFns.js';\nimport { layoutTree, type Frame2 } from './unfoldFns.js';\nimport { featureTree, ROOT_FLAT_ID, type FeatureTree } from './featureTreeFns.js';\n\nconst EPS = 1e-6;\n/** A hair of over-cut so the tool pokes through both faces cleanly. */\nconst HAIR = 0.05;\n/** Polyline segments approximating a circular boundary (hole / obround end cap). */\nconst CIRCLE_SEGMENTS = 48;\n\ntype Pt2 = [number, number];\n\n/**\n * Punch a cutout (hole / slot / polygon) through a named flat region's thickness.\n * The 2D profile is built in the region's LOCAL frame, then placed onto the correct\n * folded face via the region's world {@link FlatFrame} (origin/u/v/n) and extruded\n * through the sheet, so a feature authored at local `(x, y)` lands on the matching\n * face whether the region is the base or a folded flange. The same local profile is\n * mapped through the region's developed {@link Frame2} and recorded as a\n * {@link CutoutFeature}, so {@link unfold} emits the matching loop in the flat\n * pattern and {@link fold} can replay it. Guards a valid, single-bodied solid.\n *\n * @param part - the sheet metal part to cut.\n * @param spec - the cutout specification (kind, region, geometry).\n * @returns the updated part with the cutout feature recorded, or an error.\n */\nexport async function addCutout(part: SheetMetalPart, spec: CutoutSpec): Promise<Result<SheetMetalPart>> {\n if (part.solid === undefined) {\n return err(validationError('NO_SOLID', 'addCutout: part has no folded solid to cut'));\n }\n\n const regionId = resolveRegionId(spec.region);\n // Walk the feature tree once and thread it into both the world-frame and\n // developed-frame lookups (each would otherwise re-walk it independently).\n const treeResult = featureTree(part);\n if (!treeResult.ok) return treeResult;\n const tree = treeResult.value;\n const framesResult = worldFrames(part, tree);\n if (!framesResult.ok) return framesResult;\n const worldFrame = framesResult.value.get(regionId);\n if (worldFrame === undefined) {\n return err(validationError('UNKNOWN_REGION', `addCutout: region '${spec.region}' not found`));\n }\n\n const localResult = localLoop(spec);\n if (!localResult.ok) return localResult;\n const local = localResult.value;\n\n const bounds = regionExtent(part, regionId, worldFrame);\n const oob = checkInBounds(local, bounds);\n if (oob !== undefined) {\n return err(validationError('CUTOUT_OUT_OF_BOUNDS', oob));\n }\n\n const dev = developedFrame(part, regionId, tree);\n if (!dev.ok) return dev;\n\n let solid: Solid;\n try {\n const toolResult = await buildTool(local, worldFrame, part.thickness);\n if (!toolResult.ok) return toolResult;\n solid = normalizeSolid((await cut(part.solid, toolResult.value)) as Solid);\n } catch (e) {\n return err(kernelError('CUT_FAILED', `cutout: ${e instanceof Error ? e.message : String(e)}`));\n }\n if (!isValid(solid) || getSolids(solid).length > 1) {\n return err(\n validationError(\n 'CUTOUT_SEVERED_SOLID',\n `addCutout: ${spec.kind} on region '${spec.region}' severs the part into multiple bodies`\n )\n );\n }\n\n const loop = local.map((p) => mapToFrame2(p, dev.value));\n const feature: CutoutFeature = {\n spec,\n region: regionId,\n loop,\n area: polygonArea(loop),\n };\n\n return ok({ ...part, solid, cutouts: [...(part.cutouts ?? []), feature] });\n}\n\n/**\n * Punch a circular hole of `diameter` centred at region-local `(x, y)`.\n * @param part - the sheet metal part to cut.\n * @param region - the region name to cut into.\n * @param x - the region-local x coordinate of the hole centre.\n * @param y - the region-local y coordinate of the hole centre.\n * @param diameter - the hole diameter.\n * @returns the updated part with the hole feature recorded, or an error.\n */\nexport async function addHole(\n part: SheetMetalPart,\n region: string,\n x: number,\n y: number,\n diameter: number\n): Promise<Result<SheetMetalPart>> {\n return addCutout(part, { kind: 'hole', region, x, y, diameter });\n}\n\n/**\n * Punch a slot centred at `(x, y)`: `length` along the slot axis by `width` across.\n * @param part - the sheet metal part to cut.\n * @param region - the region name to cut into.\n * @param opts - slot geometry (x, y, length, width, angleDeg?, round?).\n * @returns the updated part with the slot feature recorded, or an error.\n */\nexport async function addSlot(\n part: SheetMetalPart,\n region: string,\n opts: { x: number; y: number; length: number; width: number; angleDeg?: number; round?: boolean }\n): Promise<Result<SheetMetalPart>> {\n return addCutout(part, { kind: 'slot', region, ...opts });\n}\n\n/**\n * Punch an arbitrary polygon cutout from its region-local `points` (≥ 3).\n * @param part - the sheet metal part to cut.\n * @param region - the region name to cut into.\n * @param points - the polygon vertices in region-local coordinates (≥ 3).\n * @returns the updated part with the polygon cutout feature recorded, or an error.\n */\nexport async function addPolygonCutout(\n part: SheetMetalPart,\n region: string,\n points: [number, number][]\n): Promise<Result<SheetMetalPart>> {\n return addCutout(part, { kind: 'polygon', region, points });\n}\n\n/**\n * `'base'` is an alias for the base region; everything else passes through.\n * @param region - the region name to resolve.\n * @returns the canonical region ID.\n */\nexport function resolveRegionId(region: string): string {\n return region === 'base' || region === 'face-0' ? ROOT_FLAT_ID : region;\n}\n\n/**\n * Resolve a region's world {@link FlatFrame} and developed {@link Frame2} in one\n * shared feature-tree walk — the lookup tabs/forms need to place geometry on the\n * correct folded face and emit the matching developed-plane loops. Mirrors the\n * frame resolution {@link addCutout} performs inline.\n * @param part - the sheet metal part.\n * @param region - the region name.\n * @returns the region ID, world frame, and developed frame, or an error.\n */\nexport function regionFrames(\n part: SheetMetalPart,\n region: string\n): Result<{ regionId: string; world: FlatFrame; dev: Frame2 }> {\n const regionId = resolveRegionId(region);\n const treeResult = featureTree(part);\n if (!treeResult.ok) return treeResult;\n const tree = treeResult.value;\n const framesResult = worldFrames(part, tree);\n if (!framesResult.ok) return framesResult;\n const world = framesResult.value.get(regionId);\n if (world === undefined) {\n return err(validationError('UNKNOWN_REGION', `region '${region}' not found`));\n }\n const dev = developedFrame(part, regionId, tree);\n if (!dev.ok) return dev;\n return ok({ regionId, world, dev: dev.value });\n}\n\n/**\n * Developed-plane (u, v) extent of a region: base dims, or a flange frame's lengths.\n * @param part - the sheet metal part.\n * @param regionId - the canonical region ID.\n * @param world - the region's world frame.\n * @returns the developed-plane extent.\n */\nexport function regionDevExtent(part: SheetMetalPart, regionId: string, world: FlatFrame): Extent {\n return regionExtent(part, regionId, world);\n}\n\n/** Closed local-plane loop (CCW, no closing-point duplicate) for a cutout spec. */\nfunction localLoop(spec: CutoutSpec): Result<Pt2[]> {\n switch (spec.kind) {\n case 'hole': {\n if (!Number.isFinite(spec.diameter) || spec.diameter <= 0) {\n return err(validationError('INVALID_CUTOUT', `hole diameter must be positive, got ${spec.diameter}`));\n }\n return ok(circlePts([spec.x, spec.y], spec.diameter / 2));\n }\n case 'slot': {\n if (!Number.isFinite(spec.length) || spec.length <= 0 || !Number.isFinite(spec.width) || spec.width <= 0) {\n return err(validationError('INVALID_CUTOUT', `slot length/width must be positive`));\n }\n if (spec.round && spec.width > spec.length + EPS) {\n return err(validationError('INVALID_CUTOUT', `obround slot width must not exceed its length`));\n }\n return ok(slotPts(spec.x, spec.y, spec.length, spec.width, spec.angleDeg ?? 0, spec.round ?? false));\n }\n case 'polygon': {\n if (spec.points.length < 3) {\n return err(validationError('INVALID_CUTOUT', `polygon cutout needs ≥ 3 points, got ${spec.points.length}`));\n }\n return ok(spec.points.map((p) => [p[0], p[1]] as Pt2));\n }\n default:\n return spec satisfies never;\n }\n}\n\nfunction circlePts(center: Pt2, r: number): Pt2[] {\n const pts: Pt2[] = [];\n for (let i = 0; i < CIRCLE_SEGMENTS; i += 1) {\n const a = (2 * Math.PI * i) / CIRCLE_SEGMENTS;\n pts.push([center[0] + r * Math.cos(a), center[1] + r * Math.sin(a)]);\n }\n return pts;\n}\n\n/**\n * Slot loop centred at `(cx, cy)`: a `length × width` rectangle (square ends), or an\n * obround (semicircular ends, radius `width/2`) when `round`. Built axis-aligned\n * (length along local +x) then rotated by `angleDeg` about the centre.\n */\nfunction slotPts(cx: number, cy: number, length: number, width: number, angleDeg: number, round: boolean): Pt2[] {\n const hw = width / 2;\n const pts: Pt2[] = [];\n if (!round) {\n const hl = length / 2;\n pts.push([-hl, -hw], [hl, -hw], [hl, hw], [-hl, hw]);\n } else {\n const straight = length / 2 - hw;\n const half = Math.max(CIRCLE_SEGMENTS / 2, 2);\n // Right semicircle (from bottom to top), then left semicircle.\n for (let i = 0; i <= half; i += 1) {\n const a = -Math.PI / 2 + (Math.PI * i) / half;\n pts.push([straight + hw * Math.cos(a), hw * Math.sin(a)]);\n }\n for (let i = 0; i <= half; i += 1) {\n const a = Math.PI / 2 + (Math.PI * i) / half;\n pts.push([-straight + hw * Math.cos(a), hw * Math.sin(a)]);\n }\n }\n const rad = (angleDeg * Math.PI) / 180;\n const c = Math.cos(rad);\n const s = Math.sin(rad);\n return pts.map(([x, y]) => [cx + x * c - y * s, cy + x * s + y * c]);\n}\n\n/** Local-frame extent the cutout must fit within. */\nexport interface Extent {\n uMax: number;\n vMax: number;\n}\n\nfunction regionExtent(part: SheetMetalPart, regionId: string, worldFrame: FlatFrame): Extent {\n if (regionId === ROOT_FLAT_ID) return { uMax: part.baseLength, vMax: part.width };\n // A flange frame's uLen = span (along the bend axis), vLen = flat length.\n return { uMax: worldFrame.uLen, vMax: worldFrame.vLen };\n}\n\n/** First out-of-bounds message, or undefined if the whole loop lies inside the region. */\nfunction checkInBounds(local: Pt2[], ext: Extent): string | undefined {\n for (const [x, y] of local) {\n if (x < -EPS || x > ext.uMax + EPS || y < -EPS || y > ext.vMax + EPS) {\n return `cutout point (${x.toFixed(3)}, ${y.toFixed(3)}) lies outside region extent [0,${ext.uMax}]×[0,${ext.vMax}]`;\n }\n }\n return undefined;\n}\n\n/**\n * Build the 3D cut tool: the local loop placed onto the region's bottom surface\n * (`origin + x·u + y·v − HAIR·n`), turned into a face, and extruded through the\n * sheet by `thickness + 2·HAIR` along +n. Using the world frame's `u`/`v`/`n` is\n * what lands the cut on the correct folded face.\n */\nasync function buildTool(local: Pt2[], f: FlatFrame, thickness: number): Promise<Result<Solid>> {\n const base = vecAdd(f.origin, vecScale(f.n, -HAIR));\n const worldPts: Vec3[] = local.map(([x, y]) =>\n vecAdd(vecAdd(base, vecScale(f.u, x)), vecScale(f.v, y))\n );\n const edges = [];\n for (let i = 0; i < worldPts.length; i += 1) {\n const a = worldPts[i];\n const b = worldPts[(i + 1) % worldPts.length];\n if (a === undefined || b === undefined) {\n return err(validationError('CUTOUT_TOOL_FAILED', 'failed to index cutout loop points'));\n }\n edges.push(line(a, b));\n }\n const wire = wireLoop(edges);\n if (!wire.ok) return wire;\n if (!isPlanarWire(wire.value)) {\n return err(validationError('CUTOUT_TOOL_FAILED', 'cutout profile wire is not planar'));\n }\n const profile = face(wire.value);\n if (!profile.ok) return profile;\n const depth = thickness + 2 * HAIR;\n try {\n return ok((await extrude(profile.value, [f.n[0] * depth, f.n[1] * depth, f.n[2] * depth])) as Solid);\n } catch (e) {\n return err(kernelError('CUTOUT_TOOL_FAILED', `extrude tool: ${e instanceof Error ? e.message : String(e)}`));\n }\n}\n\n/** Developed-plane {@link Frame2} of a region (base or flange) via the unfold layout. */\nfunction developedFrame(part: SheetMetalPart, regionId: string, tree?: FeatureTree): Result<Frame2> {\n const treeResult = tree !== undefined ? ok(tree) : featureTree(part);\n if (!treeResult.ok) return treeResult;\n const layout = layoutTree(part, treeResult.value, part.baseLength, part.width);\n if (!layout.ok) return layout;\n const placed = layout.value.flats.find((p) => p.id === regionId);\n if (placed === undefined) {\n return err(validationError('UNKNOWN_REGION', `developedFrame: region '${regionId}' missing from layout`));\n }\n return ok(placed.frame);\n}\n\n/**\n * Map a region-local `(x, y)` into developed-plane coordinates via its {@link Frame2}.\n * @param p - the region-local point.\n * @param f - the developed frame.\n * @returns the developed-plane point.\n */\nexport function mapToFrame2(p: Pt2, f: Frame2): Pt2 {\n return [\n f.origin[0] + f.u[0] * p[0] + f.v[0] * p[1],\n f.origin[1] + f.u[1] * p[0] + f.v[1] * p[1],\n ];\n}\n\n/** Shoelace area (always positive) of a closed loop. */\nfunction polygonArea(loop: Pt2[]): number {\n let sum = 0;\n for (let i = 0; i < loop.length; i += 1) {\n const a = loop[i];\n const b = loop[(i + 1) % loop.length];\n if (a === undefined || b === undefined) continue;\n sum += a[0] * b[1] - b[0] * a[1];\n }\n return Math.abs(sum) / 2;\n}\n"]}
|
package/dist/dxfFns.d.ts
CHANGED
package/dist/dxfFns.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dxfFns.d.ts","sourceRoot":"","sources":["../src/dxfFns.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"dxfFns.d.ts","sourceRoot":"","sources":["../src/dxfFns.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,KAAK,MAAM,EAAgF,MAAM,mBAAmB,CAAC;AAC7H,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,gDAAgD;AAChD,MAAM,WAAW,UAAU;IACzB,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACjC;AAeD,KAAK,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAE5B;;;;GAIG;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,EAAE,GAAG,KAAK,GAAG,CAAC;AAIzC;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,WAAW,EAAE,OAAO,GAAE,UAAe,GAAG,MAAM,CAAC,MAAM,CAAC,CAE/F;AAED,kFAAkF;AAClF,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,WAAW,CAAC;IACrB,SAAS,EAAE,UAAU,CAAC;CACvB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,aAAa,EAAE,EAAE,OAAO,GAAE,UAAe,GAAG,MAAM,CAAC,MAAM,CAAC,CA4BnG"}
|
package/dist/dxfFns.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ok, err, validationError, getEdges, curveStartPoint, curveEndPoint } from '@faicad/faijs/
|
|
1
|
+
import { ok, err, validationError, getEdges, curveStartPoint, curveEndPoint } from '@faicad/faijs/api';
|
|
2
2
|
const DEFAULT_TEXT_HEIGHT = 2.5;
|
|
3
3
|
const LAYER_OUTLINE = 'OUTLINE';
|
|
4
4
|
const LAYER_BEND_UP = 'BEND_UP';
|
package/dist/dxfFns.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dxfFns.js","sourceRoot":"","sources":["../src/dxfFns.ts"],"names":[],"mappings":"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","sourcesContent":["import { type Result, type Vec3, type Wire, ok, err, validationError, getEdges, curveStartPoint, curveEndPoint } from '@faicad/faijs/brepjs-compat';\nimport type { FlatPattern } from './types.js';\n\n/** Options for the flat-pattern DXF writers. */\nexport interface DxfOptions {\n textHeight?: number | undefined;\n}\n\nconst DEFAULT_TEXT_HEIGHT = 2.5;\nconst LAYER_OUTLINE = 'OUTLINE';\nconst LAYER_BEND_UP = 'BEND_UP';\nconst LAYER_BEND_DOWN = 'BEND_DOWN';\nconst LAYER_CUTOUT = 'CUTOUT';\nconst LAYER_FORM = 'FORM';\n\nconst COLOR_OUTLINE = 7;\nconst COLOR_BEND_UP = 1;\nconst COLOR_BEND_DOWN = 5;\nconst COLOR_CUTOUT = 3;\nconst COLOR_FORM = 4;\n\ntype Pt2 = [number, number];\n\n/**\n * A 2D point transform applied to every emitted vertex. Used by the nesting writer\n * to place each pattern at its `(x, y)` and rotation on the sheet; the default\n * single-pattern writer uses the identity transform.\n */\nexport type Transform2 = (p: Pt2) => Pt2;\n\nconst IDENTITY: Transform2 = (p) => p;\n\n/**\n * Package-local DXF writer for sheet-metal flat patterns. The core public writer\n * (`blueprintToDXF`) is R12 LINE/POLYLINE-only with no MTEXT, layer color, or\n * INSUNITS, so it cannot carry the annotated multi-layer output required here.\n *\n * Emits a strict AC1015 (R2000) DXF: `INSUNITS=4` (mm), the outline polyline on\n * layer OUTLINE, each bend line on BEND_UP / BEND_DOWN, and an MTEXT\n * angle/direction annotation (e.g. \"∠90° U\") at each bend-line midpoint. Every\n * table record and entity carries a unique handle and the AcDb subclass markers\n * R13+ readers (AutoCAD AUDIT, ODA/Teigha) require to parse the file cleanly.\n *\n * @param pattern - the flat pattern to serialize.\n * @param options - writer options (e.g. {@link DxfOptions.textHeight}).\n * @returns `Result<string>` — the complete DXF file text when `ok`.\n */\nexport function flatPatternToDXF(pattern: FlatPattern, options: DxfOptions = {}): Result<string> {\n return multiPatternToDXF([{ pattern, transform: IDENTITY }], options);\n}\n\n/** One pattern plus the sheet-placement transform applied to all its geometry. */\nexport interface PlacedPattern {\n pattern: FlatPattern;\n transform: Transform2;\n}\n\n/**\n * Emit one DXF for any number of placed patterns (the nesting per-sheet writer).\n * Each pattern's outline / bend lines / holes / forms are run through its own\n * {@link Transform2} (translate + rotate onto the sheet) before being written, so\n * the nested file is fabrication-ready. Layers/colors/handle scheme match the\n * single-pattern writer exactly.\n *\n * @param placed - the patterns to serialize, each with its sheet-placement transform.\n * @param options - writer options (e.g. {@link DxfOptions.textHeight}).\n * @returns `Result<string>` — the complete DXF file text when `ok`.\n */\nexport function multiPatternToDXF(placed: PlacedPattern[], options: DxfOptions = {}): Result<string> {\n const textHeight = options.textHeight ?? DEFAULT_TEXT_HEIGHT;\n if (!Number.isFinite(textHeight) || textHeight <= 0) {\n return err(validationError('INVALID_TEXT_HEIGHT', `textHeight must be a finite, positive number, got ${textHeight}`));\n }\n\n const prepared: { pattern: FlatPattern; transform: Transform2; outline: Pt2[] }[] = [];\n for (const p of placed) {\n const outlineResult = outlinePoints(p.pattern);\n if (!outlineResult.ok) return outlineResult;\n prepared.push({ pattern: p.pattern, transform: p.transform, outline: outlineResult.value });\n }\n\n // Tables + entities consume handles; the header's $HANDSEED must exceed them,\n // so build the body first and seed the header from the next free handle.\n const body = new DxfWriter();\n writeTables(body);\n body.pair(0, 'SECTION');\n body.pair(2, 'ENTITIES');\n for (const p of prepared) {\n writePatternEntities(body, p.outline, p.pattern, textHeight, p.transform);\n }\n body.pair(0, 'ENDSEC');\n\n const head = new DxfWriter();\n writeHeader(head, body.seedHex());\n\n return ok([head.text(), body.text(), '0', 'EOF'].join('\\n') + '\\n');\n}\n\nclass DxfWriter {\n private readonly lines: string[] = [];\n private nextHandle = 0xa0;\n\n pair(code: number, value: string | number): void {\n this.lines.push(String(code));\n this.lines.push(typeof value === 'number' ? String(value) : value);\n }\n\n /** Emit a unique entity/record handle (group code 5). */\n handle(): void {\n this.pair(5, this.nextHandle.toString(16).toUpperCase());\n this.nextHandle += 1;\n }\n\n /** Next free handle (hex) — written as the header `$HANDSEED`. */\n seedHex(): string {\n return this.nextHandle.toString(16).toUpperCase();\n }\n\n text(): string {\n return this.lines.join('\\n');\n }\n}\n\nfunction writeHeader(w: DxfWriter, handseed: string): void {\n w.pair(0, 'SECTION');\n w.pair(2, 'HEADER');\n w.pair(9, '$ACADVER');\n w.pair(1, 'AC1015');\n w.pair(9, '$INSUNITS');\n w.pair(70, 4);\n w.pair(9, '$HANDSEED');\n w.pair(5, handseed);\n w.pair(0, 'ENDSEC');\n}\n\nfunction writeTables(w: DxfWriter): void {\n w.pair(0, 'SECTION');\n w.pair(2, 'TABLES');\n w.pair(0, 'TABLE');\n w.pair(2, 'LAYER');\n w.handle();\n w.pair(100, 'AcDbSymbolTable');\n w.pair(70, 5);\n writeLayer(w, LAYER_OUTLINE, COLOR_OUTLINE);\n writeLayer(w, LAYER_BEND_UP, COLOR_BEND_UP);\n writeLayer(w, LAYER_BEND_DOWN, COLOR_BEND_DOWN);\n writeLayer(w, LAYER_CUTOUT, COLOR_CUTOUT);\n writeLayer(w, LAYER_FORM, COLOR_FORM);\n w.pair(0, 'ENDTAB');\n w.pair(0, 'ENDSEC');\n}\n\nfunction writeLayer(w: DxfWriter, name: string, color: number): void {\n w.pair(0, 'LAYER');\n w.handle();\n w.pair(100, 'AcDbSymbolTableRecord');\n w.pair(100, 'AcDbLayerTableRecord');\n w.pair(2, name);\n w.pair(70, 0);\n w.pair(62, color);\n w.pair(6, 'CONTINUOUS');\n}\n\nfunction writePatternEntities(w: DxfWriter, outline: Pt2[], pattern: FlatPattern, textHeight: number, tf: Transform2): void {\n writePolyline(w, outline.map(tf), LAYER_OUTLINE);\n\n for (const bend of pattern.bendLines) {\n const layer = bend.direction === 'down' ? LAYER_BEND_DOWN : LAYER_BEND_UP;\n const start = tf(toPt2(curveStartPoint(bend.line)));\n const end = tf(toPt2(curveEndPoint(bend.line)));\n writeLine(w, start, end, layer);\n const mid: Pt2 = [(start[0] + end[0]) / 2, (start[1] + end[1]) / 2];\n const label = annotation(bend.angleDeg, bend.direction);\n writeMText(w, mid, label, layer, textHeight);\n }\n\n for (const hole of pattern.holes) {\n writePolyline(w, loopPoints(hole).map(tf), LAYER_CUTOUT);\n }\n\n // Lofted/ruled transition developed boundaries: closed triangulated outlines on\n // the OUTLINE layer (each a separate developed blank from the rectilinear outline).\n for (const dev of pattern.loftedDevelopments) {\n writePolyline(w, loopPoints(dev).map(tf), LAYER_OUTLINE);\n }\n\n // Form features: louver U-cuts are OPEN three-side cut paths (the hinge side is\n // left uncut and drawn separately below), emboss footprints are closed marker\n // polylines, louver hinge lines are LINEs — all on the FORM layer.\n for (const cut of pattern.formCuts) {\n writeOpenPolyline(w, pathPoints(cut).map(tf), LAYER_FORM);\n }\n for (const marker of pattern.formMarkers) {\n writePolyline(w, loopPoints(marker).map(tf), LAYER_FORM);\n }\n for (const hinge of pattern.formHinges) {\n writeLine(w, tf(toPt2(curveStartPoint(hinge))), tf(toPt2(curveEndPoint(hinge))), LAYER_FORM);\n }\n}\n\nfunction writePolyline(w: DxfWriter, points: Pt2[], layer: string): void {\n writeLwPolyline(w, points, layer, true);\n}\n\n/** An OPEN LWPOLYLINE (no closing segment back to the first vertex). */\nfunction writeOpenPolyline(w: DxfWriter, points: Pt2[], layer: string): void {\n writeLwPolyline(w, points, layer, false);\n}\n\nfunction writeLwPolyline(w: DxfWriter, points: Pt2[], layer: string, closed: boolean): void {\n w.pair(0, 'LWPOLYLINE');\n w.handle();\n w.pair(100, 'AcDbEntity');\n w.pair(8, layer);\n w.pair(100, 'AcDbPolyline');\n w.pair(90, points.length);\n w.pair(70, closed ? 1 : 0);\n for (const [x, y] of points) {\n w.pair(10, x);\n w.pair(20, y);\n }\n}\n\nfunction writeLine(w: DxfWriter, start: Pt2, end: Pt2, layer: string): void {\n w.pair(0, 'LINE');\n w.handle();\n w.pair(100, 'AcDbEntity');\n w.pair(8, layer);\n w.pair(100, 'AcDbLine');\n w.pair(10, start[0]);\n w.pair(20, start[1]);\n w.pair(30, 0);\n w.pair(11, end[0]);\n w.pair(21, end[1]);\n w.pair(31, 0);\n}\n\nfunction writeMText(w: DxfWriter, at: Pt2, text: string, layer: string, height: number): void {\n w.pair(0, 'MTEXT');\n w.handle();\n w.pair(100, 'AcDbEntity');\n w.pair(8, layer);\n w.pair(100, 'AcDbMText');\n w.pair(10, at[0]);\n w.pair(20, at[1]);\n w.pair(30, 0);\n w.pair(40, height);\n w.pair(41, height * 10);\n w.pair(71, 5);\n w.pair(1, text);\n}\n\nfunction annotation(angleDeg: number, direction: 'up' | 'down'): string {\n const rounded = Math.round(angleDeg * 100) / 100;\n return `∠${rounded}° ${direction === 'down' ? 'D' : 'U'}`;\n}\n\nfunction outlinePoints(pattern: FlatPattern): Result<Pt2[]> {\n const edges = getEdges(pattern.outline);\n if (edges.length === 0) {\n return err(validationError('EMPTY_OUTLINE', 'flat pattern outline has no edges'));\n }\n const points: Pt2[] = [];\n for (const edge of edges) {\n points.push(toPt2(curveStartPoint(edge)));\n }\n return ok(points);\n}\n\n/** Ordered vertices of a closed cutout wire (one per edge start point). */\nfunction loopPoints(wire: Wire): Pt2[] {\n return getEdges(wire).map((e) => toPt2(curveStartPoint(e)));\n}\n\n/** Ordered vertices of an OPEN wire: every edge start plus the final endpoint. */\nfunction pathPoints(wire: Wire): Pt2[] {\n const edges = getEdges(wire);\n const pts = edges.map((e) => toPt2(curveStartPoint(e)));\n const last = edges[edges.length - 1];\n if (last !== undefined) pts.push(toPt2(curveEndPoint(last)));\n return pts;\n}\n\nfunction toPt2(v: Vec3): Pt2 {\n return [v[0], v[1]];\n}\n"]}
|
|
1
|
+
{"version":3,"file":"dxfFns.js","sourceRoot":"","sources":["../src/dxfFns.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Wire } from './types.js';\nimport {type Result, type Vec3, ok, err, validationError, getEdges, curveStartPoint, curveEndPoint} from '@faicad/faijs/api';\nimport type { FlatPattern } from './types.js';\n\n/** Options for the flat-pattern DXF writers. */\nexport interface DxfOptions {\n textHeight?: number | undefined;\n}\n\nconst DEFAULT_TEXT_HEIGHT = 2.5;\nconst LAYER_OUTLINE = 'OUTLINE';\nconst LAYER_BEND_UP = 'BEND_UP';\nconst LAYER_BEND_DOWN = 'BEND_DOWN';\nconst LAYER_CUTOUT = 'CUTOUT';\nconst LAYER_FORM = 'FORM';\n\nconst COLOR_OUTLINE = 7;\nconst COLOR_BEND_UP = 1;\nconst COLOR_BEND_DOWN = 5;\nconst COLOR_CUTOUT = 3;\nconst COLOR_FORM = 4;\n\ntype Pt2 = [number, number];\n\n/**\n * A 2D point transform applied to every emitted vertex. Used by the nesting writer\n * to place each pattern at its `(x, y)` and rotation on the sheet; the default\n * single-pattern writer uses the identity transform.\n */\nexport type Transform2 = (p: Pt2) => Pt2;\n\nconst IDENTITY: Transform2 = (p) => p;\n\n/**\n * Package-local DXF writer for sheet-metal flat patterns. The core public writer\n * (`blueprintToDXF`) is R12 LINE/POLYLINE-only with no MTEXT, layer color, or\n * INSUNITS, so it cannot carry the annotated multi-layer output required here.\n *\n * Emits a strict AC1015 (R2000) DXF: `INSUNITS=4` (mm), the outline polyline on\n * layer OUTLINE, each bend line on BEND_UP / BEND_DOWN, and an MTEXT\n * angle/direction annotation (e.g. \"∠90° U\") at each bend-line midpoint. Every\n * table record and entity carries a unique handle and the AcDb subclass markers\n * R13+ readers (AutoCAD AUDIT, ODA/Teigha) require to parse the file cleanly.\n *\n * @param pattern - the flat pattern to serialize.\n * @param options - writer options (e.g. {@link DxfOptions.textHeight}).\n * @returns `Result<string>` — the complete DXF file text when `ok`.\n */\nexport function flatPatternToDXF(pattern: FlatPattern, options: DxfOptions = {}): Result<string> {\n return multiPatternToDXF([{ pattern, transform: IDENTITY }], options);\n}\n\n/** One pattern plus the sheet-placement transform applied to all its geometry. */\nexport interface PlacedPattern {\n pattern: FlatPattern;\n transform: Transform2;\n}\n\n/**\n * Emit one DXF for any number of placed patterns (the nesting per-sheet writer).\n * Each pattern's outline / bend lines / holes / forms are run through its own\n * {@link Transform2} (translate + rotate onto the sheet) before being written, so\n * the nested file is fabrication-ready. Layers/colors/handle scheme match the\n * single-pattern writer exactly.\n *\n * @param placed - the patterns to serialize, each with its sheet-placement transform.\n * @param options - writer options (e.g. {@link DxfOptions.textHeight}).\n * @returns `Result<string>` — the complete DXF file text when `ok`.\n */\nexport function multiPatternToDXF(placed: PlacedPattern[], options: DxfOptions = {}): Result<string> {\n const textHeight = options.textHeight ?? DEFAULT_TEXT_HEIGHT;\n if (!Number.isFinite(textHeight) || textHeight <= 0) {\n return err(validationError('INVALID_TEXT_HEIGHT', `textHeight must be a finite, positive number, got ${textHeight}`));\n }\n\n const prepared: { pattern: FlatPattern; transform: Transform2; outline: Pt2[] }[] = [];\n for (const p of placed) {\n const outlineResult = outlinePoints(p.pattern);\n if (!outlineResult.ok) return outlineResult;\n prepared.push({ pattern: p.pattern, transform: p.transform, outline: outlineResult.value });\n }\n\n // Tables + entities consume handles; the header's $HANDSEED must exceed them,\n // so build the body first and seed the header from the next free handle.\n const body = new DxfWriter();\n writeTables(body);\n body.pair(0, 'SECTION');\n body.pair(2, 'ENTITIES');\n for (const p of prepared) {\n writePatternEntities(body, p.outline, p.pattern, textHeight, p.transform);\n }\n body.pair(0, 'ENDSEC');\n\n const head = new DxfWriter();\n writeHeader(head, body.seedHex());\n\n return ok([head.text(), body.text(), '0', 'EOF'].join('\\n') + '\\n');\n}\n\nclass DxfWriter {\n private readonly lines: string[] = [];\n private nextHandle = 0xa0;\n\n pair(code: number, value: string | number): void {\n this.lines.push(String(code));\n this.lines.push(typeof value === 'number' ? String(value) : value);\n }\n\n /** Emit a unique entity/record handle (group code 5). */\n handle(): void {\n this.pair(5, this.nextHandle.toString(16).toUpperCase());\n this.nextHandle += 1;\n }\n\n /** Next free handle (hex) — written as the header `$HANDSEED`. */\n seedHex(): string {\n return this.nextHandle.toString(16).toUpperCase();\n }\n\n text(): string {\n return this.lines.join('\\n');\n }\n}\n\nfunction writeHeader(w: DxfWriter, handseed: string): void {\n w.pair(0, 'SECTION');\n w.pair(2, 'HEADER');\n w.pair(9, '$ACADVER');\n w.pair(1, 'AC1015');\n w.pair(9, '$INSUNITS');\n w.pair(70, 4);\n w.pair(9, '$HANDSEED');\n w.pair(5, handseed);\n w.pair(0, 'ENDSEC');\n}\n\nfunction writeTables(w: DxfWriter): void {\n w.pair(0, 'SECTION');\n w.pair(2, 'TABLES');\n w.pair(0, 'TABLE');\n w.pair(2, 'LAYER');\n w.handle();\n w.pair(100, 'AcDbSymbolTable');\n w.pair(70, 5);\n writeLayer(w, LAYER_OUTLINE, COLOR_OUTLINE);\n writeLayer(w, LAYER_BEND_UP, COLOR_BEND_UP);\n writeLayer(w, LAYER_BEND_DOWN, COLOR_BEND_DOWN);\n writeLayer(w, LAYER_CUTOUT, COLOR_CUTOUT);\n writeLayer(w, LAYER_FORM, COLOR_FORM);\n w.pair(0, 'ENDTAB');\n w.pair(0, 'ENDSEC');\n}\n\nfunction writeLayer(w: DxfWriter, name: string, color: number): void {\n w.pair(0, 'LAYER');\n w.handle();\n w.pair(100, 'AcDbSymbolTableRecord');\n w.pair(100, 'AcDbLayerTableRecord');\n w.pair(2, name);\n w.pair(70, 0);\n w.pair(62, color);\n w.pair(6, 'CONTINUOUS');\n}\n\nfunction writePatternEntities(w: DxfWriter, outline: Pt2[], pattern: FlatPattern, textHeight: number, tf: Transform2): void {\n writePolyline(w, outline.map(tf), LAYER_OUTLINE);\n\n for (const bend of pattern.bendLines) {\n const layer = bend.direction === 'down' ? LAYER_BEND_DOWN : LAYER_BEND_UP;\n const start = tf(toPt2(curveStartPoint(bend.line)));\n const end = tf(toPt2(curveEndPoint(bend.line)));\n writeLine(w, start, end, layer);\n const mid: Pt2 = [(start[0] + end[0]) / 2, (start[1] + end[1]) / 2];\n const label = annotation(bend.angleDeg, bend.direction);\n writeMText(w, mid, label, layer, textHeight);\n }\n\n for (const hole of pattern.holes) {\n writePolyline(w, loopPoints(hole).map(tf), LAYER_CUTOUT);\n }\n\n // Lofted/ruled transition developed boundaries: closed triangulated outlines on\n // the OUTLINE layer (each a separate developed blank from the rectilinear outline).\n for (const dev of pattern.loftedDevelopments) {\n writePolyline(w, loopPoints(dev).map(tf), LAYER_OUTLINE);\n }\n\n // Form features: louver U-cuts are OPEN three-side cut paths (the hinge side is\n // left uncut and drawn separately below), emboss footprints are closed marker\n // polylines, louver hinge lines are LINEs — all on the FORM layer.\n for (const cut of pattern.formCuts) {\n writeOpenPolyline(w, pathPoints(cut).map(tf), LAYER_FORM);\n }\n for (const marker of pattern.formMarkers) {\n writePolyline(w, loopPoints(marker).map(tf), LAYER_FORM);\n }\n for (const hinge of pattern.formHinges) {\n writeLine(w, tf(toPt2(curveStartPoint(hinge))), tf(toPt2(curveEndPoint(hinge))), LAYER_FORM);\n }\n}\n\nfunction writePolyline(w: DxfWriter, points: Pt2[], layer: string): void {\n writeLwPolyline(w, points, layer, true);\n}\n\n/** An OPEN LWPOLYLINE (no closing segment back to the first vertex). */\nfunction writeOpenPolyline(w: DxfWriter, points: Pt2[], layer: string): void {\n writeLwPolyline(w, points, layer, false);\n}\n\nfunction writeLwPolyline(w: DxfWriter, points: Pt2[], layer: string, closed: boolean): void {\n w.pair(0, 'LWPOLYLINE');\n w.handle();\n w.pair(100, 'AcDbEntity');\n w.pair(8, layer);\n w.pair(100, 'AcDbPolyline');\n w.pair(90, points.length);\n w.pair(70, closed ? 1 : 0);\n for (const [x, y] of points) {\n w.pair(10, x);\n w.pair(20, y);\n }\n}\n\nfunction writeLine(w: DxfWriter, start: Pt2, end: Pt2, layer: string): void {\n w.pair(0, 'LINE');\n w.handle();\n w.pair(100, 'AcDbEntity');\n w.pair(8, layer);\n w.pair(100, 'AcDbLine');\n w.pair(10, start[0]);\n w.pair(20, start[1]);\n w.pair(30, 0);\n w.pair(11, end[0]);\n w.pair(21, end[1]);\n w.pair(31, 0);\n}\n\nfunction writeMText(w: DxfWriter, at: Pt2, text: string, layer: string, height: number): void {\n w.pair(0, 'MTEXT');\n w.handle();\n w.pair(100, 'AcDbEntity');\n w.pair(8, layer);\n w.pair(100, 'AcDbMText');\n w.pair(10, at[0]);\n w.pair(20, at[1]);\n w.pair(30, 0);\n w.pair(40, height);\n w.pair(41, height * 10);\n w.pair(71, 5);\n w.pair(1, text);\n}\n\nfunction annotation(angleDeg: number, direction: 'up' | 'down'): string {\n const rounded = Math.round(angleDeg * 100) / 100;\n return `∠${rounded}° ${direction === 'down' ? 'D' : 'U'}`;\n}\n\nfunction outlinePoints(pattern: FlatPattern): Result<Pt2[]> {\n const edges = getEdges(pattern.outline);\n if (edges.length === 0) {\n return err(validationError('EMPTY_OUTLINE', 'flat pattern outline has no edges'));\n }\n const points: Pt2[] = [];\n for (const edge of edges) {\n points.push(toPt2(curveStartPoint(edge)));\n }\n return ok(points);\n}\n\n/** Ordered vertices of a closed cutout wire (one per edge start point). */\nfunction loopPoints(wire: Wire): Pt2[] {\n return getEdges(wire).map((e) => toPt2(curveStartPoint(e)));\n}\n\n/** Ordered vertices of an OPEN wire: every edge start plus the final endpoint. */\nfunction pathPoints(wire: Wire): Pt2[] {\n const edges = getEdges(wire);\n const pts = edges.map((e) => toPt2(curveStartPoint(e)));\n const last = edges[edges.length - 1];\n if (last !== undefined) pts.push(toPt2(curveEndPoint(last)));\n return pts;\n}\n\nfunction toPt2(v: Vec3): Pt2 {\n return [v[0], v[1]];\n}\n"]}
|