@faicad/cq-compat 0.13.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/assembly-compare.d.ts +135 -0
- package/dist/assembly-compare.d.ts.map +1 -0
- package/dist/assembly-compare.js +266 -0
- package/dist/assembly-compare.js.map +1 -0
- package/dist/assembly.d.ts +102 -0
- package/dist/assembly.d.ts.map +1 -0
- package/dist/assembly.js +403 -0
- package/dist/assembly.js.map +1 -0
- package/dist/browser.d.ts +13 -0
- package/dist/browser.d.ts.map +1 -0
- package/dist/browser.js +13 -0
- package/dist/browser.js.map +1 -0
- package/dist/gear-test-harness.d.ts +79 -0
- package/dist/gear-test-harness.d.ts.map +1 -0
- package/dist/gear-test-harness.js +112 -0
- package/dist/gear-test-harness.js.map +1 -0
- package/dist/gears.d.ts +226 -0
- package/dist/gears.d.ts.map +1 -0
- package/dist/gears.js +274 -0
- package/dist/gears.js.map +1 -0
- package/dist/geom-types.d.ts +7 -0
- package/dist/geom-types.d.ts.map +1 -0
- package/dist/geom-types.js +2 -0
- package/dist/geom-types.js.map +1 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +23 -0
- package/dist/index.js.map +1 -0
- package/dist/step-compare.d.ts +83 -0
- package/dist/step-compare.d.ts.map +1 -0
- package/dist/step-compare.js +140 -0
- package/dist/step-compare.js.map +1 -0
- package/dist/transpile.d.ts +42 -0
- package/dist/transpile.d.ts.map +1 -0
- package/dist/transpile.js +557 -0
- package/dist/transpile.js.map +1 -0
- package/dist/workplane.d.ts +1216 -0
- package/dist/workplane.d.ts.map +1 -0
- package/dist/workplane.js +4026 -0
- package/dist/workplane.js.map +1 -0
- package/package.json +48 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gears.js","sourceRoot":"","sources":["../src/gears.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * gears — raw-handle primitives for the cq_gears port (E1–E6 companion layer).\n *\n * cq_gears' tooth faces are B-spline patches built from (u,v) point grids and\n * assembled by sewing — these need raw kernel construction primitives that no\n * Workplane op exposes (bsplineSurface / approximatePoints / interpolatePoints,\n * tolerant edge chaining, sew+makeSolid). The fai_cq_gears v1 port probed them\n * in its own `kernel.ts` / `geom-build.ts` / `spline-face.ts`; per the port\n * plan (§4) the implementations must live INSIDE cq-compat — this file is that\n * landing spot. fai_cq_gears only imports from here; it must not touch\n * occt-wasm directly.\n *\n * Everything here is a verbatim port of the proven v1 implementations (same\n * kernel call sequence → bit-identical geometry).\n */\n\nimport { initOcctWasm } from '@faicad/faijs'\nimport type { BrepEngineApi, BrepHandle, BrepVec3 } from '@faicad/faijs'\nimport type { Vec3 } from './geom-types'\n\n/** Rotation/mirror axis (occt-wasm shape). */\nexport interface GearAxis {\n point: BrepVec3\n direction: BrepVec3\n}\n\n/**\n * Raw occt-wasm capability surface — superset of `BrepEngineApi`.\n *\n * Declares exactly the methods the cq_gears port consumes; the probe test in\n * fai_cq_gears asserts each one exists on the live kernel, so a kernel upgrade\n * that renames a binding turns red immediately instead of failing at runtime\n * with `undefined is not a function`.\n */\nexport interface GearKernel extends BrepEngineApi {\n // ── surface / curve construction (CadQuery makeSplineApprox family) ──\n /** `GeomAPI_PointsToBSplineSurface`: point grid → B-spline surface → Face. */\n bsplineSurface(points: BrepVec3[], rows: number, cols: number): BrepHandle\n /** `GeomAPI_PointsToBSpline`: point list → approximated B-spline curve (with Tol3D). */\n approximatePoints(points: BrepVec3[], tolerance?: number): BrepHandle\n /** Cubic B-spline interpolation curve through all points. */\n interpolatePoints(points: BrepVec3[], periodic?: boolean): BrepHandle\n\n // ── topology construction ──\n /** `BRepBuilderAPI_Sewing`: faces → sewn shell (cq `make_shell` analogue). */\n sew(shapes: BrepHandle[], tolerance?: number): BrepHandle\n makeSolid(shell: BrepHandle): BrepHandle\n /** TopAbs_ShapeEnum type name (\"solid\" / \"shell\" / \"face\" …, for diagnostics/assertions). */\n getShapeType(shape: BrepHandle): string\n buildSolidFromFaces(faces: BrepHandle[], tolerance?: number): BrepHandle\n makeNonPlanarFace(wire: BrepHandle): BrepHandle\n makeFaceOnSurface(face: BrepHandle, wire: BrepHandle): BrepHandle\n outerWire(face: BrepHandle): BrepHandle\n\n // ── healing ──\n healWire(wire: BrepHandle, tolerance?: number): BrepHandle\n healFace(face: BrepHandle, tolerance?: number): BrepHandle\n\n // ── transforms / booleans ──\n rotate(shape: BrepHandle, axis: GearAxis, angleRad: number): BrepHandle\n /** `BRepPrimAPI_MakeRevol`: face/wire revolved around `axis` (cq `Workplane.revolve`, 360°=2π). */\n revolve(shape: BrepHandle, axis: GearAxis, angleRad: number): BrepHandle\n mirror(shape: BrepHandle, point: BrepVec3, normal: BrepVec3): BrepHandle\n reverseShape(shape: BrepHandle): BrepHandle\n vertexPosition(vertex: BrepHandle): BrepVec3\n split(shape: BrepHandle, tools: BrepHandle[]): BrepHandle\n fillet(solid: BrepHandle, edges: BrepHandle[], radius: number): BrepHandle\n shell(\n solid: BrepHandle, facesToRemove: BrepHandle[], thickness: number, tolerance: number,\n ): BrepHandle\n thicken(shape: BrepHandle, thickness: number, tolerance: number): BrepHandle\n makeCircleArc(\n center: BrepVec3, normal: BrepVec3, radius: number, startAngle: number, endAngle: number,\n ): BrepHandle\n makeHelixWire(\n origin: BrepVec3, axis: BrepVec3, pitch: number, height: number, radius: number,\n ): BrepHandle\n\n // ── queries ──\n getSurfaceArea(shape: BrepHandle): number\n projectPointOnFace(face: BrepHandle, point: BrepVec3): BrepVec3\n surfaceType(face: BrepHandle): string\n subShapeCount(shape: BrepHandle, type: 'vertex' | 'edge' | 'wire' | 'face' | 'shell' | 'solid'): number\n isFace(shape: BrepHandle): boolean\n isWire(shape: BrepHandle): boolean\n isShell(shape: BrepHandle): boolean\n isEdge(shape: BrepHandle): boolean\n}\n\nlet gearKernelPromise: Promise<GearKernel> | null = null\n\n/**\n * Get the raw occt-wasm kernel (process-wide singleton).\n *\n * Same instance as `initOcctWasm()` — this only asserts the richer\n * `GearKernel` type, never constructs a second kernel (handles are indices\n * into one kernel arena and must not cross instances).\n *\n * @returns the raw kernel (promise; initializes wasm on first call)\n */\nexport function getGearKernel(): Promise<GearKernel> {\n if (!gearKernelPromise) {\n gearKernelPromise = initOcctWasm().then((k) => k as unknown as GearKernel)\n }\n return gearKernelPromise\n}\n\n// ── tolerant edge chaining (port of fai_cq_gears v1 geom-build.ts) ─────────\n\n/** The two endpoints of an edge (order = the edge's parameter direction). */\nexport interface GearEdgeEnds {\n edge: BrepHandle\n a: Vec3\n b: Vec3\n}\n\nfunction gearDist(a: Vec3, b: Vec3): number {\n return Math.hypot(a.x - b.x, a.y - b.y, a.z - b.z)\n}\n\n/**\n * Endpoints of an edge, via the curve parameter domain.\n *\n * ⚠️ Endpoint red line (probed 2026-09-11): `getSubShapes(edge, 'vertex')`\n * returns fewer than 2 vertices for **B-spline curve edges** (OCCT stores no\n * explicit vertices on them), which used to make edge chaining treat every\n * edge as its own wire. The correct path is `curveParameters(edge)` →\n * `curvePointAtParam(edge, param)` — valid for ALL edge kinds (line / arc /\n * B-spline); the vertex fallback only covers degenerate edges without an\n * underlying curve.\n *\n * @param kernel raw OCCT kernel\n * @param edge edge handle\n * @returns both endpoints (closed edges: first == last)\n */\nexport function gearEdgeEnds(kernel: GearKernel, edge: BrepHandle): GearEdgeEnds {\n try {\n const { first, last } = kernel.curveParameters(edge)\n return {\n edge,\n a: kernel.curvePointAtParam(edge, first),\n b: kernel.curvePointAtParam(edge, last),\n }\n } catch {\n // Fallback: rare degenerate edges without an underlying curve.\n const vs = kernel.getSubShapes(edge, 'vertex')\n if (vs.length < 2) {\n const p = kernel.vertexPosition(vs[0])\n return { edge, a: p, b: p }\n }\n return { edge, a: kernel.vertexPosition(vs[0]), b: kernel.vertexPosition(vs[1]) }\n }\n}\n\n/**\n * TS port of OCCT `ShapeAnalysis_FreeBounds::ConnectEdgesToWires` — chain\n * unordered edges into wires by endpoint proximity within `tol`.\n *\n * faijs/occt-wasm has no such binding and `makeWire(edges)` is\n * `BRepBuilderAPI_MakeWire` — measured (2026-09-08) to silently drop edges on\n * unordered input (68 edges → 9-edge broken wire). Endpoint pairing is O(n²)\n * (n = 4·z teeth, at most a few hundred — fine).\n *\n * Gaps above OCCT precision (~1e-7) get a bridging line segment, matching\n * upstream ConnectEdgesToWires behaviour.\n *\n * @param kernel raw OCCT kernel\n * @param edges unordered edge set\n * @param tol endpoint pairing tolerance (mm); cq uses `wire_comb_tol = 1e-2`\n * @returns one or more wires, each with its edges chained head-to-tail\n */\nexport function connectEdgesToWires(\n kernel: GearKernel,\n edges: BrepHandle[],\n tol: number,\n): BrepHandle[] {\n if (!Number.isFinite(tol) || tol <= 0) {\n throw new Error(`connectEdgesToWires: tol must be a positive finite number, got ${String(tol)}`)\n }\n const pool: GearEdgeEnds[] = edges.map((e) => gearEdgeEnds(kernel, e))\n const used = new Array<boolean>(pool.length).fill(false)\n const wires: BrepHandle[] = []\n\n for (let start = 0; start < pool.length; start++) {\n if (used[start]) continue\n used[start] = true\n\n const chain: BrepHandle[] = [pool[start].edge]\n let tail = pool[start].b\n const head = pool[start].a\n\n // Chain forward: find an unused edge whose endpoint touches `tail` within tol.\n for (;;) {\n let found = -1\n let best = Infinity\n for (let j = 0; j < pool.length; j++) {\n if (used[j]) continue\n const dA = gearDist(pool[j].a, tail)\n const dB = gearDist(pool[j].b, tail)\n const d = Math.min(dA, dB)\n if (d <= tol && d < best) {\n best = d\n found = j\n }\n }\n if (found < 0) break\n used[found] = true\n const e = pool[found]\n const flip = gearDist(e.a, tail) <= gearDist(e.b, tail)\n if (best > 1e-7) {\n chain.push(kernel.makeLineEdge(tail, flip ? e.a : e.b))\n }\n // Edge points the wrong way: reverseShape makes a reversed copy.\n chain.push(flip ? e.edge : kernel.reverseShape(e.edge))\n tail = flip ? e.b : e.a\n // Back at the start ⇒ closed loop, stop.\n if (gearDist(tail, head) <= tol) break\n }\n\n // Close the loop: bridge the tail-to-head gap too.\n const endGap = gearDist(tail, head)\n if (chain.length > 1 && endGap > 1e-7 && endGap <= tol) {\n chain.push(kernel.makeLineEdge(tail, head))\n }\n\n wires.push(kernel.makeWire(chain))\n }\n return wires\n}\n\n/**\n * cq `Face.makeFromWires(outer, holes)` analogue.\n *\n * @param kernel raw OCCT kernel\n * @param outer outer wire\n * @param holes inner hole wires (may be empty)\n * @returns face handle\n */\nexport function gearFaceFromWires(\n kernel: GearKernel,\n outer: BrepHandle,\n holes: BrepHandle[] = [],\n): BrepHandle {\n const f = kernel.makeFace(outer)\n return holes.length > 0 ? kernel.addHolesInFace(f, holes) : f\n}\n\n/**\n * cq `make_shell(faces, tol)` + `Solid.makeSolid(shell)` analogue.\n *\n * A non-shell sew result is NOT silently swallowed — that is a construction\n * failure signal.\n *\n * @param kernel raw OCCT kernel\n * @param faces faces to sew\n * @param sewingTol sewing tolerance (mm, cq `shell_sewing_tol`)\n * @returns solid built from the sewn shell\n */\nexport function gearShellToSolid(\n kernel: GearKernel,\n faces: BrepHandle[],\n sewingTol: number,\n): BrepHandle {\n const shell = kernel.sew(faces, sewingTol)\n if (!kernel.isShell(shell) && !kernel.isSolid(shell)) {\n throw new Error(\n `gearShellToSolid: sew did not produce a shell (got ${String(kernel.getShapeType(shell))})`,\n )\n }\n return kernel.makeSolid(shell)\n}\n\n// ── spline tooth faces (port of fai_cq_gears v1 spline-face.ts) ────────────\n\n/**\n * Tooth-face B-spline face strategies (mirrors cq `Face.makeSplineApprox`).\n *\n * | strategy | how | semantic delta vs cq |\n * |---|---|---|\n * | `grid-approx` | `kernel.bsplineSurface(flat, rows, cols)` (also `GeomAPI_PointsToBSplineSurface`) | OCCT default DegMin/DegMax/Tol3D; cq passes 3/8/1e-2 explicitly |\n * | `row-approx-loft` | per-row `approximatePoints(row, tol)` → `loft(wires, false, false)` | curve-level tol same name/meaning; surface is skinned, not fitted at once |\n * | `row-interp-loft` | per-row `interpolatePoints(row)` → `loft` | passes through every sample point (interpolation) |\n */\nexport type GearSplineFaceStrategy = 'grid-approx' | 'row-approx-loft' | 'row-interp-loft'\n\n/** All selectable strategies (tests iterate in this order for the deviation table). */\nexport const GEAR_SPLINE_FACE_STRATEGIES: readonly GearSplineFaceStrategy[] = [\n 'grid-approx',\n 'row-approx-loft',\n 'row-interp-loft',\n]\n\n/**\n * Default strategy chosen by P0 measurement (2026-09-08).\n *\n * Per the measured table in the fai_cq_gears spike: S2's area relative\n * deviation is 5.6e-7 / max sample-point distance 2.6e-6 mm — about three\n * orders of magnitude better than S1/S3. See\n * `docs/analysis/2026-09-08-fai-cq-gears-spike.md`.\n */\nexport const DEFAULT_GEAR_SPLINE_FACE_STRATEGY: GearSplineFaceStrategy = 'row-approx-loft'\n\n/** Face building options (tolerance + degrees, aligned with cq `makeSplineApprox`). */\nexport interface GearSplineFaceOptions {\n /** Approximation tolerance (mm). cq's `spline_approx_tol`, default 1e-2. */\n tolerance?: number\n /** Surface minimum degree (cq: 3) — only meaningful for grid-approx/row-approx-loft. */\n minDeg?: number\n /** Surface maximum degree (cq: 8). */\n maxDeg?: number\n}\n\n/** A row×col point grid with its dimensions (structural — no gear types here). */\nexport interface GearSplineGrid {\n /** `rows` point rows of `cols` points each. */\n points: Vec3[][]\n rows: number\n cols: number\n}\n\n/**\n * Build a face from a row×col point grid with the given strategy.\n *\n * @throws kernel errors propagate verbatim (never swallowed — red line)\n *\n * @param kernel raw OCCT kernel\n * @param grid row×col point grid\n * @param strategy face building strategy\n * @param options tolerance/degree options\n * @returns single face handle (Face)\n */\nexport function buildGearSplineFace(\n kernel: GearKernel,\n grid: GearSplineGrid,\n strategy: GearSplineFaceStrategy,\n options: GearSplineFaceOptions = {},\n): BrepHandle {\n const tol = options.tolerance ?? 1e-2\n switch (strategy) {\n case 'grid-approx':\n return buildGearGridApprox(kernel, grid)\n case 'row-approx-loft':\n return buildGearRowLoft(kernel, grid, (row) => kernel.approximatePoints(row, tol))\n case 'row-interp-loft':\n return buildGearRowLoft(kernel, grid, (row) => kernel.interpolatePoints(row, false))\n }\n}\n\n/** S1: fit the whole grid to one B-spline surface at once. */\nfunction buildGearGridApprox(kernel: GearKernel, grid: GearSplineGrid): BrepHandle {\n const flat: Vec3[] = []\n for (const row of grid.points) for (const p of row) flat.push(p)\n return kernel.bsplineSurface(flat, grid.rows, grid.cols)\n}\n\n/** S2/S3: per-row curve → `loft` skinning. */\nfunction buildGearRowLoft(\n kernel: GearKernel,\n grid: GearSplineGrid,\n makeCurve: (row: Vec3[]) => BrepHandle,\n): BrepHandle {\n const wires = grid.points.map((row) => kernel.makeWire([makeCurve(row)]))\n // `loft(wires, isSolid=false, ruled=false)` returns a **shell** (measured\n // 2026-09-08), while cq's tooth face is a TopoDS_Face; downstream sew /\n // projectPointOnFace require a Face, so extract the sole face.\n return soleGearFace(kernel, kernel.loft(wires, false, false))\n}\n\n/**\n * Reduce a \"single-face shell\" to a Face; a Face passes through unchanged.\n *\n * @throws when the face count is not 1 (silently taking the first would mask problems)\n *\n * @param kernel raw OCCT kernel\n * @param shape any shape (Face / single-face shell / …)\n * @returns the sole Face\n */\nexport function soleGearFace(kernel: GearKernel, shape: BrepHandle): BrepHandle {\n if (kernel.isFace(shape)) return shape\n const faces = kernel.getSubShapes(shape, 'face')\n if (faces.length !== 1) {\n throw new Error(\n `soleGearFace: expected a single face, got ${faces.length} (shapeType=${String(kernel.getShapeType(shape))})`,\n )\n }\n return faces[0]\n}\n\n/** Distance from a point to a face (`projectPointOnFace`).\n *\n * @param kernel raw OCCT kernel\n * @param face target face\n * @param p query point\n * @returns euclidean distance (mm)\n */\nexport function gearDistanceToFace(kernel: GearKernel, face: BrepHandle, p: Vec3): number {\n const q = kernel.projectPointOnFace(face, p)\n return Math.hypot(q.x - p.x, q.y - p.y, q.z - p.z)\n}\n\n/** Distance statistics (max / RMS / sample count). */\nexport interface GearDeviationStats {\n max: number\n rms: number\n n: number\n}\n\n/**\n * Distance statistics of reference sample points against a face.\n *\n * Parameterization-independent: samples come from the cq side's (u,v) grid, so\n * the two surfaces may have different parameterizations.\n *\n * @param kernel raw OCCT kernel\n * @param face face under test\n * @param samplePoints reference sample points (3D)\n * @returns max distance / RMS / sample count\n */\nexport function gearFaceDeviation(\n kernel: GearKernel,\n face: BrepHandle,\n samplePoints: Vec3[],\n): GearDeviationStats {\n let max = 0\n let sumSq = 0\n for (const p of samplePoints) {\n const d = gearDistanceToFace(kernel, face, p)\n if (d > max) max = d\n sumSq += d * d\n }\n return { max, rms: Math.sqrt(sumSq / samplePoints.length), n: samplePoints.length }\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"geom-types.d.ts","sourceRoot":"","sources":["../src/geom-types.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,MAAM,WAAW,IAAI;IACnB,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;CACV"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"geom-types.js","sourceRoot":"","sources":["../src/geom-types.ts"],"names":[],"mappings":"","sourcesContent":["/** Shared 3D vector type for the gear primitive layer (occt-wasm shape). */\nexport interface Vec3 {\n x: number\n y: number\n z: number\n}\n"]}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @faicad/cq-compat — CadQuery API compatibility layer for faijs.
|
|
3
|
+
*
|
|
4
|
+
* Usage in .fai.js:
|
|
5
|
+
* import * as cq from '@faicad/cq-compat'
|
|
6
|
+
* let wp = cq.Workplane('XY')
|
|
7
|
+
* let wp1 = await cq.box(wp, 100, 80, 10)
|
|
8
|
+
* let wp2 = await cq.faces(wp1, '>Z')
|
|
9
|
+
* let wp3 = await cq.workplane(wp2)
|
|
10
|
+
* let result = await cq.hole(wp3, 5)
|
|
11
|
+
* let shape = cq.val(result)
|
|
12
|
+
*
|
|
13
|
+
* All methods are standalone functions taking Workplane as first argument
|
|
14
|
+
* (transpiler converts method chains to this form).
|
|
15
|
+
*/
|
|
16
|
+
export { Workplane, add, box, sphere, wedge, cylinder, torus, cone, rarray, rect, circle, ellipse, polygon, extrude, revolve, loft, cutBlind, cutThruAll, hole, cboreHole, cskHole, threadedHole, faces, edges, vertices, solids, workplane, center, pushPoints, moveTo, move2D, lineTo, line, vLine, hLine, vLineTo, hLineTo, polyline, close, wire, face, vertex, threePointArc, sagittaArc, radiusArc, tangentArcPoint, spline, translate, rotate, mirror, faceCompound, edgeCompound, Location, isLocation, composeLocations, moved, move, union, cut, compound, intersect, combine, fillet, chamfer, shell, val, vals, transformed, setColor, splineFace, helix, splitFace, twistExtrude, solidFromFaces, planarCap, } from './workplane.js';
|
|
17
|
+
export type { Workplane as WorkplaneType, RGB, CqLocation } from './workplane.js';
|
|
18
|
+
export { faceRef, pointRef, axisRef, constraint, constraintEx, buildAssembly, Color } from './assembly.js';
|
|
19
|
+
export { compareStepFiles, printCompareReport } from './step-compare.js';
|
|
20
|
+
export type { CompareOptions, StepCompareResult, MetricResult, TopologyStats, } from './step-compare.js';
|
|
21
|
+
export { compareAssemblyFiles, printAssemblyReport } from './assembly-compare.js';
|
|
22
|
+
export type { AssemblyCompareOptions, AssemblyCompareResult, PartCompareResult, } from './assembly-compare.js';
|
|
23
|
+
export { getGearKernel, connectEdgesToWires, gearFaceFromWires, gearShellToSolid, gearEdgeEnds, buildGearSplineFace, soleGearFace, gearDistanceToFace, gearFaceDeviation, DEFAULT_GEAR_SPLINE_FACE_STRATEGY, GEAR_SPLINE_FACE_STRATEGIES, } from './gears.js';
|
|
24
|
+
export type { GearKernel, GearAxis, GearEdgeEnds, GearSplineFaceStrategy, GearSplineFaceOptions, GearSplineGrid, GearDeviationStats, } from './gears.js';
|
|
25
|
+
export type { Vec3 } from './geom-types.js';
|
|
26
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EACL,SAAS,EACT,GAAG,EACH,GAAG,EACH,MAAM,EACN,KAAK,EACL,QAAQ,EACR,KAAK,EACL,IAAI,EACJ,MAAM,EACN,IAAI,EACJ,MAAM,EACN,OAAO,EACP,OAAO,EACP,OAAO,EACP,OAAO,EACP,IAAI,EACJ,QAAQ,EACR,UAAU,EACV,IAAI,EACJ,SAAS,EACT,OAAO,EACP,YAAY,EACZ,KAAK,EACL,KAAK,EACL,QAAQ,EACR,MAAM,EACN,SAAS,EACT,MAAM,EACN,UAAU,EACV,MAAM,EACN,MAAM,EACN,MAAM,EACN,IAAI,EACJ,KAAK,EACL,KAAK,EACL,OAAO,EACP,OAAO,EACP,QAAQ,EACR,KAAK,EACL,IAAI,EACJ,IAAI,EACJ,MAAM,EACN,aAAa,EACb,UAAU,EACV,SAAS,EACT,eAAe,EACf,MAAM,EACN,SAAS,EACT,MAAM,EACN,MAAM,EACN,YAAY,EACZ,YAAY,EACZ,QAAQ,EACR,UAAU,EACV,gBAAgB,EAChB,KAAK,EACL,IAAI,EACJ,KAAK,EACL,GAAG,EACH,QAAQ,EACR,SAAS,EACT,OAAO,EACP,MAAM,EACN,OAAO,EACP,KAAK,EACL,GAAG,EACH,IAAI,EACJ,WAAW,EACX,QAAQ,EACR,UAAU,EACV,KAAK,EACL,SAAS,EACT,YAAY,EACZ,cAAc,EACd,SAAS,GACV,MAAM,aAAa,CAAA;AACpB,YAAY,EAAE,SAAS,IAAI,aAAa,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAE9E,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AAEvG,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAA;AACrE,YAAY,EACV,cAAc,EAAE,iBAAiB,EAAE,YAAY,EAAE,aAAa,GAC/D,MAAM,gBAAgB,CAAA;AAEvB,OAAO,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAA;AAC9E,YAAY,EACV,sBAAsB,EAAE,qBAAqB,EAAE,iBAAiB,GACjE,MAAM,oBAAoB,CAAA;AAI3B,OAAO,EACL,aAAa,EACb,mBAAmB,EACnB,iBAAiB,EACjB,gBAAgB,EAChB,YAAY,EACZ,mBAAmB,EACnB,YAAY,EACZ,kBAAkB,EAClB,iBAAiB,EACjB,iCAAiC,EACjC,2BAA2B,GAC5B,MAAM,SAAS,CAAA;AAChB,YAAY,EACV,UAAU,EACV,QAAQ,EACR,YAAY,EACZ,sBAAsB,EACtB,qBAAqB,EACrB,cAAc,EACd,kBAAkB,GACnB,MAAM,SAAS,CAAA;AAChB,YAAY,EAAE,IAAI,EAAE,MAAM,cAAc,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @faicad/cq-compat — CadQuery API compatibility layer for faijs.
|
|
3
|
+
*
|
|
4
|
+
* Usage in .fai.js:
|
|
5
|
+
* import * as cq from '@faicad/cq-compat'
|
|
6
|
+
* let wp = cq.Workplane('XY')
|
|
7
|
+
* let wp1 = await cq.box(wp, 100, 80, 10)
|
|
8
|
+
* let wp2 = await cq.faces(wp1, '>Z')
|
|
9
|
+
* let wp3 = await cq.workplane(wp2)
|
|
10
|
+
* let result = await cq.hole(wp3, 5)
|
|
11
|
+
* let shape = cq.val(result)
|
|
12
|
+
*
|
|
13
|
+
* All methods are standalone functions taking Workplane as first argument
|
|
14
|
+
* (transpiler converts method chains to this form).
|
|
15
|
+
*/
|
|
16
|
+
export { Workplane, add, box, sphere, wedge, cylinder, torus, cone, rarray, rect, circle, ellipse, polygon, extrude, revolve, loft, cutBlind, cutThruAll, hole, cboreHole, cskHole, threadedHole, faces, edges, vertices, solids, workplane, center, pushPoints, moveTo, move2D, lineTo, line, vLine, hLine, vLineTo, hLineTo, polyline, close, wire, face, vertex, threePointArc, sagittaArc, radiusArc, tangentArcPoint, spline, translate, rotate, mirror, faceCompound, edgeCompound, Location, isLocation, composeLocations, moved, move, union, cut, compound, intersect, combine, fillet, chamfer, shell, val, vals, transformed, setColor, splineFace, helix, splitFace, twistExtrude, solidFromFaces, planarCap, } from './workplane.js';
|
|
17
|
+
export { faceRef, pointRef, axisRef, constraint, constraintEx, buildAssembly, Color } from './assembly.js';
|
|
18
|
+
export { compareStepFiles, printCompareReport } from './step-compare.js';
|
|
19
|
+
export { compareAssemblyFiles, printAssemblyReport } from './assembly-compare.js';
|
|
20
|
+
// Gear primitive layer (raw-handle kernel access for the cq_gears port;
|
|
21
|
+
// see gears.ts header). fai_cq_gears consumes these instead of occt-wasm.
|
|
22
|
+
export { getGearKernel, connectEdgesToWires, gearFaceFromWires, gearShellToSolid, gearEdgeEnds, buildGearSplineFace, soleGearFace, gearDistanceToFace, gearFaceDeviation, DEFAULT_GEAR_SPLINE_FACE_STRATEGY, GEAR_SPLINE_FACE_STRATEGIES, } from './gears.js';
|
|
23
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EACL,SAAS,EACT,GAAG,EACH,GAAG,EACH,MAAM,EACN,KAAK,EACL,QAAQ,EACR,KAAK,EACL,IAAI,EACJ,MAAM,EACN,IAAI,EACJ,MAAM,EACN,OAAO,EACP,OAAO,EACP,OAAO,EACP,OAAO,EACP,IAAI,EACJ,QAAQ,EACR,UAAU,EACV,IAAI,EACJ,SAAS,EACT,OAAO,EACP,YAAY,EACZ,KAAK,EACL,KAAK,EACL,QAAQ,EACR,MAAM,EACN,SAAS,EACT,MAAM,EACN,UAAU,EACV,MAAM,EACN,MAAM,EACN,MAAM,EACN,IAAI,EACJ,KAAK,EACL,KAAK,EACL,OAAO,EACP,OAAO,EACP,QAAQ,EACR,KAAK,EACL,IAAI,EACJ,IAAI,EACJ,MAAM,EACN,aAAa,EACb,UAAU,EACV,SAAS,EACT,eAAe,EACf,MAAM,EACN,SAAS,EACT,MAAM,EACN,MAAM,EACN,YAAY,EACZ,YAAY,EACZ,QAAQ,EACR,UAAU,EACV,gBAAgB,EAChB,KAAK,EACL,IAAI,EACJ,KAAK,EACL,GAAG,EACH,QAAQ,EACR,SAAS,EACT,OAAO,EACP,MAAM,EACN,OAAO,EACP,KAAK,EACL,GAAG,EACH,IAAI,EACJ,WAAW,EACX,QAAQ,EACR,UAAU,EACV,KAAK,EACL,SAAS,EACT,YAAY,EACZ,cAAc,EACd,SAAS,GACV,MAAM,aAAa,CAAA;AAGpB,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AAEvG,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAA;AAKrE,OAAO,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAA;AAK9E,wEAAwE;AACxE,0EAA0E;AAC1E,OAAO,EACL,aAAa,EACb,mBAAmB,EACnB,iBAAiB,EACjB,gBAAgB,EAChB,YAAY,EACZ,mBAAmB,EACnB,YAAY,EACZ,kBAAkB,EAClB,iBAAiB,EACjB,iCAAiC,EACjC,2BAA2B,GAC5B,MAAM,SAAS,CAAA","sourcesContent":["/**\n * @faicad/cq-compat — CadQuery API compatibility layer for faijs.\n *\n * Usage in .fai.js:\n * import * as cq from '@faicad/cq-compat'\n * let wp = cq.Workplane('XY')\n * let wp1 = await cq.box(wp, 100, 80, 10)\n * let wp2 = await cq.faces(wp1, '>Z')\n * let wp3 = await cq.workplane(wp2)\n * let result = await cq.hole(wp3, 5)\n * let shape = cq.val(result)\n *\n * All methods are standalone functions taking Workplane as first argument\n * (transpiler converts method chains to this form).\n */\n\nexport {\n Workplane,\n add,\n box,\n sphere,\n wedge,\n cylinder,\n torus,\n cone,\n rarray,\n rect,\n circle,\n ellipse,\n polygon,\n extrude,\n revolve,\n loft,\n cutBlind,\n cutThruAll,\n hole,\n cboreHole,\n cskHole,\n threadedHole,\n faces,\n edges,\n vertices,\n solids,\n workplane,\n center,\n pushPoints,\n moveTo,\n move2D,\n lineTo,\n line,\n vLine,\n hLine,\n vLineTo,\n hLineTo,\n polyline,\n close,\n wire,\n face,\n vertex,\n threePointArc,\n sagittaArc,\n radiusArc,\n tangentArcPoint,\n spline,\n translate,\n rotate,\n mirror,\n faceCompound,\n edgeCompound,\n Location,\n isLocation,\n composeLocations,\n moved,\n move,\n union,\n cut,\n compound,\n intersect,\n combine,\n fillet,\n chamfer,\n shell,\n val,\n vals,\n transformed,\n setColor,\n splineFace,\n helix,\n splitFace,\n twistExtrude,\n solidFromFaces,\n planarCap,\n} from './workplane'\nexport type { Workplane as WorkplaneType, RGB, CqLocation } from './workplane'\n\nexport { faceRef, pointRef, axisRef, constraint, constraintEx, buildAssembly, Color } from './assembly'\n\nexport { compareStepFiles, printCompareReport } from './step-compare'\nexport type {\n CompareOptions, StepCompareResult, MetricResult, TopologyStats,\n} from './step-compare'\n\nexport { compareAssemblyFiles, printAssemblyReport } from './assembly-compare'\nexport type {\n AssemblyCompareOptions, AssemblyCompareResult, PartCompareResult,\n} from './assembly-compare'\n\n// Gear primitive layer (raw-handle kernel access for the cq_gears port;\n// see gears.ts header). fai_cq_gears consumes these instead of occt-wasm.\nexport {\n getGearKernel,\n connectEdgesToWires,\n gearFaceFromWires,\n gearShellToSolid,\n gearEdgeEnds,\n buildGearSplineFace,\n soleGearFace,\n gearDistanceToFace,\n gearFaceDeviation,\n DEFAULT_GEAR_SPLINE_FACE_STRATEGY,\n GEAR_SPLINE_FACE_STRATEGIES,\n} from './gears'\nexport type {\n GearKernel,\n GearAxis,\n GearEdgeEnds,\n GearSplineFaceStrategy,\n GearSplineFaceOptions,\n GearSplineGrid,\n GearDeviationStats,\n} from './gears'\nexport type { Vec3 } from './geom-types'\n"]}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* STEP file equivalence comparison using BREP geometry.
|
|
3
|
+
*
|
|
4
|
+
* Compares two STEP files across five dimensions:
|
|
5
|
+
* 1. Bounding box
|
|
6
|
+
* 2. Volume
|
|
7
|
+
* 3. Center of mass
|
|
8
|
+
* 4. Topology statistics (face/edge/vertex/solid counts)
|
|
9
|
+
* 5. Boolean difference (A-B and B-A volumes)
|
|
10
|
+
*
|
|
11
|
+
* Usage:
|
|
12
|
+
* const result = await compareStepFiles('a.step', 'b.step')
|
|
13
|
+
* console.log(result.equivalent)
|
|
14
|
+
*/
|
|
15
|
+
import type { BrepBoundingBox, BrepVec3 } from '@faicad/faijs';
|
|
16
|
+
/** Tolerance options for comparison. */
|
|
17
|
+
export interface CompareOptions {
|
|
18
|
+
/** Absolute tolerance for bbox / center-of-mass coordinates (mm). Default 1e-4. */
|
|
19
|
+
linearTolerance?: number;
|
|
20
|
+
/** Relative tolerance for volume comparison (0.001 = 0.1%). Default 1e-4. */
|
|
21
|
+
volumeRelativeTolerance?: number;
|
|
22
|
+
/** Absolute tolerance for boolean difference volume (mm³). Default 1e-3. */
|
|
23
|
+
booleanVolumeTolerance?: number;
|
|
24
|
+
/** Whether to require topology counts to match exactly. Default true. */
|
|
25
|
+
strictTopology?: boolean;
|
|
26
|
+
}
|
|
27
|
+
/** Per-metric comparison result. */
|
|
28
|
+
export interface MetricResult<T> {
|
|
29
|
+
a: T;
|
|
30
|
+
b: T;
|
|
31
|
+
match: boolean;
|
|
32
|
+
}
|
|
33
|
+
/** Topology statistics for a shape. */
|
|
34
|
+
export interface TopologyStats {
|
|
35
|
+
faces: number;
|
|
36
|
+
edges: number;
|
|
37
|
+
vertices: number;
|
|
38
|
+
solids: number;
|
|
39
|
+
}
|
|
40
|
+
/** Full comparison result. */
|
|
41
|
+
export interface StepCompareResult {
|
|
42
|
+
fileA: string;
|
|
43
|
+
fileB: string;
|
|
44
|
+
equivalent: boolean;
|
|
45
|
+
bbox: MetricResult<BrepBoundingBox> & {
|
|
46
|
+
maxDiff: number;
|
|
47
|
+
};
|
|
48
|
+
volume: MetricResult<number> & {
|
|
49
|
+
diff: number;
|
|
50
|
+
diffPct: number;
|
|
51
|
+
};
|
|
52
|
+
centerOfMass: MetricResult<BrepVec3> & {
|
|
53
|
+
maxDiff: number;
|
|
54
|
+
};
|
|
55
|
+
topology: MetricResult<TopologyStats>;
|
|
56
|
+
booleanDiff: {
|
|
57
|
+
aMinusB: {
|
|
58
|
+
volume: number;
|
|
59
|
+
nonEmpty: boolean;
|
|
60
|
+
};
|
|
61
|
+
bMinusA: {
|
|
62
|
+
volume: number;
|
|
63
|
+
nonEmpty: boolean;
|
|
64
|
+
};
|
|
65
|
+
match: boolean;
|
|
66
|
+
};
|
|
67
|
+
details: string[];
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Compare two STEP files for geometric equivalence.
|
|
71
|
+
*
|
|
72
|
+
* @param fileA - Path to the first STEP file.
|
|
73
|
+
* @param fileB - Path to the second STEP file.
|
|
74
|
+
* @param options - Comparison tolerances.
|
|
75
|
+
* @returns Detailed comparison result with per-metric match status.
|
|
76
|
+
*/
|
|
77
|
+
export declare function compareStepFiles(fileA: string, fileB: string, options?: CompareOptions): Promise<StepCompareResult>;
|
|
78
|
+
/**
|
|
79
|
+
* Print a human-readable comparison report to stdout.
|
|
80
|
+
* @param result - The comparison result from compareStepFiles.
|
|
81
|
+
*/
|
|
82
|
+
export declare function printCompareReport(result: StepCompareResult): void;
|
|
83
|
+
//# sourceMappingURL=step-compare.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"step-compare.d.ts","sourceRoot":"","sources":["../src/step-compare.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH,OAAO,KAAK,EAA6B,eAAe,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAEzF,wCAAwC;AACxC,MAAM,WAAW,cAAc;IAC7B,mFAAmF;IACnF,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,6EAA6E;IAC7E,uBAAuB,CAAC,EAAE,MAAM,CAAA;IAChC,4EAA4E;IAC5E,sBAAsB,CAAC,EAAE,MAAM,CAAA;IAC/B,yEAAyE;IACzE,cAAc,CAAC,EAAE,OAAO,CAAA;CACzB;AAED,oCAAoC;AACpC,MAAM,WAAW,YAAY,CAAC,CAAC;IAC7B,CAAC,EAAE,CAAC,CAAA;IACJ,CAAC,EAAE,CAAC,CAAA;IACJ,KAAK,EAAE,OAAO,CAAA;CACf;AAED,uCAAuC;AACvC,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;CACf;AAED,8BAA8B;AAC9B,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,UAAU,EAAE,OAAO,CAAA;IACnB,IAAI,EAAE,YAAY,CAAC,eAAe,CAAC,GAAG;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAA;IACzD,MAAM,EAAE,YAAY,CAAC,MAAM,CAAC,GAAG;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAA;IAChE,YAAY,EAAE,YAAY,CAAC,QAAQ,CAAC,GAAG;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAA;IAC1D,QAAQ,EAAE,YAAY,CAAC,aAAa,CAAC,CAAA;IACrC,WAAW,EAAE;QACX,OAAO,EAAE;YAAE,MAAM,EAAE,MAAM,CAAC;YAAC,QAAQ,EAAE,OAAO,CAAA;SAAE,CAAA;QAC9C,OAAO,EAAE;YAAE,MAAM,EAAE,MAAM,CAAC;YAAC,QAAQ,EAAE,OAAO,CAAA;SAAE,CAAA;QAC9C,KAAK,EAAE,OAAO,CAAA;KACf,CAAA;IACD,OAAO,EAAE,MAAM,EAAE,CAAA;CAClB;AAqCD;;;;;;;GAOG;AACH,wBAAsB,gBAAgB,CACpC,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,cAAmB,GAC3B,OAAO,CAAC,iBAAiB,CAAC,CA2E5B;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,iBAAiB,GAAG,IAAI,CAclE"}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* STEP file equivalence comparison using BREP geometry.
|
|
3
|
+
*
|
|
4
|
+
* Compares two STEP files across five dimensions:
|
|
5
|
+
* 1. Bounding box
|
|
6
|
+
* 2. Volume
|
|
7
|
+
* 3. Center of mass
|
|
8
|
+
* 4. Topology statistics (face/edge/vertex/solid counts)
|
|
9
|
+
* 5. Boolean difference (A-B and B-A volumes)
|
|
10
|
+
*
|
|
11
|
+
* Usage:
|
|
12
|
+
* const result = await compareStepFiles('a.step', 'b.step')
|
|
13
|
+
* console.log(result.equivalent)
|
|
14
|
+
*/
|
|
15
|
+
import { readFileSync } from 'node:fs';
|
|
16
|
+
import { initOcctWasm } from '@faicad/faijs';
|
|
17
|
+
const DEFAULT_OPTIONS = {
|
|
18
|
+
linearTolerance: 1e-4,
|
|
19
|
+
volumeRelativeTolerance: 1e-4,
|
|
20
|
+
booleanVolumeTolerance: 1e-3,
|
|
21
|
+
strictTopology: true,
|
|
22
|
+
};
|
|
23
|
+
function bboxMaxDiff(a, b) {
|
|
24
|
+
return Math.max(Math.abs(a.xmin - b.xmin), Math.abs(a.xmax - b.xmax), Math.abs(a.ymin - b.ymin), Math.abs(a.ymax - b.ymax), Math.abs(a.zmin - b.zmin), Math.abs(a.zmax - b.zmax));
|
|
25
|
+
}
|
|
26
|
+
function comMaxDiff(a, b) {
|
|
27
|
+
return Math.max(Math.abs(a.x - b.x), Math.abs(a.y - b.y), Math.abs(a.z - b.z));
|
|
28
|
+
}
|
|
29
|
+
function getTopologyStats(kernel, shape) {
|
|
30
|
+
return {
|
|
31
|
+
faces: kernel.getSubShapes(shape, 'face').length,
|
|
32
|
+
edges: kernel.getSubShapes(shape, 'edge').length,
|
|
33
|
+
vertices: kernel.getSubShapes(shape, 'vertex').length,
|
|
34
|
+
solids: kernel.getSubShapes(shape, 'solid').length,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
function topologyMatch(a, b, strict) {
|
|
38
|
+
if (strict) {
|
|
39
|
+
return a.faces === b.faces && a.edges === b.edges && a.vertices === b.vertices && a.solids === b.solids;
|
|
40
|
+
}
|
|
41
|
+
return a.solids === b.solids;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Compare two STEP files for geometric equivalence.
|
|
45
|
+
*
|
|
46
|
+
* @param fileA - Path to the first STEP file.
|
|
47
|
+
* @param fileB - Path to the second STEP file.
|
|
48
|
+
* @param options - Comparison tolerances.
|
|
49
|
+
* @returns Detailed comparison result with per-metric match status.
|
|
50
|
+
*/
|
|
51
|
+
export async function compareStepFiles(fileA, fileB, options = {}) {
|
|
52
|
+
const opts = { ...DEFAULT_OPTIONS, ...options };
|
|
53
|
+
const details = [];
|
|
54
|
+
const kernel = await initOcctWasm();
|
|
55
|
+
const bufA = readFileSync(fileA);
|
|
56
|
+
const bufB = readFileSync(fileB);
|
|
57
|
+
// readFileSync returns pooled Buffers for small files: buf.buffer is the
|
|
58
|
+
// whole 8KB pool (with garbage beyond byteLength), which makes importStep
|
|
59
|
+
// fail on every STEP < ~4KB (e.g. single-sphere files). Pass an exact copy.
|
|
60
|
+
const exact = (buf) => buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
|
|
61
|
+
const shapeA = kernel.importStep(exact(bufA));
|
|
62
|
+
const shapeB = kernel.importStep(exact(bufB));
|
|
63
|
+
// 1. Bounding box
|
|
64
|
+
const bboxA = kernel.getBoundingBox(shapeA);
|
|
65
|
+
const bboxB = kernel.getBoundingBox(shapeB);
|
|
66
|
+
const bboxDiff = bboxMaxDiff(bboxA, bboxB);
|
|
67
|
+
const bboxMatch = bboxDiff <= opts.linearTolerance;
|
|
68
|
+
details.push(`bbox diff: ${bboxDiff.toExponential(3)} mm (tol ${opts.linearTolerance})`);
|
|
69
|
+
// 2. Volume
|
|
70
|
+
const volA = kernel.getVolume(shapeA);
|
|
71
|
+
const volB = kernel.getVolume(shapeB);
|
|
72
|
+
const volDiff = Math.abs(volA - volB);
|
|
73
|
+
const volPct = volA > 0 ? (volDiff / volA) * 100 : 0;
|
|
74
|
+
const volMatch = volPct <= opts.volumeRelativeTolerance * 100;
|
|
75
|
+
details.push(`volume diff: ${volDiff.toExponential(3)} mm³ (${volPct.toFixed(4)}%, tol ${opts.volumeRelativeTolerance * 100}%)`);
|
|
76
|
+
// 3. Center of mass
|
|
77
|
+
const comA = kernel.getCenterOfMass(shapeA);
|
|
78
|
+
const comB = kernel.getCenterOfMass(shapeB);
|
|
79
|
+
const comDiff = comMaxDiff(comA, comB);
|
|
80
|
+
const comMatch = comDiff <= opts.linearTolerance;
|
|
81
|
+
details.push(`center-of-mass diff: ${comDiff.toExponential(3)} mm (tol ${opts.linearTolerance})`);
|
|
82
|
+
// 4. Topology
|
|
83
|
+
const topoA = getTopologyStats(kernel, shapeA);
|
|
84
|
+
const topoB = getTopologyStats(kernel, shapeB);
|
|
85
|
+
const topoMatch = topologyMatch(topoA, topoB, opts.strictTopology);
|
|
86
|
+
details.push(`topology: A(f=${topoA.faces},e=${topoA.edges},v=${topoA.vertices},s=${topoA.solids}) B(f=${topoB.faces},e=${topoB.edges},v=${topoB.vertices},s=${topoB.solids})`);
|
|
87
|
+
// 5. Boolean difference
|
|
88
|
+
const aMinusB = kernel.cut(shapeA, shapeB);
|
|
89
|
+
const bMinusA = kernel.cut(shapeB, shapeA);
|
|
90
|
+
const volAB = kernel.getVolume(aMinusB);
|
|
91
|
+
const volBA = kernel.getVolume(bMinusA);
|
|
92
|
+
const boolMatch = volAB <= opts.booleanVolumeTolerance && volBA <= opts.booleanVolumeTolerance;
|
|
93
|
+
details.push(`boolean diff: A-B=${volAB.toExponential(3)} mm³, B-A=${volBA.toExponential(3)} mm³ (tol ${opts.booleanVolumeTolerance})`);
|
|
94
|
+
// Cleanup
|
|
95
|
+
kernel.release(aMinusB);
|
|
96
|
+
kernel.release(bMinusA);
|
|
97
|
+
kernel.release(shapeA);
|
|
98
|
+
kernel.release(shapeB);
|
|
99
|
+
const equivalent = bboxMatch && volMatch && comMatch && topoMatch && boolMatch;
|
|
100
|
+
return {
|
|
101
|
+
fileA,
|
|
102
|
+
fileB,
|
|
103
|
+
equivalent,
|
|
104
|
+
bbox: { a: bboxA, b: bboxB, match: bboxMatch, maxDiff: bboxDiff },
|
|
105
|
+
volume: { a: volA, b: volB, match: volMatch, diff: volDiff, diffPct: volPct },
|
|
106
|
+
centerOfMass: { a: comA, b: comB, match: comMatch, maxDiff: comDiff },
|
|
107
|
+
topology: { a: topoA, b: topoB, match: topoMatch },
|
|
108
|
+
booleanDiff: {
|
|
109
|
+
aMinusB: { volume: volAB, nonEmpty: volAB > opts.booleanVolumeTolerance },
|
|
110
|
+
bMinusA: { volume: volBA, nonEmpty: volBA > opts.booleanVolumeTolerance },
|
|
111
|
+
match: boolMatch,
|
|
112
|
+
},
|
|
113
|
+
details,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Print a human-readable comparison report to stdout.
|
|
118
|
+
* @param result - The comparison result from compareStepFiles.
|
|
119
|
+
*/
|
|
120
|
+
export function printCompareReport(result) {
|
|
121
|
+
console.log(`\n=== STEP Comparison: ${result.fileA} vs ${result.fileB} ===`);
|
|
122
|
+
console.log(`Overall: ${result.equivalent ? '✓ EQUIVALENT' : '✗ DIFFERENT'}\n`);
|
|
123
|
+
for (const d of result.details) {
|
|
124
|
+
console.log(` ${d}`);
|
|
125
|
+
}
|
|
126
|
+
console.log('');
|
|
127
|
+
if (!result.equivalent) {
|
|
128
|
+
if (!result.bbox.match)
|
|
129
|
+
console.log(' ✗ Bounding box mismatch');
|
|
130
|
+
if (!result.volume.match)
|
|
131
|
+
console.log(' ✗ Volume mismatch');
|
|
132
|
+
if (!result.centerOfMass.match)
|
|
133
|
+
console.log(' ✗ Center of mass mismatch');
|
|
134
|
+
if (!result.topology.match)
|
|
135
|
+
console.log(' ✗ Topology mismatch');
|
|
136
|
+
if (!result.booleanDiff.match)
|
|
137
|
+
console.log(' ✗ Boolean difference non-empty');
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
//# sourceMappingURL=step-compare.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"step-compare.js","sourceRoot":"","sources":["../src/step-compare.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * STEP file equivalence comparison using BREP geometry.\n *\n * Compares two STEP files across five dimensions:\n * 1. Bounding box\n * 2. Volume\n * 3. Center of mass\n * 4. Topology statistics (face/edge/vertex/solid counts)\n * 5. Boolean difference (A-B and B-A volumes)\n *\n * Usage:\n * const result = await compareStepFiles('a.step', 'b.step')\n * console.log(result.equivalent)\n */\n\nimport { readFileSync } from 'node:fs'\nimport { initOcctWasm } from '@faicad/faijs'\nimport type { BrepEngineApi, BrepHandle, BrepBoundingBox, BrepVec3 } from '@faicad/faijs'\n\n/** Tolerance options for comparison. */\nexport interface CompareOptions {\n /** Absolute tolerance for bbox / center-of-mass coordinates (mm). Default 1e-4. */\n linearTolerance?: number\n /** Relative tolerance for volume comparison (0.001 = 0.1%). Default 1e-4. */\n volumeRelativeTolerance?: number\n /** Absolute tolerance for boolean difference volume (mm³). Default 1e-3. */\n booleanVolumeTolerance?: number\n /** Whether to require topology counts to match exactly. Default true. */\n strictTopology?: boolean\n}\n\n/** Per-metric comparison result. */\nexport interface MetricResult<T> {\n a: T\n b: T\n match: boolean\n}\n\n/** Topology statistics for a shape. */\nexport interface TopologyStats {\n faces: number\n edges: number\n vertices: number\n solids: number\n}\n\n/** Full comparison result. */\nexport interface StepCompareResult {\n fileA: string\n fileB: string\n equivalent: boolean\n bbox: MetricResult<BrepBoundingBox> & { maxDiff: number }\n volume: MetricResult<number> & { diff: number; diffPct: number }\n centerOfMass: MetricResult<BrepVec3> & { maxDiff: number }\n topology: MetricResult<TopologyStats>\n booleanDiff: {\n aMinusB: { volume: number; nonEmpty: boolean }\n bMinusA: { volume: number; nonEmpty: boolean }\n match: boolean\n }\n details: string[]\n}\n\nconst DEFAULT_OPTIONS: Required<CompareOptions> = {\n linearTolerance: 1e-4,\n volumeRelativeTolerance: 1e-4,\n booleanVolumeTolerance: 1e-3,\n strictTopology: true,\n}\n\nfunction bboxMaxDiff(a: BrepBoundingBox, b: BrepBoundingBox): number {\n return Math.max(\n Math.abs(a.xmin - b.xmin), Math.abs(a.xmax - b.xmax),\n Math.abs(a.ymin - b.ymin), Math.abs(a.ymax - b.ymax),\n Math.abs(a.zmin - b.zmin), Math.abs(a.zmax - b.zmax),\n )\n}\n\nfunction comMaxDiff(a: BrepVec3, b: BrepVec3): number {\n return Math.max(Math.abs(a.x - b.x), Math.abs(a.y - b.y), Math.abs(a.z - b.z))\n}\n\nfunction getTopologyStats(kernel: BrepEngineApi, shape: BrepHandle): TopologyStats {\n return {\n faces: kernel.getSubShapes(shape, 'face').length,\n edges: kernel.getSubShapes(shape, 'edge').length,\n vertices: kernel.getSubShapes(shape, 'vertex').length,\n solids: kernel.getSubShapes(shape, 'solid').length,\n }\n}\n\nfunction topologyMatch(a: TopologyStats, b: TopologyStats, strict: boolean): boolean {\n if (strict) {\n return a.faces === b.faces && a.edges === b.edges && a.vertices === b.vertices && a.solids === b.solids\n }\n return a.solids === b.solids\n}\n\n/**\n * Compare two STEP files for geometric equivalence.\n *\n * @param fileA - Path to the first STEP file.\n * @param fileB - Path to the second STEP file.\n * @param options - Comparison tolerances.\n * @returns Detailed comparison result with per-metric match status.\n */\nexport async function compareStepFiles(\n fileA: string,\n fileB: string,\n options: CompareOptions = {},\n): Promise<StepCompareResult> {\n const opts = { ...DEFAULT_OPTIONS, ...options }\n const details: string[] = []\n\n const kernel = await initOcctWasm()\n\n const bufA = readFileSync(fileA)\n const bufB = readFileSync(fileB)\n // readFileSync returns pooled Buffers for small files: buf.buffer is the\n // whole 8KB pool (with garbage beyond byteLength), which makes importStep\n // fail on every STEP < ~4KB (e.g. single-sphere files). Pass an exact copy.\n const exact = (buf: Buffer): ArrayBuffer =>\n buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength) as ArrayBuffer\n const shapeA = kernel.importStep(exact(bufA))\n const shapeB = kernel.importStep(exact(bufB))\n\n // 1. Bounding box\n const bboxA = kernel.getBoundingBox(shapeA)\n const bboxB = kernel.getBoundingBox(shapeB)\n const bboxDiff = bboxMaxDiff(bboxA, bboxB)\n const bboxMatch = bboxDiff <= opts.linearTolerance\n details.push(`bbox diff: ${bboxDiff.toExponential(3)} mm (tol ${opts.linearTolerance})`)\n\n // 2. Volume\n const volA = kernel.getVolume(shapeA)\n const volB = kernel.getVolume(shapeB)\n const volDiff = Math.abs(volA - volB)\n const volPct = volA > 0 ? (volDiff / volA) * 100 : 0\n const volMatch = volPct <= opts.volumeRelativeTolerance * 100\n details.push(`volume diff: ${volDiff.toExponential(3)} mm³ (${volPct.toFixed(4)}%, tol ${opts.volumeRelativeTolerance * 100}%)`)\n\n // 3. Center of mass\n const comA = kernel.getCenterOfMass(shapeA)\n const comB = kernel.getCenterOfMass(shapeB)\n const comDiff = comMaxDiff(comA, comB)\n const comMatch = comDiff <= opts.linearTolerance\n details.push(`center-of-mass diff: ${comDiff.toExponential(3)} mm (tol ${opts.linearTolerance})`)\n\n // 4. Topology\n const topoA = getTopologyStats(kernel, shapeA)\n const topoB = getTopologyStats(kernel, shapeB)\n const topoMatch = topologyMatch(topoA, topoB, opts.strictTopology)\n details.push(`topology: A(f=${topoA.faces},e=${topoA.edges},v=${topoA.vertices},s=${topoA.solids}) B(f=${topoB.faces},e=${topoB.edges},v=${topoB.vertices},s=${topoB.solids})`)\n\n // 5. Boolean difference\n const aMinusB = kernel.cut(shapeA, shapeB)\n const bMinusA = kernel.cut(shapeB, shapeA)\n const volAB = kernel.getVolume(aMinusB)\n const volBA = kernel.getVolume(bMinusA)\n const boolMatch = volAB <= opts.booleanVolumeTolerance && volBA <= opts.booleanVolumeTolerance\n details.push(`boolean diff: A-B=${volAB.toExponential(3)} mm³, B-A=${volBA.toExponential(3)} mm³ (tol ${opts.booleanVolumeTolerance})`)\n\n // Cleanup\n kernel.release(aMinusB)\n kernel.release(bMinusA)\n kernel.release(shapeA)\n kernel.release(shapeB)\n\n const equivalent = bboxMatch && volMatch && comMatch && topoMatch && boolMatch\n\n return {\n fileA,\n fileB,\n equivalent,\n bbox: { a: bboxA, b: bboxB, match: bboxMatch, maxDiff: bboxDiff },\n volume: { a: volA, b: volB, match: volMatch, diff: volDiff, diffPct: volPct },\n centerOfMass: { a: comA, b: comB, match: comMatch, maxDiff: comDiff },\n topology: { a: topoA, b: topoB, match: topoMatch },\n booleanDiff: {\n aMinusB: { volume: volAB, nonEmpty: volAB > opts.booleanVolumeTolerance },\n bMinusA: { volume: volBA, nonEmpty: volBA > opts.booleanVolumeTolerance },\n match: boolMatch,\n },\n details,\n }\n}\n\n/**\n * Print a human-readable comparison report to stdout.\n * @param result - The comparison result from compareStepFiles.\n */\nexport function printCompareReport(result: StepCompareResult): void {\n console.log(`\\n=== STEP Comparison: ${result.fileA} vs ${result.fileB} ===`)\n console.log(`Overall: ${result.equivalent ? '✓ EQUIVALENT' : '✗ DIFFERENT'}\\n`)\n for (const d of result.details) {\n console.log(` ${d}`)\n }\n console.log('')\n if (!result.equivalent) {\n if (!result.bbox.match) console.log(' ✗ Bounding box mismatch')\n if (!result.volume.match) console.log(' ✗ Volume mismatch')\n if (!result.centerOfMass.match) console.log(' ✗ Center of mass mismatch')\n if (!result.topology.match) console.log(' ✗ Topology mismatch')\n if (!result.booleanDiff.match) console.log(' ✗ Boolean difference non-empty')\n }\n}\n"]}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CadQuery Python → .fai.js transpiler.
|
|
3
|
+
*
|
|
4
|
+
* Handles the patterns found in real-world CadQuery projects:
|
|
5
|
+
* - import cadquery as cq / import config / import math
|
|
6
|
+
* - chained Workplane calls: wp.faces(">Z").workplane().hole(d)
|
|
7
|
+
* - config constant references (inlined from config.py)
|
|
8
|
+
* - function definitions (def pin_holes(wp): ...)
|
|
9
|
+
* - list comprehensions, math.cos/radians, slicing
|
|
10
|
+
* - if __name__ == "__main__" blocks (dropped)
|
|
11
|
+
*
|
|
12
|
+
* Not a general Python transpiler — targeted at CadQuery scripts.
|
|
13
|
+
*/
|
|
14
|
+
interface AstNode {
|
|
15
|
+
_type: string;
|
|
16
|
+
[key: string]: unknown;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* extractConfig
|
|
20
|
+
* @param ast - AstNode
|
|
21
|
+
* @returns {
|
|
22
|
+
consts: Map<string, string>
|
|
23
|
+
fns: Map<string, { params: string[]; body: string[] }>
|
|
24
|
+
}
|
|
25
|
+
*/
|
|
26
|
+
export declare function extractConfig(ast: AstNode): {
|
|
27
|
+
consts: Map<string, string>;
|
|
28
|
+
fns: Map<string, {
|
|
29
|
+
params: string[];
|
|
30
|
+
body: string[];
|
|
31
|
+
}>;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* transpile
|
|
35
|
+
* @param ast - AstNode
|
|
36
|
+
* @param config - ReturnType<typeof extractConfig>
|
|
37
|
+
* @param partName - string
|
|
38
|
+
* @returns string
|
|
39
|
+
*/
|
|
40
|
+
export declare function transpile(ast: AstNode, config: ReturnType<typeof extractConfig>, partName: string): string;
|
|
41
|
+
export {};
|
|
42
|
+
//# sourceMappingURL=transpile.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transpile.d.ts","sourceRoot":"","sources":["../src/transpile.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAIH,UAAU,OAAO;IACf,KAAK,EAAE,MAAM,CAAA;IACb,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CACvB;AAmBD;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,GAAG;IAC3C,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC3B,GAAG,EAAE,GAAG,CAAC,MAAM,EAAE;QAAE,MAAM,EAAE,MAAM,EAAE,CAAC;QAAC,IAAI,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,CAAA;CACvD,CA6BA;AAQD;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,OAAO,aAAa,CAAC,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAqD1G"}
|