brepjs 20.11.0 → 20.11.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.
package/dist/brepjs.cjs CHANGED
@@ -29,7 +29,7 @@ const require_measureFns = require("./measureFns-BoNCUOKe.cjs");
29
29
  const require_cornerFinder = require("./cornerFinder-DVHtBOzM.cjs");
30
30
  const require_boolean2D = require("./boolean2D-B21GheGE.cjs");
31
31
  const require_blueprintFns = require("./blueprintFns-DDiWI0Ol.cjs");
32
- const require_importFns = require("./importFns-DI3Q8B0Q.cjs");
32
+ const require_importFns = require("./importFns-D5wRI_Cc.cjs");
33
33
  const require_textBlueprints = require("./textBlueprints-jlqCGG4p.cjs");
34
34
  const require_loftFns = require("./loftFns-tGsV_hHY.cjs");
35
35
  const require_refResolveFns = require("./refResolveFns-DvDeMCVU.cjs");
@@ -6181,7 +6181,7 @@ function sub(a, b) {
6181
6181
  function dot(a, b) {
6182
6182
  return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
6183
6183
  }
6184
- function cross(a, b) {
6184
+ function cross$1(a, b) {
6185
6185
  return [
6186
6186
  a[1] * b[2] - a[2] * b[1],
6187
6187
  a[2] * b[0] - a[0] * b[2],
@@ -6205,7 +6205,7 @@ function normalize(a) {
6205
6205
  }
6206
6206
  /** A unit vector perpendicular to `v` (for the parallel-normals degenerate case). */
6207
6207
  function anyPerpendicular(v) {
6208
- return normalize(cross(v, Math.abs(v[0]) < .9 ? [
6208
+ return normalize(cross$1(v, Math.abs(v[0]) < .9 ? [
6209
6209
  1,
6210
6210
  0,
6211
6211
  0
@@ -6285,7 +6285,7 @@ function solveAngle(ref, dep, angleRad) {
6285
6285
  1
6286
6286
  ]);
6287
6287
  const phi = Math.acos(Math.max(-1, Math.min(1, dot(nDep, nRef))));
6288
- const c = cross(nDep, nRef);
6288
+ const c = cross$1(nDep, nRef);
6289
6289
  const axis = Math.hypot(c[0], c[1], c[2]) < 1e-9 ? anyPerpendicular(nDep) : c;
6290
6290
  return {
6291
6291
  position: [
@@ -6792,63 +6792,378 @@ function parseEntities(text) {
6792
6792
  current = {
6793
6793
  type: value,
6794
6794
  layer: "0",
6795
- data: /* @__PURE__ */ new Map()
6795
+ data: /* @__PURE__ */ new Map(),
6796
+ pairs: []
6796
6797
  };
6797
6798
  continue;
6798
6799
  }
6799
6800
  if (current) {
6801
+ current.pairs.push([code, value]);
6800
6802
  if (code === 8) current.layer = value;
6801
6803
  else current.data.set(code, value);
6802
6804
  }
6803
6805
  }
6804
6806
  return entities;
6805
6807
  }
6806
- function getNum(data, code, fallback = 0) {
6807
- const v = data.get(code);
6808
- if (v === void 0) return fallback;
6809
- const n = parseFloat(v);
6808
+ function num(value, fallback = 0) {
6809
+ if (value === void 0) return fallback;
6810
+ const n = parseFloat(value);
6810
6811
  return isNaN(n) ? fallback : n;
6811
6812
  }
6812
- function entityToEdge(entity) {
6813
- const { type, data } = entity;
6814
- const kernel = require_shapeTypes.getKernel();
6815
- if (type === "LINE") return kernel.makeLineEdge([
6816
- getNum(data, 10),
6817
- getNum(data, 20),
6818
- getNum(data, 30)
6819
- ], [
6820
- getNum(data, 11),
6821
- getNum(data, 21),
6822
- getNum(data, 31)
6813
+ function getNum(data, code, fallback = 0) {
6814
+ return num(data.get(code), fallback);
6815
+ }
6816
+ /** LWPOLYLINE vertices: each 10 starts one and its 20 and 42 follow; 38 is the elevation. */
6817
+ function lwVertices(entity) {
6818
+ const z = getNum(entity.data, 38);
6819
+ const out = [];
6820
+ for (const [code, value] of entity.pairs) {
6821
+ const last = out[out.length - 1];
6822
+ if (code === 10) out.push({
6823
+ point: [
6824
+ num(value),
6825
+ 0,
6826
+ z
6827
+ ],
6828
+ bulge: 0
6829
+ });
6830
+ else if (code === 20 && last) last.point[1] = num(value);
6831
+ else if (code === 42 && last) last.bulge = num(value);
6832
+ }
6833
+ return out;
6834
+ }
6835
+ /**
6836
+ * The DXF R12 polyline: a POLYLINE header, its VERTEX entities, then SEQEND. They arrive as
6837
+ * separate entities, so the header gathers the vertices that follow it.
6838
+ */
6839
+ function gatherPolylines(entities) {
6840
+ const out = [];
6841
+ for (let i = 0; i < entities.length; i++) {
6842
+ const e = entities[i];
6843
+ if (e === void 0) continue;
6844
+ if (e.type !== "POLYLINE") {
6845
+ if (e.type !== "VERTEX" && e.type !== "SEQEND") out.push(e);
6846
+ continue;
6847
+ }
6848
+ const vertices = [];
6849
+ let j = i + 1;
6850
+ for (; j < entities.length && entities[j]?.type === "VERTEX"; j++) {
6851
+ const v = entities[j];
6852
+ if (v === void 0) continue;
6853
+ vertices.push([10, v.data.get(10) ?? "0"], [20, v.data.get(20) ?? "0"], [30, v.data.get(30) ?? "0"], [42, v.data.get(42) ?? "0"]);
6854
+ }
6855
+ if (entities[j]?.type === "SEQEND") j++;
6856
+ const own = e.pairs.filter(([code]) => code !== 10 && code !== 20 && code !== 30);
6857
+ out.push({
6858
+ ...e,
6859
+ pairs: [...own, ...vertices]
6860
+ });
6861
+ i = j - 1;
6862
+ }
6863
+ return out;
6864
+ }
6865
+ function polyVertices(entity) {
6866
+ if (entity.type === "LWPOLYLINE") return lwVertices(entity);
6867
+ const out = [];
6868
+ const p = entity.pairs;
6869
+ for (let k = 0; k < p.length; k++) {
6870
+ if (p[k]?.[0] !== 10) continue;
6871
+ out.push({
6872
+ point: [
6873
+ num(p[k]?.[1]),
6874
+ num(p[k + 1]?.[1]),
6875
+ num(p[k + 2]?.[1])
6876
+ ],
6877
+ bulge: num(p[k + 3]?.[1])
6878
+ });
6879
+ }
6880
+ return out;
6881
+ }
6882
+ var JOIN_TOL = 1e-7;
6883
+ var TURN = 2 * Math.PI;
6884
+ function cross(a, b) {
6885
+ return [
6886
+ a[1] * b[2] - a[2] * b[1],
6887
+ a[2] * b[0] - a[0] * b[2],
6888
+ a[0] * b[1] - a[1] * b[0]
6889
+ ];
6890
+ }
6891
+ function unit(v) {
6892
+ const l = Math.hypot(v[0], v[1], v[2]) || 1;
6893
+ return [
6894
+ v[0] / l,
6895
+ v[1] / l,
6896
+ v[2] / l
6897
+ ];
6898
+ }
6899
+ /**
6900
+ * The entity's object coordinate system, from its extrusion direction (210/220/230), by DXF's
6901
+ * arbitrary axis algorithm. Planar entities (circles, arcs, 2D polylines) store their points in
6902
+ * it; the default extrusion +Z makes it the world frame.
6903
+ */
6904
+ function ocsOf(data) {
6905
+ const normal = unit([
6906
+ getNum(data, 210),
6907
+ getNum(data, 220),
6908
+ getNum(data, 230, 1)
6823
6909
  ]);
6824
- if (type === "CIRCLE") return kernel.makeCircleEdge([
6825
- getNum(data, 10),
6826
- getNum(data, 20),
6827
- getNum(data, 30)
6828
- ], [
6910
+ if (normal[0] === 0 && normal[1] === 0 && normal[2] > 0) return {
6911
+ normal: [
6912
+ 0,
6913
+ 0,
6914
+ 1
6915
+ ],
6916
+ identity: true,
6917
+ toWorld: (p) => p
6918
+ };
6919
+ const ax = unit(cross(Math.abs(normal[0]) < 1 / 64 && Math.abs(normal[1]) < 1 / 64 ? [
6920
+ 0,
6921
+ 1,
6922
+ 0
6923
+ ] : [
6829
6924
  0,
6830
6925
  0,
6831
6926
  1
6832
- ], getNum(data, 40));
6833
- if (type === "ARC") {
6834
- const startAngle = getNum(data, 50) * Math.PI / 180;
6835
- const endAngle = getNum(data, 51) * Math.PI / 180;
6836
- return kernel.makeCircleArc([
6837
- getNum(data, 10),
6838
- getNum(data, 20),
6839
- getNum(data, 30)
6840
- ], [
6927
+ ], normal));
6928
+ const ay = unit(cross(normal, ax));
6929
+ return {
6930
+ normal,
6931
+ identity: false,
6932
+ toWorld: ([x, y, z]) => [
6933
+ ax[0] * x + ay[0] * y + normal[0] * z,
6934
+ ax[1] * x + ay[1] * y + normal[1] * z,
6935
+ ax[2] * x + ay[2] * y + normal[2] * z
6936
+ ]
6937
+ };
6938
+ }
6939
+ function apart(a, b) {
6940
+ return Math.hypot(a[0] - b[0], a[1] - b[1], a[2] - b[2]) > JOIN_TOL;
6941
+ }
6942
+ function linePiece(a, b) {
6943
+ return {
6944
+ start: a,
6945
+ end: b,
6946
+ build: () => require_shapeTypes.getKernel().makeLineEdge(a, b)
6947
+ };
6948
+ }
6949
+ /**
6950
+ * An ARC entity about OCS center `c`, counterclockwise from angle `from` through `sweep` (0, 2π].
6951
+ * In the default OCS, which is most files, it's built from its center and angles, which stays
6952
+ * exact for a shallow arc. In a rotated OCS, whose in-plane axes the kernel doesn't share, it goes
6953
+ * through three points on it, or is a line when its sagitta is under the tolerance.
6954
+ */
6955
+ function arcPiece(c, r, from, sweep, ocs) {
6956
+ const on = (t) => ocs.toWorld([
6957
+ c[0] + r * Math.cos(t),
6958
+ c[1] + r * Math.sin(t),
6959
+ c[2]
6960
+ ]);
6961
+ const start = on(from);
6962
+ const end = on(from + sweep);
6963
+ if (ocs.identity) return {
6964
+ start,
6965
+ end,
6966
+ build: () => require_shapeTypes.getKernel().makeCircleArc(c, [
6967
+ 0,
6968
+ 0,
6969
+ 1
6970
+ ], r, from, from + sweep)
6971
+ };
6972
+ if (r * (1 - Math.cos(sweep / 2)) <= JOIN_TOL) return linePiece(start, end);
6973
+ const mid = on(from + sweep / 2);
6974
+ return {
6975
+ start,
6976
+ end,
6977
+ build: () => require_shapeTypes.getKernel().makeArcEdge(start, mid, end)
6978
+ };
6979
+ }
6980
+ /**
6981
+ * A polyline segment in its OCS: straight, or the arc a bulge describes. A bulge is tan(θ/4) of
6982
+ * the arc's included angle, counterclockwise when positive, so the arc's midpoint lies the sagitta
6983
+ * (bulge × half the chord) off the chord's midpoint, to its right for a positive bulge. The arc
6984
+ * goes through the supplied vertices exactly, so it meets its neighbours; one whose sagitta is
6985
+ * under the tolerance is a line to the kernel and is built as one.
6986
+ */
6987
+ function segment(a, b, bulge, ocs) {
6988
+ const dx = b[0] - a[0];
6989
+ const dy = b[1] - a[1];
6990
+ const d = Math.hypot(dx, dy);
6991
+ const s = bulge * d / 2;
6992
+ if (Math.abs(s) <= JOIN_TOL) return linePiece(ocs.toWorld(a), ocs.toWorld(b));
6993
+ const mid = [
6994
+ (a[0] + b[0]) / 2 + dy / d * s,
6995
+ (a[1] + b[1]) / 2 - dx / d * s,
6996
+ a[2]
6997
+ ];
6998
+ const [wa, wm, wb] = [
6999
+ ocs.toWorld(a),
7000
+ ocs.toWorld(mid),
7001
+ ocs.toWorld(b)
7002
+ ];
7003
+ return {
7004
+ start: wa,
7005
+ end: wb,
7006
+ build: () => require_shapeTypes.getKernel().makeArcEdge(wa, wm, wb)
7007
+ };
7008
+ }
7009
+ var CLOSED = 1;
7010
+ var POLYLINE_3D = 8;
7011
+ var MESH_FORMS = 80;
7012
+ function polylinePieces(entity) {
7013
+ const flags = getNum(entity.data, 70);
7014
+ if (entity.type === "POLYLINE" && (flags & MESH_FORMS) !== 0) return [];
7015
+ const is3d = entity.type === "POLYLINE" && (flags & POLYLINE_3D) !== 0;
7016
+ const ocs = is3d ? {
7017
+ normal: [
6841
7018
  0,
6842
7019
  0,
6843
7020
  1
6844
- ], getNum(data, 40), startAngle, endAngle);
7021
+ ],
7022
+ identity: true,
7023
+ toWorld: (p) => p
7024
+ } : ocsOf(entity.data);
7025
+ const elevation = entity.type === "POLYLINE" && !is3d ? getNum(entity.data, 30) : void 0;
7026
+ const vertices = polyVertices(entity).map((v) => elevation === void 0 ? v : {
7027
+ ...v,
7028
+ point: [
7029
+ v.point[0],
7030
+ v.point[1],
7031
+ elevation
7032
+ ]
7033
+ });
7034
+ const closed = (flags & CLOSED) === CLOSED;
7035
+ const pieces = [];
7036
+ const count = closed ? vertices.length : vertices.length - 1;
7037
+ for (let k = 0; k < count; k++) {
7038
+ const a = vertices[k];
7039
+ const b = vertices[(k + 1) % vertices.length];
7040
+ if (!a || !b || !apart(a.point, b.point)) continue;
7041
+ pieces.push(segment(a.point, b.point, is3d ? 0 : a.bulge, ocs));
7042
+ }
7043
+ return pieces;
7044
+ }
7045
+ function entityToPieces(entity) {
7046
+ const { type, data } = entity;
7047
+ if (type === "LINE") {
7048
+ const start = [
7049
+ getNum(data, 10),
7050
+ getNum(data, 20),
7051
+ getNum(data, 30)
7052
+ ];
7053
+ const end = [
7054
+ getNum(data, 11),
7055
+ getNum(data, 21),
7056
+ getNum(data, 31)
7057
+ ];
7058
+ return apart(start, end) ? [linePiece(start, end)] : [];
6845
7059
  }
7060
+ if (type === "CIRCLE" || type === "ARC") {
7061
+ const ocs = ocsOf(data);
7062
+ const c = [
7063
+ getNum(data, 10),
7064
+ getNum(data, 20),
7065
+ getNum(data, 30)
7066
+ ];
7067
+ const r = getNum(data, 40);
7068
+ if (!(r > 0) || !Number.isFinite(r)) return [];
7069
+ if (type === "CIRCLE") {
7070
+ const center = ocs.toWorld(c);
7071
+ const at = ocs.toWorld([
7072
+ c[0] + r,
7073
+ c[1],
7074
+ c[2]
7075
+ ]);
7076
+ return [{
7077
+ start: at,
7078
+ end: at,
7079
+ build: () => require_shapeTypes.getKernel().makeCircleEdge(center, ocs.normal, r)
7080
+ }];
7081
+ }
7082
+ const from = getNum(data, 50) * Math.PI / 180;
7083
+ const to = getNum(data, 51) * Math.PI / 180;
7084
+ if (!Number.isFinite(from) || !Number.isFinite(to)) return [];
7085
+ let sweep = (to - from) % TURN;
7086
+ if (sweep <= 0) sweep += TURN;
7087
+ return [arcPiece(c, r, from % TURN, sweep, ocs)];
7088
+ }
7089
+ if (type === "LWPOLYLINE" || type === "POLYLINE") return polylinePieces(entity);
7090
+ return [];
7091
+ }
7092
+ /** Gives endpoints within JOIN_TOL of each other one id, by a grid of JOIN_TOL cells. */
7093
+ function pointIds() {
7094
+ const cells = /* @__PURE__ */ new Map();
7095
+ let next = 0;
7096
+ return (p) => {
7097
+ const c = p.map((v) => Math.floor(v / JOIN_TOL));
7098
+ for (let dx = -1; dx <= 1; dx++) for (let dy = -1; dy <= 1; dy++) for (let dz = -1; dz <= 1; dz++) {
7099
+ const hit = cells.get(`${(c[0] ?? 0) + dx},${(c[1] ?? 0) + dy},${(c[2] ?? 0) + dz}`)?.find((e) => !apart(e.p, p));
7100
+ if (hit) return hit.id;
7101
+ }
7102
+ const key = c.join(",");
7103
+ const id = next++;
7104
+ const cell = cells.get(key);
7105
+ if (cell) cell.push({
7106
+ p,
7107
+ id
7108
+ });
7109
+ else cells.set(key, [{
7110
+ p,
7111
+ id
7112
+ }]);
7113
+ return id;
7114
+ };
7115
+ }
7116
+ /** The pieces in connected chains, each in walking order, so every chain makes one wire. */
7117
+ function chainPieces(pieces) {
7118
+ const idOf = pointIds();
7119
+ const ends = pieces.map((p) => [idOf(p.start), idOf(p.end)]);
7120
+ const at = /* @__PURE__ */ new Map();
7121
+ ends.forEach(([s, e], i) => {
7122
+ for (const id of s === e ? [s] : [s, e]) {
7123
+ const list = at.get(id);
7124
+ if (list) list.push(i);
7125
+ else at.set(id, [i]);
7126
+ }
7127
+ });
7128
+ const used = /* @__PURE__ */ new Set();
7129
+ const cursor = /* @__PURE__ */ new Map();
7130
+ const step = (tip) => {
7131
+ const list = at.get(tip) ?? [];
7132
+ let c = cursor.get(tip) ?? 0;
7133
+ while (c < list.length && used.has(list[c] ?? -1)) c++;
7134
+ cursor.set(tip, c + 1);
7135
+ const i = list[c];
7136
+ if (i === void 0) return void 0;
7137
+ used.add(i);
7138
+ const [s, e] = ends[i] ?? [tip, tip];
7139
+ return {
7140
+ i,
7141
+ tip: s === tip ? e : s
7142
+ };
7143
+ };
7144
+ const chains = [];
7145
+ pieces.forEach((first, i) => {
7146
+ if (used.has(i)) return;
7147
+ used.add(i);
7148
+ const chain = [first];
7149
+ const [s, e] = ends[i] ?? [0, 0];
7150
+ for (const [dir, from] of [[1, e], [-1, s]]) for (let hop = step(from); hop; hop = step(hop.tip)) {
7151
+ const piece = pieces[hop.i];
7152
+ if (!piece) continue;
7153
+ if (dir === 1) chain.push(piece);
7154
+ else chain.unshift(piece);
7155
+ }
7156
+ chains.push(chain);
7157
+ });
7158
+ return chains;
6846
7159
  }
6847
7160
  /**
6848
7161
  * Import a DXF file from a Blob, returning kernel wires.
6849
7162
  *
6850
- * Parses ASCII DXF LINE, CIRCLE, and ARC entities.
6851
- * Edges are assembled into wires using `BRepBuilderAPI_MakeWire`.
7163
+ * Parses ASCII DXF LINE, CIRCLE, ARC, LWPOLYLINE and R12 POLYLINE entities, polyline bulges
7164
+ * included, in world coordinates (planar entities are mapped out of their object coordinate
7165
+ * system). Polygon and polyface meshes are skipped. Edges that meet end to end become one wire,
7166
+ * so disconnected outlines come back as separate wires.
6852
7167
  *
6853
7168
  * @param blob - A Blob containing ASCII DXF data.
6854
7169
  * @param options - Optional import settings (layer filter).
@@ -6861,22 +7176,28 @@ async function importDXF(blob, options) {
6861
7176
  } catch (cause) {
6862
7177
  return require_errors.err(require_errors.ioError(require_errors.BrepErrorCode.DXF_IMPORT_FAILED, "Failed to read DXF blob", cause));
6863
7178
  }
6864
- const allEntities = parseEntities(text);
6865
- const entities = options?.layer !== void 0 ? allEntities.filter((e) => e.layer === options.layer) : allEntities;
6866
- if (entities.length === 0) return require_errors.ok([]);
7179
+ const allEntities = gatherPolylines(parseEntities(text));
7180
+ const pieces = (options?.layer !== void 0 ? allEntities.filter((e) => e.layer === options.layer) : allEntities).flatMap(entityToPieces);
7181
+ if (pieces.length === 0) return require_errors.ok([]);
7182
+ const kernel = require_shapeTypes.getKernel();
6867
7183
  const edges = [];
7184
+ const wires = [];
6868
7185
  try {
6869
- for (const entity of entities) {
6870
- const edge = entityToEdge(entity);
6871
- if (edge !== void 0) edges.push(edge);
7186
+ for (const chain of chainPieces(pieces)) {
7187
+ const chainEdges = [];
7188
+ for (const p of chain) {
7189
+ const edge = p.build();
7190
+ edges.push(edge);
7191
+ chainEdges.push(edge);
7192
+ }
7193
+ wires.push(require_shapeTypes.createWire(kernel.makeWire(chainEdges)));
6872
7194
  }
6873
- if (edges.length === 0) return require_errors.ok([]);
6874
- const wire = require_shapeTypes.getKernel().makeWire(edges);
6875
- return require_errors.ok([require_shapeTypes.createWire(wire)]);
7195
+ return require_errors.ok(wires);
6876
7196
  } catch (cause) {
7197
+ for (const w of wires) w[Symbol.dispose]();
6877
7198
  return require_errors.err(require_errors.ioError(require_errors.BrepErrorCode.DXF_IMPORT_FAILED, "Failed to convert DXF entities to geometry", cause));
6878
7199
  } finally {
6879
- for (const edge of edges) edge.delete();
7200
+ for (const edge of edges) kernel.dispose(edge);
6880
7201
  }
6881
7202
  }
6882
7203
  // src/io/ioUtils.ts
package/dist/brepjs.js CHANGED
@@ -27,7 +27,7 @@ import { a as measureDistance, c as measureLinearProps, d as measureVolumeProps,
27
27
  import { t as cornerFinder } from "./cornerFinder-DHg0ej_Q.js";
28
28
  import { a as fuseBlueprints, c as roundedRectangleBlueprint, i as cutBlueprints, n as fuse2D, o as intersectBlueprints, r as intersect2D, s as polysidesBlueprint, t as cut2D } from "./boolean2D-DYAua9O_.js";
29
29
  import { S as reverseCurve, _ as curve2dIsOnCurve, a as isInside2D, b as curve2dSplitAt, c as scale2D, d as stretch2D, f as toSVGPathD, g as curve2dFirstPoint, h as curve2dDistanceFrom, i as getOrientation2D, l as sketchOnFace2D, m as curve2dBoundingBox, n as createCompoundBlueprint, o as mirror2D, p as translate2D, r as getBounds2D, s as rotate2D, t as createBlueprint, u as sketchOnPlane2D, v as curve2dLastPoint, x as curve2dTangentAt, y as curve2dParameter } from "./blueprintFns-C0x5vPm-.js";
30
- import { a as importSVG, c as blueprintToDXF, d as exportGltf, f as exportOBJ, i as exportSTEPConfigured, l as exportDXF, n as importSTEP, o as importSVGPathD, r as importSTL, s as exportThreeMF, t as importIGES, u as exportGlb } from "./importFns-D_tBVKl2.js";
30
+ import { a as importSVG, c as blueprintToDXF, d as exportGltf, f as exportOBJ, i as exportSTEPConfigured, l as exportDXF, n as importSTEP, o as importSVGPathD, r as importSTL, s as exportThreeMF, t as importIGES, u as exportGlb } from "./importFns-Cfw7SIjn.js";
31
31
  import { a as Sketch, b as extrudeAll, c as compoundSketchLoft, d as sketchFace, f as sketchLoft, h as sketchWires, i as Sketches, l as compoundSketchRevolve, m as sketchSweep, n as getFont, o as compoundSketchExtrude, p as sketchRevolve, r as loadFont, s as compoundSketchFace, t as textBlueprints, u as sketchExtrude, v as CompoundSketch, x as revolve$2, y as extrude$2 } from "./textBlueprints-BlfIkOUx.js";
32
32
  import { a as multiSectionSweep, c as twistExtrude, i as guidedSweep, n as loftAll, o as supportExtrude, r as complexExtrude, s as sweep$1, t as loft$2 } from "./loftFns-DIsFP0VL.js";
33
33
  import { _ as createRef, a as isVertexRef, b as defaultScorer, c as resolveRefParams, d as createVertexRef, f as resolveVertexRef, g as captureHint, h as assignRoles, i as isLineageRef, l as createDerivedFaceRef, m as resolveEdgeRef, n as isEdgeRef, o as resolveLineageRef, p as createEdgeRef, r as isFaceRef, s as resolveRefIn, t as isDerivedFaceRef, u as resolveDerivedFaceRef, v as resolveRef, y as updateRoles } from "./refResolveFns-BxvibZPu.js";
@@ -6190,7 +6190,7 @@ function sub(a, b) {
6190
6190
  function dot(a, b) {
6191
6191
  return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
6192
6192
  }
6193
- function cross(a, b) {
6193
+ function cross$1(a, b) {
6194
6194
  return [
6195
6195
  a[1] * b[2] - a[2] * b[1],
6196
6196
  a[2] * b[0] - a[0] * b[2],
@@ -6214,7 +6214,7 @@ function normalize(a) {
6214
6214
  }
6215
6215
  /** A unit vector perpendicular to `v` (for the parallel-normals degenerate case). */
6216
6216
  function anyPerpendicular(v) {
6217
- return normalize(cross(v, Math.abs(v[0]) < .9 ? [
6217
+ return normalize(cross$1(v, Math.abs(v[0]) < .9 ? [
6218
6218
  1,
6219
6219
  0,
6220
6220
  0
@@ -6294,7 +6294,7 @@ function solveAngle(ref, dep, angleRad) {
6294
6294
  1
6295
6295
  ]);
6296
6296
  const phi = Math.acos(Math.max(-1, Math.min(1, dot(nDep, nRef))));
6297
- const c = cross(nDep, nRef);
6297
+ const c = cross$1(nDep, nRef);
6298
6298
  const axis = Math.hypot(c[0], c[1], c[2]) < 1e-9 ? anyPerpendicular(nDep) : c;
6299
6299
  return {
6300
6300
  position: [
@@ -6801,63 +6801,378 @@ function parseEntities(text) {
6801
6801
  current = {
6802
6802
  type: value,
6803
6803
  layer: "0",
6804
- data: /* @__PURE__ */ new Map()
6804
+ data: /* @__PURE__ */ new Map(),
6805
+ pairs: []
6805
6806
  };
6806
6807
  continue;
6807
6808
  }
6808
6809
  if (current) {
6810
+ current.pairs.push([code, value]);
6809
6811
  if (code === 8) current.layer = value;
6810
6812
  else current.data.set(code, value);
6811
6813
  }
6812
6814
  }
6813
6815
  return entities;
6814
6816
  }
6815
- function getNum(data, code, fallback = 0) {
6816
- const v = data.get(code);
6817
- if (v === void 0) return fallback;
6818
- const n = parseFloat(v);
6817
+ function num(value, fallback = 0) {
6818
+ if (value === void 0) return fallback;
6819
+ const n = parseFloat(value);
6819
6820
  return isNaN(n) ? fallback : n;
6820
6821
  }
6821
- function entityToEdge(entity) {
6822
- const { type, data } = entity;
6823
- const kernel = getKernel();
6824
- if (type === "LINE") return kernel.makeLineEdge([
6825
- getNum(data, 10),
6826
- getNum(data, 20),
6827
- getNum(data, 30)
6828
- ], [
6829
- getNum(data, 11),
6830
- getNum(data, 21),
6831
- getNum(data, 31)
6822
+ function getNum(data, code, fallback = 0) {
6823
+ return num(data.get(code), fallback);
6824
+ }
6825
+ /** LWPOLYLINE vertices: each 10 starts one and its 20 and 42 follow; 38 is the elevation. */
6826
+ function lwVertices(entity) {
6827
+ const z = getNum(entity.data, 38);
6828
+ const out = [];
6829
+ for (const [code, value] of entity.pairs) {
6830
+ const last = out[out.length - 1];
6831
+ if (code === 10) out.push({
6832
+ point: [
6833
+ num(value),
6834
+ 0,
6835
+ z
6836
+ ],
6837
+ bulge: 0
6838
+ });
6839
+ else if (code === 20 && last) last.point[1] = num(value);
6840
+ else if (code === 42 && last) last.bulge = num(value);
6841
+ }
6842
+ return out;
6843
+ }
6844
+ /**
6845
+ * The DXF R12 polyline: a POLYLINE header, its VERTEX entities, then SEQEND. They arrive as
6846
+ * separate entities, so the header gathers the vertices that follow it.
6847
+ */
6848
+ function gatherPolylines(entities) {
6849
+ const out = [];
6850
+ for (let i = 0; i < entities.length; i++) {
6851
+ const e = entities[i];
6852
+ if (e === void 0) continue;
6853
+ if (e.type !== "POLYLINE") {
6854
+ if (e.type !== "VERTEX" && e.type !== "SEQEND") out.push(e);
6855
+ continue;
6856
+ }
6857
+ const vertices = [];
6858
+ let j = i + 1;
6859
+ for (; j < entities.length && entities[j]?.type === "VERTEX"; j++) {
6860
+ const v = entities[j];
6861
+ if (v === void 0) continue;
6862
+ vertices.push([10, v.data.get(10) ?? "0"], [20, v.data.get(20) ?? "0"], [30, v.data.get(30) ?? "0"], [42, v.data.get(42) ?? "0"]);
6863
+ }
6864
+ if (entities[j]?.type === "SEQEND") j++;
6865
+ const own = e.pairs.filter(([code]) => code !== 10 && code !== 20 && code !== 30);
6866
+ out.push({
6867
+ ...e,
6868
+ pairs: [...own, ...vertices]
6869
+ });
6870
+ i = j - 1;
6871
+ }
6872
+ return out;
6873
+ }
6874
+ function polyVertices(entity) {
6875
+ if (entity.type === "LWPOLYLINE") return lwVertices(entity);
6876
+ const out = [];
6877
+ const p = entity.pairs;
6878
+ for (let k = 0; k < p.length; k++) {
6879
+ if (p[k]?.[0] !== 10) continue;
6880
+ out.push({
6881
+ point: [
6882
+ num(p[k]?.[1]),
6883
+ num(p[k + 1]?.[1]),
6884
+ num(p[k + 2]?.[1])
6885
+ ],
6886
+ bulge: num(p[k + 3]?.[1])
6887
+ });
6888
+ }
6889
+ return out;
6890
+ }
6891
+ var JOIN_TOL = 1e-7;
6892
+ var TURN = 2 * Math.PI;
6893
+ function cross(a, b) {
6894
+ return [
6895
+ a[1] * b[2] - a[2] * b[1],
6896
+ a[2] * b[0] - a[0] * b[2],
6897
+ a[0] * b[1] - a[1] * b[0]
6898
+ ];
6899
+ }
6900
+ function unit(v) {
6901
+ const l = Math.hypot(v[0], v[1], v[2]) || 1;
6902
+ return [
6903
+ v[0] / l,
6904
+ v[1] / l,
6905
+ v[2] / l
6906
+ ];
6907
+ }
6908
+ /**
6909
+ * The entity's object coordinate system, from its extrusion direction (210/220/230), by DXF's
6910
+ * arbitrary axis algorithm. Planar entities (circles, arcs, 2D polylines) store their points in
6911
+ * it; the default extrusion +Z makes it the world frame.
6912
+ */
6913
+ function ocsOf(data) {
6914
+ const normal = unit([
6915
+ getNum(data, 210),
6916
+ getNum(data, 220),
6917
+ getNum(data, 230, 1)
6832
6918
  ]);
6833
- if (type === "CIRCLE") return kernel.makeCircleEdge([
6834
- getNum(data, 10),
6835
- getNum(data, 20),
6836
- getNum(data, 30)
6837
- ], [
6919
+ if (normal[0] === 0 && normal[1] === 0 && normal[2] > 0) return {
6920
+ normal: [
6921
+ 0,
6922
+ 0,
6923
+ 1
6924
+ ],
6925
+ identity: true,
6926
+ toWorld: (p) => p
6927
+ };
6928
+ const ax = unit(cross(Math.abs(normal[0]) < 1 / 64 && Math.abs(normal[1]) < 1 / 64 ? [
6929
+ 0,
6930
+ 1,
6931
+ 0
6932
+ ] : [
6838
6933
  0,
6839
6934
  0,
6840
6935
  1
6841
- ], getNum(data, 40));
6842
- if (type === "ARC") {
6843
- const startAngle = getNum(data, 50) * Math.PI / 180;
6844
- const endAngle = getNum(data, 51) * Math.PI / 180;
6845
- return kernel.makeCircleArc([
6846
- getNum(data, 10),
6847
- getNum(data, 20),
6848
- getNum(data, 30)
6849
- ], [
6936
+ ], normal));
6937
+ const ay = unit(cross(normal, ax));
6938
+ return {
6939
+ normal,
6940
+ identity: false,
6941
+ toWorld: ([x, y, z]) => [
6942
+ ax[0] * x + ay[0] * y + normal[0] * z,
6943
+ ax[1] * x + ay[1] * y + normal[1] * z,
6944
+ ax[2] * x + ay[2] * y + normal[2] * z
6945
+ ]
6946
+ };
6947
+ }
6948
+ function apart(a, b) {
6949
+ return Math.hypot(a[0] - b[0], a[1] - b[1], a[2] - b[2]) > JOIN_TOL;
6950
+ }
6951
+ function linePiece(a, b) {
6952
+ return {
6953
+ start: a,
6954
+ end: b,
6955
+ build: () => getKernel().makeLineEdge(a, b)
6956
+ };
6957
+ }
6958
+ /**
6959
+ * An ARC entity about OCS center `c`, counterclockwise from angle `from` through `sweep` (0, 2π].
6960
+ * In the default OCS, which is most files, it's built from its center and angles, which stays
6961
+ * exact for a shallow arc. In a rotated OCS, whose in-plane axes the kernel doesn't share, it goes
6962
+ * through three points on it, or is a line when its sagitta is under the tolerance.
6963
+ */
6964
+ function arcPiece(c, r, from, sweep, ocs) {
6965
+ const on = (t) => ocs.toWorld([
6966
+ c[0] + r * Math.cos(t),
6967
+ c[1] + r * Math.sin(t),
6968
+ c[2]
6969
+ ]);
6970
+ const start = on(from);
6971
+ const end = on(from + sweep);
6972
+ if (ocs.identity) return {
6973
+ start,
6974
+ end,
6975
+ build: () => getKernel().makeCircleArc(c, [
6976
+ 0,
6977
+ 0,
6978
+ 1
6979
+ ], r, from, from + sweep)
6980
+ };
6981
+ if (r * (1 - Math.cos(sweep / 2)) <= JOIN_TOL) return linePiece(start, end);
6982
+ const mid = on(from + sweep / 2);
6983
+ return {
6984
+ start,
6985
+ end,
6986
+ build: () => getKernel().makeArcEdge(start, mid, end)
6987
+ };
6988
+ }
6989
+ /**
6990
+ * A polyline segment in its OCS: straight, or the arc a bulge describes. A bulge is tan(θ/4) of
6991
+ * the arc's included angle, counterclockwise when positive, so the arc's midpoint lies the sagitta
6992
+ * (bulge × half the chord) off the chord's midpoint, to its right for a positive bulge. The arc
6993
+ * goes through the supplied vertices exactly, so it meets its neighbours; one whose sagitta is
6994
+ * under the tolerance is a line to the kernel and is built as one.
6995
+ */
6996
+ function segment(a, b, bulge, ocs) {
6997
+ const dx = b[0] - a[0];
6998
+ const dy = b[1] - a[1];
6999
+ const d = Math.hypot(dx, dy);
7000
+ const s = bulge * d / 2;
7001
+ if (Math.abs(s) <= JOIN_TOL) return linePiece(ocs.toWorld(a), ocs.toWorld(b));
7002
+ const mid = [
7003
+ (a[0] + b[0]) / 2 + dy / d * s,
7004
+ (a[1] + b[1]) / 2 - dx / d * s,
7005
+ a[2]
7006
+ ];
7007
+ const [wa, wm, wb] = [
7008
+ ocs.toWorld(a),
7009
+ ocs.toWorld(mid),
7010
+ ocs.toWorld(b)
7011
+ ];
7012
+ return {
7013
+ start: wa,
7014
+ end: wb,
7015
+ build: () => getKernel().makeArcEdge(wa, wm, wb)
7016
+ };
7017
+ }
7018
+ var CLOSED = 1;
7019
+ var POLYLINE_3D = 8;
7020
+ var MESH_FORMS = 80;
7021
+ function polylinePieces(entity) {
7022
+ const flags = getNum(entity.data, 70);
7023
+ if (entity.type === "POLYLINE" && (flags & MESH_FORMS) !== 0) return [];
7024
+ const is3d = entity.type === "POLYLINE" && (flags & POLYLINE_3D) !== 0;
7025
+ const ocs = is3d ? {
7026
+ normal: [
6850
7027
  0,
6851
7028
  0,
6852
7029
  1
6853
- ], getNum(data, 40), startAngle, endAngle);
7030
+ ],
7031
+ identity: true,
7032
+ toWorld: (p) => p
7033
+ } : ocsOf(entity.data);
7034
+ const elevation = entity.type === "POLYLINE" && !is3d ? getNum(entity.data, 30) : void 0;
7035
+ const vertices = polyVertices(entity).map((v) => elevation === void 0 ? v : {
7036
+ ...v,
7037
+ point: [
7038
+ v.point[0],
7039
+ v.point[1],
7040
+ elevation
7041
+ ]
7042
+ });
7043
+ const closed = (flags & CLOSED) === CLOSED;
7044
+ const pieces = [];
7045
+ const count = closed ? vertices.length : vertices.length - 1;
7046
+ for (let k = 0; k < count; k++) {
7047
+ const a = vertices[k];
7048
+ const b = vertices[(k + 1) % vertices.length];
7049
+ if (!a || !b || !apart(a.point, b.point)) continue;
7050
+ pieces.push(segment(a.point, b.point, is3d ? 0 : a.bulge, ocs));
7051
+ }
7052
+ return pieces;
7053
+ }
7054
+ function entityToPieces(entity) {
7055
+ const { type, data } = entity;
7056
+ if (type === "LINE") {
7057
+ const start = [
7058
+ getNum(data, 10),
7059
+ getNum(data, 20),
7060
+ getNum(data, 30)
7061
+ ];
7062
+ const end = [
7063
+ getNum(data, 11),
7064
+ getNum(data, 21),
7065
+ getNum(data, 31)
7066
+ ];
7067
+ return apart(start, end) ? [linePiece(start, end)] : [];
6854
7068
  }
7069
+ if (type === "CIRCLE" || type === "ARC") {
7070
+ const ocs = ocsOf(data);
7071
+ const c = [
7072
+ getNum(data, 10),
7073
+ getNum(data, 20),
7074
+ getNum(data, 30)
7075
+ ];
7076
+ const r = getNum(data, 40);
7077
+ if (!(r > 0) || !Number.isFinite(r)) return [];
7078
+ if (type === "CIRCLE") {
7079
+ const center = ocs.toWorld(c);
7080
+ const at = ocs.toWorld([
7081
+ c[0] + r,
7082
+ c[1],
7083
+ c[2]
7084
+ ]);
7085
+ return [{
7086
+ start: at,
7087
+ end: at,
7088
+ build: () => getKernel().makeCircleEdge(center, ocs.normal, r)
7089
+ }];
7090
+ }
7091
+ const from = getNum(data, 50) * Math.PI / 180;
7092
+ const to = getNum(data, 51) * Math.PI / 180;
7093
+ if (!Number.isFinite(from) || !Number.isFinite(to)) return [];
7094
+ let sweep = (to - from) % TURN;
7095
+ if (sweep <= 0) sweep += TURN;
7096
+ return [arcPiece(c, r, from % TURN, sweep, ocs)];
7097
+ }
7098
+ if (type === "LWPOLYLINE" || type === "POLYLINE") return polylinePieces(entity);
7099
+ return [];
7100
+ }
7101
+ /** Gives endpoints within JOIN_TOL of each other one id, by a grid of JOIN_TOL cells. */
7102
+ function pointIds() {
7103
+ const cells = /* @__PURE__ */ new Map();
7104
+ let next = 0;
7105
+ return (p) => {
7106
+ const c = p.map((v) => Math.floor(v / JOIN_TOL));
7107
+ for (let dx = -1; dx <= 1; dx++) for (let dy = -1; dy <= 1; dy++) for (let dz = -1; dz <= 1; dz++) {
7108
+ const hit = cells.get(`${(c[0] ?? 0) + dx},${(c[1] ?? 0) + dy},${(c[2] ?? 0) + dz}`)?.find((e) => !apart(e.p, p));
7109
+ if (hit) return hit.id;
7110
+ }
7111
+ const key = c.join(",");
7112
+ const id = next++;
7113
+ const cell = cells.get(key);
7114
+ if (cell) cell.push({
7115
+ p,
7116
+ id
7117
+ });
7118
+ else cells.set(key, [{
7119
+ p,
7120
+ id
7121
+ }]);
7122
+ return id;
7123
+ };
7124
+ }
7125
+ /** The pieces in connected chains, each in walking order, so every chain makes one wire. */
7126
+ function chainPieces(pieces) {
7127
+ const idOf = pointIds();
7128
+ const ends = pieces.map((p) => [idOf(p.start), idOf(p.end)]);
7129
+ const at = /* @__PURE__ */ new Map();
7130
+ ends.forEach(([s, e], i) => {
7131
+ for (const id of s === e ? [s] : [s, e]) {
7132
+ const list = at.get(id);
7133
+ if (list) list.push(i);
7134
+ else at.set(id, [i]);
7135
+ }
7136
+ });
7137
+ const used = /* @__PURE__ */ new Set();
7138
+ const cursor = /* @__PURE__ */ new Map();
7139
+ const step = (tip) => {
7140
+ const list = at.get(tip) ?? [];
7141
+ let c = cursor.get(tip) ?? 0;
7142
+ while (c < list.length && used.has(list[c] ?? -1)) c++;
7143
+ cursor.set(tip, c + 1);
7144
+ const i = list[c];
7145
+ if (i === void 0) return void 0;
7146
+ used.add(i);
7147
+ const [s, e] = ends[i] ?? [tip, tip];
7148
+ return {
7149
+ i,
7150
+ tip: s === tip ? e : s
7151
+ };
7152
+ };
7153
+ const chains = [];
7154
+ pieces.forEach((first, i) => {
7155
+ if (used.has(i)) return;
7156
+ used.add(i);
7157
+ const chain = [first];
7158
+ const [s, e] = ends[i] ?? [0, 0];
7159
+ for (const [dir, from] of [[1, e], [-1, s]]) for (let hop = step(from); hop; hop = step(hop.tip)) {
7160
+ const piece = pieces[hop.i];
7161
+ if (!piece) continue;
7162
+ if (dir === 1) chain.push(piece);
7163
+ else chain.unshift(piece);
7164
+ }
7165
+ chains.push(chain);
7166
+ });
7167
+ return chains;
6855
7168
  }
6856
7169
  /**
6857
7170
  * Import a DXF file from a Blob, returning kernel wires.
6858
7171
  *
6859
- * Parses ASCII DXF LINE, CIRCLE, and ARC entities.
6860
- * Edges are assembled into wires using `BRepBuilderAPI_MakeWire`.
7172
+ * Parses ASCII DXF LINE, CIRCLE, ARC, LWPOLYLINE and R12 POLYLINE entities, polyline bulges
7173
+ * included, in world coordinates (planar entities are mapped out of their object coordinate
7174
+ * system). Polygon and polyface meshes are skipped. Edges that meet end to end become one wire,
7175
+ * so disconnected outlines come back as separate wires.
6861
7176
  *
6862
7177
  * @param blob - A Blob containing ASCII DXF data.
6863
7178
  * @param options - Optional import settings (layer filter).
@@ -6870,22 +7185,28 @@ async function importDXF(blob, options) {
6870
7185
  } catch (cause) {
6871
7186
  return err(ioError(BrepErrorCode.DXF_IMPORT_FAILED, "Failed to read DXF blob", cause));
6872
7187
  }
6873
- const allEntities = parseEntities(text);
6874
- const entities = options?.layer !== void 0 ? allEntities.filter((e) => e.layer === options.layer) : allEntities;
6875
- if (entities.length === 0) return ok([]);
7188
+ const allEntities = gatherPolylines(parseEntities(text));
7189
+ const pieces = (options?.layer !== void 0 ? allEntities.filter((e) => e.layer === options.layer) : allEntities).flatMap(entityToPieces);
7190
+ if (pieces.length === 0) return ok([]);
7191
+ const kernel = getKernel();
6876
7192
  const edges = [];
7193
+ const wires = [];
6877
7194
  try {
6878
- for (const entity of entities) {
6879
- const edge = entityToEdge(entity);
6880
- if (edge !== void 0) edges.push(edge);
7195
+ for (const chain of chainPieces(pieces)) {
7196
+ const chainEdges = [];
7197
+ for (const p of chain) {
7198
+ const edge = p.build();
7199
+ edges.push(edge);
7200
+ chainEdges.push(edge);
7201
+ }
7202
+ wires.push(createWire(kernel.makeWire(chainEdges)));
6881
7203
  }
6882
- if (edges.length === 0) return ok([]);
6883
- const wire = getKernel().makeWire(edges);
6884
- return ok([createWire(wire)]);
7204
+ return ok(wires);
6885
7205
  } catch (cause) {
7206
+ for (const w of wires) w[Symbol.dispose]();
6886
7207
  return err(ioError(BrepErrorCode.DXF_IMPORT_FAILED, "Failed to convert DXF entities to geometry", cause));
6887
7208
  } finally {
6888
- for (const edge of edges) edge.delete();
7209
+ for (const edge of edges) kernel.dispose(edge);
6889
7210
  }
6890
7211
  }
6891
7212
  // src/io/ioUtils.ts
@@ -507,6 +507,50 @@ function buildBinaryBuffer(mesh) {
507
507
  return buffer;
508
508
  }
509
509
  // src/io/dxfExportFns.ts
510
+ /** One entity's group-code lines in the ENTITIES section. */
511
+ function entityLines(entity, layer) {
512
+ if (entity.type === "LINE") {
513
+ const [x1, y1] = entity.start;
514
+ const [x2, y2] = entity.end;
515
+ return [
516
+ "0",
517
+ "LINE",
518
+ "8",
519
+ layer,
520
+ "10",
521
+ String(x1),
522
+ "20",
523
+ String(y1),
524
+ "30",
525
+ "0"
526
+ ].concat([
527
+ "11",
528
+ String(x2),
529
+ "21",
530
+ String(y2),
531
+ "31",
532
+ "0"
533
+ ]);
534
+ }
535
+ const out = [
536
+ "0",
537
+ "POLYLINE",
538
+ "8",
539
+ layer,
540
+ "66",
541
+ "1",
542
+ "10",
543
+ "0",
544
+ "20",
545
+ "0",
546
+ "30",
547
+ "0"
548
+ ];
549
+ out.push("70", entity.closed ? "1" : "0");
550
+ for (const [x, y] of entity.points) out.push("0", "VERTEX", "8", layer, "10", String(x), "20", String(y), "30", "0");
551
+ out.push("0", "SEQEND", "8", layer);
552
+ return out;
553
+ }
510
554
  /**
511
555
  * Export DXF entities to a DXF R12 ASCII string.
512
556
  *
@@ -541,14 +585,7 @@ function exportDXF(entities, options) {
541
585
  lines.push("0", "ENDTAB");
542
586
  lines.push("0", "ENDSEC");
543
587
  lines.push("0", "SECTION", "2", "ENTITIES");
544
- for (const entity of entities) {
545
- const entLayer = entity.layer ?? layer;
546
- if (entity.type === "LINE") lines.push("0", "LINE", "8", entLayer, "10", String(entity.start[0]), "20", String(entity.start[1]), "30", "0", "11", String(entity.end[0]), "21", String(entity.end[1]), "31", "0");
547
- else {
548
- lines.push("0", "LWPOLYLINE", "8", entLayer, "90", String(entity.points.length), "70", entity.closed ? "1" : "0");
549
- for (const pt of entity.points) lines.push("10", String(pt[0]), "20", String(pt[1]));
550
- }
551
- }
588
+ for (const entity of entities) for (const line of entityLines(entity, entity.layer ?? layer)) lines.push(line);
552
589
  lines.push("0", "ENDSEC");
553
590
  lines.push("0", "EOF");
554
591
  return lines.join("\n") + "\n";
@@ -557,7 +594,7 @@ function exportDXF(entities, options) {
557
594
  * Convert a Blueprint (or CompoundBlueprint/Blueprints) to a DXF string.
558
595
  *
559
596
  * Each straight segment becomes a LINE entity; arcs, ellipses, splines,
560
- * and other curves are approximated as LWPOLYLINE entities.
597
+ * and other curves are approximated as POLYLINE entities.
561
598
  *
562
599
  * @param drawing - A Blueprint, CompoundBlueprint, or Blueprints collection.
563
600
  * @param options - Layer name and curve approximation settings.
@@ -507,6 +507,50 @@ function buildBinaryBuffer(mesh) {
507
507
  return buffer;
508
508
  }
509
509
  // src/io/dxfExportFns.ts
510
+ /** One entity's group-code lines in the ENTITIES section. */
511
+ function entityLines(entity, layer) {
512
+ if (entity.type === "LINE") {
513
+ const [x1, y1] = entity.start;
514
+ const [x2, y2] = entity.end;
515
+ return [
516
+ "0",
517
+ "LINE",
518
+ "8",
519
+ layer,
520
+ "10",
521
+ String(x1),
522
+ "20",
523
+ String(y1),
524
+ "30",
525
+ "0"
526
+ ].concat([
527
+ "11",
528
+ String(x2),
529
+ "21",
530
+ String(y2),
531
+ "31",
532
+ "0"
533
+ ]);
534
+ }
535
+ const out = [
536
+ "0",
537
+ "POLYLINE",
538
+ "8",
539
+ layer,
540
+ "66",
541
+ "1",
542
+ "10",
543
+ "0",
544
+ "20",
545
+ "0",
546
+ "30",
547
+ "0"
548
+ ];
549
+ out.push("70", entity.closed ? "1" : "0");
550
+ for (const [x, y] of entity.points) out.push("0", "VERTEX", "8", layer, "10", String(x), "20", String(y), "30", "0");
551
+ out.push("0", "SEQEND", "8", layer);
552
+ return out;
553
+ }
510
554
  /**
511
555
  * Export DXF entities to a DXF R12 ASCII string.
512
556
  *
@@ -541,14 +585,7 @@ function exportDXF(entities, options) {
541
585
  lines.push("0", "ENDTAB");
542
586
  lines.push("0", "ENDSEC");
543
587
  lines.push("0", "SECTION", "2", "ENTITIES");
544
- for (const entity of entities) {
545
- const entLayer = entity.layer ?? layer;
546
- if (entity.type === "LINE") lines.push("0", "LINE", "8", entLayer, "10", String(entity.start[0]), "20", String(entity.start[1]), "30", "0", "11", String(entity.end[0]), "21", String(entity.end[1]), "31", "0");
547
- else {
548
- lines.push("0", "LWPOLYLINE", "8", entLayer, "90", String(entity.points.length), "70", entity.closed ? "1" : "0");
549
- for (const pt of entity.points) lines.push("10", String(pt[0]), "20", String(pt[1]));
550
- }
551
- }
588
+ for (const entity of entities) for (const line of entityLines(entity, entity.layer ?? layer)) lines.push(line);
552
589
  lines.push("0", "ENDSEC");
553
590
  lines.push("0", "EOF");
554
591
  return lines.join("\n") + "\n";
@@ -557,7 +594,7 @@ function exportDXF(entities, options) {
557
594
  * Convert a Blueprint (or CompoundBlueprint/Blueprints) to a DXF string.
558
595
  *
559
596
  * Each straight segment becomes a LINE entity; arcs, ellipses, splines,
560
- * and other curves are approximated as LWPOLYLINE entities.
597
+ * and other curves are approximated as POLYLINE entities.
561
598
  *
562
599
  * @param drawing - A Blueprint, CompoundBlueprint, or Blueprints collection.
563
600
  * @param options - Layer name and curve approximation settings.
@@ -6,7 +6,7 @@ import { default as Blueprints } from '../2d/blueprints/blueprints.js';
6
6
  * A single DXF entity (LINE or POLYLINE).
7
7
  *
8
8
  * LINE maps directly to a DXF LINE entity.
9
- * POLYLINE maps to an LWPOLYLINE with optional closure.
9
+ * POLYLINE maps to an R12 POLYLINE (its VERTEX entities and a SEQEND), optionally closed.
10
10
  */
11
11
  export type DXFEntity = {
12
12
  type: 'LINE';
@@ -51,7 +51,7 @@ export declare function exportDXF(entities: DXFEntity[], options?: DXFExportOpti
51
51
  * Convert a Blueprint (or CompoundBlueprint/Blueprints) to a DXF string.
52
52
  *
53
53
  * Each straight segment becomes a LINE entity; arcs, ellipses, splines,
54
- * and other curves are approximated as LWPOLYLINE entities.
54
+ * and other curves are approximated as POLYLINE entities.
55
55
  *
56
56
  * @param drawing - A Blueprint, CompoundBlueprint, or Blueprints collection.
57
57
  * @param options - Layer name and curve approximation settings.
@@ -6,8 +6,10 @@ export interface DXFImportOptions {
6
6
  /**
7
7
  * Import a DXF file from a Blob, returning kernel wires.
8
8
  *
9
- * Parses ASCII DXF LINE, CIRCLE, and ARC entities.
10
- * Edges are assembled into wires using `BRepBuilderAPI_MakeWire`.
9
+ * Parses ASCII DXF LINE, CIRCLE, ARC, LWPOLYLINE and R12 POLYLINE entities, polyline bulges
10
+ * included, in world coordinates (planar entities are mapped out of their object coordinate
11
+ * system). Polygon and polyface meshes are skipped. Edges that meet end to end become one wire,
12
+ * so disconnected outlines come back as separate wires.
11
13
  *
12
14
  * @param blob - A Blob containing ASCII DXF data.
13
15
  * @param options - Optional import settings (layer filter).
package/dist/io.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_meshFns = require("./meshFns-0W1-80zv.cjs");
3
- const require_importFns = require("./importFns-DI3Q8B0Q.cjs");
3
+ const require_importFns = require("./importFns-D5wRI_Cc.cjs");
4
4
  exports.blueprintToDXF = require_importFns.blueprintToDXF;
5
5
  exports.exportDXF = require_importFns.exportDXF;
6
6
  exports.exportGlb = require_importFns.exportGlb;
package/dist/io.js CHANGED
@@ -1,3 +1,3 @@
1
1
  import { n as exportSTEP, r as exportSTL, t as exportIGES } from "./meshFns-B9jPfcB2.js";
2
- import { a as importSVG, c as blueprintToDXF, d as exportGltf, f as exportOBJ, i as exportSTEPConfigured, l as exportDXF, n as importSTEP, o as importSVGPathD, r as importSTL, s as exportThreeMF, t as importIGES, u as exportGlb } from "./importFns-D_tBVKl2.js";
2
+ import { a as importSVG, c as blueprintToDXF, d as exportGltf, f as exportOBJ, i as exportSTEPConfigured, l as exportDXF, n as importSTEP, o as importSVGPathD, r as importSTL, s as exportThreeMF, t as importIGES, u as exportGlb } from "./importFns-Cfw7SIjn.js";
3
3
  export { blueprintToDXF, exportDXF, exportGlb, exportGltf, exportIGES, exportOBJ, exportSTEP, exportSTEPConfigured, exportSTL, exportThreeMF, importIGES, importSTEP, importSTL, importSVG, importSVGPathD };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brepjs",
3
- "version": "20.11.0",
3
+ "version": "20.11.1",
4
4
  "description": "Web CAD library with pluggable geometry kernel",
5
5
  "keywords": [
6
6
  "cad",