dxf-kit 1.0.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.
Files changed (3) hide show
  1. package/dist/index.cjs +2126 -0
  2. package/dist/index.mjs +2121 -0
  3. package/package.json +25 -0
package/dist/index.mjs ADDED
@@ -0,0 +1,2121 @@
1
+ /**
2
+ * ============================================================
3
+ * dxf-kit/src/dxfParser.js — ESM 版
4
+ *
5
+ * 与你原来的 CJS 版相比,改动只有 3 处:
6
+ * 1. 删掉顶部 const fs = require("fs").promises
7
+ * 2. parseDxf 内部按需 import("fs")
8
+ * 3. 底部 module.exports → export
9
+ * ============================================================
10
+ */
11
+
12
+ // ─────────────────── 工具函数 ───────────────────
13
+ // (这部分和你原来的完全一样,一行不改)
14
+
15
+ function tokenize(dxfString) {
16
+ const lines = dxfString.split(/\r?\n/);
17
+ const pairs = [];
18
+ for (let i = 0; i + 1 < lines.length; i += 2) {
19
+ const code = parseInt(lines[i].trim(), 10);
20
+ const value = lines[i + 1].trim();
21
+ pairs.push({ code, value });
22
+ }
23
+ return pairs;
24
+ }
25
+
26
+ function autoConvert(code, value) {
27
+ if (
28
+ (code >= 10 && code <= 59) ||
29
+ (code >= 110 && code <= 149) ||
30
+ (code >= 210 && code <= 239) ||
31
+ (code >= 460 && code <= 469) ||
32
+ (code >= 1010 && code <= 1059)
33
+ ) {
34
+ return parseFloat(value);
35
+ }
36
+ if (
37
+ (code >= 60 && code <= 79) ||
38
+ (code >= 90 && code <= 99) ||
39
+ (code >= 170 && code <= 179) ||
40
+ (code >= 270 && code <= 289) ||
41
+ (code >= 370 && code <= 389) ||
42
+ (code >= 400 && code <= 409) ||
43
+ (code >= 1060 && code <= 1079)
44
+ ) {
45
+ return parseInt(value, 10);
46
+ }
47
+ return value;
48
+ }
49
+
50
+ function splitSections(pairs) {
51
+ const sections = {};
52
+ let i = 0;
53
+ while (i < pairs.length) {
54
+ if (pairs[i].code === 0 && pairs[i].value === "SECTION") {
55
+ i++;
56
+ const sectionName = pairs[i].value;
57
+ i++;
58
+ const start = i;
59
+ while (i < pairs.length && !(pairs[i].code === 0 && pairs[i].value === "ENDSEC")) {
60
+ i++;
61
+ }
62
+ sections[sectionName] = pairs.slice(start, i);
63
+ i++;
64
+ } else {
65
+ i++;
66
+ }
67
+ }
68
+ return sections;
69
+ }
70
+
71
+ function parseHeader(pairs) {
72
+ if (!pairs || pairs.length === 0) return {};
73
+ const header = {};
74
+ let i = 0;
75
+ while (i < pairs.length) {
76
+ if (pairs[i].code === 9) {
77
+ const varName = pairs[i].value;
78
+ i++;
79
+ const values = {};
80
+ while (i < pairs.length && pairs[i].code !== 9 && pairs[i].code !== 0) {
81
+ values[pairs[i].code] = autoConvert(pairs[i].code, pairs[i].value);
82
+ i++;
83
+ }
84
+ const keys = Object.keys(values);
85
+ header[varName] = keys.length === 1 ? values[keys[0]] : values;
86
+ } else {
87
+ i++;
88
+ }
89
+ }
90
+ return header;
91
+ }
92
+
93
+ function parseClasses(pairs) {
94
+ if (!pairs || pairs.length === 0) return [];
95
+ const classes = [];
96
+ let i = 0;
97
+ while (i < pairs.length) {
98
+ if (pairs[i].code === 0 && pairs[i].value === "CLASS") {
99
+ i++;
100
+ const cls = {};
101
+ while (i < pairs.length && pairs[i].code !== 0) {
102
+ switch (pairs[i].code) {
103
+ case 1: cls.dxfRecord = pairs[i].value; break;
104
+ case 2: cls.cppClassName = pairs[i].value; break;
105
+ case 3: cls.appName = pairs[i].value; break;
106
+ case 90: cls.proxyCapabilities = parseInt(pairs[i].value, 10); break;
107
+ case 91: cls.instanceCount = parseInt(pairs[i].value, 10); break;
108
+ case 280: cls.wasProxy = parseInt(pairs[i].value, 10); break;
109
+ case 281: cls.isEntity = parseInt(pairs[i].value, 10); break;
110
+ default: cls[`code_${pairs[i].code}`] = pairs[i].value;
111
+ }
112
+ i++;
113
+ }
114
+ classes.push(cls);
115
+ } else {
116
+ i++;
117
+ }
118
+ }
119
+ return classes;
120
+ }
121
+
122
+ function parseTables(pairs) {
123
+ if (!pairs || pairs.length === 0) return {};
124
+ const tables = {};
125
+ let i = 0;
126
+ while (i < pairs.length) {
127
+ if (pairs[i].code === 0 && pairs[i].value === "TABLE") {
128
+ i++;
129
+ const tableName = pairs[i].value;
130
+ i++;
131
+ const start = i;
132
+ while (i < pairs.length && !(pairs[i].code === 0 && pairs[i].value === "ENDTAB")) {
133
+ i++;
134
+ }
135
+ tables[tableName] = parseTableEntries(tableName, pairs.slice(start, i));
136
+ i++;
137
+ } else {
138
+ i++;
139
+ }
140
+ }
141
+ return tables;
142
+ }
143
+
144
+ function parseTableEntries(tableName, pairs) {
145
+ const entries = [];
146
+ let i = 0;
147
+ while (i < pairs.length) {
148
+ if (pairs[i].code === 0) {
149
+ const entryType = pairs[i].value;
150
+ i++;
151
+ const entry = { _type: entryType };
152
+ while (i < pairs.length && pairs[i].code !== 0) {
153
+ const code = pairs[i].code;
154
+ const val = autoConvert(code, pairs[i].value);
155
+ switch (code) {
156
+ case 2: entry.name = val; break;
157
+ case 5: entry.handle = val; break;
158
+ case 6: entry.linetypeName = val; break;
159
+ case 7: entry.styleName = val; break;
160
+ case 62: entry.colorNumber = val; break;
161
+ case 70: entry.flags = val; break;
162
+ case 40: entry.height = val; break;
163
+ case 41: entry.widthFactor = val; break;
164
+ case 50: entry.angle = val; break;
165
+ case 100: entry.subclassMarker = entry.subclassMarker ? [].concat(entry.subclassMarker, val) : val; break;
166
+ case 330: entry.ownerHandle = val; break;
167
+ case 370: entry.lineweight = val; break;
168
+ case 390: entry.plotStyleHandle = val; break;
169
+ default:
170
+ if (entry[`code_${code}`] !== undefined) {
171
+ entry[`code_${code}`] = [].concat(entry[`code_${code}`], val);
172
+ } else {
173
+ entry[`code_${code}`] = val;
174
+ }
175
+ }
176
+ i++;
177
+ }
178
+ entries.push(entry);
179
+ } else {
180
+ i++;
181
+ }
182
+ }
183
+ return entries;
184
+ }
185
+
186
+ function parseBlocks(pairs) {
187
+ if (!pairs || pairs.length === 0) return [];
188
+ const blocks = [];
189
+ let i = 0;
190
+ while (i < pairs.length) {
191
+ if (pairs[i].code === 0 && pairs[i].value === "BLOCK") {
192
+ i++;
193
+ const block = { entities: [] };
194
+ while (i < pairs.length && pairs[i].code !== 0) {
195
+ switch (pairs[i].code) {
196
+ case 2: block.name = pairs[i].value; break;
197
+ case 3: block.name2 = pairs[i].value; break;
198
+ case 5: block.handle = pairs[i].value; break;
199
+ case 8: block.layer = pairs[i].value; break;
200
+ case 10: block.basePointX = parseFloat(pairs[i].value); break;
201
+ case 20: block.basePointY = parseFloat(pairs[i].value); break;
202
+ case 30: block.basePointZ = parseFloat(pairs[i].value); break;
203
+ case 70: block.flags = parseInt(pairs[i].value, 10); break;
204
+ case 100: break;
205
+ case 330: block.ownerHandle = pairs[i].value; break;
206
+ default: block[`code_${pairs[i].code}`] = pairs[i].value;
207
+ }
208
+ i++;
209
+ }
210
+ const entityPairs = [];
211
+ while (i < pairs.length && !(pairs[i].code === 0 && pairs[i].value === "ENDBLK")) {
212
+ entityPairs.push(pairs[i]);
213
+ i++;
214
+ }
215
+ if (entityPairs.length > 0) block.entities = parseEntityList(entityPairs);
216
+ if (i < pairs.length) {
217
+ i++;
218
+ while (i < pairs.length && pairs[i].code !== 0) i++;
219
+ }
220
+ blocks.push(block);
221
+ } else {
222
+ i++;
223
+ }
224
+ }
225
+ return blocks;
226
+ }
227
+
228
+ // ─────────────────── ENTITIES 解析 ───────────────────
229
+
230
+ function parseEntityList(pairs) {
231
+ const entities = [];
232
+ let i = 0;
233
+ while (i < pairs.length) {
234
+ if (pairs[i].code === 0) {
235
+ const entityType = pairs[i].value;
236
+ i++;
237
+ const entityPairs = [];
238
+ while (i < pairs.length && pairs[i].code !== 0) {
239
+ entityPairs.push(pairs[i]);
240
+ i++;
241
+ }
242
+ const entity = parseOneEntity(entityType, entityPairs);
243
+ if (entity) entities.push(entity);
244
+ } else {
245
+ i++;
246
+ }
247
+ }
248
+ return entities;
249
+ }
250
+
251
+ function parseOneEntity(type, pairs) {
252
+ const base = { type };
253
+ for (const p of pairs) {
254
+ switch (p.code) {
255
+ case 5: base.handle = p.value; break;
256
+ case 8: base.layer = p.value; break;
257
+ case 6: base.linetypeName = p.value; break;
258
+ case 48: base.linetypeScale = parseFloat(p.value); break;
259
+ case 60: base.visible = parseInt(p.value, 10) === 0; break;
260
+ case 62: base.colorNumber = parseInt(p.value, 10); break;
261
+ case 67: base.inPaperSpace = parseInt(p.value, 10) === 1; break;
262
+ case 330: base.ownerHandle = p.value; break;
263
+ case 370: base.lineweight = parseInt(p.value, 10); break;
264
+ case 420: base.trueColor = parseInt(p.value, 10); break;
265
+ }
266
+ }
267
+ switch (type) {
268
+ case "LINE": return parseLineEntity(base, pairs);
269
+ case "LWPOLYLINE": return parseLWPolylineEntity(base, pairs);
270
+ case "POLYLINE": return parsePolylineEntity(base, pairs);
271
+ case "VERTEX": return parseVertexEntity(base, pairs);
272
+ case "ARC": return parseArcEntity(base, pairs);
273
+ case "CIRCLE": return parseCircleEntity(base, pairs);
274
+ case "ELLIPSE": return parseEllipseEntity(base, pairs);
275
+ case "POINT": return parsePointEntity(base, pairs);
276
+ case "TEXT": return parseTextEntity(base, pairs);
277
+ case "MTEXT": return parseMTextEntity(base, pairs);
278
+ case "ATTRIB": return parseAttribEntity(base, pairs);
279
+ case "ATTDEF": return parseAttribEntity(base, pairs);
280
+ case "INSERT": return parseInsertEntity(base, pairs);
281
+ case "DIMENSION": return parseDimensionEntity(base, pairs);
282
+ case "LEADER": return parseLeaderEntity(base, pairs);
283
+ case "MLEADER": return parseMLeaderEntity(base, pairs);
284
+ case "HATCH": return parseHatchEntity(base, pairs);
285
+ case "SPLINE": return parseSplineEntity(base, pairs);
286
+ case "SOLID": case "3DSOLID": case "TRACE": return parseSolidEntity(base, pairs);
287
+ case "3DFACE": return parse3DFaceEntity(base, pairs);
288
+ case "RAY": case "XLINE": return parseRayEntity(base, pairs);
289
+ case "VIEWPORT": return parseViewportEntity(base, pairs);
290
+ case "IMAGE": case "WIPEOUT": return parseImageEntity(base, pairs);
291
+ case "TOLERANCE": return parseToleranceEntity(base, pairs);
292
+ case "HELIX": return parseHelixEntity(base, pairs);
293
+ case "MESH": return parseMeshEntity(base, pairs);
294
+ case "REGION": case "BODY": case "SURFACE": return parseRegionEntity(base, pairs);
295
+ case "OLE2FRAME": return parseOle2Entity(base, pairs);
296
+ case "TABLE": return parseTableEntity(base, pairs);
297
+ case "MLINE": return parseMLineEntity(base, pairs);
298
+ case "SEQEND": return base;
299
+ default: return parseGenericEntity(base, pairs);
300
+ }
301
+ }
302
+
303
+ // ── 以下所有实体解析器与你原来的完全一样,不需要任何改动 ──
304
+ // (这里为了完整性全部保留)
305
+
306
+ function parseLineEntity(base, pairs) {
307
+ base.startPoint = { x: 0, y: 0, z: 0 };
308
+ base.endPoint = { x: 0, y: 0, z: 0 };
309
+ base.thickness = 0;
310
+ base.extrusionDirection = { x: 0, y: 0, z: 1 };
311
+ for (const p of pairs) {
312
+ switch (p.code) {
313
+ case 10: base.startPoint.x = parseFloat(p.value); break;
314
+ case 20: base.startPoint.y = parseFloat(p.value); break;
315
+ case 30: base.startPoint.z = parseFloat(p.value); break;
316
+ case 11: base.endPoint.x = parseFloat(p.value); break;
317
+ case 21: base.endPoint.y = parseFloat(p.value); break;
318
+ case 31: base.endPoint.z = parseFloat(p.value); break;
319
+ case 39: base.thickness = parseFloat(p.value); break;
320
+ case 210: base.extrusionDirection.x = parseFloat(p.value); break;
321
+ case 220: base.extrusionDirection.y = parseFloat(p.value); break;
322
+ case 230: base.extrusionDirection.z = parseFloat(p.value); break;
323
+ }
324
+ }
325
+ return base;
326
+ }
327
+
328
+ function parseLWPolylineEntity(base, pairs) {
329
+ base.closed = false; base.constantWidth = 0; base.elevation = 0;
330
+ base.thickness = 0; base.vertices = [];
331
+ base.extrusionDirection = { x: 0, y: 0, z: 1 };
332
+ let currentVertex = null;
333
+ for (const p of pairs) {
334
+ switch (p.code) {
335
+ case 70: base.closed = (parseInt(p.value, 10) & 1) === 1; break;
336
+ case 38: base.elevation = parseFloat(p.value); break;
337
+ case 39: base.thickness = parseFloat(p.value); break;
338
+ case 43: base.constantWidth = parseFloat(p.value); break;
339
+ case 90: base.vertexCount = parseInt(p.value, 10); break;
340
+ case 10:
341
+ if (currentVertex) base.vertices.push(currentVertex);
342
+ currentVertex = { x: parseFloat(p.value), y: 0, bulge: 0, startWidth: 0, endWidth: 0 };
343
+ break;
344
+ case 20: if (currentVertex) currentVertex.y = parseFloat(p.value); break;
345
+ case 40: if (currentVertex) currentVertex.startWidth = parseFloat(p.value); break;
346
+ case 41: if (currentVertex) currentVertex.endWidth = parseFloat(p.value); break;
347
+ case 42: if (currentVertex) currentVertex.bulge = parseFloat(p.value); break;
348
+ case 210: base.extrusionDirection.x = parseFloat(p.value); break;
349
+ case 220: base.extrusionDirection.y = parseFloat(p.value); break;
350
+ case 230: base.extrusionDirection.z = parseFloat(p.value); break;
351
+ }
352
+ }
353
+ if (currentVertex) base.vertices.push(currentVertex);
354
+ return base;
355
+ }
356
+
357
+ function parsePolylineEntity(base, pairs) {
358
+ base.closed = false; base.is3DPolyline = false; base.is3DMesh = false;
359
+ base.isPolyfaceMesh = false; base.meshMVertexCount = 0; base.meshNVertexCount = 0;
360
+ base.defaultStartWidth = 0; base.defaultEndWidth = 0; base.elevation = 0;
361
+ base.vertices = [];
362
+ for (const p of pairs) {
363
+ switch (p.code) {
364
+ case 10: base.dummyX = parseFloat(p.value); break;
365
+ case 20: base.dummyY = parseFloat(p.value); break;
366
+ case 30: base.elevation = parseFloat(p.value); break;
367
+ case 39: base.thickness = parseFloat(p.value); break;
368
+ case 40: base.defaultStartWidth = parseFloat(p.value); break;
369
+ case 41: base.defaultEndWidth = parseFloat(p.value); break;
370
+ case 70: {
371
+ const flags = parseInt(p.value, 10);
372
+ base.closed = (flags & 1) === 1; base.is3DPolyline = (flags & 8) === 8;
373
+ base.is3DMesh = (flags & 16) === 16; base.isPolyfaceMesh = (flags & 64) === 64;
374
+ base.flags = flags; break;
375
+ }
376
+ case 71: base.meshMVertexCount = parseInt(p.value, 10); break;
377
+ case 72: base.meshNVertexCount = parseInt(p.value, 10); break;
378
+ case 210: (base.extrusionDirection = base.extrusionDirection || {}).x = parseFloat(p.value); break;
379
+ case 220: (base.extrusionDirection = base.extrusionDirection || {}).y = parseFloat(p.value); break;
380
+ case 230: (base.extrusionDirection = base.extrusionDirection || {}).z = parseFloat(p.value); break;
381
+ }
382
+ }
383
+ return base;
384
+ }
385
+
386
+ function parseVertexEntity(base, pairs) {
387
+ base.position = { x: 0, y: 0, z: 0 }; base.bulge = 0;
388
+ base.startWidth = 0; base.endWidth = 0; base.flags = 0;
389
+ for (const p of pairs) {
390
+ switch (p.code) {
391
+ case 10: base.position.x = parseFloat(p.value); break;
392
+ case 20: base.position.y = parseFloat(p.value); break;
393
+ case 30: base.position.z = parseFloat(p.value); break;
394
+ case 40: base.startWidth = parseFloat(p.value); break;
395
+ case 41: base.endWidth = parseFloat(p.value); break;
396
+ case 42: base.bulge = parseFloat(p.value); break;
397
+ case 70: base.flags = parseInt(p.value, 10); break;
398
+ case 71: base.faceVertex1 = parseInt(p.value, 10); break;
399
+ case 72: base.faceVertex2 = parseInt(p.value, 10); break;
400
+ case 73: base.faceVertex3 = parseInt(p.value, 10); break;
401
+ case 74: base.faceVertex4 = parseInt(p.value, 10); break;
402
+ }
403
+ }
404
+ return base;
405
+ }
406
+
407
+ function parseArcEntity(base, pairs) {
408
+ base.center = { x: 0, y: 0, z: 0 }; base.radius = 0;
409
+ base.startAngle = 0; base.endAngle = 360; base.thickness = 0;
410
+ base.extrusionDirection = { x: 0, y: 0, z: 1 };
411
+ for (const p of pairs) {
412
+ switch (p.code) {
413
+ case 10: base.center.x = parseFloat(p.value); break;
414
+ case 20: base.center.y = parseFloat(p.value); break;
415
+ case 30: base.center.z = parseFloat(p.value); break;
416
+ case 39: base.thickness = parseFloat(p.value); break;
417
+ case 40: base.radius = parseFloat(p.value); break;
418
+ case 50: base.startAngle = parseFloat(p.value); break;
419
+ case 51: base.endAngle = parseFloat(p.value); break;
420
+ case 210: base.extrusionDirection.x = parseFloat(p.value); break;
421
+ case 220: base.extrusionDirection.y = parseFloat(p.value); break;
422
+ case 230: base.extrusionDirection.z = parseFloat(p.value); break;
423
+ }
424
+ }
425
+ return base;
426
+ }
427
+
428
+ function parseCircleEntity(base, pairs) {
429
+ base.center = { x: 0, y: 0, z: 0 }; base.radius = 0; base.thickness = 0;
430
+ base.extrusionDirection = { x: 0, y: 0, z: 1 };
431
+ for (const p of pairs) {
432
+ switch (p.code) {
433
+ case 10: base.center.x = parseFloat(p.value); break;
434
+ case 20: base.center.y = parseFloat(p.value); break;
435
+ case 30: base.center.z = parseFloat(p.value); break;
436
+ case 39: base.thickness = parseFloat(p.value); break;
437
+ case 40: base.radius = parseFloat(p.value); break;
438
+ case 210: base.extrusionDirection.x = parseFloat(p.value); break;
439
+ case 220: base.extrusionDirection.y = parseFloat(p.value); break;
440
+ case 230: base.extrusionDirection.z = parseFloat(p.value); break;
441
+ }
442
+ }
443
+ return base;
444
+ }
445
+
446
+ function parseEllipseEntity(base, pairs) {
447
+ base.center = { x: 0, y: 0, z: 0 };
448
+ base.majorAxisEndPoint = { x: 0, y: 0, z: 0 };
449
+ base.axisRatio = 1; base.startAngle = 0; base.endAngle = Math.PI * 2;
450
+ base.extrusionDirection = { x: 0, y: 0, z: 1 };
451
+ for (const p of pairs) {
452
+ switch (p.code) {
453
+ case 10: base.center.x = parseFloat(p.value); break;
454
+ case 20: base.center.y = parseFloat(p.value); break;
455
+ case 30: base.center.z = parseFloat(p.value); break;
456
+ case 11: base.majorAxisEndPoint.x = parseFloat(p.value); break;
457
+ case 21: base.majorAxisEndPoint.y = parseFloat(p.value); break;
458
+ case 31: base.majorAxisEndPoint.z = parseFloat(p.value); break;
459
+ case 40: base.axisRatio = parseFloat(p.value); break;
460
+ case 41: base.startAngle = parseFloat(p.value); break;
461
+ case 42: base.endAngle = parseFloat(p.value); break;
462
+ case 210: base.extrusionDirection.x = parseFloat(p.value); break;
463
+ case 220: base.extrusionDirection.y = parseFloat(p.value); break;
464
+ case 230: base.extrusionDirection.z = parseFloat(p.value); break;
465
+ }
466
+ }
467
+ return base;
468
+ }
469
+
470
+ function parsePointEntity(base, pairs) {
471
+ base.position = { x: 0, y: 0, z: 0 }; base.thickness = 0;
472
+ base.extrusionDirection = { x: 0, y: 0, z: 1 };
473
+ for (const p of pairs) {
474
+ switch (p.code) {
475
+ case 10: base.position.x = parseFloat(p.value); break;
476
+ case 20: base.position.y = parseFloat(p.value); break;
477
+ case 30: base.position.z = parseFloat(p.value); break;
478
+ case 39: base.thickness = parseFloat(p.value); break;
479
+ case 50: base.angle = parseFloat(p.value); break;
480
+ case 210: base.extrusionDirection.x = parseFloat(p.value); break;
481
+ case 220: base.extrusionDirection.y = parseFloat(p.value); break;
482
+ case 230: base.extrusionDirection.z = parseFloat(p.value); break;
483
+ }
484
+ }
485
+ return base;
486
+ }
487
+
488
+ function parseTextEntity(base, pairs) {
489
+ base.insertionPoint = { x: 0, y: 0, z: 0 }; base.alignmentPoint = { x: 0, y: 0, z: 0 };
490
+ base.height = 0; base.text = ""; base.rotation = 0; base.xScale = 1;
491
+ base.oblique = 0; base.styleName = "STANDARD"; base.generationFlags = 0;
492
+ base.hAlign = 0; base.vAlign = 0; base.thickness = 0;
493
+ base.extrusionDirection = { x: 0, y: 0, z: 1 };
494
+ for (const p of pairs) {
495
+ switch (p.code) {
496
+ case 1: base.text = p.value; break; case 7: base.styleName = p.value; break;
497
+ case 10: base.insertionPoint.x = parseFloat(p.value); break;
498
+ case 20: base.insertionPoint.y = parseFloat(p.value); break;
499
+ case 30: base.insertionPoint.z = parseFloat(p.value); break;
500
+ case 11: base.alignmentPoint.x = parseFloat(p.value); break;
501
+ case 21: base.alignmentPoint.y = parseFloat(p.value); break;
502
+ case 31: base.alignmentPoint.z = parseFloat(p.value); break;
503
+ case 39: base.thickness = parseFloat(p.value); break;
504
+ case 40: base.height = parseFloat(p.value); break;
505
+ case 41: base.xScale = parseFloat(p.value); break;
506
+ case 50: base.rotation = parseFloat(p.value); break;
507
+ case 51: base.oblique = parseFloat(p.value); break;
508
+ case 71: base.generationFlags = parseInt(p.value, 10); break;
509
+ case 72: base.hAlign = parseInt(p.value, 10); break;
510
+ case 73: base.vAlign = parseInt(p.value, 10); break;
511
+ case 210: base.extrusionDirection.x = parseFloat(p.value); break;
512
+ case 220: base.extrusionDirection.y = parseFloat(p.value); break;
513
+ case 230: base.extrusionDirection.z = parseFloat(p.value); break;
514
+ }
515
+ }
516
+ return base;
517
+ }
518
+
519
+ function parseMTextEntity(base, pairs) {
520
+ base.insertionPoint = { x: 0, y: 0, z: 0 }; base.direction = { x: 1, y: 0, z: 0 };
521
+ base.height = 0; base.width = 0; base.text = ""; base.rotation = 0;
522
+ base.styleName = "STANDARD"; base.attachmentPoint = 1; base.drawingDirection = 1;
523
+ base.lineSpacing = 1; base.lineSpacingStyle = 1;
524
+ base.extrusionDirection = { x: 0, y: 0, z: 1 };
525
+ let textParts = [];
526
+ for (const p of pairs) {
527
+ switch (p.code) {
528
+ case 1: textParts.push(p.value); break; case 3: textParts.push(p.value); break;
529
+ case 7: base.styleName = p.value; break;
530
+ case 10: base.insertionPoint.x = parseFloat(p.value); break;
531
+ case 20: base.insertionPoint.y = parseFloat(p.value); break;
532
+ case 30: base.insertionPoint.z = parseFloat(p.value); break;
533
+ case 11: base.direction.x = parseFloat(p.value); break;
534
+ case 21: base.direction.y = parseFloat(p.value); break;
535
+ case 31: base.direction.z = parseFloat(p.value); break;
536
+ case 40: base.height = parseFloat(p.value); break;
537
+ case 41: base.width = parseFloat(p.value); break;
538
+ case 44: base.lineSpacing = parseFloat(p.value); break;
539
+ case 50: base.rotation = parseFloat(p.value); break;
540
+ case 71: base.attachmentPoint = parseInt(p.value, 10); break;
541
+ case 72: base.drawingDirection = parseInt(p.value, 10); break;
542
+ case 73: base.lineSpacingStyle = parseInt(p.value, 10); break;
543
+ case 210: base.extrusionDirection.x = parseFloat(p.value); break;
544
+ case 220: base.extrusionDirection.y = parseFloat(p.value); break;
545
+ case 230: base.extrusionDirection.z = parseFloat(p.value); break;
546
+ }
547
+ }
548
+ base.text = textParts.join("");
549
+ return base;
550
+ }
551
+
552
+ function parseAttribEntity(base, pairs) {
553
+ base.insertionPoint = { x: 0, y: 0, z: 0 }; base.alignmentPoint = { x: 0, y: 0, z: 0 };
554
+ base.height = 0; base.text = ""; base.tag = ""; base.prompt = "";
555
+ base.rotation = 0; base.flags = 0; base.styleName = "STANDARD";
556
+ for (const p of pairs) {
557
+ switch (p.code) {
558
+ case 1: base.text = p.value; break; case 2: base.tag = p.value; break;
559
+ case 3: base.prompt = p.value; break; case 7: base.styleName = p.value; break;
560
+ case 10: base.insertionPoint.x = parseFloat(p.value); break;
561
+ case 20: base.insertionPoint.y = parseFloat(p.value); break;
562
+ case 30: base.insertionPoint.z = parseFloat(p.value); break;
563
+ case 11: base.alignmentPoint.x = parseFloat(p.value); break;
564
+ case 21: base.alignmentPoint.y = parseFloat(p.value); break;
565
+ case 31: base.alignmentPoint.z = parseFloat(p.value); break;
566
+ case 40: base.height = parseFloat(p.value); break;
567
+ case 41: base.xScale = parseFloat(p.value); break;
568
+ case 50: base.rotation = parseFloat(p.value); break;
569
+ case 70: base.flags = parseInt(p.value, 10); break;
570
+ case 71: base.generationFlags = parseInt(p.value, 10); break;
571
+ case 72: base.hAlign = parseInt(p.value, 10); break;
572
+ case 73: base.vAlign = parseInt(p.value, 10); break;
573
+ }
574
+ }
575
+ return base;
576
+ }
577
+
578
+ function parseInsertEntity(base, pairs) {
579
+ base.blockName = ""; base.insertionPoint = { x: 0, y: 0, z: 0 };
580
+ base.scale = { x: 1, y: 1, z: 1 }; base.rotation = 0;
581
+ base.columnCount = 1; base.rowCount = 1; base.columnSpacing = 0; base.rowSpacing = 0;
582
+ base.extrusionDirection = { x: 0, y: 0, z: 1 }; base.attributes = [];
583
+ for (const p of pairs) {
584
+ switch (p.code) {
585
+ case 2: base.blockName = p.value; break;
586
+ case 10: base.insertionPoint.x = parseFloat(p.value); break;
587
+ case 20: base.insertionPoint.y = parseFloat(p.value); break;
588
+ case 30: base.insertionPoint.z = parseFloat(p.value); break;
589
+ case 41: base.scale.x = parseFloat(p.value); break;
590
+ case 42: base.scale.y = parseFloat(p.value); break;
591
+ case 43: base.scale.z = parseFloat(p.value); break;
592
+ case 44: base.columnSpacing = parseFloat(p.value); break;
593
+ case 45: base.rowSpacing = parseFloat(p.value); break;
594
+ case 50: base.rotation = parseFloat(p.value); break;
595
+ case 66: base.hasAttributes = parseInt(p.value, 10) === 1; break;
596
+ case 70: base.columnCount = parseInt(p.value, 10); break;
597
+ case 71: base.rowCount = parseInt(p.value, 10); break;
598
+ case 210: base.extrusionDirection.x = parseFloat(p.value); break;
599
+ case 220: base.extrusionDirection.y = parseFloat(p.value); break;
600
+ case 230: base.extrusionDirection.z = parseFloat(p.value); break;
601
+ }
602
+ }
603
+ return base;
604
+ }
605
+
606
+ function parseDimensionEntity(base, pairs) {
607
+ base.blockName = ""; base.definitionPoint = { x: 0, y: 0, z: 0 };
608
+ base.middleOfText = { x: 0, y: 0, z: 0 }; base.insertionPoint = { x: 0, y: 0, z: 0 };
609
+ base.dimensionType = 0; base.text = ""; base.styleName = "STANDARD"; base.rotation = 0;
610
+ base.point1 = { x: 0, y: 0, z: 0 }; base.point2 = { x: 0, y: 0, z: 0 };
611
+ for (const p of pairs) {
612
+ switch (p.code) {
613
+ case 1: base.text = p.value; break; case 2: base.blockName = p.value; break;
614
+ case 3: base.styleName = p.value; break;
615
+ case 10: base.definitionPoint.x = parseFloat(p.value); break;
616
+ case 20: base.definitionPoint.y = parseFloat(p.value); break;
617
+ case 30: base.definitionPoint.z = parseFloat(p.value); break;
618
+ case 11: base.middleOfText.x = parseFloat(p.value); break;
619
+ case 21: base.middleOfText.y = parseFloat(p.value); break;
620
+ case 31: base.middleOfText.z = parseFloat(p.value); break;
621
+ case 13: base.point1.x = parseFloat(p.value); break;
622
+ case 23: base.point1.y = parseFloat(p.value); break;
623
+ case 33: base.point1.z = parseFloat(p.value); break;
624
+ case 14: base.point2.x = parseFloat(p.value); break;
625
+ case 24: base.point2.y = parseFloat(p.value); break;
626
+ case 34: base.point2.z = parseFloat(p.value); break;
627
+ case 40: base.leaderLength = parseFloat(p.value); break;
628
+ case 50: base.rotation = parseFloat(p.value); break;
629
+ case 70: base.dimensionType = parseInt(p.value, 10); break;
630
+ case 71: base.attachmentPoint = parseInt(p.value, 10); break;
631
+ }
632
+ }
633
+ return base;
634
+ }
635
+
636
+ function parseLeaderEntity(base, pairs) {
637
+ base.styleName = "STANDARD"; base.arrowheadFlag = 1; base.pathType = 0;
638
+ base.creationType = 0; base.hooklineFlag = 0; base.vertices = [];
639
+ base.annotationReference = null;
640
+ base.normal = { x: 0, y: 0, z: 1 }; base.horizontalDirection = { x: 1, y: 0, z: 0 };
641
+ let currentX = null;
642
+ for (const p of pairs) {
643
+ switch (p.code) {
644
+ case 3: base.styleName = p.value; break;
645
+ case 10: currentX = parseFloat(p.value); break;
646
+ case 20:
647
+ if (currentX !== null) { base.vertices.push({ x: currentX, y: parseFloat(p.value), z: 0 }); currentX = null; } break;
648
+ case 30: if (base.vertices.length > 0) base.vertices[base.vertices.length - 1].z = parseFloat(p.value); break;
649
+ case 40: base.textHeight = parseFloat(p.value); break;
650
+ case 41: base.textWidth = parseFloat(p.value); break;
651
+ case 71: base.arrowheadFlag = parseInt(p.value, 10); break;
652
+ case 72: base.pathType = parseInt(p.value, 10); break;
653
+ case 73: base.creationType = parseInt(p.value, 10); break;
654
+ case 76: base.vertexCount = parseInt(p.value, 10); break;
655
+ case 340: base.annotationReference = p.value; break;
656
+ }
657
+ }
658
+ return base;
659
+ }
660
+
661
+ function parseMLeaderEntity(base, pairs) {
662
+ base.styleName = ""; base.text = ""; base.leaderPoints = [];
663
+ base.contentType = 0; base.textStyleId = "";
664
+ let currentX = null;
665
+ for (const p of pairs) {
666
+ switch (p.code) {
667
+ case 3: base.styleName = p.value; break;
668
+ case 304: base.text = p.value; break;
669
+ case 170: base.contentType = parseInt(p.value, 10); break;
670
+ case 340: base.textStyleId = p.value; break;
671
+ case 10: currentX = parseFloat(p.value); break;
672
+ case 20:
673
+ if (currentX !== null) { base.leaderPoints.push({ x: currentX, y: parseFloat(p.value), z: 0 }); currentX = null; } break;
674
+ case 30: if (base.leaderPoints.length > 0) base.leaderPoints[base.leaderPoints.length - 1].z = parseFloat(p.value); break;
675
+ case 40: base.doglegLength = parseFloat(p.value); break;
676
+ case 41: base.arrowheadSize = parseFloat(p.value); break;
677
+ case 42: base.textHeight = parseFloat(p.value); break;
678
+ }
679
+ }
680
+ return base;
681
+ }
682
+
683
+ function parseHatchEntity(base, pairs) {
684
+ base.patternName = ""; base.solid = false; base.associative = false;
685
+ base.style = 0; base.patternType = 0; base.patternAngle = 0; base.patternScale = 1;
686
+ base.elevation = 0; base.extrusionDirection = { x: 0, y: 0, z: 1 };
687
+ base.boundaryPaths = []; base.seedPoints = [];
688
+ let readingBoundary = false; let currentPath = null;
689
+ for (const p of pairs) {
690
+ switch (p.code) {
691
+ case 2: base.patternName = p.value; break;
692
+ case 10: if (readingBoundary && currentPath) { currentPath.points = currentPath.points || []; currentPath.points.push({ x: parseFloat(p.value) }); } break;
693
+ case 20: if (readingBoundary && currentPath && currentPath.points && currentPath.points.length > 0) currentPath.points[currentPath.points.length - 1].y = parseFloat(p.value); break;
694
+ case 30: base.elevation = parseFloat(p.value); break;
695
+ case 41: base.patternScale = parseFloat(p.value); break;
696
+ case 52: base.patternAngle = parseFloat(p.value); break;
697
+ case 70: base.solid = parseInt(p.value, 10) === 1; break;
698
+ case 71: base.associative = parseInt(p.value, 10) === 1; break;
699
+ case 75: base.style = parseInt(p.value, 10); break;
700
+ case 76: base.patternType = parseInt(p.value, 10); break;
701
+ case 91: readingBoundary = true; break;
702
+ case 92: if (currentPath) base.boundaryPaths.push(currentPath); currentPath = { type: parseInt(p.value, 10), edges: [], points: [] }; break;
703
+ case 93: if (currentPath) currentPath.edgeCount = parseInt(p.value, 10); break;
704
+ case 97: if (currentPath) base.boundaryPaths.push(currentPath); currentPath = null; readingBoundary = false; break;
705
+ case 210: base.extrusionDirection.x = parseFloat(p.value); break;
706
+ case 220: base.extrusionDirection.y = parseFloat(p.value); break;
707
+ case 230: base.extrusionDirection.z = parseFloat(p.value); break;
708
+ }
709
+ }
710
+ if (currentPath) base.boundaryPaths.push(currentPath);
711
+ return base;
712
+ }
713
+
714
+ function parseSplineEntity(base, pairs) {
715
+ base.normal = { x: 0, y: 0, z: 1 }; base.degree = 3;
716
+ base.closed = false; base.periodic = false; base.rational = false; base.planar = false;
717
+ base.knotTolerance = 0.0000001; base.controlPointTolerance = 0.0000001;
718
+ base.fitTolerance = 0.0000000001;
719
+ base.startTangent = { x: 0, y: 0, z: 0 }; base.endTangent = { x: 0, y: 0, z: 0 };
720
+ base.knots = []; base.weights = []; base.controlPoints = []; base.fitPoints = [];
721
+ let cpX = null, fpX = null;
722
+ for (const p of pairs) {
723
+ switch (p.code) {
724
+ case 10: cpX = parseFloat(p.value); break;
725
+ case 20: if (cpX !== null) { base.controlPoints.push({ x: cpX, y: parseFloat(p.value), z: 0 }); cpX = null; } break;
726
+ case 30: if (base.controlPoints.length > 0) base.controlPoints[base.controlPoints.length - 1].z = parseFloat(p.value); break;
727
+ case 11: fpX = parseFloat(p.value); break;
728
+ case 21: if (fpX !== null) { base.fitPoints.push({ x: fpX, y: parseFloat(p.value), z: 0 }); fpX = null; } break;
729
+ case 31: if (base.fitPoints.length > 0) base.fitPoints[base.fitPoints.length - 1].z = parseFloat(p.value); break;
730
+ case 12: base.startTangent.x = parseFloat(p.value); break;
731
+ case 22: base.startTangent.y = parseFloat(p.value); break;
732
+ case 32: base.startTangent.z = parseFloat(p.value); break;
733
+ case 13: base.endTangent.x = parseFloat(p.value); break;
734
+ case 23: base.endTangent.y = parseFloat(p.value); break;
735
+ case 33: base.endTangent.z = parseFloat(p.value); break;
736
+ case 40: base.knots.push(parseFloat(p.value)); break;
737
+ case 41: base.weights.push(parseFloat(p.value)); break;
738
+ case 42: base.knotTolerance = parseFloat(p.value); break;
739
+ case 43: base.controlPointTolerance = parseFloat(p.value); break;
740
+ case 44: base.fitTolerance = parseFloat(p.value); break;
741
+ case 70: { const f = parseInt(p.value, 10); base.closed = (f & 1) === 1; base.periodic = (f & 2) === 2; base.rational = (f & 4) === 4; base.planar = (f & 8) === 8; base.flags = f; break; }
742
+ case 71: base.degree = parseInt(p.value, 10); break;
743
+ case 72: base.knotCount = parseInt(p.value, 10); break;
744
+ case 73: base.controlPointCount = parseInt(p.value, 10); break;
745
+ case 74: base.fitPointCount = parseInt(p.value, 10); break;
746
+ case 210: base.normal.x = parseFloat(p.value); break;
747
+ case 220: base.normal.y = parseFloat(p.value); break;
748
+ case 230: base.normal.z = parseFloat(p.value); break;
749
+ }
750
+ }
751
+ return base;
752
+ }
753
+
754
+ function parseSolidEntity(base, pairs) {
755
+ base.points = [{ x: 0, y: 0, z: 0 }, { x: 0, y: 0, z: 0 }, { x: 0, y: 0, z: 0 }, { x: 0, y: 0, z: 0 }];
756
+ base.thickness = 0; base.extrusionDirection = { x: 0, y: 0, z: 1 };
757
+ for (const p of pairs) {
758
+ switch (p.code) {
759
+ case 10: base.points[0].x = parseFloat(p.value); break; case 20: base.points[0].y = parseFloat(p.value); break; case 30: base.points[0].z = parseFloat(p.value); break;
760
+ case 11: base.points[1].x = parseFloat(p.value); break; case 21: base.points[1].y = parseFloat(p.value); break; case 31: base.points[1].z = parseFloat(p.value); break;
761
+ case 12: base.points[2].x = parseFloat(p.value); break; case 22: base.points[2].y = parseFloat(p.value); break; case 32: base.points[2].z = parseFloat(p.value); break;
762
+ case 13: base.points[3].x = parseFloat(p.value); break; case 23: base.points[3].y = parseFloat(p.value); break; case 33: base.points[3].z = parseFloat(p.value); break;
763
+ case 39: base.thickness = parseFloat(p.value); break;
764
+ case 210: base.extrusionDirection.x = parseFloat(p.value); break;
765
+ case 220: base.extrusionDirection.y = parseFloat(p.value); break;
766
+ case 230: base.extrusionDirection.z = parseFloat(p.value); break;
767
+ }
768
+ }
769
+ return base;
770
+ }
771
+
772
+ function parse3DFaceEntity(base, pairs) {
773
+ base.vertices = [{ x: 0, y: 0, z: 0 }, { x: 0, y: 0, z: 0 }, { x: 0, y: 0, z: 0 }, { x: 0, y: 0, z: 0 }];
774
+ base.invisibleEdgeFlags = 0;
775
+ for (const p of pairs) {
776
+ switch (p.code) {
777
+ case 10: base.vertices[0].x = parseFloat(p.value); break; case 20: base.vertices[0].y = parseFloat(p.value); break; case 30: base.vertices[0].z = parseFloat(p.value); break;
778
+ case 11: base.vertices[1].x = parseFloat(p.value); break; case 21: base.vertices[1].y = parseFloat(p.value); break; case 31: base.vertices[1].z = parseFloat(p.value); break;
779
+ case 12: base.vertices[2].x = parseFloat(p.value); break; case 22: base.vertices[2].y = parseFloat(p.value); break; case 32: base.vertices[2].z = parseFloat(p.value); break;
780
+ case 13: base.vertices[3].x = parseFloat(p.value); break; case 23: base.vertices[3].y = parseFloat(p.value); break; case 33: base.vertices[3].z = parseFloat(p.value); break;
781
+ case 70: base.invisibleEdgeFlags = parseInt(p.value, 10); break;
782
+ }
783
+ }
784
+ return base;
785
+ }
786
+
787
+ function parseRayEntity(base, pairs) {
788
+ base.basePoint = { x: 0, y: 0, z: 0 }; base.direction = { x: 1, y: 0, z: 0 };
789
+ for (const p of pairs) {
790
+ switch (p.code) {
791
+ case 10: base.basePoint.x = parseFloat(p.value); break; case 20: base.basePoint.y = parseFloat(p.value); break; case 30: base.basePoint.z = parseFloat(p.value); break;
792
+ case 11: base.direction.x = parseFloat(p.value); break; case 21: base.direction.y = parseFloat(p.value); break; case 31: base.direction.z = parseFloat(p.value); break;
793
+ }
794
+ }
795
+ return base;
796
+ }
797
+
798
+ function parseViewportEntity(base, pairs) {
799
+ base.center = { x: 0, y: 0, z: 0 }; base.width = 0; base.height = 0;
800
+ base.viewTarget = { x: 0, y: 0, z: 0 }; base.viewDirection = { x: 0, y: 0, z: 1 };
801
+ base.viewTwistAngle = 0; base.viewHeight = 0; base.viewCenter = { x: 0, y: 0 };
802
+ base.perspectiveLensLength = 50; base.statusField = 0;
803
+ base.clippingBoundary = null; base.frozenLayers = [];
804
+ for (const p of pairs) {
805
+ switch (p.code) {
806
+ case 10: base.center.x = parseFloat(p.value); break; case 20: base.center.y = parseFloat(p.value); break; case 30: base.center.z = parseFloat(p.value); break;
807
+ case 12: base.viewCenter.x = parseFloat(p.value); break; case 22: base.viewCenter.y = parseFloat(p.value); break;
808
+ case 40: base.width = parseFloat(p.value); break; case 41: base.height = parseFloat(p.value); break;
809
+ case 45: base.viewHeight = parseFloat(p.value); break;
810
+ case 51: base.viewTwistAngle = parseFloat(p.value); break;
811
+ case 68: base.statusField = parseInt(p.value, 10); break;
812
+ case 69: base.id = parseInt(p.value, 10); break;
813
+ case 331: base.frozenLayers.push(p.value); break;
814
+ case 340: base.clippingBoundary = p.value; break;
815
+ }
816
+ }
817
+ return base;
818
+ }
819
+
820
+ function parseImageEntity(base, pairs) {
821
+ base.insertionPoint = { x: 0, y: 0, z: 0 }; base.uVector = { x: 1, y: 0, z: 0 };
822
+ base.vVector = { x: 0, y: 1, z: 0 }; base.imageSize = { u: 0, v: 0 };
823
+ base.imageDefHandle = ""; base.displayFlags = 0; base.clippingType = 0;
824
+ base.brightness = 50; base.contrast = 50; base.fade = 0; base.clippingVertices = [];
825
+ for (const p of pairs) {
826
+ switch (p.code) {
827
+ case 10: base.insertionPoint.x = parseFloat(p.value); break; case 20: base.insertionPoint.y = parseFloat(p.value); break; case 30: base.insertionPoint.z = parseFloat(p.value); break;
828
+ case 13: base.imageSize.u = parseFloat(p.value); break; case 23: base.imageSize.v = parseFloat(p.value); break;
829
+ case 14: base.clippingVertices.push({ x: parseFloat(p.value) }); break;
830
+ case 24: if (base.clippingVertices.length > 0) base.clippingVertices[base.clippingVertices.length - 1].y = parseFloat(p.value); break;
831
+ case 70: base.displayFlags = parseInt(p.value, 10); break;
832
+ case 340: base.imageDefHandle = p.value; break;
833
+ }
834
+ }
835
+ return base;
836
+ }
837
+
838
+ function parseToleranceEntity(base, pairs) {
839
+ base.insertionPoint = { x: 0, y: 0, z: 0 }; base.direction = { x: 1, y: 0, z: 0 };
840
+ base.styleName = "STANDARD"; base.text = "";
841
+ for (const p of pairs) {
842
+ switch (p.code) {
843
+ case 1: base.text = p.value; break; case 3: base.styleName = p.value; break;
844
+ case 10: base.insertionPoint.x = parseFloat(p.value); break; case 20: base.insertionPoint.y = parseFloat(p.value); break; case 30: base.insertionPoint.z = parseFloat(p.value); break;
845
+ case 11: base.direction.x = parseFloat(p.value); break; case 21: base.direction.y = parseFloat(p.value); break; case 31: base.direction.z = parseFloat(p.value); break;
846
+ }
847
+ }
848
+ return base;
849
+ }
850
+
851
+ function parseHelixEntity(base, pairs) {
852
+ base.axisBasePoint = { x: 0, y: 0, z: 0 }; base.startPoint = { x: 0, y: 0, z: 0 };
853
+ base.axisVector = { x: 0, y: 0, z: 1 }; base.radius = 0; base.turns = 1;
854
+ base.turnHeight = 0; base.handedness = 0; base.constraint = 0;
855
+ for (const p of pairs) {
856
+ switch (p.code) {
857
+ case 10: base.axisBasePoint.x = parseFloat(p.value); break; case 20: base.axisBasePoint.y = parseFloat(p.value); break; case 30: base.axisBasePoint.z = parseFloat(p.value); break;
858
+ case 11: base.startPoint.x = parseFloat(p.value); break; case 21: base.startPoint.y = parseFloat(p.value); break; case 31: base.startPoint.z = parseFloat(p.value); break;
859
+ case 40: base.radius = parseFloat(p.value); break; case 41: base.turns = parseFloat(p.value); break; case 42: base.turnHeight = parseFloat(p.value); break;
860
+ case 290: base.handedness = parseInt(p.value, 10); break; case 280: base.constraint = parseInt(p.value, 10); break;
861
+ }
862
+ }
863
+ return base;
864
+ }
865
+
866
+ function parseMeshEntity(base, pairs) {
867
+ base.version = 2; base.subdivisionLevel = 0; base.vertices = [];
868
+ base.faces = []; base.edges = []; base.edgeCreases = [];
869
+ let currentX = null; let readingFaces = false; let readingEdges = false; let readingCreases = false;
870
+ for (const p of pairs) {
871
+ switch (p.code) {
872
+ case 71: base.version = parseInt(p.value, 10); break;
873
+ case 72: base.subdivisionLevel = parseInt(p.value, 10); break;
874
+ case 91: readingFaces = false; break;
875
+ case 92: readingFaces = true; break;
876
+ case 93: readingEdges = true; readingFaces = false; break;
877
+ case 94: readingCreases = true; readingEdges = false; break;
878
+ case 95: readingCreases = false; break;
879
+ case 10: currentX = parseFloat(p.value); break;
880
+ case 20: if (currentX !== null) { base.vertices.push({ x: currentX, y: parseFloat(p.value), z: 0 }); currentX = null; } break;
881
+ case 30: if (base.vertices.length > 0) base.vertices[base.vertices.length - 1].z = parseFloat(p.value); break;
882
+ case 90: if (readingFaces) base.faces.push(parseInt(p.value, 10)); else if (readingEdges) base.edges.push(parseInt(p.value, 10)); break;
883
+ case 140: if (readingCreases) base.edgeCreases.push(parseFloat(p.value)); break;
884
+ }
885
+ }
886
+ return base;
887
+ }
888
+
889
+ function parseRegionEntity(base, pairs) {
890
+ base.acisData = [];
891
+ for (const p of pairs) { if (p.code === 1 || p.code === 3) base.acisData.push(p.value); if (p.code === 70) base.modelerFormat = parseInt(p.value, 10); }
892
+ base.acisDataString = base.acisData.join("");
893
+ return base;
894
+ }
895
+
896
+ function parseOle2Entity(base, pairs) {
897
+ base.upperLeftCorner = { x: 0, y: 0, z: 0 }; base.lowerRightCorner = { x: 0, y: 0, z: 0 };
898
+ base.oleType = 0; base.tileMode = 0;
899
+ for (const p of pairs) {
900
+ switch (p.code) {
901
+ case 10: base.upperLeftCorner.x = parseFloat(p.value); break; case 20: base.upperLeftCorner.y = parseFloat(p.value); break;
902
+ case 11: base.lowerRightCorner.x = parseFloat(p.value); break; case 21: base.lowerRightCorner.y = parseFloat(p.value); break;
903
+ case 70: base.oleType = parseInt(p.value, 10); break; case 71: base.tileMode = parseInt(p.value, 10); break;
904
+ case 1: case 3: base.binaryData = base.binaryData || []; base.binaryData.push(p.value); break;
905
+ }
906
+ }
907
+ return base;
908
+ }
909
+
910
+ function parseTableEntity(base, pairs) {
911
+ base.insertionPoint = { x: 0, y: 0, z: 0 }; base.blockName = "";
912
+ base.direction = { x: 1, y: 0, z: 0 }; base.rows = 0; base.columns = 0;
913
+ for (const p of pairs) {
914
+ switch (p.code) {
915
+ case 2: base.blockName = p.value; break;
916
+ case 10: base.insertionPoint.x = parseFloat(p.value); break; case 20: base.insertionPoint.y = parseFloat(p.value); break;
917
+ case 11: base.direction.x = parseFloat(p.value); break; case 21: base.direction.y = parseFloat(p.value); break;
918
+ case 91: base.rows = parseInt(p.value, 10); break; case 92: base.columns = parseInt(p.value, 10); break;
919
+ case 142: base.rowHeights = base.rowHeights || []; base.rowHeights.push(parseFloat(p.value)); break;
920
+ case 141: base.columnWidths = base.columnWidths || []; base.columnWidths.push(parseFloat(p.value)); break;
921
+ }
922
+ }
923
+ return base;
924
+ }
925
+
926
+ function parseMLineEntity(base, pairs) {
927
+ base.styleName = "STANDARD"; base.scale = 1; base.justification = 0; base.flags = 0;
928
+ base.startPoint = { x: 0, y: 0, z: 0 }; base.normal = { x: 0, y: 0, z: 1 }; base.vertices = [];
929
+ let currentX = null;
930
+ for (const p of pairs) {
931
+ switch (p.code) {
932
+ case 2: base.styleName = p.value; break;
933
+ case 10: base.startPoint.x = parseFloat(p.value); break; case 20: base.startPoint.y = parseFloat(p.value); break; case 30: base.startPoint.z = parseFloat(p.value); break;
934
+ case 11: currentX = parseFloat(p.value); break;
935
+ case 21: if (currentX !== null) { base.vertices.push({ x: currentX, y: parseFloat(p.value), z: 0 }); currentX = null; } break;
936
+ case 31: if (base.vertices.length > 0) base.vertices[base.vertices.length - 1].z = parseFloat(p.value); break;
937
+ case 40: base.scale = parseFloat(p.value); break;
938
+ case 70: base.justification = parseInt(p.value, 10); break;
939
+ case 71: base.flags = parseInt(p.value, 10); break;
940
+ case 72: base.vertexCount = parseInt(p.value, 10); break;
941
+ case 340: base.styleHandle = p.value; break;
942
+ case 210: base.normal.x = parseFloat(p.value); break; case 220: base.normal.y = parseFloat(p.value); break; case 230: base.normal.z = parseFloat(p.value); break;
943
+ }
944
+ }
945
+ return base;
946
+ }
947
+
948
+ function parseGenericEntity(base, pairs) {
949
+ base.data = {};
950
+ for (const p of pairs) {
951
+ const key = `code_${p.code}`;
952
+ const val = autoConvert(p.code, p.value);
953
+ if (base.data[key] !== undefined) base.data[key] = [].concat(base.data[key], val);
954
+ else base.data[key] = val;
955
+ }
956
+ return base;
957
+ }
958
+
959
+ function parseObjects(pairs) {
960
+ if (!pairs || pairs.length === 0) return [];
961
+ const objects = []; let i = 0;
962
+ while (i < pairs.length) {
963
+ if (pairs[i].code === 0) {
964
+ const objType = pairs[i].value; i++;
965
+ const obj = { type: objType };
966
+ while (i < pairs.length && pairs[i].code !== 0) {
967
+ const code = pairs[i].code; const val = autoConvert(code, pairs[i].value);
968
+ switch (code) {
969
+ case 5: obj.handle = val; break; case 2: obj.name = val; break;
970
+ case 3: obj.entryName = obj.entryName ? [].concat(obj.entryName, val) : val; break;
971
+ case 330: obj.ownerHandle = val; break;
972
+ case 350: obj.softPointer = obj.softPointer ? [].concat(obj.softPointer, val) : val; break;
973
+ case 360: obj.hardPointer = obj.hardPointer ? [].concat(obj.hardPointer, val) : val; break;
974
+ default: const key = `code_${code}`; if (obj[key] !== undefined) obj[key] = [].concat(obj[key], val); else obj[key] = val;
975
+ }
976
+ i++;
977
+ }
978
+ objects.push(obj);
979
+ } else { i++; }
980
+ }
981
+ return objects;
982
+ }
983
+
984
+ function assemblePolylineVertices(entities) {
985
+ const result = []; let currentPolyline = null;
986
+ for (const entity of entities) {
987
+ if (entity.type === "POLYLINE") { currentPolyline = entity; result.push(entity); }
988
+ else if (entity.type === "VERTEX" && currentPolyline) {
989
+ currentPolyline.vertices.push({ x: entity.position.x, y: entity.position.y, z: entity.position.z, bulge: entity.bulge, startWidth: entity.startWidth, endWidth: entity.endWidth, flags: entity.flags, faceVertex1: entity.faceVertex1, faceVertex2: entity.faceVertex2, faceVertex3: entity.faceVertex3, faceVertex4: entity.faceVertex4 });
990
+ } else if (entity.type === "SEQEND") { currentPolyline = null; }
991
+ else { currentPolyline = null; result.push(entity); }
992
+ }
993
+ return result;
994
+ }
995
+
996
+ function assembleInsertAttributes(entities) {
997
+ const result = []; let currentInsert = null;
998
+ for (const entity of entities) {
999
+ if (entity.type === "INSERT" && entity.hasAttributes) { currentInsert = entity; result.push(entity); }
1000
+ else if (entity.type === "ATTRIB" && currentInsert) { currentInsert.attributes.push(entity); }
1001
+ else if (entity.type === "SEQEND") { currentInsert = null; }
1002
+ else { currentInsert = null; result.push(entity); }
1003
+ }
1004
+ return result;
1005
+ }
1006
+
1007
+ // ─────────────────── 输入适配(关键改动部分) ───────────────────
1008
+
1009
+ /**
1010
+ * ★ 改动点: 从 URL 获取 DXF 文本
1011
+ */
1012
+ async function fetchDxfFromUrl(url) {
1013
+ if (typeof fetch === "function") {
1014
+ const resp = await fetch(url);
1015
+ if (!resp.ok) throw new Error(`获取 DXF 失败: HTTP ${resp.status} — ${url}`);
1016
+ return await resp.text();
1017
+ }
1018
+ // Node < 18 降级
1019
+ if (typeof process !== "undefined" && process.versions?.node) {
1020
+ const { default: http } = url.startsWith("https") ? await import('https') : await import('http');
1021
+ return new Promise((resolve, reject) => {
1022
+ http.get(url, (res) => {
1023
+ if (res.statusCode !== 200) { reject(new Error(`HTTP ${res.statusCode}`)); return; }
1024
+ let data = ""; res.setEncoding("utf-8");
1025
+ res.on("data", (c) => (data += c)); res.on("end", () => resolve(data)); res.on("error", reject);
1026
+ }).on("error", reject);
1027
+ });
1028
+ }
1029
+ throw new Error("当前环境不支持 HTTP 请求");
1030
+ }
1031
+
1032
+ /**
1033
+ * ★ 改动点: 读取本地文件,动态 import("fs")
1034
+ */
1035
+ async function readLocalFile(filePath) {
1036
+ if (typeof process !== "undefined" && process.versions?.node) {
1037
+ const { readFile } = await import('fs/promises');
1038
+ return await readFile(filePath, "utf-8");
1039
+ }
1040
+ throw new Error("浏览器不支持读取本地路径,请用 URL 或 File 对象");
1041
+ }
1042
+
1043
+ // ─────────────────── 主入口 ───────────────────
1044
+
1045
+ /**
1046
+ * ★ 改动点: 支持 字符串 / 文件路径 / URL / File 对象
1047
+ */
1048
+ async function parseDxf(input) {
1049
+ let dxfString;
1050
+
1051
+ if (typeof Blob !== "undefined" && input instanceof Blob) {
1052
+ dxfString = await input.text();
1053
+ } else if (typeof input === "string") {
1054
+ if (input.includes("\n") || input.includes("SECTION")) {
1055
+ dxfString = input;
1056
+ } else if (/^https?:\/\//i.test(input)) {
1057
+ dxfString = await fetchDxfFromUrl(input);
1058
+ } else {
1059
+ dxfString = await readLocalFile(input);
1060
+ }
1061
+ } else {
1062
+ throw new Error("parseDxf: 请传入 DXF 字符串、文件路径、URL 或 File 对象");
1063
+ }
1064
+
1065
+ const pairs = tokenize(dxfString);
1066
+ const sections = splitSections(pairs);
1067
+ const result = {
1068
+ header: parseHeader(sections.HEADER),
1069
+ classes: parseClasses(sections.CLASSES),
1070
+ tables: parseTables(sections.TABLES),
1071
+ blocks: parseBlocks(sections.BLOCKS),
1072
+ entities: parseEntityList(sections.ENTITIES || []),
1073
+ objects: parseObjects(sections.OBJECTS),
1074
+ };
1075
+ result.entities = assemblePolylineVertices(result.entities);
1076
+ result.entities = assembleInsertAttributes(result.entities);
1077
+ for (const block of result.blocks) {
1078
+ if (block.entities && block.entities.length > 0) {
1079
+ block.entities = assemblePolylineVertices(block.entities);
1080
+ block.entities = assembleInsertAttributes(block.entities);
1081
+ }
1082
+ }
1083
+ return result;
1084
+ }
1085
+
1086
+ /**
1087
+ * ============================================================
1088
+ * DXF 文件写入器
1089
+ * 将 parseDxf() 输出的 JSON 数据结构写回 DXF 文本格式
1090
+ * 支持所有 parser 中解析的实体类型
1091
+ *
1092
+ * ★ CJS → ESM 改动:
1093
+ * ① writeDxfFile 内 require("fs") → await import("fs/promises")
1094
+ * ② 末尾 module.exports → export
1095
+ * ③ 新增 downloadDxfFile() 浏览器下载方法
1096
+ * ============================================================
1097
+ */
1098
+
1099
+ // ─────────────────── 工具函数 ───────────────────
1100
+
1101
+ /**
1102
+ * 写入一对 group code + value
1103
+ */
1104
+ function pair(code, value) {
1105
+ if (value === undefined || value === null) return "";
1106
+ return ` ${code}\n${value}\n`;
1107
+ }
1108
+
1109
+ /** 写入浮点数,保留合理精度 */
1110
+ function pairF(code, value) {
1111
+ if (value === undefined || value === null) return "";
1112
+ return pair(code, formatFloat(value));
1113
+ }
1114
+
1115
+ /** 格式化浮点数:去掉多余尾零,但至少保留一位小数 */
1116
+ function formatFloat(v) {
1117
+ if (typeof v !== "number" || isNaN(v)) return "0.0";
1118
+ let s = v.toFixed(6);
1119
+ s = s.replace(/(\.\d*?)0+$/, "$1");
1120
+ if (s.endsWith(".")) s += "0";
1121
+ return s;
1122
+ }
1123
+
1124
+ /** 写入整数 */
1125
+ function pairI(code, value) {
1126
+ if (value === undefined || value === null) return "";
1127
+ return pair(code, Math.round(value));
1128
+ }
1129
+
1130
+ /** 写入 3D 点 (codeX, codeX+10, codeX+20) */
1131
+ function writePoint3D(baseCode, point) {
1132
+ if (!point) return "";
1133
+ return (
1134
+ pairF(baseCode, point.x) +
1135
+ pairF(baseCode + 10, point.y) +
1136
+ pairF(baseCode + 20, point.z)
1137
+ );
1138
+ }
1139
+
1140
+ /** 写入 2D 点 */
1141
+ function writePoint2D(baseCode, point) {
1142
+ if (!point) return "";
1143
+ return pairF(baseCode, point.x) + pairF(baseCode + 10, point.y);
1144
+ }
1145
+
1146
+ /** 写入挤出方向 (210/220/230) */
1147
+ function writeExtrusion(ext) {
1148
+ if (!ext) return "";
1149
+ if (ext.x === 0 && ext.y === 0 && ext.z === 1) return "";
1150
+ return pairF(210, ext.x) + pairF(220, ext.y) + pairF(230, ext.z);
1151
+ }
1152
+
1153
+ // ─────────────────── HEADER 写入 ───────────────────
1154
+
1155
+ function writeHeader(header) {
1156
+ if (!header || Object.keys(header).length === 0) return "";
1157
+ let out = pair(0, "SECTION") + pair(2, "HEADER");
1158
+
1159
+ for (const [varName, values] of Object.entries(header)) {
1160
+ out += pair(9, varName);
1161
+ if (typeof values === "object" && values !== null && !Array.isArray(values)) {
1162
+ for (const [code, val] of Object.entries(values)) {
1163
+ out += pair(parseInt(code), val);
1164
+ }
1165
+ } else {
1166
+ out += pair(1, values);
1167
+ }
1168
+ }
1169
+
1170
+ out += pair(0, "ENDSEC");
1171
+ return out;
1172
+ }
1173
+
1174
+ // ─────────────────── CLASSES 写入 ───────────────────
1175
+
1176
+ function writeClasses(classes) {
1177
+ if (!classes || classes.length === 0) return "";
1178
+ let out = pair(0, "SECTION") + pair(2, "CLASSES");
1179
+
1180
+ for (const cls of classes) {
1181
+ out += pair(0, "CLASS");
1182
+ if (cls.dxfRecord) out += pair(1, cls.dxfRecord);
1183
+ if (cls.cppClassName) out += pair(2, cls.cppClassName);
1184
+ if (cls.appName) out += pair(3, cls.appName);
1185
+ if (cls.proxyCapabilities !== undefined) out += pairI(90, cls.proxyCapabilities);
1186
+ if (cls.instanceCount !== undefined) out += pairI(91, cls.instanceCount);
1187
+ if (cls.wasProxy !== undefined) out += pairI(280, cls.wasProxy);
1188
+ if (cls.isEntity !== undefined) out += pairI(281, cls.isEntity);
1189
+ }
1190
+
1191
+ out += pair(0, "ENDSEC");
1192
+ return out;
1193
+ }
1194
+
1195
+ // ─────────────────── TABLES 写入 ───────────────────
1196
+
1197
+ function writeTables(tables) {
1198
+ if (!tables || Object.keys(tables).length === 0) return "";
1199
+ let out = pair(0, "SECTION") + pair(2, "TABLES");
1200
+
1201
+ for (const [tableName, entries] of Object.entries(tables)) {
1202
+ out += pair(0, "TABLE");
1203
+ out += pair(2, tableName);
1204
+ if (entries.length > 0) {
1205
+ const first = entries[0];
1206
+ if (first.handle) out += pair(5, first.handle);
1207
+ if (first.ownerHandle) out += pair(330, first.ownerHandle);
1208
+ out += pair(100, "AcDbSymbolTable");
1209
+ out += pairI(70, entries.length);
1210
+ }
1211
+
1212
+ for (const entry of entries) {
1213
+ out += pair(0, entry._type || tableName);
1214
+ if (entry.handle) out += pair(5, entry.handle);
1215
+ if (entry.ownerHandle) out += pair(330, entry.ownerHandle);
1216
+ if (entry.subclassMarker) {
1217
+ const markers = [].concat(entry.subclassMarker);
1218
+ for (const m of markers) {
1219
+ out += pair(100, m);
1220
+ }
1221
+ }
1222
+ if (entry.name) out += pair(2, entry.name);
1223
+ if (entry.flags !== undefined) out += pairI(70, entry.flags);
1224
+ if (entry.colorNumber !== undefined) out += pairI(62, entry.colorNumber);
1225
+ if (entry.linetypeName) out += pair(6, entry.linetypeName);
1226
+ if (entry.styleName) out += pair(7, entry.styleName);
1227
+ if (entry.height) out += pairF(40, entry.height);
1228
+ if (entry.widthFactor) out += pairF(41, entry.widthFactor);
1229
+ if (entry.angle) out += pairF(50, entry.angle);
1230
+ if (entry.lineweight !== undefined) out += pairI(370, entry.lineweight);
1231
+ if (entry.plotStyleHandle) out += pair(390, entry.plotStyleHandle);
1232
+
1233
+ if (entry.extra) {
1234
+ for (const [key, val] of Object.entries(entry)) {
1235
+ if (key.startsWith("code_")) {
1236
+ const code = parseInt(key.replace("code_", ""));
1237
+ const vals = [].concat(val);
1238
+ for (const v of vals) {
1239
+ out += pair(code, v);
1240
+ }
1241
+ }
1242
+ }
1243
+ }
1244
+ }
1245
+
1246
+ out += pair(0, "ENDTAB");
1247
+ }
1248
+
1249
+ out += pair(0, "ENDSEC");
1250
+ return out;
1251
+ }
1252
+
1253
+ // ─────────────────── BLOCKS 写入 ───────────────────
1254
+
1255
+ function writeBlocks(blocks) {
1256
+ if (!blocks || blocks.length === 0) return "";
1257
+ let out = pair(0, "SECTION") + pair(2, "BLOCKS");
1258
+
1259
+ for (const block of blocks) {
1260
+ out += pair(0, "BLOCK");
1261
+ if (block.handle) out += pair(5, block.handle);
1262
+ if (block.ownerHandle) out += pair(330, block.ownerHandle);
1263
+ out += pair(100, "AcDbEntity");
1264
+ if (block.layer) out += pair(8, block.layer);
1265
+ out += pair(100, "AcDbBlockBegin");
1266
+ if (block.name) out += pair(2, block.name);
1267
+ if (block.flags !== undefined) out += pairI(70, block.flags);
1268
+ out += pairF(10, block.basePointX || (block.basePoint && block.basePoint.x) || 0);
1269
+ out += pairF(20, block.basePointY || (block.basePoint && block.basePoint.y) || 0);
1270
+ out += pairF(30, block.basePointZ || (block.basePoint && block.basePoint.z) || 0);
1271
+ if (block.name2 || block.name) out += pair(3, block.name2 || block.name);
1272
+
1273
+ if (block.entities && block.entities.length > 0) {
1274
+ out += writeEntityList(block.entities);
1275
+ }
1276
+
1277
+ out += pair(0, "ENDBLK");
1278
+ if (block.handle) out += pair(5, block.handle);
1279
+ if (block.ownerHandle) out += pair(330, block.ownerHandle);
1280
+ out += pair(100, "AcDbEntity");
1281
+ if (block.layer) out += pair(8, block.layer);
1282
+ out += pair(100, "AcDbBlockEnd");
1283
+ }
1284
+
1285
+ out += pair(0, "ENDSEC");
1286
+ return out;
1287
+ }
1288
+
1289
+ // ─────────────────── ENTITIES 写入 ───────────────────
1290
+
1291
+ function writeEntityList(entities) {
1292
+ let out = "";
1293
+ for (const entity of entities) {
1294
+ out += writeOneEntity(entity);
1295
+ }
1296
+ return out;
1297
+ }
1298
+
1299
+ function writeEntitiesSection(entities) {
1300
+ if (!entities || entities.length === 0) return "";
1301
+ let out = pair(0, "SECTION") + pair(2, "ENTITIES");
1302
+ out += writeEntityList(entities);
1303
+ out += pair(0, "ENDSEC");
1304
+ return out;
1305
+ }
1306
+
1307
+ /** 写入通用实体头部字段 */
1308
+ function writeCommonFields(entity) {
1309
+ let out = "";
1310
+ if (entity.handle) out += pair(5, entity.handle);
1311
+ if (entity.ownerHandle) out += pair(330, entity.ownerHandle);
1312
+ out += pair(100, "AcDbEntity");
1313
+ if (entity.inPaperSpace) out += pairI(67, 1);
1314
+ if (entity.layer) out += pair(8, entity.layer);
1315
+ if (entity.linetypeName) out += pair(6, entity.linetypeName);
1316
+ if (entity.colorNumber !== undefined && entity.colorNumber !== 0) out += pairI(62, entity.colorNumber);
1317
+ if (entity.lineweight !== undefined) out += pairI(370, entity.lineweight);
1318
+ if (entity.linetypeScale !== undefined && entity.linetypeScale !== 1) out += pairF(48, entity.linetypeScale);
1319
+ if (entity.visible === false) out += pairI(60, 1);
1320
+ if (entity.trueColor) out += pairI(420, entity.trueColor);
1321
+ return out;
1322
+ }
1323
+
1324
+ /** 写入单个实体 */
1325
+ function writeOneEntity(entity) {
1326
+ if (!entity || !entity.type) return "";
1327
+
1328
+ let out = pair(0, entity.type);
1329
+ out += writeCommonFields(entity);
1330
+
1331
+ switch (entity.type) {
1332
+ case "LINE": out += writeLineEntity(entity); break;
1333
+ case "LWPOLYLINE": out += writeLWPolylineEntity(entity); break;
1334
+ case "POLYLINE": out += writePolylineEntity(entity); break;
1335
+ case "ARC": out += writeArcEntity(entity); break;
1336
+ case "CIRCLE": out += writeCircleEntity(entity); break;
1337
+ case "ELLIPSE": out += writeEllipseEntity(entity); break;
1338
+ case "POINT": out += writePointEntity(entity); break;
1339
+ case "TEXT": out += writeTextEntity(entity); break;
1340
+ case "MTEXT": out += writeMTextEntity(entity); break;
1341
+ case "ATTRIB":
1342
+ case "ATTDEF": out += writeAttribEntity(entity); break;
1343
+ case "INSERT": out += writeInsertEntity(entity); break;
1344
+ case "DIMENSION": out += writeDimensionEntity(entity); break;
1345
+ case "LEADER": out += writeLeaderEntity(entity); break;
1346
+ case "MLEADER": out += writeMLeaderEntity(entity); break;
1347
+ case "HATCH": out += writeHatchEntity(entity); break;
1348
+ case "SPLINE": out += writeSplineEntity(entity); break;
1349
+ case "SOLID":
1350
+ case "3DSOLID":
1351
+ case "TRACE": out += writeSolidEntity(entity); break;
1352
+ case "3DFACE": out += write3DFaceEntity(entity); break;
1353
+ case "RAY":
1354
+ case "XLINE": out += writeRayEntity(entity); break;
1355
+ case "VIEWPORT": out += writeViewportEntity(entity); break;
1356
+ case "IMAGE":
1357
+ case "WIPEOUT": out += writeImageEntity(entity); break;
1358
+ case "TOLERANCE": out += writeToleranceEntity(entity); break;
1359
+ case "HELIX": out += writeHelixEntity(entity); break;
1360
+ case "MESH": out += writeMeshEntity(entity); break;
1361
+ case "REGION":
1362
+ case "BODY":
1363
+ case "SURFACE": out += writeRegionEntity(entity); break;
1364
+ case "OLE2FRAME": out += writeOle2Entity(entity); break;
1365
+ case "TABLE": out += writeTableEntity(entity); break;
1366
+ case "MLINE": out += writeMLineEntity(entity); break;
1367
+ case "SEQEND": break;
1368
+ default: out += writeGenericEntity(entity); break;
1369
+ }
1370
+
1371
+ return out;
1372
+ }
1373
+
1374
+ // ─────────────────── 各实体写入器 ───────────────────
1375
+
1376
+ function writeLineEntity(e) {
1377
+ let out = pair(100, "AcDbLine");
1378
+ if (e.thickness) out += pairF(39, e.thickness);
1379
+ out += writePoint3D(10, e.startPoint);
1380
+ out += writePoint3D(11, e.endPoint);
1381
+ out += writeExtrusion(e.extrusionDirection);
1382
+ return out;
1383
+ }
1384
+
1385
+ function writeLWPolylineEntity(e) {
1386
+ let out = pair(100, "AcDbPolyline");
1387
+ const verts = e.vertices || [];
1388
+ out += pairI(90, verts.length);
1389
+
1390
+ let flags = 0;
1391
+ if (e.closed) flags |= 1;
1392
+ out += pairI(70, flags);
1393
+
1394
+ if (e.constantWidth) out += pairF(43, e.constantWidth);
1395
+ if (e.elevation) out += pairF(38, e.elevation);
1396
+ if (e.thickness) out += pairF(39, e.thickness);
1397
+
1398
+ for (const v of verts) {
1399
+ out += pairF(10, v.x);
1400
+ out += pairF(20, v.y);
1401
+ if (v.startWidth) out += pairF(40, v.startWidth);
1402
+ if (v.endWidth) out += pairF(41, v.endWidth);
1403
+ if (v.bulge) out += pairF(42, v.bulge);
1404
+ }
1405
+
1406
+ out += writeExtrusion(e.extrusionDirection);
1407
+ return out;
1408
+ }
1409
+
1410
+ function writePolylineEntity(e) {
1411
+ let out = pair(100, "AcDb2dPolyline");
1412
+
1413
+ let flags = e.flags || 0;
1414
+ if (e.closed) flags |= 1;
1415
+ out += pairI(70, flags);
1416
+
1417
+ out += pairF(10, 0);
1418
+ out += pairF(20, 0);
1419
+ if (e.elevation) out += pairF(30, e.elevation);
1420
+ if (e.thickness) out += pairF(39, e.thickness);
1421
+ if (e.defaultStartWidth) out += pairF(40, e.defaultStartWidth);
1422
+ if (e.defaultEndWidth) out += pairF(41, e.defaultEndWidth);
1423
+ if (e.meshMVertexCount) out += pairI(71, e.meshMVertexCount);
1424
+ if (e.meshNVertexCount) out += pairI(72, e.meshNVertexCount);
1425
+ out += writeExtrusion(e.extrusionDirection);
1426
+
1427
+ if (e.vertices && e.vertices.length > 0) {
1428
+ for (const v of e.vertices) {
1429
+ out += pair(0, "VERTEX");
1430
+ if (e.handle) out += pair(5, "");
1431
+ out += pair(100, "AcDbEntity");
1432
+ if (e.layer) out += pair(8, e.layer);
1433
+ out += pair(100, "AcDbVertex");
1434
+ out += pair(100, "AcDb2dVertex");
1435
+ out += pairF(10, v.x);
1436
+ out += pairF(20, v.y);
1437
+ out += pairF(30, v.z || 0);
1438
+ if (v.startWidth) out += pairF(40, v.startWidth);
1439
+ if (v.endWidth) out += pairF(41, v.endWidth);
1440
+ if (v.bulge) out += pairF(42, v.bulge);
1441
+ if (v.flags) out += pairI(70, v.flags);
1442
+ }
1443
+ out += pair(0, "SEQEND");
1444
+ out += pair(100, "AcDbEntity");
1445
+ if (e.layer) out += pair(8, e.layer);
1446
+ }
1447
+
1448
+ return out;
1449
+ }
1450
+
1451
+ function writeArcEntity(e) {
1452
+ let out = pair(100, "AcDbCircle");
1453
+ if (e.thickness) out += pairF(39, e.thickness);
1454
+ out += writePoint3D(10, e.center);
1455
+ out += pairF(40, e.radius);
1456
+ out += writeExtrusion(e.extrusionDirection);
1457
+ out += pair(100, "AcDbArc");
1458
+ out += pairF(50, e.startAngle);
1459
+ out += pairF(51, e.endAngle);
1460
+ return out;
1461
+ }
1462
+
1463
+ function writeCircleEntity(e) {
1464
+ let out = pair(100, "AcDbCircle");
1465
+ if (e.thickness) out += pairF(39, e.thickness);
1466
+ out += writePoint3D(10, e.center);
1467
+ out += pairF(40, e.radius);
1468
+ out += writeExtrusion(e.extrusionDirection);
1469
+ return out;
1470
+ }
1471
+
1472
+ function writeEllipseEntity(e) {
1473
+ let out = pair(100, "AcDbEllipse");
1474
+ out += writePoint3D(10, e.center);
1475
+ out += writePoint3D(11, e.majorAxisEndPoint);
1476
+ out += writeExtrusion(e.extrusionDirection);
1477
+ out += pairF(40, e.axisRatio);
1478
+ out += pairF(41, e.startAngle);
1479
+ out += pairF(42, e.endAngle);
1480
+ return out;
1481
+ }
1482
+
1483
+ function writePointEntity(e) {
1484
+ let out = pair(100, "AcDbPoint");
1485
+ out += writePoint3D(10, e.position);
1486
+ if (e.thickness) out += pairF(39, e.thickness);
1487
+ if (e.angle) out += pairF(50, e.angle);
1488
+ out += writeExtrusion(e.extrusionDirection);
1489
+ return out;
1490
+ }
1491
+
1492
+ function writeTextEntity(e) {
1493
+ let out = pair(100, "AcDbText");
1494
+ if (e.thickness) out += pairF(39, e.thickness);
1495
+ out += writePoint3D(10, e.insertionPoint);
1496
+ out += pairF(40, e.height);
1497
+ out += pair(1, e.text);
1498
+ if (e.rotation) out += pairF(50, e.rotation);
1499
+ if (e.xScale && e.xScale !== 1) out += pairF(41, e.xScale);
1500
+ if (e.oblique) out += pairF(51, e.oblique);
1501
+ if (e.styleName && e.styleName !== "STANDARD") out += pair(7, e.styleName);
1502
+ if (e.generationFlags) out += pairI(71, e.generationFlags);
1503
+ if (e.hAlign) out += pairI(72, e.hAlign);
1504
+ out += writePoint3D(11, e.alignmentPoint);
1505
+ out += writeExtrusion(e.extrusionDirection);
1506
+ out += pair(100, "AcDbText");
1507
+ if (e.vAlign) out += pairI(73, e.vAlign);
1508
+ return out;
1509
+ }
1510
+
1511
+ function writeMTextEntity(e) {
1512
+ let out = pair(100, "AcDbMText");
1513
+ out += writePoint3D(10, e.insertionPoint);
1514
+ out += pairF(40, e.height);
1515
+ if (e.width) out += pairF(41, e.width);
1516
+ if (e.attachmentPoint) out += pairI(71, e.attachmentPoint);
1517
+ if (e.drawingDirection) out += pairI(72, e.drawingDirection);
1518
+
1519
+ const text = e.text || "";
1520
+ if (text.length > 250) {
1521
+ let remaining = text;
1522
+ while (remaining.length > 250) {
1523
+ out += pair(3, remaining.substring(0, 250));
1524
+ remaining = remaining.substring(250);
1525
+ }
1526
+ out += pair(1, remaining);
1527
+ } else {
1528
+ out += pair(1, text);
1529
+ }
1530
+
1531
+ if (e.styleName && e.styleName !== "STANDARD") out += pair(7, e.styleName);
1532
+ if (e.rotation) out += pairF(50, e.rotation);
1533
+ if (e.lineSpacingStyle) out += pairI(73, e.lineSpacingStyle);
1534
+ if (e.lineSpacing && e.lineSpacing !== 1) out += pairF(44, e.lineSpacing);
1535
+ out += writePoint3D(11, e.direction);
1536
+ out += writeExtrusion(e.extrusionDirection);
1537
+ return out;
1538
+ }
1539
+
1540
+ function writeAttribEntity(e) {
1541
+ const isAttdef = e.type === "ATTDEF";
1542
+ let out = pair(100, isAttdef ? "AcDbAttributeDefinition" : "AcDbAttribute");
1543
+ out += writePoint3D(10, e.insertionPoint);
1544
+ out += pairF(40, e.height);
1545
+ out += pair(1, e.text);
1546
+ if (e.tag) out += pair(2, e.tag);
1547
+ if (e.prompt) out += pair(3, e.prompt);
1548
+ if (e.flags) out += pairI(70, e.flags);
1549
+ if (e.rotation) out += pairF(50, e.rotation);
1550
+ if (e.xScale && e.xScale !== 1) out += pairF(41, e.xScale);
1551
+ if (e.styleName && e.styleName !== "STANDARD") out += pair(7, e.styleName);
1552
+ if (e.generationFlags) out += pairI(71, e.generationFlags);
1553
+ if (e.hAlign) out += pairI(72, e.hAlign);
1554
+ if (e.vAlign) out += pairI(73, e.vAlign);
1555
+ out += writePoint3D(11, e.alignmentPoint);
1556
+ return out;
1557
+ }
1558
+
1559
+ function writeInsertEntity(e) {
1560
+ let out = pair(100, "AcDbBlockReference");
1561
+ if (e.hasAttributes) out += pairI(66, 1);
1562
+ out += pair(2, e.blockName);
1563
+ out += writePoint3D(10, e.insertionPoint);
1564
+ if (e.scale) {
1565
+ if (e.scale.x !== 1) out += pairF(41, e.scale.x);
1566
+ if (e.scale.y !== 1) out += pairF(42, e.scale.y);
1567
+ if (e.scale.z !== 1) out += pairF(43, e.scale.z);
1568
+ }
1569
+ if (e.rotation) out += pairF(50, e.rotation);
1570
+ if (e.columnCount && e.columnCount !== 1) out += pairI(70, e.columnCount);
1571
+ if (e.rowCount && e.rowCount !== 1) out += pairI(71, e.rowCount);
1572
+ if (e.columnSpacing) out += pairF(44, e.columnSpacing);
1573
+ if (e.rowSpacing) out += pairF(45, e.rowSpacing);
1574
+ out += writeExtrusion(e.extrusionDirection);
1575
+
1576
+ if (e.attributes && e.attributes.length > 0) {
1577
+ for (const attr of e.attributes) {
1578
+ out += writeOneEntity(attr);
1579
+ }
1580
+ out += pair(0, "SEQEND");
1581
+ out += pair(100, "AcDbEntity");
1582
+ if (e.layer) out += pair(8, e.layer);
1583
+ }
1584
+
1585
+ return out;
1586
+ }
1587
+
1588
+ function writeDimensionEntity(e) {
1589
+ let out = pair(100, "AcDbDimension");
1590
+ if (e.blockName) out += pair(2, e.blockName);
1591
+ out += writePoint3D(10, e.definitionPoint);
1592
+ out += writePoint3D(11, e.middleOfText);
1593
+ if (e.dimensionType !== undefined) out += pairI(70, e.dimensionType);
1594
+ if (e.attachmentPoint) out += pairI(71, e.attachmentPoint);
1595
+ if (e.text) out += pair(1, e.text);
1596
+ if (e.styleName && e.styleName !== "STANDARD") out += pair(3, e.styleName);
1597
+ if (e.rotation) out += pairF(50, e.rotation);
1598
+
1599
+ const dimType = (e.dimensionType || 0) & 0x0f;
1600
+ switch (dimType) {
1601
+ case 0:
1602
+ case 1:
1603
+ out += pair(100, "AcDbAlignedDimension");
1604
+ out += writePoint3D(13, e.point1);
1605
+ out += writePoint3D(14, e.point2);
1606
+ if (e.extensionLineAngle) out += pairF(52, e.extensionLineAngle);
1607
+ if (dimType === 0) {
1608
+ out += pair(100, "AcDbRotatedDimension");
1609
+ }
1610
+ break;
1611
+ case 2:
1612
+ out += pair(100, "AcDb3PointAngularDimension");
1613
+ out += writePoint3D(13, e.point1);
1614
+ out += writePoint3D(14, e.point2);
1615
+ if (e.point3) out += writePoint3D(15, e.point3);
1616
+ break;
1617
+ case 3:
1618
+ case 4:
1619
+ out += pair(100, dimType === 3 ? "AcDbDiametricDimension" : "AcDbRadialDimension");
1620
+ out += writePoint3D(15, e.point3 || e.point1);
1621
+ if (e.leaderLength) out += pairF(40, e.leaderLength);
1622
+ break;
1623
+ case 5:
1624
+ out += pair(100, "AcDb3PointAngularDimension");
1625
+ out += writePoint3D(13, e.point1);
1626
+ out += writePoint3D(14, e.point2);
1627
+ if (e.point3) out += writePoint3D(15, e.point3);
1628
+ break;
1629
+ case 6:
1630
+ out += pair(100, "AcDbOrdinateDimension");
1631
+ out += writePoint3D(13, e.point1);
1632
+ out += writePoint3D(14, e.point2);
1633
+ break;
1634
+ default:
1635
+ out += writePoint3D(13, e.point1);
1636
+ out += writePoint3D(14, e.point2);
1637
+ break;
1638
+ }
1639
+
1640
+ return out;
1641
+ }
1642
+
1643
+ function writeLeaderEntity(e) {
1644
+ let out = pair(100, "AcDbLeader");
1645
+ if (e.styleName) out += pair(3, e.styleName);
1646
+ if (e.arrowheadFlag !== undefined) out += pairI(71, e.arrowheadFlag);
1647
+ if (e.pathType !== undefined) out += pairI(72, e.pathType);
1648
+ if (e.creationType !== undefined) out += pairI(73, e.creationType);
1649
+ if (e.hooklineDirectionFlag !== undefined) out += pairI(74, e.hooklineDirectionFlag);
1650
+ if (e.hooklineFlag !== undefined) out += pairI(75, e.hooklineFlag);
1651
+ if (e.textHeight) out += pairF(40, e.textHeight);
1652
+ if (e.textWidth) out += pairF(41, e.textWidth);
1653
+
1654
+ const verts = e.vertices || [];
1655
+ out += pairI(76, verts.length);
1656
+ for (const v of verts) {
1657
+ out += writePoint3D(10, v);
1658
+ }
1659
+
1660
+ if (e.annotationReference) out += pair(340, e.annotationReference);
1661
+ if (e.normal) {
1662
+ out += pairF(210, e.normal.x);
1663
+ out += pairF(220, e.normal.y);
1664
+ out += pairF(230, e.normal.z);
1665
+ }
1666
+ if (e.horizontalDirection) {
1667
+ out += pairF(211, e.horizontalDirection.x);
1668
+ out += pairF(221, e.horizontalDirection.y);
1669
+ out += pairF(231, e.horizontalDirection.z);
1670
+ }
1671
+ return out;
1672
+ }
1673
+
1674
+ function writeMLeaderEntity(e) {
1675
+ let out = pair(100, "AcDbMLeader");
1676
+ if (e.styleName) out += pair(3, e.styleName);
1677
+ if (e.contentType !== undefined) out += pairI(170, e.contentType);
1678
+ if (e.text) out += pair(304, e.text);
1679
+ if (e.textStyleId) out += pair(340, e.textStyleId);
1680
+ if (e.doglegLength) out += pairF(40, e.doglegLength);
1681
+ if (e.arrowheadSize) out += pairF(41, e.arrowheadSize);
1682
+ if (e.textHeight) out += pairF(42, e.textHeight);
1683
+
1684
+ if (e.leaderPoints) {
1685
+ for (const pt of e.leaderPoints) {
1686
+ out += writePoint3D(10, pt);
1687
+ }
1688
+ }
1689
+ return out;
1690
+ }
1691
+
1692
+ function writeHatchEntity(e) {
1693
+ let out = pair(100, "AcDbHatch");
1694
+ out += pairF(10, 0);
1695
+ out += pairF(20, 0);
1696
+ out += pairF(30, e.elevation || 0);
1697
+ out += writeExtrusion(e.extrusionDirection);
1698
+ out += pair(2, e.patternName || "");
1699
+ out += pairI(70, e.solid ? 1 : 0);
1700
+ out += pairI(71, e.associative ? 1 : 0);
1701
+
1702
+ const paths = e.boundaryPaths || [];
1703
+ out += pairI(91, paths.length);
1704
+ for (const path of paths) {
1705
+ out += pairI(92, path.type || 0);
1706
+
1707
+ if (path.points && path.points.length > 0) {
1708
+ out += pairI(93, path.points.length);
1709
+ for (const pt of path.points) {
1710
+ out += pairF(10, pt.x);
1711
+ out += pairF(20, pt.y || 0);
1712
+ }
1713
+ }
1714
+ }
1715
+
1716
+ out += pairI(75, e.style || 0);
1717
+ out += pairI(76, e.patternType || 0);
1718
+ if (!e.solid) {
1719
+ if (e.patternAngle) out += pairF(52, e.patternAngle);
1720
+ if (e.patternScale) out += pairF(41, e.patternScale);
1721
+ }
1722
+
1723
+ const seeds = e.seedPoints || [];
1724
+ out += pairI(98, seeds.length);
1725
+ for (const s of seeds) {
1726
+ out += pairF(10, s.x);
1727
+ out += pairF(20, s.y || 0);
1728
+ }
1729
+
1730
+ return out;
1731
+ }
1732
+
1733
+ function writeSplineEntity(e) {
1734
+ let out = pair(100, "AcDbSpline");
1735
+
1736
+ if (e.normal) {
1737
+ out += pairF(210, e.normal.x);
1738
+ out += pairF(220, e.normal.y);
1739
+ out += pairF(230, e.normal.z);
1740
+ }
1741
+
1742
+ let flags = e.flags || 0;
1743
+ if (e.closed) flags |= 1;
1744
+ if (e.periodic) flags |= 2;
1745
+ if (e.rational) flags |= 4;
1746
+ if (e.planar) flags |= 8;
1747
+ out += pairI(70, flags);
1748
+
1749
+ out += pairI(71, e.degree || 3);
1750
+
1751
+ const knots = e.knots || [];
1752
+ const cps = e.controlPoints || [];
1753
+ const fps = e.fitPoints || [];
1754
+ out += pairI(72, knots.length);
1755
+ out += pairI(73, cps.length);
1756
+ out += pairI(74, fps.length);
1757
+
1758
+ if (e.knotTolerance) out += pairF(42, e.knotTolerance);
1759
+ if (e.controlPointTolerance) out += pairF(43, e.controlPointTolerance);
1760
+ if (e.fitTolerance) out += pairF(44, e.fitTolerance);
1761
+
1762
+ if (e.startTangent) out += writePoint3D(12, e.startTangent);
1763
+ if (e.endTangent) out += writePoint3D(13, e.endTangent);
1764
+
1765
+ for (const k of knots) {
1766
+ out += pairF(40, k);
1767
+ }
1768
+ if (e.weights) {
1769
+ for (const w of e.weights) {
1770
+ out += pairF(41, w);
1771
+ }
1772
+ }
1773
+ for (const cp of cps) {
1774
+ out += writePoint3D(10, cp);
1775
+ }
1776
+ for (const fp of fps) {
1777
+ out += writePoint3D(11, fp);
1778
+ }
1779
+
1780
+ return out;
1781
+ }
1782
+
1783
+ function writeSolidEntity(e) {
1784
+ let subclass = "AcDbTrace";
1785
+ if (e.type === "SOLID") subclass = "AcDbTrace";
1786
+ else if (e.type === "3DSOLID") subclass = "AcDb3dSolid";
1787
+
1788
+ let out = pair(100, subclass);
1789
+
1790
+ const pts = e.points || [];
1791
+ if (pts[0]) out += writePoint3D(10, pts[0]);
1792
+ if (pts[1]) out += writePoint3D(11, pts[1]);
1793
+ if (pts[2]) out += writePoint3D(12, pts[2]);
1794
+ if (pts[3]) out += writePoint3D(13, pts[3]);
1795
+ if (e.thickness) out += pairF(39, e.thickness);
1796
+ out += writeExtrusion(e.extrusionDirection);
1797
+ return out;
1798
+ }
1799
+
1800
+ function write3DFaceEntity(e) {
1801
+ let out = pair(100, "AcDbFace");
1802
+ const verts = e.vertices || [];
1803
+ if (verts[0]) out += writePoint3D(10, verts[0]);
1804
+ if (verts[1]) out += writePoint3D(11, verts[1]);
1805
+ if (verts[2]) out += writePoint3D(12, verts[2]);
1806
+ if (verts[3]) out += writePoint3D(13, verts[3]);
1807
+ if (e.invisibleEdgeFlags) out += pairI(70, e.invisibleEdgeFlags);
1808
+ return out;
1809
+ }
1810
+
1811
+ function writeRayEntity(e) {
1812
+ const subclass = e.type === "RAY" ? "AcDbRay" : "AcDbXline";
1813
+ let out = pair(100, subclass);
1814
+ out += writePoint3D(10, e.basePoint);
1815
+ out += writePoint3D(11, e.direction);
1816
+ return out;
1817
+ }
1818
+
1819
+ function writeViewportEntity(e) {
1820
+ let out = pair(100, "AcDbViewport");
1821
+ out += writePoint3D(10, e.center);
1822
+ out += pairF(40, e.width);
1823
+ out += pairF(41, e.height);
1824
+ if (e.id) out += pairI(69, e.id);
1825
+ if (e.viewCenter) out += writePoint2D(12, e.viewCenter);
1826
+ if (e.snapBase) out += writePoint2D(13, e.snapBase);
1827
+ if (e.snapSpacing) out += writePoint2D(14, e.snapSpacing);
1828
+ if (e.gridSpacing) out += writePoint2D(15, e.gridSpacing);
1829
+ out += writePoint3D(16, e.viewDirection);
1830
+ out += writePoint3D(17, e.viewTarget);
1831
+ if (e.perspectiveLensLength) out += pairF(42, e.perspectiveLensLength);
1832
+ if (e.frontClipPlane) out += pairF(43, e.frontClipPlane);
1833
+ if (e.backClipPlane) out += pairF(44, e.backClipPlane);
1834
+ if (e.viewHeight) out += pairF(45, e.viewHeight);
1835
+ if (e.snapAngle) out += pairF(50, e.snapAngle);
1836
+ if (e.viewTwistAngle) out += pairF(51, e.viewTwistAngle);
1837
+ if (e.statusField) out += pairI(68, e.statusField);
1838
+
1839
+ if (e.frozenLayers) {
1840
+ for (const fl of e.frozenLayers) {
1841
+ out += pair(331, fl);
1842
+ }
1843
+ }
1844
+ if (e.clippingBoundary) out += pair(340, e.clippingBoundary);
1845
+ return out;
1846
+ }
1847
+
1848
+ function writeImageEntity(e) {
1849
+ let out = pair(100, e.type === "WIPEOUT" ? "AcDbWipeout" : "AcDbRasterImage");
1850
+ out += writePoint3D(10, e.insertionPoint);
1851
+ out += writePoint3D(11, e.uVector);
1852
+ out += writePoint3D(12, e.vVector);
1853
+ out += pairF(13, e.imageSize ? e.imageSize.u : 0);
1854
+ out += pairF(23, e.imageSize ? e.imageSize.v : 0);
1855
+ if (e.imageDefHandle) out += pair(340, e.imageDefHandle);
1856
+ if (e.displayFlags) out += pairI(70, e.displayFlags);
1857
+ if (e.clippingType) out += pairI(71, e.clippingType);
1858
+ if (e.brightness !== undefined) out += pairI(280, e.brightness);
1859
+ if (e.contrast !== undefined) out += pairI(281, e.contrast);
1860
+ if (e.fade !== undefined) out += pairI(282, e.fade);
1861
+
1862
+ if (e.clippingVertices) {
1863
+ for (const cv of e.clippingVertices) {
1864
+ out += pairF(14, cv.x);
1865
+ out += pairF(24, cv.y || 0);
1866
+ }
1867
+ }
1868
+ return out;
1869
+ }
1870
+
1871
+ function writeToleranceEntity(e) {
1872
+ let out = pair(100, "AcDbFcf");
1873
+ if (e.styleName) out += pair(3, e.styleName);
1874
+ out += writePoint3D(10, e.insertionPoint);
1875
+ if (e.text) out += pair(1, e.text);
1876
+ out += writePoint3D(11, e.direction);
1877
+ return out;
1878
+ }
1879
+
1880
+ function writeHelixEntity(e) {
1881
+ let out = pair(100, "AcDbHelix");
1882
+ out += writePoint3D(10, e.axisBasePoint);
1883
+ out += writePoint3D(11, e.startPoint);
1884
+ out += writePoint3D(12, e.axisVector);
1885
+ out += pairF(40, e.radius);
1886
+ out += pairF(41, e.turns);
1887
+ out += pairF(42, e.turnHeight);
1888
+ if (e.handedness !== undefined) out += pairI(290, e.handedness);
1889
+ if (e.constraint !== undefined) out += pairI(280, e.constraint);
1890
+ return out;
1891
+ }
1892
+
1893
+ function writeMeshEntity(e) {
1894
+ let out = pair(100, "AcDbSubDMesh");
1895
+ out += pairI(71, e.version || 2);
1896
+ out += pairI(72, e.subdivisionLevel || 0);
1897
+
1898
+ const verts = e.vertices || [];
1899
+ out += pairI(91, verts.length);
1900
+ for (const v of verts) {
1901
+ out += writePoint3D(10, v);
1902
+ }
1903
+
1904
+ const faces = e.faces || [];
1905
+ out += pairI(92, faces.length);
1906
+ for (const f of faces) {
1907
+ out += pairI(90, f);
1908
+ }
1909
+
1910
+ const edges = e.edges || [];
1911
+ out += pairI(93, edges.length);
1912
+ for (const ed of edges) {
1913
+ out += pairI(90, ed);
1914
+ }
1915
+
1916
+ const creases = e.edgeCreases || [];
1917
+ out += pairI(94, creases.length);
1918
+ for (const c of creases) {
1919
+ out += pairF(140, c);
1920
+ }
1921
+
1922
+ return out;
1923
+ }
1924
+
1925
+ function writeRegionEntity(e) {
1926
+ let subclass = "AcDbRegion";
1927
+ if (e.type === "BODY") subclass = "AcDbBody";
1928
+ else if (e.type === "SURFACE") subclass = "AcDbSurface";
1929
+
1930
+ let out = pair(100, subclass);
1931
+ if (e.modelerFormat !== undefined) out += pairI(70, e.modelerFormat);
1932
+
1933
+ const acisStr = e.acisDataString || (e.acisData || []).join("");
1934
+ if (acisStr) {
1935
+ let remaining = acisStr;
1936
+ while (remaining.length > 255) {
1937
+ out += pair(3, remaining.substring(0, 255));
1938
+ remaining = remaining.substring(255);
1939
+ }
1940
+ out += pair(1, remaining);
1941
+ }
1942
+
1943
+ return out;
1944
+ }
1945
+
1946
+ function writeOle2Entity(e) {
1947
+ let out = pair(100, "AcDbOle2Frame");
1948
+ out += pairI(70, e.oleType || 0);
1949
+ out += pairI(71, e.tileMode || 0);
1950
+ out += writePoint3D(10, e.upperLeftCorner);
1951
+ out += writePoint3D(11, e.lowerRightCorner);
1952
+ if (e.binaryData) {
1953
+ for (const bd of e.binaryData) {
1954
+ out += pair(1, bd);
1955
+ }
1956
+ }
1957
+ return out;
1958
+ }
1959
+
1960
+ function writeTableEntity(e) {
1961
+ let out = pair(100, "AcDbBlockReference");
1962
+ if (e.blockName) out += pair(2, e.blockName);
1963
+ out += writePoint3D(10, e.insertionPoint);
1964
+ out += pair(100, "AcDbTable");
1965
+ out += writePoint3D(11, e.direction);
1966
+ if (e.rows) out += pairI(91, e.rows);
1967
+ if (e.columns) out += pairI(92, e.columns);
1968
+ if (e.rowHeights) {
1969
+ for (const rh of e.rowHeights) {
1970
+ out += pairF(142, rh);
1971
+ }
1972
+ }
1973
+ if (e.columnWidths) {
1974
+ for (const cw of e.columnWidths) {
1975
+ out += pairF(141, cw);
1976
+ }
1977
+ }
1978
+ return out;
1979
+ }
1980
+
1981
+ function writeMLineEntity(e) {
1982
+ let out = pair(100, "AcDbMline");
1983
+ if (e.styleName) out += pair(2, e.styleName);
1984
+ if (e.styleHandle) out += pair(340, e.styleHandle);
1985
+ out += pairF(40, e.scale || 1);
1986
+ out += pairI(70, e.justification || 0);
1987
+ out += pairI(71, e.flags || 0);
1988
+
1989
+ const verts = e.vertices || [];
1990
+ out += pairI(72, verts.length);
1991
+ if (e.elementCount) out += pairI(73, e.elementCount);
1992
+
1993
+ out += writePoint3D(10, e.startPoint);
1994
+
1995
+ if (e.normal) {
1996
+ out += pairF(210, e.normal.x);
1997
+ out += pairF(220, e.normal.y);
1998
+ out += pairF(230, e.normal.z);
1999
+ }
2000
+
2001
+ for (const v of verts) {
2002
+ out += pairF(11, v.x);
2003
+ out += pairF(21, v.y);
2004
+ out += pairF(31, v.z || 0);
2005
+ }
2006
+
2007
+ return out;
2008
+ }
2009
+
2010
+ /** 通用 / 未知实体写入 */
2011
+ function writeGenericEntity(e) {
2012
+ let out = "";
2013
+ if (e.data) {
2014
+ for (const [key, val] of Object.entries(e.data)) {
2015
+ if (key.startsWith("code_")) {
2016
+ const code = parseInt(key.replace("code_", ""));
2017
+ const vals = [].concat(val);
2018
+ for (const v of vals) {
2019
+ out += pair(code, v);
2020
+ }
2021
+ }
2022
+ }
2023
+ }
2024
+ return out;
2025
+ }
2026
+
2027
+ // ─────────────────── OBJECTS 写入 ───────────────────
2028
+
2029
+ function writeObjects(objects) {
2030
+ if (!objects || objects.length === 0) return "";
2031
+ let out = pair(0, "SECTION") + pair(2, "OBJECTS");
2032
+
2033
+ for (const obj of objects) {
2034
+ out += pair(0, obj.type);
2035
+ if (obj.handle) out += pair(5, obj.handle);
2036
+ if (obj.ownerHandle) out += pair(330, obj.ownerHandle);
2037
+ if (obj.name) out += pair(2, obj.name);
2038
+ if (obj.entryName) {
2039
+ const names = [].concat(obj.entryName);
2040
+ for (const n of names) {
2041
+ out += pair(3, n);
2042
+ }
2043
+ }
2044
+ if (obj.softPointer) {
2045
+ const ptrs = [].concat(obj.softPointer);
2046
+ for (const p of ptrs) {
2047
+ out += pair(350, p);
2048
+ }
2049
+ }
2050
+ if (obj.hardPointer) {
2051
+ const ptrs = [].concat(obj.hardPointer);
2052
+ for (const p of ptrs) {
2053
+ out += pair(360, p);
2054
+ }
2055
+ }
2056
+ for (const [key, val] of Object.entries(obj)) {
2057
+ if (key.startsWith("code_")) {
2058
+ const code = parseInt(key.replace("code_", ""));
2059
+ const vals = [].concat(val);
2060
+ for (const v of vals) {
2061
+ out += pair(code, v);
2062
+ }
2063
+ }
2064
+ }
2065
+ }
2066
+
2067
+ out += pair(0, "ENDSEC");
2068
+ return out;
2069
+ }
2070
+
2071
+ // ─────────────────── 主入口 ───────────────────
2072
+
2073
+ /**
2074
+ * 将 parseDxf() 输出的数据结构写回 DXF 文本格式
2075
+ * @param {Object} dxfData - parseDxf() 返回的完整数据结构
2076
+ * @returns {string} DXF 文本内容
2077
+ */
2078
+ function writeDxf(dxfData) {
2079
+ let out = "";
2080
+ out += writeHeader(dxfData.header);
2081
+ out += writeClasses(dxfData.classes);
2082
+ out += writeTables(dxfData.tables);
2083
+ out += writeBlocks(dxfData.blocks);
2084
+ out += writeEntitiesSection(dxfData.entities);
2085
+ out += writeObjects(dxfData.objects);
2086
+ out += pair(0, "EOF");
2087
+ return out;
2088
+ }
2089
+
2090
+ /**
2091
+ * ★ 改动点①: 写入文件 — 动态 import 替代 require
2092
+ * @param {Object} dxfData - parseDxf() 返回的完整数据结构
2093
+ * @param {string} filePath - 输出文件路径(仅 Node.js)
2094
+ */
2095
+ async function writeDxfFile(dxfData, filePath) {
2096
+ const content = writeDxf(dxfData);
2097
+ if (typeof process !== "undefined" && process.versions?.node) {
2098
+ const { writeFile } = await import('fs/promises');
2099
+ await writeFile(filePath, content, "utf-8");
2100
+ } else {
2101
+ throw new Error("writeDxfFile 仅支持 Node.js 环境,浏览器请用 downloadDxfFile()");
2102
+ }
2103
+ }
2104
+
2105
+ /**
2106
+ * ★ 改动点③: 浏览器下载
2107
+ * @param {Object} dxfData - parseDxf() 返回的完整数据结构
2108
+ * @param {string} [filename="output.dxf"] - 下载文件名
2109
+ */
2110
+ function downloadDxfFile(dxfData, filename = "output.dxf") {
2111
+ const content = writeDxf(dxfData);
2112
+ const blob = new Blob([content], { type: "application/dxf" });
2113
+ const url = URL.createObjectURL(blob);
2114
+ const a = document.createElement("a");
2115
+ a.href = url;
2116
+ a.download = filename;
2117
+ a.click();
2118
+ URL.revokeObjectURL(url);
2119
+ }
2120
+
2121
+ export { downloadDxfFile, parseDxf, writeDxf, writeDxfFile };