brepjs 18.81.1 → 18.82.1

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.
Files changed (84) hide show
  1. package/dist/2d.cjs +6 -6
  2. package/dist/2d.js +6 -6
  3. package/dist/{blueprint-BQmmwEKa.js → blueprint-DYSvNd4o.js} +5 -5
  4. package/dist/{blueprint-B59GQsSD.cjs → blueprint-nc1fOuKL.cjs} +5 -5
  5. package/dist/{blueprintFns-DSK1nrwo.js → blueprintFns-BCwcL8yU.js} +2 -2
  6. package/dist/{blueprintFns-C1kKA5qn.cjs → blueprintFns-DfWBxCq0.cjs} +2 -2
  7. package/dist/{blueprintSketcher-BJPBKhF3.cjs → blueprintSketcher-B-3xs7WQ.cjs} +3 -3
  8. package/dist/{blueprintSketcher-EYqKWQh0.js → blueprintSketcher-CAW_XLlt.js} +3 -3
  9. package/dist/{boolean2D-COHH5tLX.js → boolean2D-DNJmkzwX.js} +4 -4
  10. package/dist/{boolean2D-B1Di3Bpn.cjs → boolean2D-KbMyZhkl.cjs} +4 -4
  11. package/dist/brepjs.cjs +28 -25
  12. package/dist/brepjs.js +26 -26
  13. package/dist/{cameraFns-BZ_8L3gd.cjs → cameraFns-B6TZAVBS.cjs} +2 -2
  14. package/dist/{cameraFns-BMrhkkOV.js → cameraFns-DVHzAi9k.js} +2 -2
  15. package/dist/core.cjs +1 -1
  16. package/dist/core.js +1 -1
  17. package/dist/{cornerFinder-Fi7obttG.js → cornerFinder-CTjcdE3-.js} +1 -1
  18. package/dist/{cornerFinder-Ey3oibK1.cjs → cornerFinder-_Ob4SRyO.cjs} +1 -1
  19. package/dist/{curveFns-Ca59CNS0.cjs → curveFns-DbD2YJ13.cjs} +1 -1
  20. package/dist/{curveFns-CoD5E2Vl.js → curveFns-sKYw6m8M.js} +1 -1
  21. package/dist/{drawFns-D_LwehLr.js → drawFns-CEPVoNO2.js} +12 -12
  22. package/dist/{drawFns-DTpCthM5.cjs → drawFns-QFxOvbxQ.cjs} +12 -12
  23. package/dist/{faceFns-Cuxdy-7R.js → faceFns-CGE7tH4w.js} +2 -2
  24. package/dist/{faceFns-0sI6cbHe.cjs → faceFns-lCFbFZoJ.cjs} +2 -2
  25. package/dist/{healingFns-Bm-NdBj_.js → healingFns-DLgqleYe.js} +5 -5
  26. package/dist/{healingFns--PtL9j2K.cjs → healingFns-GDygrmEZ.cjs} +5 -5
  27. package/dist/{helpers-B8mE35Fm.cjs → helpers-CgnVqLn7.cjs} +6 -6
  28. package/dist/{helpers-CD8EMZ5l.js → helpers-CmB_i8jR.js} +6 -6
  29. package/dist/{importFns-vnxBqXwv.js → importFns-CCf9yq82.js} +2 -2
  30. package/dist/{importFns-BHEgzDLw.cjs → importFns-D95ntTc4.cjs} +2 -2
  31. package/dist/index.d.ts +2 -0
  32. package/dist/io.cjs +2 -2
  33. package/dist/io.js +2 -2
  34. package/dist/kernel/occtWasm/occtWasmAdapter.cjs +1 -1
  35. package/dist/kernel/occtWasm/occtWasmAdapter.js +1 -1
  36. package/dist/{loftFns-DiSsI2PM.cjs → loftFns-B1rnJSS9.cjs} +1 -1
  37. package/dist/{loftFns-CsHOwded.js → loftFns-BXoTCIjU.js} +1 -1
  38. package/dist/{measureFns-nF-OXcDV.cjs → measureFns-BhqwziaR.cjs} +3 -3
  39. package/dist/{measureFns-CdcufPNp.js → measureFns-DWuQ69pP.js} +3 -3
  40. package/dist/measurement.cjs +1 -1
  41. package/dist/measurement.js +1 -1
  42. package/dist/{meshFns-BCgtjlbs.js → meshFns-BpZaGQen.js} +3 -3
  43. package/dist/{meshFns-CnckZa6H.cjs → meshFns-asBOhb1B.cjs} +3 -3
  44. package/dist/{occtWasmAdapter-W86SRBpi.cjs → occtWasmAdapter-5hzzhkME.cjs} +3 -1
  45. package/dist/{occtWasmAdapter-BrTkhQYK.js → occtWasmAdapter-BMV179Ji.js} +3 -1
  46. package/dist/operations/dhFns.d.ts +34 -0
  47. package/dist/operations/jointFns.d.ts +7 -0
  48. package/dist/operations/urdfFns.d.ts +33 -0
  49. package/dist/operations.cjs +5 -2
  50. package/dist/operations.d.ts +2 -0
  51. package/dist/operations.js +3 -3
  52. package/dist/{primitiveFns-BSKbI4Kl.js → primitiveFns-BvhWliRA.js} +5 -5
  53. package/dist/{primitiveFns-DEBQdEkG.cjs → primitiveFns-CtlwDr67.cjs} +5 -5
  54. package/dist/projection.cjs +1 -1
  55. package/dist/projection.js +1 -1
  56. package/dist/query.cjs +2 -2
  57. package/dist/query.js +2 -2
  58. package/dist/{shapeFns-BzlBMNRO.js → shapeFns-BDE-a54O.js} +2 -2
  59. package/dist/{shapeFns-CecvqmYJ.cjs → shapeFns-B_jJcU_6.cjs} +2 -2
  60. package/dist/shapeRef.cjs +1 -1
  61. package/dist/shapeRef.js +1 -1
  62. package/dist/{shapeRefFns-BDACYYRr.cjs → shapeRefFns-BoVBUAL3.cjs} +4 -4
  63. package/dist/{shapeRefFns-CpRRMBKz.js → shapeRefFns-CHzb4b94.js} +4 -4
  64. package/dist/{shapeTypes-DfQi9Kvk.js → shapeTypes-CIyYAXl9.js} +1 -1
  65. package/dist/{shapeTypes-DWxL75W3.cjs → shapeTypes-D7OMlW79.cjs} +1 -1
  66. package/dist/sketching.cjs +3 -3
  67. package/dist/sketching.js +3 -3
  68. package/dist/{solidBuilders-osLCTRG0.cjs → solidBuilders-CwM49RI7.cjs} +2 -2
  69. package/dist/{solidBuilders-DnIrQYZd.js → solidBuilders-D10YA4nh.js} +2 -2
  70. package/dist/{surfaceBuilders-hTXdNCYm.js → surfaceBuilders-D8mMa19A.js} +2 -2
  71. package/dist/{surfaceBuilders-B8aVZamB.cjs → surfaceBuilders-D_CoR7GM.cjs} +2 -2
  72. package/dist/text.cjs +2 -2
  73. package/dist/text.js +2 -2
  74. package/dist/{textBlueprints-CI3k7Izc.cjs → textBlueprints-B8qqMC0Q.cjs} +7 -7
  75. package/dist/{textBlueprints-BpqDGwdx.js → textBlueprints-BXMr-Bbx.js} +7 -7
  76. package/dist/{textMetrics-C4RrG9jL.cjs → textMetrics-CMDiXmaV.cjs} +1 -1
  77. package/dist/{textMetrics-kFw-DE2w.js → textMetrics-Duye7Kw_.js} +1 -1
  78. package/dist/{threadFns-By0Gy2f9.cjs → threadFns-BvoPZ4ei.cjs} +281 -8
  79. package/dist/{threadFns-YzJhd0Kz.js → threadFns-D5dsDFQ5.js} +265 -10
  80. package/dist/topology.cjs +7 -7
  81. package/dist/topology.js +7 -7
  82. package/dist/{topologyQueryFns-DfOxu8tG.cjs → topologyQueryFns-BQq-qkqb.cjs} +1 -1
  83. package/dist/{topologyQueryFns-CqjEReSt.js → topologyQueryFns-DIKovx5V.js} +1 -1
  84. package/package.json +1 -1
