tsdraft 0.1.0 → 0.2.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/CHANGELOG.md +111 -0
- package/README.md +184 -13
- package/dist/drawing.d.ts +49 -15
- package/dist/drawing.js +108 -29
- package/dist/dxf-r12.d.ts +11 -0
- package/dist/dxf-r12.js +115 -0
- package/dist/dxf-r2018.d.ts +10 -0
- package/dist/dxf-r2018.js +344 -0
- package/dist/dxf.d.ts +9 -7
- package/dist/dxf.js +14 -108
- package/dist/extents.d.ts +3 -0
- package/dist/extents.js +42 -0
- package/dist/format.d.ts +38 -0
- package/dist/format.js +75 -2
- package/dist/index.d.ts +4 -2
- package/dist/index.js +1 -0
- package/dist/layers.d.ts +55 -0
- package/dist/layers.js +178 -0
- package/dist/lint.d.ts +39 -0
- package/dist/lint.js +188 -0
- package/dist/rules.d.ts +116 -0
- package/dist/rules.js +164 -0
- package/dist/svg.js +10 -41
- package/package.json +6 -3
package/dist/dxf-r12.js
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { dxfText, fmt, writtenAngle } from "./format.js";
|
|
2
|
+
import { layerZeroFirst } from "./layers.js";
|
|
3
|
+
/**
|
|
4
|
+
* Writes an ASCII DXF (AC1009, R12). R12 is the most widely readable DXF:
|
|
5
|
+
* AutoCAD, BricsCAD, LibreCAD, QCAD and most converters open it.
|
|
6
|
+
*
|
|
7
|
+
* R12 has no header variable for drawing units, so coordinates are written as
|
|
8
|
+
* plain numbers in the drawing's units and `drawing.units` is not written.
|
|
9
|
+
* R12 predates Unicode DXF, so text outside ASCII is written as `\U+XXXX`.
|
|
10
|
+
* Layer `0` is written first, wherever it is in `drawing.layers`.
|
|
11
|
+
*/
|
|
12
|
+
export function toDXFR12(drawing) {
|
|
13
|
+
const out = [];
|
|
14
|
+
const g = (code, value) => {
|
|
15
|
+
out.push(String(code), typeof value === "number" ? fmt(value) : value);
|
|
16
|
+
};
|
|
17
|
+
g(0, "SECTION");
|
|
18
|
+
g(2, "HEADER");
|
|
19
|
+
g(9, "$ACADVER");
|
|
20
|
+
g(1, "AC1009");
|
|
21
|
+
g(0, "ENDSEC");
|
|
22
|
+
g(0, "SECTION");
|
|
23
|
+
g(2, "TABLES");
|
|
24
|
+
g(0, "TABLE");
|
|
25
|
+
g(2, "LTYPE");
|
|
26
|
+
g(70, "1");
|
|
27
|
+
g(0, "LTYPE");
|
|
28
|
+
g(2, "CONTINUOUS");
|
|
29
|
+
g(70, "0");
|
|
30
|
+
g(3, "Solid line");
|
|
31
|
+
g(72, "65");
|
|
32
|
+
g(73, "0");
|
|
33
|
+
g(40, "0.0");
|
|
34
|
+
g(0, "ENDTAB");
|
|
35
|
+
g(0, "TABLE");
|
|
36
|
+
g(2, "LAYER");
|
|
37
|
+
g(70, String(drawing.layers.length));
|
|
38
|
+
for (const layer of layerZeroFirst(drawing.layers)) {
|
|
39
|
+
g(0, "LAYER");
|
|
40
|
+
g(2, layer.name);
|
|
41
|
+
g(70, "0");
|
|
42
|
+
g(62, String(layer.color));
|
|
43
|
+
g(6, "CONTINUOUS");
|
|
44
|
+
}
|
|
45
|
+
g(0, "ENDTAB");
|
|
46
|
+
g(0, "ENDSEC");
|
|
47
|
+
g(0, "SECTION");
|
|
48
|
+
g(2, "ENTITIES");
|
|
49
|
+
for (const entity of drawing.entities)
|
|
50
|
+
writeEntity(entity, g);
|
|
51
|
+
g(0, "ENDSEC");
|
|
52
|
+
g(0, "EOF");
|
|
53
|
+
return out.join("\r\n") + "\r\n";
|
|
54
|
+
}
|
|
55
|
+
function writeEntity(e, g) {
|
|
56
|
+
switch (e.kind) {
|
|
57
|
+
case "line":
|
|
58
|
+
g(0, "LINE");
|
|
59
|
+
g(8, e.layer);
|
|
60
|
+
g(10, e.from[0]);
|
|
61
|
+
g(20, e.from[1]);
|
|
62
|
+
g(30, 0);
|
|
63
|
+
g(11, e.to[0]);
|
|
64
|
+
g(21, e.to[1]);
|
|
65
|
+
g(31, 0);
|
|
66
|
+
return;
|
|
67
|
+
case "polyline":
|
|
68
|
+
g(0, "POLYLINE");
|
|
69
|
+
g(8, e.layer);
|
|
70
|
+
g(66, "1");
|
|
71
|
+
g(10, 0);
|
|
72
|
+
g(20, 0);
|
|
73
|
+
g(30, 0);
|
|
74
|
+
g(70, e.closed ? "1" : "0");
|
|
75
|
+
for (const [x, y] of e.points) {
|
|
76
|
+
g(0, "VERTEX");
|
|
77
|
+
g(8, e.layer);
|
|
78
|
+
g(10, x);
|
|
79
|
+
g(20, y);
|
|
80
|
+
g(30, 0);
|
|
81
|
+
}
|
|
82
|
+
g(0, "SEQEND");
|
|
83
|
+
g(8, e.layer);
|
|
84
|
+
return;
|
|
85
|
+
case "circle":
|
|
86
|
+
g(0, "CIRCLE");
|
|
87
|
+
g(8, e.layer);
|
|
88
|
+
g(10, e.center[0]);
|
|
89
|
+
g(20, e.center[1]);
|
|
90
|
+
g(30, 0);
|
|
91
|
+
g(40, e.radius);
|
|
92
|
+
return;
|
|
93
|
+
case "arc":
|
|
94
|
+
g(0, "ARC");
|
|
95
|
+
g(8, e.layer);
|
|
96
|
+
g(10, e.center[0]);
|
|
97
|
+
g(20, e.center[1]);
|
|
98
|
+
g(30, 0);
|
|
99
|
+
g(40, e.radius);
|
|
100
|
+
g(50, writtenAngle(e.startAngle));
|
|
101
|
+
g(51, writtenAngle(e.endAngle));
|
|
102
|
+
return;
|
|
103
|
+
case "text":
|
|
104
|
+
g(0, "TEXT");
|
|
105
|
+
g(8, e.layer);
|
|
106
|
+
g(10, e.at[0]);
|
|
107
|
+
g(20, e.at[1]);
|
|
108
|
+
g(30, 0);
|
|
109
|
+
g(40, e.height);
|
|
110
|
+
g(1, dxfText(e.value, true));
|
|
111
|
+
if (e.rotation !== 0)
|
|
112
|
+
g(50, e.rotation);
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Drawing } from "./drawing.ts";
|
|
2
|
+
/**
|
|
3
|
+
* Writes an ASCII DXF for AutoCAD 2018 and later (AC1032), the newest DXF
|
|
4
|
+
* version Autodesk has published. AutoCAD 2027 still reads and writes AC1032.
|
|
5
|
+
*
|
|
6
|
+
* Unlike R12, this version has handles, owner links, an OBJECTS section and the
|
|
7
|
+
* `$INSUNITS` header, so the drawing's units reach CAD and xref and block
|
|
8
|
+
* insertion scale correctly. Text is UTF-8.
|
|
9
|
+
*/
|
|
10
|
+
export declare function toDXFR2018(drawing: Drawing): string;
|
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
import { extents } from "./extents.js";
|
|
2
|
+
import { dxfText, fmt, readable, writtenAngle } from "./format.js";
|
|
3
|
+
import { layerZeroFirst } from "./layers.js";
|
|
4
|
+
/** `$INSUNITS` codes from the DXF Reference. */
|
|
5
|
+
const INSUNITS = { in: 1, ft: 2, mm: 4, cm: 5, m: 6 };
|
|
6
|
+
/**
|
|
7
|
+
* Writes an ASCII DXF for AutoCAD 2018 and later (AC1032), the newest DXF
|
|
8
|
+
* version Autodesk has published. AutoCAD 2027 still reads and writes AC1032.
|
|
9
|
+
*
|
|
10
|
+
* Unlike R12, this version has handles, owner links, an OBJECTS section and the
|
|
11
|
+
* `$INSUNITS` header, so the drawing's units reach CAD and xref and block
|
|
12
|
+
* insertion scale correctly. Text is UTF-8.
|
|
13
|
+
*/
|
|
14
|
+
export function toDXFR2018(drawing) {
|
|
15
|
+
let seed = 1;
|
|
16
|
+
const handle = () => (seed++).toString(16).toUpperCase();
|
|
17
|
+
const body = [];
|
|
18
|
+
const g = groupWriter(body);
|
|
19
|
+
const rootDictionary = handle();
|
|
20
|
+
const groupDictionary = handle();
|
|
21
|
+
const plotStyleDictionary = handle();
|
|
22
|
+
const normalPlotStyle = handle();
|
|
23
|
+
const modelSpace = handle();
|
|
24
|
+
const paperSpace = handle();
|
|
25
|
+
// The extents are worked out here, so they are brought into the range AutoCAD reads.
|
|
26
|
+
const [left, bottom, right, top] = extents(drawing.entities);
|
|
27
|
+
const [minX, minY, maxX, maxY] = [
|
|
28
|
+
readable(left),
|
|
29
|
+
readable(bottom),
|
|
30
|
+
readable(right),
|
|
31
|
+
readable(top),
|
|
32
|
+
];
|
|
33
|
+
g(0, "SECTION");
|
|
34
|
+
g(2, "CLASSES");
|
|
35
|
+
g(0, "ENDSEC");
|
|
36
|
+
g(0, "SECTION");
|
|
37
|
+
g(2, "TABLES");
|
|
38
|
+
writeTable(g, handle, "VPORT", [
|
|
39
|
+
{
|
|
40
|
+
type: "VPORT",
|
|
41
|
+
subclass: "AcDbViewportTableRecord",
|
|
42
|
+
name: "*Active",
|
|
43
|
+
handle: handle(),
|
|
44
|
+
fields: (g) => writeActiveViewport(g, minX, minY, maxX, maxY),
|
|
45
|
+
},
|
|
46
|
+
]);
|
|
47
|
+
writeTable(g, handle, "LTYPE", [
|
|
48
|
+
["ByBlock", ""],
|
|
49
|
+
["ByLayer", ""],
|
|
50
|
+
["Continuous", "Solid line"],
|
|
51
|
+
].map(([name = "", description = ""]) => ({
|
|
52
|
+
type: "LTYPE",
|
|
53
|
+
subclass: "AcDbLinetypeTableRecord",
|
|
54
|
+
name,
|
|
55
|
+
handle: handle(),
|
|
56
|
+
fields: (g) => {
|
|
57
|
+
g(3, description);
|
|
58
|
+
g(72, 65);
|
|
59
|
+
g(73, 0);
|
|
60
|
+
g(40, 0);
|
|
61
|
+
},
|
|
62
|
+
})));
|
|
63
|
+
writeTable(g, handle, "LAYER",
|
|
64
|
+
// AUDIT reports "Layer zero name found at incorrect index" unless layer 0 comes first.
|
|
65
|
+
layerZeroFirst(drawing.layers).map((layer) => ({
|
|
66
|
+
type: "LAYER",
|
|
67
|
+
subclass: "AcDbLayerTableRecord",
|
|
68
|
+
name: layer.name,
|
|
69
|
+
handle: handle(),
|
|
70
|
+
fields: (g) => {
|
|
71
|
+
g(62, layer.color);
|
|
72
|
+
g(6, "Continuous");
|
|
73
|
+
g(370, -3); // lineweight: default
|
|
74
|
+
g(390, normalPlotStyle); // AutoCAD rejects a layer without a plot style
|
|
75
|
+
},
|
|
76
|
+
})));
|
|
77
|
+
writeTable(g, handle, "STYLE", [
|
|
78
|
+
{
|
|
79
|
+
type: "STYLE",
|
|
80
|
+
subclass: "AcDbTextStyleTableRecord",
|
|
81
|
+
name: "Standard",
|
|
82
|
+
handle: handle(),
|
|
83
|
+
fields: (g) => {
|
|
84
|
+
g(40, 0); // fixed height: none
|
|
85
|
+
g(41, 1); // width factor
|
|
86
|
+
g(50, 0); // oblique angle
|
|
87
|
+
g(71, 0); // generation flags
|
|
88
|
+
g(42, 0.2); // last height used
|
|
89
|
+
g(3, "txt");
|
|
90
|
+
g(4, "");
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
]);
|
|
94
|
+
writeTable(g, handle, "VIEW", []);
|
|
95
|
+
writeTable(g, handle, "UCS", []);
|
|
96
|
+
writeTable(g, handle, "APPID", [
|
|
97
|
+
{ type: "APPID", subclass: "AcDbRegAppTableRecord", name: "ACAD", handle: handle() },
|
|
98
|
+
]);
|
|
99
|
+
writeTable(g, handle, "DIMSTYLE", [
|
|
100
|
+
{ type: "DIMSTYLE", subclass: "AcDbDimStyleTableRecord", name: "Standard", handle: handle() },
|
|
101
|
+
]);
|
|
102
|
+
const blockRecordFields = (g) => {
|
|
103
|
+
g(280, 1); // explodable
|
|
104
|
+
g(281, 0); // scale uniformly: no
|
|
105
|
+
};
|
|
106
|
+
writeTable(g, handle, "BLOCK_RECORD", [
|
|
107
|
+
{
|
|
108
|
+
type: "BLOCK_RECORD",
|
|
109
|
+
subclass: "AcDbBlockTableRecord",
|
|
110
|
+
name: "*Model_Space",
|
|
111
|
+
handle: modelSpace,
|
|
112
|
+
fields: blockRecordFields,
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
type: "BLOCK_RECORD",
|
|
116
|
+
subclass: "AcDbBlockTableRecord",
|
|
117
|
+
name: "*Paper_Space",
|
|
118
|
+
handle: paperSpace,
|
|
119
|
+
fields: blockRecordFields,
|
|
120
|
+
},
|
|
121
|
+
]);
|
|
122
|
+
g(0, "ENDSEC");
|
|
123
|
+
g(0, "SECTION");
|
|
124
|
+
g(2, "BLOCKS");
|
|
125
|
+
writeBlock(g, handle, "*Model_Space", modelSpace, false);
|
|
126
|
+
writeBlock(g, handle, "*Paper_Space", paperSpace, true);
|
|
127
|
+
g(0, "ENDSEC");
|
|
128
|
+
g(0, "SECTION");
|
|
129
|
+
g(2, "ENTITIES");
|
|
130
|
+
for (const entity of drawing.entities)
|
|
131
|
+
writeEntity(g, entity, handle(), modelSpace);
|
|
132
|
+
g(0, "ENDSEC");
|
|
133
|
+
g(0, "SECTION");
|
|
134
|
+
g(2, "OBJECTS");
|
|
135
|
+
g(0, "DICTIONARY");
|
|
136
|
+
g(5, rootDictionary);
|
|
137
|
+
g(330, "0");
|
|
138
|
+
g(100, "AcDbDictionary");
|
|
139
|
+
g(281, 1); // duplicate records: keep existing
|
|
140
|
+
g(3, "ACAD_GROUP");
|
|
141
|
+
g(350, groupDictionary);
|
|
142
|
+
g(3, "ACAD_PLOTSTYLENAME");
|
|
143
|
+
g(350, plotStyleDictionary);
|
|
144
|
+
g(0, "DICTIONARY");
|
|
145
|
+
ownedBy(g, groupDictionary, rootDictionary);
|
|
146
|
+
g(100, "AcDbDictionary");
|
|
147
|
+
g(281, 1);
|
|
148
|
+
// Plot style names: a dictionary with a default, holding the "Normal" placeholder.
|
|
149
|
+
g(0, "ACDBDICTIONARYWDFLT");
|
|
150
|
+
ownedBy(g, plotStyleDictionary, rootDictionary);
|
|
151
|
+
g(100, "AcDbDictionary");
|
|
152
|
+
g(281, 1);
|
|
153
|
+
g(3, "Normal");
|
|
154
|
+
g(350, normalPlotStyle);
|
|
155
|
+
g(100, "AcDbDictionaryWithDefault");
|
|
156
|
+
g(340, normalPlotStyle);
|
|
157
|
+
g(0, "ACDBPLACEHOLDER");
|
|
158
|
+
ownedBy(g, normalPlotStyle, plotStyleDictionary);
|
|
159
|
+
g(0, "ENDSEC");
|
|
160
|
+
g(0, "EOF");
|
|
161
|
+
// The header goes first in the file but is written last, once every handle is assigned.
|
|
162
|
+
const header = [];
|
|
163
|
+
const h = groupWriter(header);
|
|
164
|
+
h(0, "SECTION");
|
|
165
|
+
h(2, "HEADER");
|
|
166
|
+
h(9, "$ACADVER");
|
|
167
|
+
h(1, "AC1032");
|
|
168
|
+
h(9, "$DWGCODEPAGE");
|
|
169
|
+
h(3, "ANSI_1252");
|
|
170
|
+
h(9, "$INSBASE");
|
|
171
|
+
point(h, 10, 0, 0);
|
|
172
|
+
h(9, "$EXTMIN");
|
|
173
|
+
point(h, 10, minX, minY);
|
|
174
|
+
h(9, "$EXTMAX");
|
|
175
|
+
point(h, 10, maxX, maxY);
|
|
176
|
+
h(9, "$INSUNITS");
|
|
177
|
+
h(70, INSUNITS[drawing.units]);
|
|
178
|
+
h(9, "$MEASUREMENT");
|
|
179
|
+
h(70, drawing.units === "in" || drawing.units === "ft" ? 0 : 1);
|
|
180
|
+
h(9, "$CLAYER");
|
|
181
|
+
h(8, "0");
|
|
182
|
+
h(9, "$HANDSEED");
|
|
183
|
+
h(5, handle());
|
|
184
|
+
h(0, "ENDSEC");
|
|
185
|
+
return [...header, ...body].join("\r\n") + "\r\n";
|
|
186
|
+
}
|
|
187
|
+
function groupWriter(out) {
|
|
188
|
+
return (code, value) => {
|
|
189
|
+
out.push(String(code), typeof value === "number" ? fmt(value) : value);
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
/** Handle, reactor and owner fields for an object owned by a dictionary. */
|
|
193
|
+
function ownedBy(g, handle, owner) {
|
|
194
|
+
g(5, handle);
|
|
195
|
+
g(102, "{ACAD_REACTORS");
|
|
196
|
+
g(330, owner);
|
|
197
|
+
g(102, "}");
|
|
198
|
+
g(330, owner);
|
|
199
|
+
}
|
|
200
|
+
function point(g, code, x, y) {
|
|
201
|
+
g(code, x);
|
|
202
|
+
g(code + 10, y);
|
|
203
|
+
g(code + 20, 0);
|
|
204
|
+
}
|
|
205
|
+
function writeTable(g, handle, name, records) {
|
|
206
|
+
const table = handle();
|
|
207
|
+
g(0, "TABLE");
|
|
208
|
+
g(2, name);
|
|
209
|
+
g(5, table);
|
|
210
|
+
g(330, "0");
|
|
211
|
+
g(100, "AcDbSymbolTable");
|
|
212
|
+
g(70, records.length);
|
|
213
|
+
if (name === "DIMSTYLE") {
|
|
214
|
+
g(100, "AcDbDimStyleTable");
|
|
215
|
+
g(71, records.length);
|
|
216
|
+
for (const r of records)
|
|
217
|
+
g(340, r.handle);
|
|
218
|
+
}
|
|
219
|
+
for (const r of records) {
|
|
220
|
+
g(0, r.type);
|
|
221
|
+
// DIMSTYLE records use 105 for their handle, because 5 is DIMBLK's code there.
|
|
222
|
+
g(r.type === "DIMSTYLE" ? 105 : 5, r.handle);
|
|
223
|
+
g(330, table);
|
|
224
|
+
g(100, "AcDbSymbolTableRecord");
|
|
225
|
+
g(100, r.subclass);
|
|
226
|
+
g(2, r.name);
|
|
227
|
+
g(70, 0);
|
|
228
|
+
r.fields?.(g);
|
|
229
|
+
}
|
|
230
|
+
g(0, "ENDTAB");
|
|
231
|
+
}
|
|
232
|
+
/** The *Active viewport, zoomed to the drawing's extents so the file opens on the drawing. */
|
|
233
|
+
function writeActiveViewport(g, minX, minY, maxX, maxY) {
|
|
234
|
+
const aspect = 1.6;
|
|
235
|
+
// A drawing can span more than AutoCAD can read as one number, so the height is capped.
|
|
236
|
+
const height = readable(Math.max(maxY - minY, (maxX - minX) / aspect, 1) * 1.1);
|
|
237
|
+
g(10, 0); // lower-left corner of the viewport on screen
|
|
238
|
+
g(20, 0);
|
|
239
|
+
g(11, 1); // upper-right corner
|
|
240
|
+
g(21, 1);
|
|
241
|
+
g(12, (minX + maxX) / 2); // view center
|
|
242
|
+
g(22, (minY + maxY) / 2);
|
|
243
|
+
g(13, 0); // snap base
|
|
244
|
+
g(23, 0);
|
|
245
|
+
g(14, 0.5); // snap spacing
|
|
246
|
+
g(24, 0.5);
|
|
247
|
+
g(15, 0.5); // grid spacing
|
|
248
|
+
g(25, 0.5);
|
|
249
|
+
g(16, 0); // view direction
|
|
250
|
+
g(26, 0);
|
|
251
|
+
g(36, 1);
|
|
252
|
+
g(17, 0); // view target
|
|
253
|
+
g(27, 0);
|
|
254
|
+
g(37, 0);
|
|
255
|
+
g(40, height);
|
|
256
|
+
g(41, aspect);
|
|
257
|
+
g(42, 50); // lens length
|
|
258
|
+
g(43, 0); // front clip
|
|
259
|
+
g(44, 0); // back clip
|
|
260
|
+
g(50, 0); // snap rotation
|
|
261
|
+
g(51, 0); // view twist
|
|
262
|
+
g(71, 0); // view mode
|
|
263
|
+
g(72, 1000); // circle zoom percent
|
|
264
|
+
g(73, 1); // fast zoom
|
|
265
|
+
g(74, 3); // UCS icon
|
|
266
|
+
g(75, 0); // snap off
|
|
267
|
+
g(76, 0); // grid off
|
|
268
|
+
g(77, 0); // snap style
|
|
269
|
+
g(78, 0); // snap isopair
|
|
270
|
+
}
|
|
271
|
+
function writeBlock(g, handle, name, owner, paper) {
|
|
272
|
+
g(0, "BLOCK");
|
|
273
|
+
g(5, handle());
|
|
274
|
+
g(330, owner);
|
|
275
|
+
g(100, "AcDbEntity");
|
|
276
|
+
if (paper)
|
|
277
|
+
g(67, 1);
|
|
278
|
+
g(8, "0");
|
|
279
|
+
g(100, "AcDbBlockBegin");
|
|
280
|
+
g(2, name);
|
|
281
|
+
g(70, 0);
|
|
282
|
+
point(g, 10, 0, 0);
|
|
283
|
+
g(3, name);
|
|
284
|
+
g(1, "");
|
|
285
|
+
g(0, "ENDBLK");
|
|
286
|
+
g(5, handle());
|
|
287
|
+
g(330, owner);
|
|
288
|
+
g(100, "AcDbEntity");
|
|
289
|
+
if (paper)
|
|
290
|
+
g(67, 1);
|
|
291
|
+
g(8, "0");
|
|
292
|
+
g(100, "AcDbBlockEnd");
|
|
293
|
+
}
|
|
294
|
+
function writeEntity(g, e, handle, owner) {
|
|
295
|
+
const begin = (type, subclass) => {
|
|
296
|
+
g(0, type);
|
|
297
|
+
g(5, handle);
|
|
298
|
+
g(330, owner);
|
|
299
|
+
g(100, "AcDbEntity");
|
|
300
|
+
g(8, e.layer);
|
|
301
|
+
g(100, subclass);
|
|
302
|
+
};
|
|
303
|
+
switch (e.kind) {
|
|
304
|
+
case "line":
|
|
305
|
+
begin("LINE", "AcDbLine");
|
|
306
|
+
point(g, 10, ...e.from);
|
|
307
|
+
point(g, 11, ...e.to);
|
|
308
|
+
return;
|
|
309
|
+
case "polyline":
|
|
310
|
+
begin("LWPOLYLINE", "AcDbPolyline");
|
|
311
|
+
g(90, e.points.length);
|
|
312
|
+
g(70, e.closed ? 1 : 0);
|
|
313
|
+
g(43, 0); // constant width
|
|
314
|
+
for (const [x, y] of e.points) {
|
|
315
|
+
g(10, x);
|
|
316
|
+
g(20, y);
|
|
317
|
+
}
|
|
318
|
+
return;
|
|
319
|
+
case "circle":
|
|
320
|
+
begin("CIRCLE", "AcDbCircle");
|
|
321
|
+
point(g, 10, ...e.center);
|
|
322
|
+
g(40, e.radius);
|
|
323
|
+
return;
|
|
324
|
+
case "arc":
|
|
325
|
+
begin("ARC", "AcDbCircle");
|
|
326
|
+
point(g, 10, ...e.center);
|
|
327
|
+
g(40, e.radius);
|
|
328
|
+
g(100, "AcDbArc");
|
|
329
|
+
g(50, writtenAngle(e.startAngle));
|
|
330
|
+
g(51, writtenAngle(e.endAngle));
|
|
331
|
+
return;
|
|
332
|
+
case "text":
|
|
333
|
+
begin("TEXT", "AcDbText");
|
|
334
|
+
point(g, 10, ...e.at);
|
|
335
|
+
g(40, e.height);
|
|
336
|
+
g(1, dxfText(e.value));
|
|
337
|
+
if (e.rotation !== 0)
|
|
338
|
+
g(50, e.rotation);
|
|
339
|
+
g(7, "Standard");
|
|
340
|
+
// TEXT repeats its subclass marker before the vertical alignment fields.
|
|
341
|
+
g(100, "AcDbText");
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
}
|
package/dist/dxf.d.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import type { Drawing } from "./drawing.ts";
|
|
1
|
+
import type { Drawing, DxfVersion } from "./drawing.ts";
|
|
2
|
+
export interface DxfOptions {
|
|
3
|
+
/** Defaults to the version the drawing was created for, which is `"R12"` unless set. */
|
|
4
|
+
readonly version?: DxfVersion;
|
|
5
|
+
}
|
|
2
6
|
/**
|
|
3
|
-
* Writes
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* R12 has no header variable for drawing units, so coordinates are written as
|
|
7
|
-
* plain numbers in the drawing's units and `drawing.units` is not written.
|
|
7
|
+
* Writes the drawing as ASCII DXF. Throws a `DrawingError` listing every lint
|
|
8
|
+
* error when the drawing breaks a rule of the version being written, because
|
|
9
|
+
* AutoCAD would reject or garble the file.
|
|
8
10
|
*/
|
|
9
|
-
export declare function toDXF(drawing: Drawing): string;
|
|
11
|
+
export declare function toDXF(drawing: Drawing, options?: DxfOptions): string;
|
package/dist/dxf.js
CHANGED
|
@@ -1,112 +1,18 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { toDXFR12 } from "./dxf-r12.js";
|
|
2
|
+
import { toDXFR2018 } from "./dxf-r2018.js";
|
|
3
|
+
import { DrawingError, lint } from "./lint.js";
|
|
2
4
|
/**
|
|
3
|
-
* Writes
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* R12 has no header variable for drawing units, so coordinates are written as
|
|
7
|
-
* plain numbers in the drawing's units and `drawing.units` is not written.
|
|
5
|
+
* Writes the drawing as ASCII DXF. Throws a `DrawingError` listing every lint
|
|
6
|
+
* error when the drawing breaks a rule of the version being written, because
|
|
7
|
+
* AutoCAD would reject or garble the file.
|
|
8
8
|
*/
|
|
9
|
-
export function toDXF(drawing) {
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
};
|
|
14
|
-
g(0, "SECTION");
|
|
15
|
-
g(2, "HEADER");
|
|
16
|
-
g(9, "$ACADVER");
|
|
17
|
-
g(1, "AC1009");
|
|
18
|
-
g(0, "ENDSEC");
|
|
19
|
-
g(0, "SECTION");
|
|
20
|
-
g(2, "TABLES");
|
|
21
|
-
g(0, "TABLE");
|
|
22
|
-
g(2, "LTYPE");
|
|
23
|
-
g(70, "1");
|
|
24
|
-
g(0, "LTYPE");
|
|
25
|
-
g(2, "CONTINUOUS");
|
|
26
|
-
g(70, "0");
|
|
27
|
-
g(3, "Solid line");
|
|
28
|
-
g(72, "65");
|
|
29
|
-
g(73, "0");
|
|
30
|
-
g(40, "0.0");
|
|
31
|
-
g(0, "ENDTAB");
|
|
32
|
-
g(0, "TABLE");
|
|
33
|
-
g(2, "LAYER");
|
|
34
|
-
g(70, String(drawing.layers.length));
|
|
35
|
-
for (const layer of drawing.layers) {
|
|
36
|
-
g(0, "LAYER");
|
|
37
|
-
g(2, layer.name);
|
|
38
|
-
g(70, "0");
|
|
39
|
-
g(62, String(layer.color));
|
|
40
|
-
g(6, "CONTINUOUS");
|
|
41
|
-
}
|
|
42
|
-
g(0, "ENDTAB");
|
|
43
|
-
g(0, "ENDSEC");
|
|
44
|
-
g(0, "SECTION");
|
|
45
|
-
g(2, "ENTITIES");
|
|
46
|
-
for (const entity of drawing.entities)
|
|
47
|
-
writeEntity(entity, g);
|
|
48
|
-
g(0, "ENDSEC");
|
|
49
|
-
g(0, "EOF");
|
|
50
|
-
return out.join("\r\n") + "\r\n";
|
|
51
|
-
}
|
|
52
|
-
function writeEntity(e, g) {
|
|
53
|
-
switch (e.kind) {
|
|
54
|
-
case "line":
|
|
55
|
-
g(0, "LINE");
|
|
56
|
-
g(8, e.layer);
|
|
57
|
-
g(10, e.from[0]);
|
|
58
|
-
g(20, e.from[1]);
|
|
59
|
-
g(30, 0);
|
|
60
|
-
g(11, e.to[0]);
|
|
61
|
-
g(21, e.to[1]);
|
|
62
|
-
g(31, 0);
|
|
63
|
-
return;
|
|
64
|
-
case "polyline":
|
|
65
|
-
g(0, "POLYLINE");
|
|
66
|
-
g(8, e.layer);
|
|
67
|
-
g(66, "1");
|
|
68
|
-
g(10, 0);
|
|
69
|
-
g(20, 0);
|
|
70
|
-
g(30, 0);
|
|
71
|
-
g(70, e.closed ? "1" : "0");
|
|
72
|
-
for (const [x, y] of e.points) {
|
|
73
|
-
g(0, "VERTEX");
|
|
74
|
-
g(8, e.layer);
|
|
75
|
-
g(10, x);
|
|
76
|
-
g(20, y);
|
|
77
|
-
g(30, 0);
|
|
78
|
-
}
|
|
79
|
-
g(0, "SEQEND");
|
|
80
|
-
g(8, e.layer);
|
|
81
|
-
return;
|
|
82
|
-
case "circle":
|
|
83
|
-
g(0, "CIRCLE");
|
|
84
|
-
g(8, e.layer);
|
|
85
|
-
g(10, e.center[0]);
|
|
86
|
-
g(20, e.center[1]);
|
|
87
|
-
g(30, 0);
|
|
88
|
-
g(40, e.radius);
|
|
89
|
-
return;
|
|
90
|
-
case "arc":
|
|
91
|
-
g(0, "ARC");
|
|
92
|
-
g(8, e.layer);
|
|
93
|
-
g(10, e.center[0]);
|
|
94
|
-
g(20, e.center[1]);
|
|
95
|
-
g(30, 0);
|
|
96
|
-
g(40, e.radius);
|
|
97
|
-
g(50, e.startAngle);
|
|
98
|
-
g(51, e.endAngle);
|
|
99
|
-
return;
|
|
100
|
-
case "text":
|
|
101
|
-
g(0, "TEXT");
|
|
102
|
-
g(8, e.layer);
|
|
103
|
-
g(10, e.at[0]);
|
|
104
|
-
g(20, e.at[1]);
|
|
105
|
-
g(30, 0);
|
|
106
|
-
g(40, e.height);
|
|
107
|
-
g(1, e.value);
|
|
108
|
-
if (e.rotation !== 0)
|
|
109
|
-
g(50, e.rotation);
|
|
110
|
-
return;
|
|
9
|
+
export function toDXF(drawing, options = {}) {
|
|
10
|
+
const version = options.version ?? drawing.dxf;
|
|
11
|
+
if (version !== "R12" && version !== "R2018") {
|
|
12
|
+
throw new Error(`Unsupported DXF version: ${JSON.stringify(version)}`);
|
|
111
13
|
}
|
|
14
|
+
const errors = lint(drawing, { version }).filter((d) => d.severity === "error");
|
|
15
|
+
if (errors.length > 0)
|
|
16
|
+
throw new DrawingError(errors);
|
|
17
|
+
return version === "R12" ? toDXFR12(drawing) : toDXFR2018(drawing);
|
|
112
18
|
}
|
package/dist/extents.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/** Approximate extents. Arcs use their full circle; text assumes 0.6 × height per character. */
|
|
2
|
+
export function extents(entities) {
|
|
3
|
+
let minX = Infinity;
|
|
4
|
+
let minY = Infinity;
|
|
5
|
+
let maxX = -Infinity;
|
|
6
|
+
let maxY = -Infinity;
|
|
7
|
+
const add = (x, y) => {
|
|
8
|
+
minX = Math.min(minX, x);
|
|
9
|
+
minY = Math.min(minY, y);
|
|
10
|
+
maxX = Math.max(maxX, x);
|
|
11
|
+
maxY = Math.max(maxY, y);
|
|
12
|
+
};
|
|
13
|
+
for (const e of entities) {
|
|
14
|
+
switch (e.kind) {
|
|
15
|
+
case "line":
|
|
16
|
+
add(...e.from);
|
|
17
|
+
add(...e.to);
|
|
18
|
+
break;
|
|
19
|
+
case "polyline":
|
|
20
|
+
for (const p of e.points)
|
|
21
|
+
add(...p);
|
|
22
|
+
break;
|
|
23
|
+
case "circle":
|
|
24
|
+
case "arc":
|
|
25
|
+
add(e.center[0] - e.radius, e.center[1] - e.radius);
|
|
26
|
+
add(e.center[0] + e.radius, e.center[1] + e.radius);
|
|
27
|
+
break;
|
|
28
|
+
case "text": {
|
|
29
|
+
const len = e.value.length * e.height * 0.6;
|
|
30
|
+
const r = rad(e.rotation);
|
|
31
|
+
add(...e.at);
|
|
32
|
+
add(e.at[0] + len * Math.cos(r), e.at[1] + len * Math.sin(r));
|
|
33
|
+
add(e.at[0] - e.height * Math.sin(r), e.at[1] + e.height * Math.cos(r));
|
|
34
|
+
break;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return minX === Infinity ? [0, 0, 1, 1] : [minX, minY, maxX, maxY];
|
|
39
|
+
}
|
|
40
|
+
function rad(degrees) {
|
|
41
|
+
return (degrees * Math.PI) / 180;
|
|
42
|
+
}
|