brepjs 18.81.0 → 18.82.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/brepjs.cjs CHANGED
@@ -17,7 +17,7 @@ const require_arrayAccess = require("./arrayAccess-e4H9cBfh.cjs");
17
17
  const require_surfaceBuilders = require("./surfaceBuilders-B8aVZamB.cjs");
18
18
  const require_primitiveFns = require("./primitiveFns-DEBQdEkG.cjs");
19
19
  const require_healingFns = require("./healingFns--PtL9j2K.cjs");
20
- const require_threadFns = require("./threadFns-ubwexS-v.cjs");
20
+ const require_threadFns = require("./threadFns-DOAHxp0G.cjs");
21
21
  const require_blueprintSketcher = require("./blueprintSketcher-BJPBKhF3.cjs");
22
22
  const require_helpers = require("./helpers-B8mE35Fm.cjs");
23
23
  const require_drawFns = require("./drawFns-DTpCthM5.cjs");
@@ -2444,8 +2444,11 @@ function solveMate(c, ref, dep) {
2444
2444
  switch (c.type) {
2445
2445
  case "concentric": return solveConcentric(ref, dep);
2446
2446
  case "angle": return solveAngle(ref, dep, (c.value ?? 0) * Math.PI / 180);
2447
- case "distance": return solveTranslational(ref, dep, c.value ?? 0) ?? solvePlanePair(ref, dep, c.value ?? 0);
2448
- default: return solveTranslational(ref, dep, 0) ?? solvePlanePair(ref, dep, 0);
2447
+ default: {
2448
+ const pose = solveTranslational(ref, dep, c.type === "distance" ? c.value ?? 0 : 0);
2449
+ if (!pose) throw new Error(`solveMate: unsupported entity pair escaped filter: ${ref.type}-${dep.type}`);
2450
+ return pose;
2451
+ }
2449
2452
  }
2450
2453
  }
2451
2454
  /**
@@ -6642,6 +6645,7 @@ exports.exportSTEP = require_meshFns.exportSTEP;
6642
6645
  exports.exportSTEPConfigured = require_importFns.exportSTEPConfigured;
6643
6646
  exports.exportSTL = require_meshFns.exportSTL;
6644
6647
  exports.exportThreeMF = require_importFns.exportThreeMF;
6648
+ exports.exportURDF = require_threadFns.exportURDF;
6645
6649
  exports.extrude = extrude;
6646
6650
  exports.extrudeAll = require_loftFns.extrudeAll;
6647
6651
  exports.face = require_primitiveFns.face;
@@ -6731,6 +6735,7 @@ exports.importSTL = require_importFns.importSTL;
6731
6735
  exports.importSVG = require_importFns.importSVG;
6732
6736
  exports.importSVGPathD = require_importFns.importSVGPathD;
6733
6737
  exports.importThreeMF = importThreeMF;
6738
+ exports.importURDF = require_threadFns.importURDF;
6734
6739
  exports.init = require_shapeTypes.init;
6735
6740
  exports.initFromManifold = require_shapeTypes.initFromManifold;
6736
6741
  exports.initFromOC = require_shapeTypes.initFromOC;
@@ -6796,6 +6801,7 @@ exports.iterVertices = require_topologyQueryFns.iterVertices;
6796
6801
  exports.iterWires = require_topologyQueryFns.iterWires;
6797
6802
  exports.jointTrajectory = require_threadFns.jointTrajectory;
6798
6803
  exports.jointTransform = require_threadFns.jointTransform;
6804
+ exports.jointsFromDH = require_threadFns.jointsFromDH;
6799
6805
  exports.kernelCall = require_topologyQueryFns.kernelCall;
6800
6806
  exports.kernelCallRaw = require_topologyQueryFns.kernelCallRaw;
6801
6807
  exports.kernelCallScoped = require_topologyQueryFns.kernelCallScoped;
package/dist/brepjs.js CHANGED
@@ -15,7 +15,7 @@ import { n as getAtOrThrow, r as lastOrThrow, t as firstOrThrow } from "./arrayA
15
15
  import { _ as makeThreePointArc, d as makeCircle, h as makeLine, l as makeBSplineInterpolation, n as fill, r as makeFace, s as assembleWire } from "./surfaceBuilders-hTXdNCYm.js";
16
16
  import { A as cutAll, C as threePointArc, D as wireLoop, E as wire, F as sectionToFace$1, I as slice$1, L as split$1, M as fuseAll, N as intersect$2, O as booleanPipeline, P as section$1, S as tangentArc, T as vertex, _ as polygon, a as circle, b as sphere$1, c as cylinder, d as ellipsoid, f as face, g as offsetFace, h as line, i as bsplineApprox, j as fuse$2, k as cut$2, l as ellipse, m as helix, n as bezier, o as compound, p as filledFace, r as box, s as cone, t as addHoles, u as ellipseArc, v as sewShells, w as torus$1, x as subFace, y as solid } from "./primitiveFns-BSKbI4Kl.js";
17
17
  import { A as edgesOfFace, C as shellWithEvolution, D as getNurbsCurveData, E as fuseAllBisect, F as chamferDistAngle, I as toBufferGeometryData, L as toGroupedBufferGeometryData, M as sharedEdges, N as verticesOfEdge, O as getNurbsSurfaceData, P as wiresOfFace, R as toLODGeometryData, S as intersectWithEvolution, T as cutAllBisect, _ as positionOnCurve, a as healFace, b as filletWithEvolution, c as isValid$1, d as draft$1, f as fillet$1, g as variableFillet, h as thicken$1, i as heal$1, j as facesOfEdge, k as adjacentFaces, l as solidFromShell, m as shell$1, n as fixSelfIntersection, o as healSolid, p as offset$1, r as fixShape, s as healWire, t as autoHeal, u as chamfer$1, v as chamferWithEvolution, w as checkBoolean, x as fuseWithEvolution, y as cutWithEvolution, z as toLineGeometryData } from "./healingFns-Bm-NdBj_.js";
18
- import { A as quatFromAxisAngle, B as walkAssembly, C as mechanismDOF, D as setJointValue, E as revoluteJoint, F as countNodes, G as createAssembly, I as createAssemblyNode, L as findNode, M as quatRotate, N as addChild, O as setJointValues, P as collectShapes, R as removeChild, S as jointTransform, T as prismaticJoint, U as linearPattern, V as circularPattern, W as exportAssemblySTEP, _ as inverseKinematics, a as deserializeHistory, b as cylindricalJoint, c as modifyStep, d as replayFrom, f as replayHistory, g as undoLast, h as stepsFrom, i as createRegistry, j as quatFromTo, k as sphericalJoint, l as registerOperation, m as stepCount, n as addStep, o as findStep, p as serializeHistory, r as createHistory, s as getShape, t as thread, u as registerShape, v as jointTrajectory, w as planarJoint, x as forwardKinematics, y as addJoint, z as updateNode } from "./threadFns-D_CxgFXg.js";
18
+ import { A as setJointValue, B as findNode, C as cylindricalJoint, D as planarJoint, E as mechanismDOF, F as quatRotate, H as updateNode, I as addChild, J as createAssembly, K as linearPattern, L as collectShapes, M as sphericalJoint, N as quatFromAxisAngle, O as prismaticJoint, P as quatFromTo, R as countNodes, S as addJoint, T as jointTransform, U as walkAssembly, V as removeChild, W as circularPattern, _ as exportURDF, a as deserializeHistory, b as inverseKinematics, c as modifyStep, d as replayFrom, f as replayHistory, g as undoLast, h as stepsFrom, i as createRegistry, j as setJointValues, k as revoluteJoint, l as registerOperation, m as stepCount, n as addStep, o as findStep, p as serializeHistory, q as exportAssemblySTEP, r as createHistory, s as getShape, t as thread, u as registerShape, v as importURDF, w as forwardKinematics, x as jointTrajectory, y as jointsFromDH, z as createAssemblyNode } from "./threadFns-D0TPhxRK.js";
19
19
  import { n as BaseSketcher2d, r as organiseBlueprints, t as BlueprintSketcher } from "./blueprintSketcher-EYqKWQh0.js";
20
20
  import { a as createTypedFinder, i as wireFinder, n as edgeFinder, r as faceFinder, t as getSingleFace } from "./helpers-CD8EMZ5l.js";
21
21
  import { A as sketchEllipse, D as makeBaseBox, E as deserializeDrawing, F as sketchRectangle, I as sketchRoundedRectangle, L as FaceSketcher, M as sketchHelix, N as sketchParametricFunction, O as polysideInnerRadius, P as sketchPolysides, R as Sketcher, S as drawText, _ as drawPolysides, a as drawingIntersect, b as drawSingleCircle, c as rotateDrawing, d as drawFaceOutline, f as drawProjection, g as drawPointsInterpolation, h as drawParametricFunction, i as drawingFuse, j as sketchFaceOffset, k as sketchCircle, l as scaleDrawing, m as drawEllipse, n as drawingCut, o as drawingToSketchOnPlane, p as drawCircle, r as drawingFillet, s as mirrorDrawing, t as drawingChamfer, u as translateDrawing, v as drawRectangle, w as draw, x as drawSingleEllipse, y as drawRoundedRectangle } from "./drawFns-D_LwehLr.js";
@@ -2455,8 +2455,11 @@ function solveMate(c, ref, dep) {
2455
2455
  switch (c.type) {
2456
2456
  case "concentric": return solveConcentric(ref, dep);
2457
2457
  case "angle": return solveAngle(ref, dep, (c.value ?? 0) * Math.PI / 180);
2458
- case "distance": return solveTranslational(ref, dep, c.value ?? 0) ?? solvePlanePair(ref, dep, c.value ?? 0);
2459
- default: return solveTranslational(ref, dep, 0) ?? solvePlanePair(ref, dep, 0);
2458
+ default: {
2459
+ const pose = solveTranslational(ref, dep, c.type === "distance" ? c.value ?? 0 : 0);
2460
+ if (!pose) throw new Error(`solveMate: unsupported entity pair escaped filter: ${ref.type}-${dep.type}`);
2461
+ return pose;
2462
+ }
2460
2463
  }
2461
2464
  }
2462
2465
  /**
@@ -6454,4 +6457,4 @@ var csg_exports = /* @__PURE__ */ __exportAll({
6454
6457
  withEvaluator: () => withEvaluator
6455
6458
  });
6456
6459
  //#endregion