package/dist/text.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_textBlueprints = require("./textBlueprints-CI3k7Izc.cjs");
3
- const require_textMetrics = require("./textMetrics-C4RrG9jL.cjs");
2
+ const require_textBlueprints = require("./textBlueprints-B8qqMC0Q.cjs");
3
+ const require_textMetrics = require("./textMetrics-CMDiXmaV.cjs");
4
4
  exports.fontMetrics = require_textMetrics.fontMetrics;
5
5
  exports.getFont = require_textBlueprints.getFont;
6
6
  exports.loadFont = require_textBlueprints.loadFont;
package/dist/text.js CHANGED
@@ -1,3 +1,3 @@
1
- import { n as getFont, r as loadFont, t as textBlueprints } from "./textBlueprints-BpqDGwdx.js";
2
- import { n as textMetrics, r as sketchText, t as fontMetrics } from "./textMetrics-kFw-DE2w.js";
1
+ import { n as getFont, r as loadFont, t as textBlueprints } from "./textBlueprints-BXMr-Bbx.js";
2
+ import { n as textMetrics, r as sketchText, t as fontMetrics } from "./textMetrics-Duye7Kw_.js";
3
3
  export { fontMetrics, getFont, loadFont, sketchText, textBlueprints, textMetrics };
@@ -29,18 +29,18 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
29
29
  enumerable: true
30
30
  }) : target, mod));
31
31
  //#endregion
32
- const require_shapeTypes = require("./shapeTypes-DWxL75W3.cjs");
32
+ const require_shapeTypes = require("./shapeTypes-D7OMlW79.cjs");
33
33
  const require_errors = require("./errors-CXJtc4I7.cjs");
34
34
  const require_types = require("./types-KjA8tY4Y.cjs");
35
35
  const require_vecOps = require("./vecOps-CCnJt-yH.cjs");
36
36
  const require_planeOps = require("./planeOps-BA4HfgQu.cjs");
37
- const require_faceFns = require("./faceFns-0sI6cbHe.cjs");
38
- const require_curveFns = require("./curveFns-Ca59CNS0.cjs");
37
+ const require_faceFns = require("./faceFns-lCFbFZoJ.cjs");
38
+ const require_curveFns = require("./curveFns-DbD2YJ13.cjs");
39
39
  const require_arrayAccess = require("./arrayAccess-e4H9cBfh.cjs");
40
- const require_surfaceBuilders = require("./surfaceBuilders-B8aVZamB.cjs");
41
- const require_blueprintSketcher = require("./blueprintSketcher-BJPBKhF3.cjs");
42
- const require_solidBuilders = require("./solidBuilders-osLCTRG0.cjs");
43
- const require_loftFns = require("./loftFns-DiSsI2PM.cjs");
40
+ const require_surfaceBuilders = require("./surfaceBuilders-D_CoR7GM.cjs");
41
+ const require_blueprintSketcher = require("./blueprintSketcher-B-3xs7WQ.cjs");
42
+ const require_solidBuilders = require("./solidBuilders-CwM49RI7.cjs");
43
+ const require_loftFns = require("./loftFns-B1rnJSS9.cjs");
44
44
  let opentype_js = require("opentype.js");
45
45
  opentype_js = __toESM(opentype_js, 1);
46
46
  //#region src/sketching/compoundSketch.ts
@@ -1,15 +1,15 @@
1
- import { Z as getKernel, o as createFace, p as isFace, u as createWire } from "./shapeTypes-DfQi9Kvk.js";
1
+ import { Z as getKernel, o as createFace, p as isFace, u as createWire } from "./shapeTypes-CIyYAXl9.js";
2
2
  import { A as ok, R as unwrap, T as isOk, b as err, h as bug, r as ioError, t as BrepErrorCode } from "./errors-DNWJsfVU.js";
3
3
  import { r as toVec3 } from "./types-D24Y27N0.js";
4
4
  import { d as vecNormalize, h as vecScale, r as vecCross } from "./vecOps-SKPRvPH-.js";
5
5
  import { n as createPlane } from "./planeOps-DSjjtrjg.js";
6
- import { _ as cast, v as downcast } from "./faceFns-Cuxdy-7R.js";
7
- import { l as curveStartPoint, u as curveTangentAt } from "./curveFns-CoD5E2Vl.js";
6
+ import { _ as cast, v as downcast } from "./faceFns-CGE7tH4w.js";
7
+ import { l as curveStartPoint, u as curveTangentAt } from "./curveFns-sKYw6m8M.js";
8
8
  import { n as getAtOrThrow, t as firstOrThrow } from "./arrayAccess-DrUGPADn.js";
9
- import { i as makeNewFaceWithinFace, r as makeFace, t as addHolesInFace } from "./surfaceBuilders-hTXdNCYm.js";
10
- import { r as organiseBlueprints, t as BlueprintSketcher } from "./blueprintSketcher-EYqKWQh0.js";
11
- import { o as makeSolid, t as makeCompound } from "./solidBuilders-DnIrQYZd.js";
12
- import { a as revolve, d as twistExtrude, o as complexExtrude, r as extrude, t as loft, u as sweep } from "./loftFns-CsHOwded.js";
9
+ import { i as makeNewFaceWithinFace, r as makeFace, t as addHolesInFace } from "./surfaceBuilders-D8mMa19A.js";
10
+ import { r as organiseBlueprints, t as BlueprintSketcher } from "./blueprintSketcher-CAW_XLlt.js";
11
+ import { o as makeSolid, t as makeCompound } from "./solidBuilders-D10YA4nh.js";
12
+ import { a as revolve, d as twistExtrude, o as complexExtrude, r as extrude, t as loft, u as sweep } from "./loftFns-BXoTCIjU.js";
13
13
  import opentype from "opentype.js";