6457
- export { BaseSketcher2d, BlueprintSketcher, BrepBugError, BrepErrorCode, BrepWrapperError, BrepkitAdapter, CompoundSketch, DEFAULT_CAPABILITIES, DEG2RAD, DisposalScope, EXACT_BREP_CAPABILITIES, FaceSketcher, HASH_CODE_MAX, OK, OcctWasmAdapter, RAD2DEG, Sketch, Sketcher, Sketches, addChild, addHoles, addJoint, addMate, addStep, adjacentFaces, all, andThen, applyGlue, applyMatrix, approximateCurve, as2D, as3D, asTopo, assignRoles, autoHeal, bezier, blueprintToDXF, booleanPipeline, booleans_exports as booleans, boss, box, bsplineApprox, bug, cameraFromPlane, cameraLookAt, captureHint, cast, castShape, castShape3D, chamfer, chamferDistAngle as chamferDistAngleShape, chamferWithEvolution, checkAllInterferences, checkBoolean, checkInterference, circle, circularPattern, classifyPointOnFace, clearMeshCache, clone, closedWire, collect, collectShapes, colorFaces, colorShape, complexExtrude, composeTransforms, compound, compoundSketchExtrude, compoundSketchFace, compoundSketchLoft, compoundSketchRevolve, computationError, computeStraightSkeleton, cone, construction_exports as construction, convexHull, cornerFinder, countNodes, createAssembly, createAssemblyNode, createBlueprint, createCamera, createCompound, createCompoundBlueprint, createDistanceQuery, createEdge, createFace, createHandle, createHistory, createKernelHandle, createMeshCache, createNamedPlane, createOperationRegistry, createPlane, createRef, createRegistry, createShell, createSolid, createTaskQueue, createVertex, createWire, createWorkerClient, createWorkerHandler, csg_exports as csg, currentQuality, curve2dBoundingBox, curve2dDistanceFrom, curve2dFirstPoint, curve2dIsOnCurve, curve2dLastPoint, curve2dParameter, curve2dSplitAt, curve2dTangentAt, curveAxis, curveEndPoint, curveIsClosed, curveIsPeriodic, curveLength, curvePeriod, curvePointAt, curveStartPoint, curveTangentAt, cut, cut2D, cutAll, cutAllBisect, cutBlueprints, cutWithEvolution, cylinder, cylindricalJoint, defaultScorer, dequeueTask, describe, deserializeDrawing, deserializeHistory, fromBREP as deserializeShape, downcast, draft, draw, drawCircle, drawEllipse, drawFaceOutline, drawParametricFunction, drawPointsInterpolation, drawPolysides, drawProjection, drawRectangle, drawRoundedRectangle, drawSingleCircle, drawSingleEllipse, drawText, drawingChamfer, drawingCut, drawingFillet, drawingFuse, drawingIntersect, drawingToSketchOnPlane, drill, edgeFinder, edgesOfFace, ellipse, ellipseArc, ellipsoid, enqueueTask, err, exportAssemblySTEP, exportDXF, exportGlb, exportGltf, exportIGES, exportOBJ, exportSTEP, exportSTEPConfigured, exportSTL, exportThreeMF, extrude, extrudeAll, face, faceAxis, faceCenter, faceFinder, faceGeomType, faceOrientation, facesOfEdge, fieldBoolean, fieldContour, fieldOffset, fieldReinit, fieldShell, fill, filledFace, fillet, filletWithEvolution, findFacesByTag, findNode, findStep, fixSelfIntersection, fixShape, flatMap, flatten, flipFaceOrientation, flipOrientation, fontMetrics, forwardKinematics, fromBREP$1 as fromBREP, fromKernelDir, fromKernelPnt, fromKernelVec, fromNullable, fuse, fuse2D, fuseAll, fuseAllBisect, fuseBlueprints, fuseWithEvolution, gearGeometry, getActiveVoxelId, getBounds, getBounds2D, getCompSolids, getCurveType, getDisposalStats, getEdges, getFaceColor, getFaceOrigins, getFaceTags, getFaces, getFont, getHashCode, getShape as getHistoryShape, getKernel, getKernelCapabilities, getKernelTier, getNurbsCurveData, getNurbsSurfaceData, getOrientation, getOrientation2D, getPerformanceStats, getShapeColor, getShapeKind, getShells, getSingleFace, getSolids, getSurfaceType, getTagMetadata, getVertices, getVoxel, getWires, guidedSweep, heal, healFace, healSolid, healWire, helix, hull, importDXF, importGLB, importIGES, importOBJ, importSTEP, importSTL, importSVG, importSVGPathD, importThreeMF, init, initFromManifold, initFromOC, initVoxel, innerWires, interpolateCurve, intersect, intersect2D, intersectBlueprints, intersectWithEvolution, invalidateShapeCache, inverseKinematics, ioNs_exports as io, ioError, is2D, is3D, isChamferRadius, isClosedWire, isCompSolid, isCompound, isDisposeRequest, isEdge, isEmpty, isEqualShape, isErr, isErrorResponse, isFace, isFilletRadius, isInitRequest, isInside2D, isLive, isManifoldShell, isNumber, isOk, isOperationRequest, isOrientedFace, isPlanarFace, isPlanarWire, isProjectionPlane, isEmpty$1 as isQueueEmpty, isSameShape, isShape1D, isShape3D, isShell, isSolid, isSuccessResponse, isValid, isValidSolid, isVertex, isWire, iterCompSolids, iterEdges, iterFaces, iterShells, iterSolids, iterTopo, iterVertices, iterWires, jointTrajectory, jointTransform, kernelCall, kernelCallRaw, kernelCallScoped, kernelError, latticeInfill, latticeInfillShape, line, linearPattern, loadFont, loft, loftAll, makeBaseBox, makeExternalGear, makeInternalGear, makePlane, makePlanetaryGear, makeProjectedEdges, manifoldShell, map, mapBoth, mapErr, match, measureArea, measureCurvatureAt, measureCurvatureAtMid, measureDistance, measureDistanceProps, measureLength, measureLinearProps, measureSurfaceProps, measureVolume, measureVolumeProps, measurement_exports as measurement, mechanismDOF, mesh, meshEdges, meshMultiLOD, minkowski, mirror, mirror2D, mirrorDrawing, mirrorJoin, modifiers_exports as modifiers, modifyStep, moduleInitError, multiSectionSweep, normalAt, offset, offsetFace, offsetMesh, offsetShape, offsetWire2D, ok, or, orElse, organiseBlueprints, orientedFace, outerWire, patterns_exports as patterns, pendingCount, pipeline, pivotPlane, planarFace, planarJoint, planarWire, planetPlacements, pocket, pointOnSurface, pointsInside, polygon, polyhedron, polysideInnerRadius, polysidesBlueprint, positionOnCurve, prewarm, primitives_exports as primitives, prismaticJoint, projectEdges, projectPointOnFace, query_exports as query, queryError, rectangularPattern, registerHandler, registerKernel, registerKernelTier, registerOperation, registerShape, registerVoxel, rejectAll, removeChild, removeHolesFromFace, repairMesh, replayFrom, replayHistory, resetDisposalStats, resetPerformanceStats, resize, resolve, resolve3D, resolveDirection, resolvePlane, resolveRef, reverseCurve, revoluteJoint, revolve, roof, rotate, rotate2D, rotateDrawing, roundedRectangleBlueprint, scale, scale2D, scaleDrawing, box$1 as sdfBox, capsule as sdfCapsule, cone$1 as sdfCone, cylinder$1 as sdfCylinder, fieldAxialRamp as sdfFieldAxialRamp, fieldClamp as sdfFieldClamp, fieldConst as sdfFieldConst, fieldFromSdf as sdfFieldFromSdf, fieldRadialRamp as sdfFieldRadialRamp, lattice as sdfLattice, plane as sdfPlane, roundedBox as sdfRoundedBox, sphere as sdfSphere, strutLattice as sdfStrutLattice, sweep as sdfSweep, torus as sdfTorus, section, sectionToFace, serializeHistory, setJointValue, setJointValues, setShapeOrigin, setTagMetadata, sewShells, shape, shapeToMeshInput, shapeType, sharedEdges, shell, shellMesh, shellShape, shellWithEvolution, simplify, sketchCircle, sketchEllipse, sketchExtrude, sketchFace, sketchFaceOffset, sketchHelix, sketchLoft, sketchOnFace2D, sketchOnPlane2D, sketchParametricFunction, sketchPolysides, sketchRectangle, sketchRevolve, sketchRoundedRectangle, sketchSweep, sketchText, sketchWires, sketcherStateError, slice, solid, solidFromShell, solveAssembly, sphere$1 as sphere, sphericalJoint, split, stepCount, stepsFrom, stretch2D, subFace, supportExtrude, supportsConstraintSketch, supportsProjection, surfaceFromGrid, surfaceFromImage, sweep$1 as sweep, tagFaces, tangentArc, tap, tapErr, textBlueprints, textMetrics, thicken, thread, threePointArc, toBREP, toBufferGeometryData, toGroupedBufferGeometryData, toKernelVec, toLODGeometryData, toLineGeometryData, toSVGPathD, toVec2, toVec3, torus$1 as torus, tpmsLattice, transformCopy, transforms_exports as transforms, translate, translate2D, translateDrawing, translatePlane, tryCatch, tryCatchAsync, twistExtrude, typeCastError, undoLast, unsupportedError, unwrap, unwrapErr, unwrapOr, unwrapOrElse, updateNode, updateRoles, uvBounds, uvCoordinates, validSolid, validatePlanetary, validationError, variableFillet, vecAdd, vecAngle, vecCross, vecDistance, vecDot, vecEquals, vecIsZero, vecLength, vecLengthSq, vecNegate, vecNormalize, vecProjectToPlane, vecRepr, vecRotate, vecScale, vecSub, vertex, vertexFinder, vertexPosition, verticesOfEdge, voxelBoolean, voxelBooleanField, voxelBooleanFieldShapes, voxelBooleanShapes, voxelField, voxelFieldFromShape, walkAssembly, windingNumbers, wire, wireFinder, wireLoop, wiresOfFace, withKernel, withKernelDir, withKernelPnt, withKernelVec, withQuality, withScope, withScopeResult, withScopeResultAsync, withTier, zip as zipResults };
6460
+ export { BaseSketcher2d, BlueprintSketcher, BrepBugError, BrepErrorCode, BrepWrapperError, BrepkitAdapter, CompoundSketch, DEFAULT_CAPABILITIES, DEG2RAD, DisposalScope, EXACT_BREP_CAPABILITIES, FaceSketcher, HASH_CODE_MAX, OK, OcctWasmAdapter, RAD2DEG, Sketch, Sketcher, Sketches, addChild, addHoles, addJoint, addMate, addStep, adjacentFaces, all, andThen, applyGlue, applyMatrix, approximateCurve, as2D, as3D, asTopo, assignRoles, autoHeal, bezier, blueprintToDXF, booleanPipeline, booleans_exports as booleans, boss, box, bsplineApprox, bug, cameraFromPlane, cameraLookAt, captureHint, cast, castShape, castShape3D, chamfer, chamferDistAngle as chamferDistAngleShape, chamferWithEvolution, checkAllInterferences, checkBoolean, checkInterference, circle, circularPattern, classifyPointOnFace, clearMeshCache, clone, closedWire, collect, collectShapes, colorFaces, colorShape, complexExtrude, composeTransforms, compound, compoundSketchExtrude, compoundSketchFace, compoundSketchLoft, compoundSketchRevolve, computationError, computeStraightSkeleton, cone, construction_exports as construction, convexHull, cornerFinder, countNodes, createAssembly, createAssemblyNode, createBlueprint, createCamera, createCompound, createCompoundBlueprint, createDistanceQuery, createEdge, createFace, createHandle, createHistory, createKernelHandle, createMeshCache, createNamedPlane, createOperationRegistry, createPlane, createRef, createRegistry, createShell, createSolid, createTaskQueue, createVertex, createWire, createWorkerClient, createWorkerHandler, csg_exports as csg, currentQuality, curve2dBoundingBox, curve2dDistanceFrom, curve2dFirstPoint, curve2dIsOnCurve, curve2dLastPoint, curve2dParameter, curve2dSplitAt, curve2dTangentAt, curveAxis, curveEndPoint, curveIsClosed, curveIsPeriodic, curveLength, curvePeriod, curvePointAt, curveStartPoint, curveTangentAt, cut, cut2D, cutAll, cutAllBisect, cutBlueprints, cutWithEvolution, cylinder, cylindricalJoint, defaultScorer, dequeueTask, describe, deserializeDrawing, deserializeHistory, fromBREP as deserializeShape, downcast, draft, draw, drawCircle, drawEllipse, drawFaceOutline, drawParametricFunction, drawPointsInterpolation, drawPolysides, drawProjection, drawRectangle, drawRoundedRectangle, drawSingleCircle, drawSingleEllipse, drawText, drawingChamfer, drawingCut, drawingFillet, drawingFuse, drawingIntersect, drawingToSketchOnPlane, drill, edgeFinder, edgesOfFace, ellipse, ellipseArc, ellipsoid, enqueueTask, err, exportAssemblySTEP, exportDXF, exportGlb, exportGltf, exportIGES, exportOBJ, exportSTEP, exportSTEPConfigured, exportSTL, exportThreeMF, exportURDF, extrude, extrudeAll, face, faceAxis, faceCenter, faceFinder, faceGeomType, faceOrientation, facesOfEdge, fieldBoolean, fieldContour, fieldOffset, fieldReinit, fieldShell, fill, filledFace, fillet, filletWithEvolution, findFacesByTag, findNode, findStep, fixSelfIntersection, fixShape, flatMap, flatten, flipFaceOrientation, flipOrientation, fontMetrics, forwardKinematics, fromBREP$1 as fromBREP, fromKernelDir, fromKernelPnt, fromKernelVec, fromNullable, fuse, fuse2D, fuseAll, fuseAllBisect, fuseBlueprints, fuseWithEvolution, gearGeometry, getActiveVoxelId, getBounds, getBounds2D, getCompSolids, getCurveType, getDisposalStats, getEdges, getFaceColor, getFaceOrigins, getFaceTags, getFaces, getFont, getHashCode, getShape as getHistoryShape, getKernel, getKernelCapabilities, getKernelTier, getNurbsCurveData, getNurbsSurfaceData, getOrientation, getOrientation2D, getPerformanceStats, getShapeColor, getShapeKind, getShells, getSingleFace, getSolids, getSurfaceType, getTagMetadata, getVertices, getVoxel, getWires, guidedSweep, heal, healFace, healSolid, healWire, helix, hull, importDXF, importGLB, importIGES, importOBJ, importSTEP, importSTL, importSVG, importSVGPathD, importThreeMF, importURDF, init, initFromManifold, initFromOC, initVoxel, innerWires, interpolateCurve, intersect, intersect2D, intersectBlueprints, intersectWithEvolution, invalidateShapeCache, inverseKinematics, ioNs_exports as io, ioError, is2D, is3D, isChamferRadius, isClosedWire, isCompSolid, isCompound, isDisposeRequest, isEdge, isEmpty, isEqualShape, isErr, isErrorResponse, isFace, isFilletRadius, isInitRequest, isInside2D, isLive, isManifoldShell, isNumber, isOk, isOperationRequest, isOrientedFace, isPlanarFace, isPlanarWire, isProjectionPlane, isEmpty$1 as isQueueEmpty, isSameShape, isShape1D, isShape3D, isShell, isSolid, isSuccessResponse, isValid, isValidSolid, isVertex, isWire, iterCompSolids, iterEdges, iterFaces, iterShells, iterSolids, iterTopo, iterVertices, iterWires, jointTrajectory, jointTransform, jointsFromDH, kernelCall, kernelCallRaw, kernelCallScoped, kernelError, latticeInfill, latticeInfillShape, line, linearPattern, loadFont, loft, loftAll, makeBaseBox, makeExternalGear, makeInternalGear, makePlane, makePlanetaryGear, makeProjectedEdges, manifoldShell, map, mapBoth, mapErr, match, measureArea, measureCurvatureAt, measureCurvatureAtMid, measureDistance, measureDistanceProps, measureLength, measureLinearProps, measureSurfaceProps, measureVolume, measureVolumeProps, measurement_exports as measurement, mechanismDOF, mesh, meshEdges, meshMultiLOD, minkowski, mirror, mirror2D, mirrorDrawing, mirrorJoin, modifiers_exports as modifiers, modifyStep, moduleInitError, multiSectionSweep, normalAt, offset, offsetFace, offsetMesh, offsetShape, offsetWire2D, ok, or, orElse, organiseBlueprints, orientedFace, outerWire, patterns_exports as patterns, pendingCount, pipeline, pivotPlane, planarFace, planarJoint, planarWire, planetPlacements, pocket, pointOnSurface, pointsInside, polygon, polyhedron, polysideInnerRadius, polysidesBlueprint, positionOnCurve, prewarm, primitives_exports as primitives, prismaticJoint, projectEdges, projectPointOnFace, query_exports as query, queryError, rectangularPattern, registerHandler, registerKernel, registerKernelTier, registerOperation, registerShape, registerVoxel, rejectAll, removeChild, removeHolesFromFace, repairMesh, replayFrom, replayHistory, resetDisposalStats, resetPerformanceStats, resize, resolve, resolve3D, resolveDirection, resolvePlane, resolveRef, reverseCurve, revoluteJoint, revolve, roof, rotate, rotate2D, rotateDrawing, roundedRectangleBlueprint, scale, scale2D, scaleDrawing, box$1 as sdfBox, capsule as sdfCapsule, cone$1 as sdfCone, cylinder$1 as sdfCylinder, fieldAxialRamp as sdfFieldAxialRamp, fieldClamp as sdfFieldClamp, fieldConst as sdfFieldConst, fieldFromSdf as sdfFieldFromSdf, fieldRadialRamp as sdfFieldRadialRamp, lattice as sdfLattice, plane as sdfPlane, roundedBox as sdfRoundedBox, sphere as sdfSphere, strutLattice as sdfStrutLattice, sweep as sdfSweep, torus as sdfTorus, section, sectionToFace, serializeHistory, setJointValue, setJointValues, setShapeOrigin, setTagMetadata, sewShells, shape, shapeToMeshInput, shapeType, sharedEdges, shell, shellMesh, shellShape, shellWithEvolution, simplify, sketchCircle, sketchEllipse, sketchExtrude, sketchFace, sketchFaceOffset, sketchHelix, sketchLoft, sketchOnFace2D, sketchOnPlane2D, sketchParametricFunction, sketchPolysides, sketchRectangle, sketchRevolve, sketchRoundedRectangle, sketchSweep, sketchText, sketchWires, sketcherStateError, slice, solid, solidFromShell, solveAssembly, sphere$1 as sphere, sphericalJoint, split, stepCount, stepsFrom, stretch2D, subFace, supportExtrude, supportsConstraintSketch, supportsProjection, surfaceFromGrid, surfaceFromImage, sweep$1 as sweep, tagFaces, tangentArc, tap, tapErr, textBlueprints, textMetrics, thicken, thread, threePointArc, toBREP, toBufferGeometryData, toGroupedBufferGeometryData, toKernelVec, toLODGeometryData, toLineGeometryData, toSVGPathD, toVec2, toVec3, torus$1 as torus, tpmsLattice, transformCopy, transforms_exports as transforms, translate, translate2D, translateDrawing, translatePlane, tryCatch, tryCatchAsync, twistExtrude, typeCastError, undoLast, unsupportedError, unwrap, unwrapErr, unwrapOr, unwrapOrElse, updateNode, updateRoles, uvBounds, uvCoordinates, validSolid, validatePlanetary, validationError, variableFillet, vecAdd, vecAngle, vecCross, vecDistance, vecDot, vecEquals, vecIsZero, vecLength, vecLengthSq, vecNegate, vecNormalize, vecProjectToPlane, vecRepr, vecRotate, vecScale, vecSub, vertex, vertexFinder, vertexPosition, verticesOfEdge, voxelBoolean, voxelBooleanField, voxelBooleanFieldShapes, voxelBooleanShapes, voxelField, voxelFieldFromShape, walkAssembly, windingNumbers, wire, wireFinder, wireLoop, wiresOfFace, withKernel, withKernelDir, withKernelPnt, withKernelVec, withQuality, withScope, withScopeResult, withScopeResultAsync, withTier, zip as zipResults };
package/dist/index.d.ts CHANGED
@@ -123,6 +123,8 @@ export { createAssemblyNode, addChild, removeChild, updateNode, findNode, walkAs
123
123
  export { addMate, solveAssembly, type MateConstraint, type MateEntity, type AssemblySolveResult, } from './operations/mateFns.js';
124
124
  export { revoluteJoint, prismaticJoint, cylindricalJoint, planarJoint, sphericalJoint, setJointValue, setJointValues, jointTransform, addJoint, forwardKinematics, mechanismDOF, type Joint, type JointDOF, type JointAxis, type JointType, type JointPose, type JointOptions, type CylindricalOptions, type PlanarOptions, type SphericalOptions, } from './operations/jointFns.js';
125
125
  export { inverseKinematics, jointTrajectory, type IKTarget, type IKOptions, type IKResult, type TrajectorySample, } from './operations/ikFns.js';
126
+ export { jointsFromDH, type DHRow, type DHOptions } from './operations/dhFns.js';
127
+ export { exportURDF, importURDF, type UrdfExportOptions, type UrdfDocument, } from './operations/urdfFns.js';
126
128
  export { createHistory, addStep, undoLast, findStep, getShape as getHistoryShape, stepCount, stepsFrom, registerShape, createRegistry, registerOperation, replayHistory, replayFrom, modifyStep, serializeHistory, deserializeHistory, type OperationStep, type ModelHistory, type SerializedHistory, type OperationFn, type OperationRegistry as HistoryOperationRegistry, } from './operations/historyFns.js';
127
129
  export { measureVolume, measureArea, measureLength, measureDistance, measureDistanceProps, createDistanceQuery, measureVolumeProps, measureSurfaceProps, measureLinearProps, type PhysicalProps, type VolumeProps, type SurfaceProps, type LinearProps, type DistanceProps, measureCurvatureAt, measureCurvatureAtMid, type CurvatureResult, } from './measurement/measureFns.js';
128
130
  export { checkInterference, checkAllInterferences, type InterferenceResult, type InterferencePair, } from './measurement/interferenceFns.js';
@@ -0,0 +1,34 @@
1
+ import { Joint } from './jointFns.js';
2
+ /** One row of a Denavit-Hartenberg table (angles in degrees). */
3
+ export interface DHRow {
4
+ /** Link length: translation along the (rotated) x axis. */
5
+ readonly a: number;
6
+ /** Link twist: rotation about the x axis, in degrees. */
7
+ readonly alpha: number;
8
+ /** Link offset: translation along the z axis. For prismatic, the home offset. */
9
+ readonly d: number;
10
+ /** Joint angle about z, in degrees. For revolute, the home angle. */
11
+ readonly theta: number;
12
+ /** Which parameter is the joint variable. Default `'revolute'` (θ varies). */
13
+ readonly type?: 'revolute' | 'prismatic';
14
+ /** Joint range lower bound (degrees for revolute, length for prismatic). */
15
+ readonly min?: number;
16
+ /** Joint range upper bound. */
17
+ readonly max?: number;
18
+ /** Initial joint value (clamped to range). Default 0. */
19
+ readonly value?: number;
20
+ /** Child link name produced by this row. Default `link{i+1}`. */
21
+ readonly name?: string;
22
+ }
23
+ export interface DHOptions {
24
+ /** Name of the base (root) link. Default `'base'`. */
25
+ base?: string;
26
+ }
27
+ /**
28
+ * Build a serial revolute/prismatic joint chain from a DH table. Joint `i`
29
+ * connects the previous link to row `i`'s link; its motion is the variable
30
+ * parameter (θ for revolute, d for prismatic) about/along +z, and its fixed
31
+ * link geometry is carried on `Joint.offset`. The result drives correctly
32
+ * through `forwardKinematics` and reports `rows.length` DOF via `mechanismDOF`.
33
+ */
34
+ export declare function jointsFromDH(rows: readonly DHRow[], options?: DHOptions): Joint[];
@@ -32,6 +32,13 @@ export interface Joint {
32
32
  readonly value: number;
33
33
  /** All drivable degrees of freedom, in composition order. */
34
34
  readonly dofs: readonly JointDOF[];
35
+ /**
36
+ * Optional fixed transform applied to the child frame *after* the joint
37
+ * motion (`childWorld = parentWorld ∘ jointTransform ∘ offset`). Lets a single
38
+ * joint carry a static link offset (e.g. a Denavit-Hartenberg link geometry)
39
+ * without an extra body. Defaults to identity. See `jointsFromDH`.
40
+ */
41
+ readonly offset?: JointPose;
35
42
  }
36
43
  /** A rigid transform: translation + quaternion rotation `[w, x, y, z]`. */
37
44
  export interface JointPose {
@@ -50,7 +57,12 @@ export interface JointOptions {
50
57
  export interface CylindricalOptions {
51
58
  /** Rotation DOF (degrees). Default range -180..180. */
52
59
  rotation?: JointOptions;
53
- /** Translation DOF (length). Default range 0..100. */
60
+ /**
61
+ * Translation DOF (length). Default range 0..100, matching `prismaticJoint`
62
+ * (both model a slide along an axis). This is deliberately asymmetric with
63
+ * `planarJoint`'s in-plane translations, which default to -100..100 because
64
+ * an unanchored in-plane slide is naturally bidirectional.
65
+ */
54
66
  translation?: JointOptions;
55
67
  }
56
68
  /** Per-DOF ranges for a planar joint (two in-plane translations + a rotation). */
@@ -0,0 +1,33 @@
1
+ import { Result } from '../core/result.js';
2
+ import { AssemblyNode } from './assemblyFns.js';
3
+ import { Joint } from './jointFns.js';
4
+ export interface UrdfExportOptions {
5
+ /** `<robot name="...">`. Default `'robot'`. */
6
+ name?: string;
7
+ /** Default `effort` limit emitted for every joint. Default 0. */
8
+ effort?: number;
9
+ /** Default `velocity` limit emitted for every joint. Default 0. */
10
+ velocity?: number;
11
+ /** Per-link mesh filename for a `<visual>` reference, keyed by node name. */
12
+ meshes?: Readonly<Record<string, string>>;
13
+ }
14
+ export interface UrdfDocument {
15
+ readonly name: string;
16
+ readonly links: string[];
17
+ readonly joints: Joint[];
18
+ }
19
+ /**
20
+ * Serialize an assembly's links and revolute/prismatic joints to a URDF string.
21
+ * Returns an error if any joint is multi-DOF or carries a fixed `offset`, since
22
+ * URDF cannot represent those as a single joint.
23
+ */
24
+ export declare function exportURDF(assembly: AssemblyNode, options?: UrdfExportOptions): Result<string>;
25
+ /**
26
+ * Parse a URDF document into its robot name, link names, and revolute/prismatic
27
+ * joints. `continuous` joints become revolute with a full -180..180 range;
28
+ * `fixed`/`floating`/`planar` joints are skipped (their links are still listed).
29
+ * A non-zero `<origin rpy>` is folded into the joint axis direction; its
30
+ * residual frame rotation is not represented (URDF's pre-motion frame offset has
31
+ * no single-joint brepjs equivalent).
32
+ */
33
+ export declare function importURDF(xml: string): Result<UrdfDocument>;
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_threadFns = require("./threadFns-ubwexS-v.cjs");
2
+ const require_threadFns = require("./threadFns-DOAHxp0G.cjs");
3
3
  const require_loftFns = require("./loftFns-DiSsI2PM.cjs");
4
4
  exports.addChild = require_threadFns.addChild;
5
5
  exports.addJoint = require_threadFns.addJoint;
@@ -14,14 +14,17 @@ exports.createHistory = require_threadFns.createHistory;
14
14
  exports.createRegistry = require_threadFns.createRegistry;
15
15
  exports.cylindricalJoint = require_threadFns.cylindricalJoint;
16
16
  exports.exportAssemblySTEP = require_threadFns.exportAssemblySTEP;
17
+ exports.exportURDF = require_threadFns.exportURDF;
17
18
  exports.findNode = require_threadFns.findNode;
18
19
  exports.findStep = require_threadFns.findStep;
19
20
  exports.forwardKinematics = require_threadFns.forwardKinematics;
20
21
  exports.getHistoryShape = require_threadFns.getShape;
21
22
  exports.gridPattern = require_threadFns.gridPattern;
23
+ exports.importURDF = require_threadFns.importURDF;
22
24
  exports.inverseKinematics = require_threadFns.inverseKinematics;
23
25
  exports.jointTrajectory = require_threadFns.jointTrajectory;
24
26
  exports.jointTransform = require_threadFns.jointTransform;
27
+ exports.jointsFromDH = require_threadFns.jointsFromDH;
25
28
  exports.linearPattern = require_threadFns.linearPattern;
26
29
  exports.mechanismDOF = require_threadFns.mechanismDOF;
27
30
  exports.modifyStep = require_threadFns.modifyStep;
@@ -12,6 +12,8 @@ export { linearPattern, circularPattern, gridPattern } from './operations/patter
12
12
  export { createAssemblyNode, addChild, removeChild, updateNode, findNode, walkAssembly, countNodes, collectShapes, type AssemblyNode, type AssemblyNodeOptions, } from './operations/assemblyFns.js';
13
13
  export { revoluteJoint, prismaticJoint, cylindricalJoint, planarJoint, sphericalJoint, setJointValue, setJointValues, jointTransform, addJoint, forwardKinematics, mechanismDOF, type Joint, type JointDOF, type JointAxis, type JointType, type JointPose, type JointOptions, type CylindricalOptions, type PlanarOptions, type SphericalOptions, } from './operations/jointFns.js';
14
14
  export { inverseKinematics, jointTrajectory, type IKTarget, type IKOptions, type IKResult, type TrajectorySample, } from './operations/ikFns.js';
15
+ export { jointsFromDH, type DHRow, type DHOptions } from './operations/dhFns.js';
16
+ export { exportURDF, importURDF, type UrdfExportOptions, type UrdfDocument, } from './operations/urdfFns.js';
15
17
  export { exportAssemblySTEP, type ShapeOptions, type SupportedUnit, } from './operations/exporterFns.js';
16
18
  export { createHistory, addStep, undoLast, findStep, getShape as getHistoryShape, stepCount, stepsFrom, registerShape, createRegistry, registerOperation, replayHistory, replayFrom, modifyStep, type OperationStep, type ModelHistory, type OperationFn, type OperationRegistry as HistoryOperationRegistry, } from './operations/historyFns.js';
17
19
  export { type AssemblyExporter, createAssembly } from './operations/exporters.js';
@@ -1,3 +1,3 @@
1
- import { B as walkAssembly, C as mechanismDOF, D as setJointValue, E as revoluteJoint, F as countNodes, G as createAssembly, H as gridPattern, I as createAssemblyNode, L as findNode, N as addChild, O as setJointValues, P as collectShapes, R as removeChild, S as jointTransform, T as prismaticJoint, U as linearPattern, V as circularPattern, W as exportAssemblySTEP, _ as inverseKinematics, b as cylindricalJoint, c as modifyStep, d as replayFrom, f as replayHistory, g as undoLast, h as stepsFrom, i as createRegistry, k as sphericalJoint, l as registerOperation, m as stepCount, n as addStep, o as findStep, r as createHistory, s as getShape, t as thread, u as registerShape, v as jointTrajectory, w as planarJoint, x as forwardKinematics, y as addJoint, z as updateNode } from "./threadFns-D_CxgFXg.js";
1
+ import { A as setJointValue, B as findNode, C as cylindricalJoint, D as planarJoint, E as mechanismDOF, G as gridPattern, H as updateNode, I as addChild, J as createAssembly, K as linearPattern, L as collectShapes, M as sphericalJoint, O as prismaticJoint, R as countNodes, S as addJoint, T as jointTransform, U as walkAssembly, V as removeChild, W as circularPattern, _ as exportURDF, b as inverseKinematics, c as modifyStep, d as replayFrom, f as replayHistory, g as undoLast, h as stepsFrom, i as createRegistry, j as setJointValues, k as revoluteJoint, l as registerOperation, m as stepCount, n as addStep, o as findStep, q as exportAssemblySTEP, r as createHistory, s as getShape, t as thread, u as registerShape, v as importURDF, w as forwardKinematics, x as jointTrajectory, y as jointsFromDH, z as createAssemblyNode } from "./threadFns-D0TPhxRK.js";
2
2
  import { d as twistExtrude, l as supportExtrude, o as complexExtrude, u as sweep } from "./loftFns-CsHOwded.js";
3
- export { addChild, addJoint, addStep, circularPattern, collectShapes, complexExtrude, countNodes, createAssembly, createAssemblyNode, createHistory, createRegistry, cylindricalJoint, exportAssemblySTEP, findNode, findStep, forwardKinematics, getShape as getHistoryShape, gridPattern, inverseKinematics, jointTrajectory, jointTransform, linearPattern, mechanismDOF, modifyStep, planarJoint, prismaticJoint, registerOperation, registerShape, removeChild, replayFrom, replayHistory, revoluteJoint, setJointValue, setJointValues, sphericalJoint, stepCount, stepsFrom, supportExtrude, sweep, thread, twistExtrude, undoLast, updateNode, walkAssembly };
3
+ export { addChild, addJoint, addStep, circularPattern, collectShapes, complexExtrude, countNodes, createAssembly, createAssemblyNode, createHistory, createRegistry, cylindricalJoint, exportAssemblySTEP, exportURDF, findNode, findStep, forwardKinematics, getShape as getHistoryShape, gridPattern, importURDF, inverseKinematics, jointTrajectory, jointTransform, jointsFromDH, linearPattern, mechanismDOF, modifyStep, planarJoint, prismaticJoint, registerOperation, registerShape, removeChild, replayFrom, replayHistory, revoluteJoint, setJointValue, setJointValues, sphericalJoint, stepCount, stepsFrom, supportExtrude, sweep, thread, twistExtrude, undoLast, updateNode, walkAssembly };
@@ -1,5 +1,5 @@
1
1
  import { B as createKernelHandle, R as DisposalScope, Y as _usingCtx, Z as getKernel, t as castShape } from "./shapeTypes-DfQi9Kvk.js";
2
- import { A as ok, T as isOk, b as err, d as validationError, n as computationError, r as ioError } from "./errors-DNWJsfVU.js";
2
+ import { A as ok, T as isOk, b as err, d as validationError, n as computationError, r as ioError, t as BrepErrorCode } from "./errors-DNWJsfVU.js";
3
3
  import { d as vecNormalize, s as vecIsZero } from "./vecOps-SKPRvPH-.js";
4
4
  import { y as fromBREP } from "./faceFns-Cuxdy-7R.js";
5
5
  import { s as toBREP } from "./shapeFns-BzlBMNRO.js";
@@ -344,7 +344,7 @@ function quatMultiply(a, b) {
344
344
  }
345
345
  //#endregion
346
346
  //#region src/operations/jointFns.ts
347
- var DEG2RAD = Math.PI / 180;
347
+ var DEG2RAD$2 = Math.PI / 180;
348
348
  function clamp(value, min, max) {
349
349
  return Math.min(max, Math.max(min, value));
350
350
  }
@@ -539,7 +539,7 @@ function dofPose(origin, dof, value) {
539
539
  0
540
540
  ]
541
541
  };
542
- const rotation = quatFromAxisAngle(dof.axis, value * DEG2RAD);
542
+ const rotation = quatFromAxisAngle(dof.axis, value * DEG2RAD$2);
543
543
  const ro = quatRotate(rotation, origin);
544
544
  return {
545
545
  position: [
@@ -653,7 +653,8 @@ function forwardKinematics(assembly, jointValues = {}) {
653
653
  progress = true;
654
654
  if (poses.has(j.child)) continue;
655
655
  const value = jointValues[j.child] ?? j.value;
656
- poses.set(j.child, composePose(parentPose, jointTransform(j, value)));
656
+ const local = j.offset ? composePose(jointTransform(j, value), j.offset) : jointTransform(j, value);
657
+ poses.set(j.child, composePose(parentPose, local));
657
658
  }
658
659
  }
659
660
  for (const j of pending) if (!poses.has(j.child)) poses.set(j.child, IDENTITY_POSE$1);
@@ -837,23 +838,30 @@ function residualTwist(out, pose, target, tip, m) {
837
838
  for (let i = 0; i < m; i++) s += el(out, i) ** 2;
838
839
  return Math.sqrt(s);
839
840
  }
840
- /** Finite-difference Jacobian: column `j` is the end-effector twist from δq[j]. */
841
- function fillJacobian(J, q, n, m, base, tip, eps, tipPose) {
841
+ /**
842
+ * Finite-difference Jacobian: column `j` is the end-effector twist from δq[j].
843
+ * The probe steps *inward* from a bound — `forwardKinematics` clamps each DOF to
844
+ * its range, so a forward `+eps` at the upper limit would yield a zero column and
845
+ * trap the solver at the ceiling. Stepping `-eps` there (and dividing by the
846
+ * signed step) keeps every column a true one-sided derivative.
847
+ */
848
+ function fillJacobian(J, q, n, m, lo, hi, base, tip, eps, tipPose) {
842
849
  const basePos = applyPose(base, tip);
843
850
  for (let j = 0; j < n; j++) {
844
851
  const saved = el(q, j);
845
- q[j] = saved + eps;
852
+ const h = saved + eps > el(hi, j) && saved - eps >= el(lo, j) ? -eps : eps;
853
+ q[j] = saved + h;
846
854
  const p2 = tipPose(q);
847
855
  q[j] = saved;
848
856
  const pos2 = applyPose(p2, tip);
849
- J[j] = (pos2[0] - basePos[0]) / eps;
850
- J[n + j] = (pos2[1] - basePos[1]) / eps;
851
- J[2 * n + j] = (pos2[2] - basePos[2]) / eps;
857
+ J[j] = (pos2[0] - basePos[0]) / h;
858
+ J[n + j] = (pos2[1] - basePos[1]) / h;
859
+ J[2 * n + j] = (pos2[2] - basePos[2]) / h;
852
860
  if (m === 6) {
853
861
  const dr = rotationError(base.rotation, p2.rotation);
854
- J[3 * n + j] = dr[0] / eps;
855
- J[4 * n + j] = dr[1] / eps;
856
- J[5 * n + j] = dr[2] / eps;
862
+ J[3 * n + j] = dr[0] / h;
863
+ J[4 * n + j] = dr[1] / h;
864
+ J[5 * n + j] = dr[2] / h;
857
865
  }
858
866
  }
859
867
  }
@@ -906,7 +914,7 @@ function inverseKinematics(assembly, endEffector, target, options = {}) {
906
914
  let err = residualTwist(e, pose, target, tip, m);
907
915
  let iter = 0;
908
916
  for (; iter < maxIterations && n > 0 && err > tolerance; iter++) {
909
- fillJacobian(J, q, n, m, pose, tip, eps, tipPose);
917
+ fillJacobian(J, q, n, m, lo, hi, pose, tip, eps, tipPose);
910
918
  const dq = dlsStep(J, e, n, m, lambda);
911
919
  if (!dq) break;
912
920
  for (let j = 0; j < n; j++) {
@@ -962,6 +970,260 @@ function jointTrajectory(assembly, from, to, steps) {
962
970
  return samples;
963
971
  }
964
972
  //#endregion
973
+ //#region src/operations/dhFns.ts
974
+ /**
975
+ * Denavit-Hartenberg interchange: build a brepjs joint chain from a standard
976
+ * (distal) DH parameter table.
977
+ *
978
+ * Each DH row maps the previous link frame to the next via
979
+ * `Rz(θ) · Tz(d) · Tx(a) · Rx(α)`, with one parameter the joint variable
980
+ * (θ for revolute, d for prismatic) and the rest fixed link geometry. A row
981
+ * becomes a single revolute/prismatic joint whose motion is the variable part
982
+ * and whose fixed link geometry rides on the joint's post-motion `offset`
983
+ * (see `Joint.offset`), so the chain composes correctly through
984
+ * `forwardKinematics` and each row contributes exactly one DOF.
985
+ */
986
+ var DEG2RAD$1 = Math.PI / 180;
987
+ /**
988
+ * The fixed DH link transform `Rz(θ) · Tz(d) · Tx(a) · Rx(α)` as a pose.
989
+ * Carries the constant link geometry that rides on the joint's `offset`.
990
+ */
991
+ function linkOffset(thetaDeg, d, a, alphaDeg) {
992
+ const theta = thetaDeg * DEG2RAD$1;
993
+ const alpha = alphaDeg * DEG2RAD$1;
994
+ const rotation = quatMultiply(quatFromAxisAngle([
995
+ 0,
996
+ 0,
997
+ 1
998
+ ], theta), quatFromAxisAngle([
999
+ 1,
1000
+ 0,
1001
+ 0
1002
+ ], alpha));
1003
+ return {
1004
+ position: [
1005
+ a * Math.cos(theta),
1006
+ a * Math.sin(theta),
1007
+ d
1008
+ ],
1009
+ rotation
1010
+ };
1011
+ }
1012
+ /**
1013
+ * Build a serial revolute/prismatic joint chain from a DH table. Joint `i`
1014
+ * connects the previous link to row `i`'s link; its motion is the variable
1015
+ * parameter (θ for revolute, d for prismatic) about/along +z, and its fixed
1016
+ * link geometry is carried on `Joint.offset`. The result drives correctly
1017
+ * through `forwardKinematics` and reports `rows.length` DOF via `mechanismDOF`.
1018
+ */
1019
+ function jointsFromDH(rows, options = {}) {
1020
+ const base = options.base ?? "base";
1021
+ const names = rows.map((r, i) => r.name ?? `link${i + 1}`);
1022
+ return rows.map((row, i) => {
1023
+ const parent = i === 0 ? base : names[i - 1] ?? `link${i}`;
1024
+ const child = names[i] ?? `link${i + 1}`;
1025
+ const offset = linkOffset(row.theta, row.d, row.a, row.alpha);
1026
+ const axis = {
1027
+ origin: [
1028
+ 0,
1029
+ 0,
1030
+ 0
1031
+ ],
1032
+ direction: [
1033
+ 0,
1034
+ 0,
1035
+ 1
1036
+ ]
1037
+ };
1038
+ const opts = {
1039
+ ...row.min !== void 0 ? { min: row.min } : {},
1040
+ ...row.max !== void 0 ? { max: row.max } : {},
1041
+ ...row.value !== void 0 ? { value: row.value } : {}
1042
+ };
1043
+ return {
1044
+ ...row.type === "prismatic" ? prismaticJoint(parent, child, axis, opts) : revoluteJoint(parent, child, axis, opts),
1045
+ offset
1046
+ };
1047
+ });
1048
+ }
1049
+ //#endregion
1050
+ //#region src/operations/urdfFns.ts
1051
+ /**
1052
+ * URDF interchange: export a brepjs assembly + its joints to URDF, and import a
1053
+ * URDF document back into links and joints.
1054
+ *
1055
+ * URDF natively models single-DOF joints, so `revolute` and `prismatic` joints
1056
+ * round-trip (parents, axes, and limits preserved). Multi-DOF joints
1057
+ * (cylindrical/planar/spherical) and joints carrying a fixed `offset` (e.g.
1058
+ * Denavit-Hartenberg links) have no faithful single-joint URDF representation
1059
+ * and are reported as an error on export.
1060
+ *
1061
+ * Revolute limits are radians in URDF and degrees in brepjs, so they are
1062
+ * converted on the boundary. The XML is parsed with a small URDF-shaped reader
1063
+ * (no XML dependency); it handles the standard `<robot>/<link>/<joint>` subset.
1064
+ */
1065
+ var DEG2RAD = Math.PI / 180;
1066
+ var RAD2DEG = 180 / Math.PI;
1067
+ function escapeXml(s) {
1068
+ return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
1069
+ }
1070
+ /** Format a number for XML, normalizing -0 to 0. */
1071
+ function fmt(n) {
1072
+ return String(n === 0 ? 0 : n);
1073
+ }
1074
+ function vec(v) {
1075
+ return `${fmt(v[0])} ${fmt(v[1])} ${fmt(v[2])}`;
1076
+ }
1077
+ /**
1078
+ * Serialize an assembly's links and revolute/prismatic joints to a URDF string.
1079
+ * Returns an error if any joint is multi-DOF or carries a fixed `offset`, since
1080
+ * URDF cannot represent those as a single joint.
1081
+ */
1082
+ function exportURDF(assembly, options = {}) {
1083
+ const robot = options.name ?? "robot";
1084
+ const effort = options.effort ?? 0;
1085
+ const velocity = options.velocity ?? 0;
1086
+ const meshes = options.meshes ?? {};
1087
+ const links = [];
1088
+ const joints = [];
1089
+ walkAssembly(assembly, (n) => {
1090
+ links.push(n.name);
1091
+ if (n.joints) joints.push(...n.joints);
1092
+ });
1093
+ for (const j of joints) {
1094
+ if (j.type !== "revolute" && j.type !== "prismatic") return err(validationError(BrepErrorCode.VALIDATION_FAILED, `exportURDF: joint '${j.parent}->${j.child}' is '${j.type}'; URDF supports only revolute and prismatic joints`));
1095
+ if (j.offset) return err(validationError(BrepErrorCode.VALIDATION_FAILED, `exportURDF: joint '${j.parent}->${j.child}' carries a fixed offset (e.g. a DH link) that URDF cannot represent as a single joint`));
1096
+ }
1097
+ const lines = ["<?xml version=\"1.0\"?>", `<robot name="${escapeXml(robot)}">`];
1098
+ for (const link of links) {
1099
+ const mesh = meshes[link];
1100
+ if (mesh) lines.push(` <link name="${escapeXml(link)}">`, ` <visual><geometry><mesh filename="${escapeXml(mesh)}"/></geometry></visual>`, ` </link>`);
1101
+ else lines.push(` <link name="${escapeXml(link)}"/>`);
1102
+ }
1103
+ for (const j of joints) {
1104
+ const toRad = j.type === "revolute";
1105
+ const lower = toRad ? j.min * DEG2RAD : j.min;
1106
+ const upper = toRad ? j.max * DEG2RAD : j.max;
1107
+ const origin = j.type === "prismatic" ? [
1108
+ 0,
1109
+ 0,
1110
+ 0
1111
+ ] : j.axis.origin;
1112
+ lines.push(` <joint name="${escapeXml(`${j.parent}_to_${j.child}`)}" type="${j.type}">`, ` <parent link="${escapeXml(j.parent)}"/>`, ` <child link="${escapeXml(j.child)}"/>`, ` <origin xyz="${vec(origin)}" rpy="0 0 0"/>`, ` <axis xyz="${vec(j.axis.direction)}"/>`, ` <limit lower="${fmt(lower)}" upper="${fmt(upper)}" effort="${fmt(effort)}" velocity="${fmt(velocity)}"/>`, ` </joint>`);
1113
+ }
1114
+ lines.push("</robot>", "");
1115
+ return ok(lines.join("\n"));
1116
+ }
1117
+ /** Read a string attribute from a tag's attribute text. */
1118
+ function attr(text, name) {
1119
+ return new RegExp(`\\b${name}\\s*=\\s*"([^"]*)"`).exec(text)?.[1];
1120
+ }
1121
+ /** Parse a whitespace-separated numeric triple, defaulting missing components to 0. */
1122
+ function triple(s, fallback) {
1123
+ if (!s) return fallback;
1124
+ const parts = s.trim().split(/\s+/).map(Number);
1125
+ return [
1126
+ parts[0] ?? 0,
1127
+ parts[1] ?? 0,
1128
+ parts[2] ?? 0
1129
+ ];
1130
+ }
1131
+ /** Rotate an axis by a URDF roll-pitch-yaw (fixed-axis XYZ) triple in radians. */
1132
+ function applyRpy(axis, rpy) {
1133
+ return quatRotate(quatMultiply(quatMultiply(quatFromAxisAngle([
1134
+ 0,
1135
+ 0,
1136
+ 1
1137
+ ], rpy[2]), quatFromAxisAngle([
1138
+ 0,
1139
+ 1,
1140
+ 0
1141
+ ], rpy[1])), quatFromAxisAngle([
1142
+ 1,
1143
+ 0,
1144
+ 0
1145
+ ], rpy[0])), axis);
1146
+ }
1147
+ /**
1148
+ * Parse a URDF document into its robot name, link names, and revolute/prismatic
1149
+ * joints. `continuous` joints become revolute with a full -180..180 range;
1150
+ * `fixed`/`floating`/`planar` joints are skipped (their links are still listed).
1151
+ * A non-zero `<origin rpy>` is folded into the joint axis direction; its
1152
+ * residual frame rotation is not represented (URDF's pre-motion frame offset has
1153
+ * no single-joint brepjs equivalent).
1154
+ */
1155
+ function importURDF(xml) {
1156
+ const robotMatch = /<robot\b([^>]*)>/.exec(xml);
1157
+ if (!robotMatch) return err(validationError(BrepErrorCode.VALIDATION_FAILED, "importURDF: no <robot> element found"));
1158
+ const name = attr(robotMatch[1] ?? "", "name") ?? "robot";
1159
+ const links = [];
1160
+ const linkRe = /<link\b([^>]*?)(?:\/>|>)/g;
1161
+ for (let m = linkRe.exec(xml); m; m = linkRe.exec(xml)) {
1162
+ const n = attr(m[1] ?? "", "name");
1163
+ if (n) links.push(n);
1164
+ }
1165
+ const joints = [];
1166
+ const jointRe = /<joint\b([^>]*)>([\s\S]*?)<\/joint>/g;
1167
+ for (let m = jointRe.exec(xml); m; m = jointRe.exec(xml)) {
1168
+ const head = m[1] ?? "";
1169
+ const body = m[2] ?? "";
1170
+ const type = attr(head, "type") ?? "fixed";
1171
+ if (type === "fixed" || type === "floating" || type === "planar") continue;
1172
+ const parentTag = /<parent\b([^>]*?)\/?>/.exec(body);
1173
+ const childTag = /<child\b([^>]*?)\/?>/.exec(body);
1174
+ const parent = attr(parentTag?.[1] ?? "", "link");
1175
+ const child = attr(childTag?.[1] ?? "", "link");
1176
+ if (!parent || !child) return err(validationError(BrepErrorCode.VALIDATION_FAILED, `importURDF: joint missing parent/child link (${attr(head, "name") ?? "unnamed"})`));
1177
+ const originTag = /<origin\b([^>]*?)\/?>/.exec(body);
1178
+ const axisTag = /<axis\b([^>]*?)\/?>/.exec(body);
1179
+ const limitTag = /<limit\b([^>]*?)\/?>/.exec(body);
1180
+ const origin = triple(attr(originTag?.[1] ?? "", "xyz"), [
1181
+ 0,
1182
+ 0,
1183
+ 0
1184
+ ]);
1185
+ const rpy = triple(attr(originTag?.[1] ?? "", "rpy"), [
1186
+ 0,
1187
+ 0,
1188
+ 0
1189
+ ]);
1190
+ const direction = applyRpy(triple(attr(axisTag?.[1] ?? "", "xyz"), [
1191
+ 1,
1192
+ 0,
1193
+ 0
1194
+ ]), rpy);
1195
+ const lowerRaw = Number(attr(limitTag?.[1] ?? "", "lower") ?? NaN);
1196
+ const upperRaw = Number(attr(limitTag?.[1] ?? "", "upper") ?? NaN);
1197
+ if (type === "prismatic") {
1198
+ const opts = Number.isFinite(lowerRaw) && Number.isFinite(upperRaw) ? {
1199
+ min: lowerRaw,
1200
+ max: upperRaw
1201
+ } : {};
1202
+ joints.push(prismaticJoint(parent, child, {
1203
+ origin,
1204
+ direction
1205
+ }, opts));
1206
+ } else if (type === "revolute" || type === "continuous") {
1207
+ const opts = type === "continuous" || !Number.isFinite(lowerRaw) || !Number.isFinite(upperRaw) ? {
1208
+ min: -180,
1209
+ max: 180
1210
+ } : {
1211
+ min: lowerRaw * RAD2DEG,
1212
+ max: upperRaw * RAD2DEG
1213
+ };
1214
+ joints.push(revoluteJoint(parent, child, {
1215
+ origin,
1216
+ direction
1217
+ }, opts));
1218
+ } else return err(validationError(BrepErrorCode.VALIDATION_FAILED, `importURDF: joint '${attr(head, "name") ?? "unnamed"}' has unrecognized type '${type}'`));
1219
+ }
1220
+ return ok({
1221
+ name,
1222
+ links,
1223
+ joints
1224
+ });
1225
+ }
1226
+ //#endregion
965
1227
  //#region src/operations/historyFns.ts
966
1228
  /** Create a new empty history. */
967
1229
  function createHistory() {
@@ -1229,4 +1491,4 @@ function thread(options) {
1229
1491
  }
1230
1492
  }
1231
1493
  //#endregion
1232
- export { quatFromAxisAngle as A, walkAssembly as B, mechanismDOF as C, setJointValue as D, revoluteJoint as E, countNodes as F, createAssembly as G, gridPattern as H, createAssemblyNode as I, findNode as L, quatRotate as M, addChild as N, setJointValues as O, collectShapes as P, removeChild as R, jointTransform as S, prismaticJoint as T, linearPattern as U, circularPattern as V, exportAssemblySTEP as W, inverseKinematics as _, deserializeHistory as a, cylindricalJoint as b, modifyStep as c, replayFrom as d, replayHistory as f, undoLast as g, stepsFrom as h, createRegistry as i, quatFromTo as j, sphericalJoint as k, registerOperation as l, stepCount as m, addStep as n, findStep as o, serializeHistory as p, createHistory as r, getShape as s, thread as t, registerShape as u, jointTrajectory as v, planarJoint as w, forwardKinematics as x, addJoint as y, updateNode as z };
1494
+ export { setJointValue as A, findNode as B, cylindricalJoint as C, planarJoint as D, mechanismDOF as E, quatRotate as F, gridPattern as G, updateNode as H, addChild as I, createAssembly as J, linearPattern as K, collectShapes as L, sphericalJoint as M, quatFromAxisAngle as N, prismaticJoint as O, quatFromTo as P, countNodes as R, addJoint as S, jointTransform as T, walkAssembly as U, removeChild as V, circularPattern as W, exportURDF as _, deserializeHistory as a, inverseKinematics as b, modifyStep as c, replayFrom as d, replayHistory as f, undoLast as g, stepsFrom as h, createRegistry as i, setJointValues as j, revoluteJoint as k, registerOperation as l, stepCount as m, addStep as n, findStep as o, serializeHistory as p, exportAssemblySTEP as q, createHistory as r, getShape as s, thread as t, registerShape as u, importURDF as v, forwardKinematics as w, jointTrajectory as x, jointsFromDH as y, createAssemblyNode as z };
@@ -344,7 +344,7 @@ function quatMultiply(a, b) {
344
344
  }
345
345
  //#endregion
346
346
  //#region src/operations/jointFns.ts
347
- var DEG2RAD = Math.PI / 180;
347
+ var DEG2RAD$2 = Math.PI / 180;
348
348
  function clamp(value, min, max) {
349
349
  return Math.min(max, Math.max(min, value));
350
350
  }
@@ -539,7 +539,7 @@ function dofPose(origin, dof, value) {
539
539
  0
540
540
  ]
541
541
  };
542
- const rotation = quatFromAxisAngle(dof.axis, value * DEG2RAD);
542
+ const rotation = quatFromAxisAngle(dof.axis, value * DEG2RAD$2);
543
543
  const ro = quatRotate(rotation, origin);
544
544
  return {
545
545
  position: [
@@ -653,7 +653,8 @@ function forwardKinematics(assembly, jointValues = {}) {
653
653
  progress = true;
654
654
  if (poses.has(j.child)) continue;
655
655
  const value = jointValues[j.child] ?? j.value;
656
- poses.set(j.child, composePose(parentPose, jointTransform(j, value)));
656
+ const local = j.offset ? composePose(jointTransform(j, value), j.offset) : jointTransform(j, value);
657
+ poses.set(j.child, composePose(parentPose, local));
657
658
  }
658
659
  }
659
660
  for (const j of pending) if (!poses.has(j.child)) poses.set(j.child, IDENTITY_POSE$1);
@@ -837,23 +838,30 @@ function residualTwist(out, pose, target, tip, m) {
837
838
  for (let i = 0; i < m; i++) s += el(out, i) ** 2;
838
839
  return Math.sqrt(s);
839
840
  }
840
- /** Finite-difference Jacobian: column `j` is the end-effector twist from δq[j]. */
841
- function fillJacobian(J, q, n, m, base, tip, eps, tipPose) {
841
+ /**
842
+ * Finite-difference Jacobian: column `j` is the end-effector twist from δq[j].
843
+ * The probe steps *inward* from a bound — `forwardKinematics` clamps each DOF to
844
+ * its range, so a forward `+eps` at the upper limit would yield a zero column and
845
+ * trap the solver at the ceiling. Stepping `-eps` there (and dividing by the
846
+ * signed step) keeps every column a true one-sided derivative.
847
+ */
848
+ function fillJacobian(J, q, n, m, lo, hi, base, tip, eps, tipPose) {
842
849
  const basePos = applyPose(base, tip);
843
850
  for (let j = 0; j < n; j++) {
844
851
  const saved = el(q, j);
845
- q[j] = saved + eps;
852
+ const h = saved + eps > el(hi, j) && saved - eps >= el(lo, j) ? -eps : eps;
853
+ q[j] = saved + h;
846
854
  const p2 = tipPose(q);
847
855
  q[j] = saved;
848
856
  const pos2 = applyPose(p2, tip);
849
- J[j] = (pos2[0] - basePos[0]) / eps;
850
- J[n + j] = (pos2[1] - basePos[1]) / eps;
851
- J[2 * n + j] = (pos2[2] - basePos[2]) / eps;
857
+ J[j] = (pos2[0] - basePos[0]) / h;
858
+ J[n + j] = (pos2[1] - basePos[1]) / h;
859
+ J[2 * n + j] = (pos2[2] - basePos[2]) / h;
852
860
  if (m === 6) {
853
861
  const dr = rotationError(base.rotation, p2.rotation);
854
- J[3 * n + j] = dr[0] / eps;
855
- J[4 * n + j] = dr[1] / eps;
856
- J[5 * n + j] = dr[2] / eps;
862
+ J[3 * n + j] = dr[0] / h;
863
+ J[4 * n + j] = dr[1] / h;
864
+ J[5 * n + j] = dr[2] / h;
857
865
  }
858
866
  }
859
867
  }
@@ -906,7 +914,7 @@ function inverseKinematics(assembly, endEffector, target, options = {}) {
906
914
  let err = residualTwist(e, pose, target, tip, m);
907
915
  let iter = 0;
908
916
  for (; iter < maxIterations && n > 0 && err > tolerance; iter++) {
909
- fillJacobian(J, q, n, m, pose, tip, eps, tipPose);
917
+ fillJacobian(J, q, n, m, lo, hi, pose, tip, eps, tipPose);
910
918
  const dq = dlsStep(J, e, n, m, lambda);
911
919
  if (!dq) break;
912
920
  for (let j = 0; j < n; j++) {
@@ -962,6 +970,260 @@ function jointTrajectory(assembly, from, to, steps) {
962
970
  return samples;
963
971
  }
964
972
  //#endregion
973
+ //#region src/operations/dhFns.ts
974
+ /**
975
+ * Denavit-Hartenberg interchange: build a brepjs joint chain from a standard
976
+ * (distal) DH parameter table.
977
+ *
978
+ * Each DH row maps the previous link frame to the next via
979
+ * `Rz(θ) · Tz(d) · Tx(a) · Rx(α)`, with one parameter the joint variable
980
+ * (θ for revolute, d for prismatic) and the rest fixed link geometry. A row
981
+ * becomes a single revolute/prismatic joint whose motion is the variable part
982
+ * and whose fixed link geometry rides on the joint's post-motion `offset`
983
+ * (see `Joint.offset`), so the chain composes correctly through
984
+ * `forwardKinematics` and each row contributes exactly one DOF.
985
+ */
986
+ var DEG2RAD$1 = Math.PI / 180;
987
+ /**
988
+ * The fixed DH link transform `Rz(θ) · Tz(d) · Tx(a) · Rx(α)` as a pose.
989
+ * Carries the constant link geometry that rides on the joint's `offset`.
990
+ */
991
+ function linkOffset(thetaDeg, d, a, alphaDeg) {
992
+ const theta = thetaDeg * DEG2RAD$1;
993
+ const alpha = alphaDeg * DEG2RAD$1;
994
+ const rotation = quatMultiply(quatFromAxisAngle([
995
+ 0,
996
+ 0,
997
+ 1
998
+ ], theta), quatFromAxisAngle([
999
+ 1,
1000
+ 0,
1001
+ 0
1002
+ ], alpha));
1003
+ return {
1004
+ position: [
1005
+ a * Math.cos(theta),
1006
+ a * Math.sin(theta),
1007
+ d
1008
+ ],
1009
+ rotation
1010
+ };
1011
+ }
1012
+ /**
1013
+ * Build a serial revolute/prismatic joint chain from a DH table. Joint `i`
1014
+ * connects the previous link to row `i`'s link; its motion is the variable
1015
+ * parameter (θ for revolute, d for prismatic) about/along +z, and its fixed
1016
+ * link geometry is carried on `Joint.offset`. The result drives correctly
1017
+ * through `forwardKinematics` and reports `rows.length` DOF via `mechanismDOF`.
1018
+ */
1019
+ function jointsFromDH(rows, options = {}) {
1020
+ const base = options.base ?? "base";
1021
+ const names = rows.map((r, i) => r.name ?? `link${i + 1}`);
1022
+ return rows.map((row, i) => {
1023
+ const parent = i === 0 ? base : names[i - 1] ?? `link${i}`;
1024
+ const child = names[i] ?? `link${i + 1}`;
1025
+ const offset = linkOffset(row.theta, row.d, row.a, row.alpha);
1026
+ const axis = {
1027
+ origin: [
1028
+ 0,
1029
+ 0,
1030
+ 0
1031
+ ],
1032
+ direction: [
1033
+ 0,
1034
+ 0,
1035
+ 1
1036
+ ]
1037
+ };
1038
+ const opts = {
1039
+ ...row.min !== void 0 ? { min: row.min } : {},
1040
+ ...row.max !== void 0 ? { max: row.max } : {},
1041
+ ...row.value !== void 0 ? { value: row.value } : {}
1042
+ };
1043
+ return {
1044
+ ...row.type === "prismatic" ? prismaticJoint(parent, child, axis, opts) : revoluteJoint(parent, child, axis, opts),
1045
+ offset
1046
+ };
1047
+ });
1048
+ }
1049
+ //#endregion
1050
+ //#region src/operations/urdfFns.ts
1051
+ /**
1052
+ * URDF interchange: export a brepjs assembly + its joints to URDF, and import a
1053
+ * URDF document back into links and joints.
1054
+ *
1055
+ * URDF natively models single-DOF joints, so `revolute` and `prismatic` joints
1056
+ * round-trip (parents, axes, and limits preserved). Multi-DOF joints
1057
+ * (cylindrical/planar/spherical) and joints carrying a fixed `offset` (e.g.
1058
+ * Denavit-Hartenberg links) have no faithful single-joint URDF representation
1059
+ * and are reported as an error on export.
1060
+ *
1061
+ * Revolute limits are radians in URDF and degrees in brepjs, so they are
1062
+ * converted on the boundary. The XML is parsed with a small URDF-shaped reader
1063
+ * (no XML dependency); it handles the standard `<robot>/<link>/<joint>` subset.
1064
+ */
1065
+ var DEG2RAD = Math.PI / 180;
1066
+ var RAD2DEG = 180 / Math.PI;
1067
+ function escapeXml(s) {
1068
+ return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
1069
+ }
1070
+ /** Format a number for XML, normalizing -0 to 0. */
1071
+ function fmt(n) {
1072
+ return String(n === 0 ? 0 : n);
1073
+ }
1074
+ function vec(v) {
1075
+ return `${fmt(v[0])} ${fmt(v[1])} ${fmt(v[2])}`;
1076
+ }
1077
+ /**
1078
+ * Serialize an assembly's links and revolute/prismatic joints to a URDF string.
1079
+ * Returns an error if any joint is multi-DOF or carries a fixed `offset`, since
1080
+ * URDF cannot represent those as a single joint.
1081
+ */
1082
+ function exportURDF(assembly, options = {}) {
1083
+ const robot = options.name ?? "robot";
1084
+ const effort = options.effort ?? 0;
1085
+ const velocity = options.velocity ?? 0;
1086
+ const meshes = options.meshes ?? {};
1087
+ const links = [];
1088
+ const joints = [];
1089
+ walkAssembly(assembly, (n) => {
1090
+ links.push(n.name);
1091
+ if (n.joints) joints.push(...n.joints);
1092
+ });
1093
+ for (const j of joints) {
1094
+ if (j.type !== "revolute" && j.type !== "prismatic") return require_errors.err(require_errors.validationError(require_errors.BrepErrorCode.VALIDATION_FAILED, `exportURDF: joint '${j.parent}->${j.child}' is '${j.type}'; URDF supports only revolute and prismatic joints`));
1095
+ if (j.offset) return require_errors.err(require_errors.validationError(require_errors.BrepErrorCode.VALIDATION_FAILED, `exportURDF: joint '${j.parent}->${j.child}' carries a fixed offset (e.g. a DH link) that URDF cannot represent as a single joint`));
1096
+ }
1097
+ const lines = ["<?xml version=\"1.0\"?>", `<robot name="${escapeXml(robot)}">`];
1098
+ for (const link of links) {
1099
+ const mesh = meshes[link];
1100
+ if (mesh) lines.push(` <link name="${escapeXml(link)}">`, ` <visual><geometry><mesh filename="${escapeXml(mesh)}"/></geometry></visual>`, ` </link>`);
1101
+ else lines.push(` <link name="${escapeXml(link)}"/>`);
1102
+ }
1103
+ for (const j of joints) {
1104
+ const toRad = j.type === "revolute";
1105
+ const lower = toRad ? j.min * DEG2RAD : j.min;
1106
+ const upper = toRad ? j.max * DEG2RAD : j.max;
1107
+ const origin = j.type === "prismatic" ? [
1108
+ 0,
1109
+ 0,
1110
+ 0
1111
+ ] : j.axis.origin;
1112
+ lines.push(` <joint name="${escapeXml(`${j.parent}_to_${j.child}`)}" type="${j.type}">`, ` <parent link="${escapeXml(j.parent)}"/>`, ` <child link="${escapeXml(j.child)}"/>`, ` <origin xyz="${vec(origin)}" rpy="0 0 0"/>`, ` <axis xyz="${vec(j.axis.direction)}"/>`, ` <limit lower="${fmt(lower)}" upper="${fmt(upper)}" effort="${fmt(effort)}" velocity="${fmt(velocity)}"/>`, ` </joint>`);
1113
+ }
1114
+ lines.push("</robot>", "");
1115
+ return require_errors.ok(lines.join("\n"));
1116
+ }
1117
+ /** Read a string attribute from a tag's attribute text. */
1118
+ function attr(text, name) {
1119
+ return new RegExp(`\\b${name}\\s*=\\s*"([^"]*)"`).exec(text)?.[1];
1120
+ }
1121
+ /** Parse a whitespace-separated numeric triple, defaulting missing components to 0. */
1122
+ function triple(s, fallback) {
1123
+ if (!s) return fallback;
1124
+ const parts = s.trim().split(/\s+/).map(Number);
1125
+ return [
1126
+ parts[0] ?? 0,
1127
+ parts[1] ?? 0,
1128
+ parts[2] ?? 0
1129
+ ];
1130
+ }
1131
+ /** Rotate an axis by a URDF roll-pitch-yaw (fixed-axis XYZ) triple in radians. */
1132
+ function applyRpy(axis, rpy) {
1133
+ return quatRotate(quatMultiply(quatMultiply(quatFromAxisAngle([
1134
+ 0,
1135
+ 0,
1136
+ 1
1137
+ ], rpy[2]), quatFromAxisAngle([
1138
+ 0,
1139
+ 1,
1140
+ 0
1141
+ ], rpy[1])), quatFromAxisAngle([
1142
+ 1,
1143
+ 0,
1144
+ 0
1145
+ ], rpy[0])), axis);
1146
+ }
1147
+ /**
1148
+ * Parse a URDF document into its robot name, link names, and revolute/prismatic
1149
+ * joints. `continuous` joints become revolute with a full -180..180 range;
1150
+ * `fixed`/`floating`/`planar` joints are skipped (their links are still listed).
1151
+ * A non-zero `<origin rpy>` is folded into the joint axis direction; its
1152
+ * residual frame rotation is not represented (URDF's pre-motion frame offset has
1153
+ * no single-joint brepjs equivalent).
1154
+ */
1155
+ function importURDF(xml) {
1156
+ const robotMatch = /<robot\b([^>]*)>/.exec(xml);
1157
+ if (!robotMatch) return require_errors.err(require_errors.validationError(require_errors.BrepErrorCode.VALIDATION_FAILED, "importURDF: no <robot> element found"));
1158
+ const name = attr(robotMatch[1] ?? "", "name") ?? "robot";
1159
+ const links = [];
1160
+ const linkRe = /<link\b([^>]*?)(?:\/>|>)/g;
1161
+ for (let m = linkRe.exec(xml); m; m = linkRe.exec(xml)) {
1162
+ const n = attr(m[1] ?? "", "name");
1163
+ if (n) links.push(n);
1164
+ }
1165
+ const joints = [];
1166
+ const jointRe = /<joint\b([^>]*)>([\s\S]*?)<\/joint>/g;
1167
+ for (let m = jointRe.exec(xml); m; m = jointRe.exec(xml)) {
1168
+ const head = m[1] ?? "";
1169
+ const body = m[2] ?? "";
1170
+ const type = attr(head, "type") ?? "fixed";
1171
+ if (type === "fixed" || type === "floating" || type === "planar") continue;
1172
+ const parentTag = /<parent\b([^>]*?)\/?>/.exec(body);
1173
+ const childTag = /<child\b([^>]*?)\/?>/.exec(body);
1174
+ const parent = attr(parentTag?.[1] ?? "", "link");
1175
+ const child = attr(childTag?.[1] ?? "", "link");
1176
+ if (!parent || !child) return require_errors.err(require_errors.validationError(require_errors.BrepErrorCode.VALIDATION_FAILED, `importURDF: joint missing parent/child link (${attr(head, "name") ?? "unnamed"})`));
1177
+ const originTag = /<origin\b([^>]*?)\/?>/.exec(body);
1178
+ const axisTag = /<axis\b([^>]*?)\/?>/.exec(body);
1179
+ const limitTag = /<limit\b([^>]*?)\/?>/.exec(body);
1180
+ const origin = triple(attr(originTag?.[1] ?? "", "xyz"), [
1181
+ 0,
1182
+ 0,
1183
+ 0
1184
+ ]);
1185
+ const rpy = triple(attr(originTag?.[1] ?? "", "rpy"), [
1186
+ 0,
1187
+ 0,
1188
+ 0
1189
+ ]);
1190
+ const direction = applyRpy(triple(attr(axisTag?.[1] ?? "", "xyz"), [
1191
+ 1,
1192
+ 0,
1193
+ 0
1194
+ ]), rpy);
1195
+ const lowerRaw = Number(attr(limitTag?.[1] ?? "", "lower") ?? NaN);
1196
+ const upperRaw = Number(attr(limitTag?.[1] ?? "", "upper") ?? NaN);
1197
+ if (type === "prismatic") {
1198
+ const opts = Number.isFinite(lowerRaw) && Number.isFinite(upperRaw) ? {
1199
+ min: lowerRaw,
1200
+ max: upperRaw
1201
+ } : {};
1202
+ joints.push(prismaticJoint(parent, child, {
1203
+ origin,
1204
+ direction
1205
+ }, opts));
1206
+ } else if (type === "revolute" || type === "continuous") {
1207
+ const opts = type === "continuous" || !Number.isFinite(lowerRaw) || !Number.isFinite(upperRaw) ? {
1208
+ min: -180,
1209
+ max: 180
1210
+ } : {
1211
+ min: lowerRaw * RAD2DEG,
1212
+ max: upperRaw * RAD2DEG
1213
+ };
1214
+ joints.push(revoluteJoint(parent, child, {
1215
+ origin,
1216
+ direction
1217
+ }, opts));
1218
+ } else return require_errors.err(require_errors.validationError(require_errors.BrepErrorCode.VALIDATION_FAILED, `importURDF: joint '${attr(head, "name") ?? "unnamed"}' has unrecognized type '${type}'`));
1219
+ }
1220
+ return require_errors.ok({
1221
+ name,
1222
+ links,
1223
+ joints
1224
+ });
1225
+ }
1226
+ //#endregion
965
1227
  //#region src/operations/historyFns.ts
966
1228
  /** Create a new empty history. */
967
1229
  function createHistory() {
@@ -1307,6 +1569,12 @@ Object.defineProperty(exports, "exportAssemblySTEP", {
1307
1569
  return exportAssemblySTEP;
1308
1570
  }
1309
1571
  });
1572
+ Object.defineProperty(exports, "exportURDF", {
1573
+ enumerable: true,
1574
+ get: function() {
1575
+ return exportURDF;
1576
+ }
1577
+ });
1310
1578
  Object.defineProperty(exports, "findNode", {
1311
1579
  enumerable: true,
1312
1580
  get: function() {
@@ -1337,6 +1605,12 @@ Object.defineProperty(exports, "gridPattern", {
1337
1605
  return gridPattern;
1338
1606
  }
1339
1607
  });
1608
+ Object.defineProperty(exports, "importURDF", {
1609
+ enumerable: true,
1610
+ get: function() {
1611
+ return importURDF;
1612
+ }
1613
+ });
1340
1614
  Object.defineProperty(exports, "inverseKinematics", {
1341
1615
  enumerable: true,
1342
1616
  get: function() {
@@ -1355,6 +1629,12 @@ Object.defineProperty(exports, "jointTransform", {
1355
1629
  return jointTransform;
1356
1630
  }
1357
1631
  });
1632
+ Object.defineProperty(exports, "jointsFromDH", {
1633
+ enumerable: true,
1634
+ get: function() {
1635
+ return jointsFromDH;
1636
+ }
1637
+ });
1358
1638
  Object.defineProperty(exports, "linearPattern", {
1359
1639
  enumerable: true,
1360
1640
  get: function() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brepjs",
3
- "version": "18.81.0",
3
+ "version": "18.82.0",
4
4
  "description": "Web CAD library with pluggable geometry kernel",
5
5
  "keywords": [
6
6
  "cad",