14
14
  //#region src/sketching/compoundSketch.ts
15
15
  /**
@@ -1,4 +1,4 @@
1
- const require_textBlueprints = require("./textBlueprints-CI3k7Izc.cjs");
1
+ const require_textBlueprints = require("./textBlueprints-B8qqMC0Q.cjs");
2
2
  const require_errors = require("./errors-CXJtc4I7.cjs");
3
3
  //#region src/text/sketchText.ts
4
4
  /**
@@ -1,5 +1,5 @@
1
1
  import { A as ok, b as err, d as validationError, t as BrepErrorCode } from "./errors-DNWJsfVU.js";
2
- import { g as wrapSketchData, i as Sketches, n as getFont, t as textBlueprints, v as CompoundSketch } from "./textBlueprints-BpqDGwdx.js";
2
+ import { g as wrapSketchData, i as Sketches, n as getFont, t as textBlueprints, v as CompoundSketch } from "./textBlueprints-BXMr-Bbx.js";
3
3
  //#region src/text/sketchText.ts
4
4
  /**
5
5
  * Render text as 3D sketch outlines on a plane.
@@ -1,10 +1,10 @@
1
- const require_shapeTypes = require("./shapeTypes-DWxL75W3.cjs");
1
+ const require_shapeTypes = require("./shapeTypes-D7OMlW79.cjs");
2
2
  const require_errors = require("./errors-CXJtc4I7.cjs");
3
3
  const require_vecOps = require("./vecOps-CCnJt-yH.cjs");
4
- const require_faceFns = require("./faceFns-0sI6cbHe.cjs");
5
- const require_shapeFns = require("./shapeFns-CecvqmYJ.cjs");
6
- const require_primitiveFns = require("./primitiveFns-DEBQdEkG.cjs");
7
- const require_loftFns = require("./loftFns-DiSsI2PM.cjs");
4
+ const require_faceFns = require("./faceFns-lCFbFZoJ.cjs");
5
+ const require_shapeFns = require("./shapeFns-B_jJcU_6.cjs");
6
+ const require_primitiveFns = require("./primitiveFns-CtlwDr67.cjs");
7
+ const require_loftFns = require("./loftFns-B1rnJSS9.cjs");
8
8
  //#region src/utils/uuid.ts
9
9
  /** Generate a v4-style UUID string using `crypto.getRandomValues`. */
10
10
  function uuidv() {
@@ -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);
@@ -969,6 +970,260 @@ function jointTrajectory(assembly, from, to, steps) {
969
970
  return samples;
970
971
  }
971
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
972
1227
  //#region src/operations/historyFns.ts
973
1228
  /** Create a new empty history. */
974
1229
  function createHistory() {
@@ -1314,6 +1569,12 @@ Object.defineProperty(exports, "exportAssemblySTEP", {
1314
1569
  return exportAssemblySTEP;
1315
1570
  }
1316
1571
  });
1572
+ Object.defineProperty(exports, "exportURDF", {
1573
+ enumerable: true,
1574
+ get: function() {
1575
+ return exportURDF;
1576
+ }
1577
+ });
1317
1578
  Object.defineProperty(exports, "findNode", {
1318
1579
  enumerable: true,
1319
1580
  get: function() {
@@ -1344,6 +1605,12 @@ Object.defineProperty(exports, "gridPattern", {
1344
1605
  return gridPattern;
1345
1606
  }
1346
1607
  });
1608
+ Object.defineProperty(exports, "importURDF", {
1609
+ enumerable: true,
1610
+ get: function() {
1611
+ return importURDF;
1612
+ }
1613
+ });
1347
1614
  Object.defineProperty(exports, "inverseKinematics", {
1348
1615
  enumerable: true,
1349
1616
  get: function() {
@@ -1362,6 +1629,12 @@ Object.defineProperty(exports, "jointTransform", {
1362
1629
  return jointTransform;
1363
1630
  }
1364
1631
  });
1632
+ Object.defineProperty(exports, "jointsFromDH", {
1633
+ enumerable: true,
1634
+ get: function() {
1635
+ return jointsFromDH;
1636
+ }
1637
+ });
1365
1638
  Object.defineProperty(exports, "linearPattern", {
1366
1639
  enumerable: true,
1367
1640
  get: function() {