tsdraft 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +39 -16
- package/README.md +23 -5
- package/dist/callsite.d.ts +47 -0
- package/dist/callsite.js +82 -0
- package/dist/drawing.d.ts +37 -11
- package/dist/drawing.js +205 -101
- package/dist/index.d.ts +4 -1
- package/dist/index.js +2 -0
- package/dist/json.d.ts +21 -0
- package/dist/json.js +244 -0
- package/dist/layers.d.ts +1 -1
- package/dist/layers.js +2 -3
- package/dist/lint.d.ts +17 -5
- package/dist/lint.js +37 -7
- package/dist/paper.d.ts +33 -0
- package/dist/paper.js +58 -0
- package/dist/rules.d.ts +59 -3
- package/dist/rules.js +79 -3
- package/dist/svg.d.ts +2 -1
- package/dist/svg.js +5 -3
- package/package.json +1 -2
package/dist/drawing.js
CHANGED
|
@@ -1,16 +1,53 @@
|
|
|
1
|
+
import { holdCall, readCall, recordCalls } from "./callsite.js";
|
|
1
2
|
import { fmt, written } from "./format.js";
|
|
2
3
|
import { layerAuditKey, layerKey } from "./layers.js";
|
|
3
4
|
import { BUILT_IN_LINETYPES, builtInLinetype } from "./linetypes.js";
|
|
4
|
-
import {
|
|
5
|
+
import { DrawingError } from "./lint.js";
|
|
6
|
+
import { arcProblem, duplicateIdProblem, idKey, idProblem, layerColorProblem, layerFlagProblem, layerLinetypeProblem, layerStateProblem, lineweightProblem, linetypeNameProblem, linetypeProblem, linetypeScaleProblem, magnitudeProblem, layerNameProblem, layerTwinProblem, R12_ONLY, sizeProblem, textProblem, undefinedLinetypeProblem, } from "./rules.js";
|
|
5
7
|
const UNITS = ["in", "ft", "mm", "cm", "m"];
|
|
6
8
|
const DXF_VERSIONS = ["R12", "R2018"];
|
|
9
|
+
/** A broken rule, thrown inside a builder method and turned into a `DrawingError` on its way out. */
|
|
10
|
+
class Problem {
|
|
11
|
+
rule;
|
|
12
|
+
message;
|
|
13
|
+
constructor(rule, message) {
|
|
14
|
+
this.rule = rule;
|
|
15
|
+
this.message = message;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function refuse(rule, message) {
|
|
19
|
+
throw new Problem(rule, message);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* What a builder method throws for something thrown inside it: for a `Problem`, a `DrawingError`
|
|
23
|
+
* whose one diagnostic names the caller's call of `method` and what the call was `about`.
|
|
24
|
+
*/
|
|
25
|
+
function located(thrown, method, about) {
|
|
26
|
+
if (!(thrown instanceof Problem))
|
|
27
|
+
return thrown;
|
|
28
|
+
const call = readCall(holdCall(method));
|
|
29
|
+
const diagnostic = {
|
|
30
|
+
rule: thrown.rule,
|
|
31
|
+
severity: "error",
|
|
32
|
+
message: thrown.message,
|
|
33
|
+
...about,
|
|
34
|
+
...(call ? { call } : {}),
|
|
35
|
+
};
|
|
36
|
+
return new DrawingError([diagnostic], thrown.message);
|
|
37
|
+
}
|
|
38
|
+
/** Whether a tool that runs drawing files has asked every builder to record calls. */
|
|
39
|
+
function recordsCalls() {
|
|
40
|
+
return globalThis.TSDRAFT_RECORD_CALLS === true;
|
|
41
|
+
}
|
|
7
42
|
/**
|
|
8
43
|
* Builds a drawing. Entities go on the current layer, which starts as "0".
|
|
9
44
|
* `layer(name, opts, draw)` scopes everything drawn inside `draw` to that layer.
|
|
10
45
|
*
|
|
11
46
|
* Mistakes written as literals, such as a layer name the DXF version does not
|
|
12
47
|
* allow or a negative radius, are compile errors. The same checks run again
|
|
13
|
-
* when the code runs, for values the compiler cannot see
|
|
48
|
+
* when the code runs, for values the compiler cannot see: `layer`, `linetype`
|
|
49
|
+
* and the entity methods then throw a `DrawingError` whose diagnostic has the
|
|
50
|
+
* rule, the entity's `id` and the `call` that broke it.
|
|
14
51
|
*/
|
|
15
52
|
export class DrawingBuilder {
|
|
16
53
|
units;
|
|
@@ -25,6 +62,10 @@ export class DrawingBuilder {
|
|
|
25
62
|
/** Seeded with the built-in linetypes, whose twins AUDIT reports as it reports any other. */
|
|
26
63
|
#linetypesAudited = new Map(BUILT_IN_LINETYPES.map((name) => [layerAuditKey(name), name]));
|
|
27
64
|
#entities = [];
|
|
65
|
+
/** The first spelling of each entity id, by `idKey`. */
|
|
66
|
+
#ids = new Map();
|
|
67
|
+
/** The call that drew each entity, when calls are recorded. */
|
|
68
|
+
#calls;
|
|
28
69
|
#current = "0";
|
|
29
70
|
constructor(options = {}) {
|
|
30
71
|
const units = options.units ?? "in";
|
|
@@ -47,6 +88,7 @@ export class DrawingBuilder {
|
|
|
47
88
|
this.units = units;
|
|
48
89
|
this.dxf = dxf;
|
|
49
90
|
this.linetypeScale = scale ?? 1;
|
|
91
|
+
this.#calls = (options.recordCalls ?? recordsCalls()) ? [] : undefined;
|
|
50
92
|
}
|
|
51
93
|
/**
|
|
52
94
|
* Defines a linetype, or updates the one of the same name. `dashed` and
|
|
@@ -54,9 +96,17 @@ export class DrawingBuilder {
|
|
|
54
96
|
* tsdraft exports the linetypes plans use, such as `DASHED` and `HIDDEN`.
|
|
55
97
|
*/
|
|
56
98
|
linetype(linetype) {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
99
|
+
const definition = linetype;
|
|
100
|
+
try {
|
|
101
|
+
if (this.dxf === "R12")
|
|
102
|
+
refuse("linetype", R12_ONLY.linetypes);
|
|
103
|
+
this.#define(definition);
|
|
104
|
+
}
|
|
105
|
+
catch (thrown) {
|
|
106
|
+
const name = definition?.name;
|
|
107
|
+
const about = typeof name === "string" ? { linetype: name } : {};
|
|
108
|
+
throw located(thrown, DrawingBuilder.prototype.linetype, about);
|
|
109
|
+
}
|
|
60
110
|
return this;
|
|
61
111
|
}
|
|
62
112
|
/**
|
|
@@ -67,23 +117,41 @@ export class DrawingBuilder {
|
|
|
67
117
|
layer(name, options = {}, draw) {
|
|
68
118
|
// The parameter type is the error message when a literal name is wrong, so read it as a string.
|
|
69
119
|
const requested = name;
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
120
|
+
let layer;
|
|
121
|
+
try {
|
|
122
|
+
layer = this.#layer(requested, options);
|
|
123
|
+
}
|
|
124
|
+
catch (thrown) {
|
|
125
|
+
throw located(thrown, DrawingBuilder.prototype.layer, { layer: requested });
|
|
126
|
+
}
|
|
127
|
+
if (draw) {
|
|
128
|
+
const previous = this.#current;
|
|
129
|
+
this.#current = layer.name;
|
|
130
|
+
try {
|
|
131
|
+
draw();
|
|
132
|
+
}
|
|
133
|
+
finally {
|
|
134
|
+
this.#current = previous;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return this;
|
|
138
|
+
}
|
|
139
|
+
/** Checks a layer and stores it. */
|
|
140
|
+
#layer(requested, o) {
|
|
141
|
+
const fail = (rule, problem) => refuse(rule, `Layer ${JSON.stringify(requested)}: ${problem}`);
|
|
73
142
|
const nameProblem = layerNameProblem(requested, this.dxf);
|
|
74
143
|
if (nameProblem)
|
|
75
|
-
fail(nameProblem);
|
|
144
|
+
fail("layer-name", nameProblem);
|
|
76
145
|
const key = layerKey(requested);
|
|
77
146
|
const existing = this.#layers.get(key);
|
|
78
147
|
const auditKey = layerAuditKey(requested);
|
|
79
148
|
const twinProblem = layerTwinProblem(requested, this.#audited.get(auditKey) ?? requested);
|
|
80
149
|
if (twinProblem)
|
|
81
|
-
fail(twinProblem);
|
|
82
|
-
const color =
|
|
150
|
+
fail("duplicate-layer", twinProblem);
|
|
151
|
+
const color = o.color ?? existing?.color ?? 7;
|
|
83
152
|
const colorProblem = layerColorProblem(color);
|
|
84
153
|
if (colorProblem)
|
|
85
|
-
fail(colorProblem);
|
|
86
|
-
const o = options;
|
|
154
|
+
fail("layer-color", colorProblem);
|
|
87
155
|
const layer = { name: existing?.name ?? requested, color };
|
|
88
156
|
const set = (field, value, plain) => {
|
|
89
157
|
const kept = value === undefined ? existing?.[field] : value;
|
|
@@ -94,80 +162,81 @@ export class DrawingBuilder {
|
|
|
94
162
|
let linetype;
|
|
95
163
|
if (o.linetype !== undefined) {
|
|
96
164
|
if (r12 && !isBuiltIn(o.linetype, "Continuous"))
|
|
97
|
-
fail(R12_ONLY.linetypes);
|
|
165
|
+
fail("linetype", R12_ONLY.linetypes);
|
|
98
166
|
linetype = this.#linetypeName(o.linetype, fail);
|
|
99
167
|
const problem = layerLinetypeProblem(linetype);
|
|
100
168
|
if (problem)
|
|
101
|
-
fail(problem);
|
|
169
|
+
fail("linetype", problem);
|
|
102
170
|
}
|
|
103
171
|
set("linetype", linetype === undefined ? undefined : (builtInLinetype(linetype) ?? linetype), "Continuous");
|
|
104
172
|
if (o.lineweight !== undefined) {
|
|
105
173
|
const problem = lineweightProblem(o.lineweight, "layer");
|
|
106
174
|
if (problem)
|
|
107
|
-
fail(problem);
|
|
175
|
+
fail("lineweight", problem);
|
|
108
176
|
if (r12 && o.lineweight !== "Default")
|
|
109
|
-
fail(R12_ONLY.lineweights);
|
|
177
|
+
fail("lineweight", R12_ONLY.lineweights);
|
|
110
178
|
}
|
|
111
179
|
set("lineweight", o.lineweight, "Default");
|
|
112
180
|
for (const state of ["off", "frozen", "locked", "plot"]) {
|
|
113
181
|
const value = o[state];
|
|
114
182
|
const flagProblem = layerFlagProblem(state, value);
|
|
115
183
|
if (flagProblem)
|
|
116
|
-
fail(flagProblem);
|
|
117
|
-
if (r12 && value !== undefined && value !== (state === "plot"))
|
|
118
|
-
fail(R12_ONLY.layerStates);
|
|
184
|
+
fail("layer-state", flagProblem);
|
|
185
|
+
if (r12 && value !== undefined && value !== (state === "plot")) {
|
|
186
|
+
fail("layer-state", R12_ONLY.layerStates);
|
|
187
|
+
}
|
|
119
188
|
set(state, value, state === "plot");
|
|
120
189
|
}
|
|
121
190
|
const stateProblem = layerStateProblem(layer.name, layer.frozen);
|
|
122
191
|
if (stateProblem)
|
|
123
|
-
fail(stateProblem);
|
|
192
|
+
fail("layer-state", stateProblem);
|
|
124
193
|
this.#layers.set(key, layer);
|
|
125
194
|
if (!this.#audited.has(auditKey))
|
|
126
195
|
this.#audited.set(auditKey, layer.name);
|
|
127
|
-
|
|
128
|
-
const previous = this.#current;
|
|
129
|
-
this.#current = layer.name;
|
|
130
|
-
try {
|
|
131
|
-
draw();
|
|
132
|
-
}
|
|
133
|
-
finally {
|
|
134
|
-
this.#current = previous;
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
return this;
|
|
196
|
+
return layer;
|
|
138
197
|
}
|
|
139
198
|
line(from, to, options = {}) {
|
|
140
|
-
return this.#add(
|
|
199
|
+
return this.#add(DrawingBuilder.prototype.line, options, () => ({
|
|
200
|
+
kind: "line",
|
|
201
|
+
layer: this.#current,
|
|
202
|
+
from: pt(from),
|
|
203
|
+
to: pt(to),
|
|
204
|
+
}));
|
|
141
205
|
}
|
|
142
206
|
polyline(points, closed = false, options = {}) {
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
207
|
+
return this.#add(DrawingBuilder.prototype.polyline, options, () => {
|
|
208
|
+
if (points.length < 2)
|
|
209
|
+
refuse("geometry", "A polyline needs at least 2 points");
|
|
210
|
+
return { kind: "polyline", layer: this.#current, points: points.map(pt), closed };
|
|
211
|
+
});
|
|
146
212
|
}
|
|
147
213
|
/** Closed rectangle from its lower-left corner. */
|
|
148
214
|
rect(corner, width, height, options = {}) {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
[
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
215
|
+
return this.#add(DrawingBuilder.prototype.rect, options, () => {
|
|
216
|
+
const [x, y] = pt(corner);
|
|
217
|
+
const w = positive(width, "width");
|
|
218
|
+
const h = positive(height, "height");
|
|
219
|
+
// A side can still vanish in the file: a corner and the one across from it may round together.
|
|
220
|
+
if (written(x + w) === written(x))
|
|
221
|
+
refuse("geometry", lostSide("width", "x", x));
|
|
222
|
+
if (written(y + h) === written(y))
|
|
223
|
+
refuse("geometry", lostSide("height", "y", y));
|
|
224
|
+
const corners = [
|
|
225
|
+
[x, y],
|
|
226
|
+
[x + w, y],
|
|
227
|
+
[x + w, y + h],
|
|
228
|
+
[x, y + h],
|
|
229
|
+
];
|
|
230
|
+
return { kind: "polyline", layer: this.#current, points: corners.map(pt), closed: true };
|
|
231
|
+
});
|
|
163
232
|
}
|
|
164
233
|
circle(center, radius, options = {}) {
|
|
165
|
-
return this.#add({
|
|
234
|
+
return this.#add(DrawingBuilder.prototype.circle, options, () => ({
|
|
166
235
|
kind: "circle",
|
|
167
236
|
layer: this.#current,
|
|
168
237
|
center: pt(center),
|
|
169
238
|
radius: positive(radius, "radius"),
|
|
170
|
-
}
|
|
239
|
+
}));
|
|
171
240
|
}
|
|
172
241
|
/**
|
|
173
242
|
* Arc from `startAngle` counterclockwise to `endAngle`. Angles are stored
|
|
@@ -179,61 +248,93 @@ export class DrawingBuilder {
|
|
|
179
248
|
* arc of no length, so it throws: draw a circle instead.
|
|
180
249
|
*/
|
|
181
250
|
arc(center, radius, startAngle, endAngle, options = {}) {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
251
|
+
return this.#add(DrawingBuilder.prototype.arc, options, () => {
|
|
252
|
+
const start = turn(num(startAngle, "startAngle"));
|
|
253
|
+
const end = turn(num(endAngle, "endAngle"));
|
|
254
|
+
if (start === end)
|
|
255
|
+
refuse("geometry", "An arc's start and end angles must differ");
|
|
256
|
+
const problem = arcProblem(start, end);
|
|
257
|
+
if (problem)
|
|
258
|
+
refuse("geometry", problem);
|
|
259
|
+
return {
|
|
260
|
+
kind: "arc",
|
|
261
|
+
layer: this.#current,
|
|
262
|
+
center: pt(center),
|
|
263
|
+
radius: positive(radius, "radius"),
|
|
264
|
+
startAngle: start,
|
|
265
|
+
endAngle: end,
|
|
266
|
+
};
|
|
267
|
+
});
|
|
197
268
|
}
|
|
198
269
|
text(at, value, options = {}) {
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
270
|
+
return this.#add(DrawingBuilder.prototype.text, options, () => {
|
|
271
|
+
const problem = textProblem(value);
|
|
272
|
+
if (problem)
|
|
273
|
+
refuse("text", problem);
|
|
274
|
+
return {
|
|
275
|
+
kind: "text",
|
|
276
|
+
layer: this.#current,
|
|
277
|
+
at: pt(at),
|
|
278
|
+
value,
|
|
279
|
+
height: positive(options.height ?? 0.125, "height"),
|
|
280
|
+
rotation: kept(options.rotation ?? 0, "rotation"),
|
|
281
|
+
};
|
|
282
|
+
});
|
|
210
283
|
}
|
|
211
284
|
build() {
|
|
285
|
+
const entities = [...this.#entities];
|
|
286
|
+
if (this.#calls)
|
|
287
|
+
recordCalls(entities, [...this.#calls]);
|
|
212
288
|
return {
|
|
289
|
+
format: 0,
|
|
213
290
|
units: this.units,
|
|
214
291
|
dxf: this.dxf,
|
|
215
292
|
layers: [...this.#layers.values()],
|
|
216
|
-
entities
|
|
293
|
+
entities,
|
|
217
294
|
linetypes: [...this.#linetypes.values()],
|
|
218
295
|
linetypeScale: this.linetypeScale,
|
|
219
296
|
};
|
|
220
297
|
}
|
|
221
|
-
/**
|
|
222
|
-
|
|
223
|
-
|
|
298
|
+
/**
|
|
299
|
+
* Checks the entity that `make` gives and the `EntityOptions` it was drawn with, whose type
|
|
300
|
+
* parameters differ from method to method, and stores it. `method` is the builder method the
|
|
301
|
+
* caller called, which a diagnostic names the call of.
|
|
302
|
+
*/
|
|
303
|
+
#add(method, options, make) {
|
|
304
|
+
const o = options;
|
|
305
|
+
const id = o.id;
|
|
306
|
+
try {
|
|
307
|
+
const key = id === undefined ? undefined : this.#idKey(id);
|
|
308
|
+
const named = id === undefined ? {} : { id: id };
|
|
309
|
+
this.#entities.push({ ...make(), ...named, ...this.#style(o) });
|
|
310
|
+
if (key !== undefined)
|
|
311
|
+
this.#ids.set(key, id);
|
|
312
|
+
}
|
|
313
|
+
catch (thrown) {
|
|
314
|
+
throw located(thrown, method, typeof id === "string" ? { id } : {});
|
|
315
|
+
}
|
|
316
|
+
this.#calls?.push(holdCall(method));
|
|
224
317
|
return this;
|
|
225
318
|
}
|
|
319
|
+
/** Checks an id the drawing does not have yet, and returns the key it is kept by. */
|
|
320
|
+
#idKey(id) {
|
|
321
|
+
const problem = idProblem(id);
|
|
322
|
+
if (problem)
|
|
323
|
+
refuse("id", problem);
|
|
324
|
+
const key = idKey(id);
|
|
325
|
+
const first = this.#ids.get(key);
|
|
326
|
+
if (first !== undefined)
|
|
327
|
+
refuse("duplicate-id", duplicateIdProblem(id, first));
|
|
328
|
+
return key;
|
|
329
|
+
}
|
|
226
330
|
/** The linetype and lineweight an entity is stored with. */
|
|
227
331
|
#style(options) {
|
|
228
|
-
const fail = (problem) => {
|
|
229
|
-
throw new Error(problem);
|
|
230
|
-
};
|
|
231
332
|
const style = {};
|
|
232
333
|
if (options.linetype !== undefined) {
|
|
233
334
|
if (this.dxf === "R12" && !isBuiltIn(options.linetype, "ByLayer")) {
|
|
234
|
-
|
|
335
|
+
refuse("linetype", R12_ONLY.entityStyle);
|
|
235
336
|
}
|
|
236
|
-
const name = this.#linetypeName(options.linetype,
|
|
337
|
+
const name = this.#linetypeName(options.linetype, refuse);
|
|
237
338
|
const builtIn = builtInLinetype(name);
|
|
238
339
|
if (builtIn !== "ByLayer")
|
|
239
340
|
style.linetype = builtIn ?? name;
|
|
@@ -242,9 +343,9 @@ export class DrawingBuilder {
|
|
|
242
343
|
if (weight !== undefined) {
|
|
243
344
|
const problem = lineweightProblem(weight, "entity");
|
|
244
345
|
if (problem)
|
|
245
|
-
|
|
346
|
+
refuse("lineweight", problem);
|
|
246
347
|
if (this.dxf === "R12" && weight !== "ByLayer")
|
|
247
|
-
|
|
348
|
+
refuse("lineweight", R12_ONLY.entityStyle);
|
|
248
349
|
if (weight !== "ByLayer")
|
|
249
350
|
style.lineweight = weight;
|
|
250
351
|
}
|
|
@@ -258,14 +359,17 @@ export class DrawingBuilder {
|
|
|
258
359
|
if (typeof choice === "object" && choice !== null) {
|
|
259
360
|
return this.#define(choice, true);
|
|
260
361
|
}
|
|
261
|
-
if (typeof choice !== "string")
|
|
262
|
-
return fail("A linetype is a name or a LinetypeDefinition");
|
|
362
|
+
if (typeof choice !== "string") {
|
|
363
|
+
return fail("undefined-linetype", "A linetype is a name or a LinetypeDefinition");
|
|
364
|
+
}
|
|
263
365
|
const problem = linetypeNameProblem(choice);
|
|
264
366
|
if (problem)
|
|
265
|
-
fail(`Linetype ${JSON.stringify(choice)}: ${problem}`);
|
|
367
|
+
fail("undefined-linetype", `Linetype ${JSON.stringify(choice)}: ${problem}`);
|
|
266
368
|
const known = builtInLinetype(choice) ?? this.#linetypes.get(layerKey(choice))?.name;
|
|
267
|
-
if (known === undefined)
|
|
268
|
-
|
|
369
|
+
if (known === undefined) {
|
|
370
|
+
const absent = `${undefinedLinetypeProblem(choice)}. Define it with linetype() first`;
|
|
371
|
+
fail("undefined-linetype", absent);
|
|
372
|
+
}
|
|
269
373
|
return known;
|
|
270
374
|
}
|
|
271
375
|
/**
|
|
@@ -277,7 +381,7 @@ export class DrawingBuilder {
|
|
|
277
381
|
const label = `Linetype ${JSON.stringify(definition.name)}`;
|
|
278
382
|
const problem = linetypeProblem(definition.name, definition.description ?? "", definition.pattern);
|
|
279
383
|
if (problem)
|
|
280
|
-
|
|
384
|
+
refuse("linetype", `${label}: ${problem}`);
|
|
281
385
|
// linetypeProblem has checked the types.
|
|
282
386
|
const name = definition.name;
|
|
283
387
|
const description = (definition.description ?? "");
|
|
@@ -286,14 +390,14 @@ export class DrawingBuilder {
|
|
|
286
390
|
const auditKey = layerAuditKey(name);
|
|
287
391
|
const twin = layerTwinProblem(name, this.#linetypesAudited.get(auditKey) ?? name, "linetype");
|
|
288
392
|
if (twin)
|
|
289
|
-
|
|
393
|
+
refuse("duplicate-linetype", `${label}: ${twin}`);
|
|
290
394
|
const existing = this.#linetypes.get(key);
|
|
291
395
|
if (inline && existing) {
|
|
292
396
|
const same = existing.description === description &&
|
|
293
397
|
existing.pattern.length === lengths.length &&
|
|
294
398
|
existing.pattern.every((length, i) => length === lengths[i]);
|
|
295
399
|
if (!same) {
|
|
296
|
-
|
|
400
|
+
refuse("linetype", `${label}: the drawing has a linetype of this name with another pattern or description. Pass its name, or change it with linetype()`);
|
|
297
401
|
}
|
|
298
402
|
return existing.name;
|
|
299
403
|
}
|
|
@@ -327,17 +431,17 @@ function turn(degrees) {
|
|
|
327
431
|
}
|
|
328
432
|
function num(value, label) {
|
|
329
433
|
if (!Number.isFinite(value))
|
|
330
|
-
|
|
434
|
+
refuse("geometry", `${label} must be a finite number`);
|
|
331
435
|
return value;
|
|
332
436
|
}
|
|
333
437
|
/** Takes `unknown` because a `Positive<N>` parameter is a message string when the literal is wrong. */
|
|
334
438
|
function positive(value, label) {
|
|
335
439
|
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
336
|
-
|
|
440
|
+
return refuse("geometry", `${label} must be a finite number`);
|
|
337
441
|
}
|
|
338
442
|
const problem = sizeProblem(value);
|
|
339
443
|
if (problem)
|
|
340
|
-
|
|
444
|
+
refuse("geometry", `${label}: ${problem}`);
|
|
341
445
|
return value;
|
|
342
446
|
}
|
|
343
447
|
function lostSide(size, axis, at) {
|
|
@@ -347,7 +451,7 @@ function lostSide(size, axis, at) {
|
|
|
347
451
|
function kept(value, label) {
|
|
348
452
|
const problem = magnitudeProblem(num(value, label));
|
|
349
453
|
if (problem)
|
|
350
|
-
|
|
454
|
+
refuse("geometry", `${label}: ${problem}`);
|
|
351
455
|
return value;
|
|
352
456
|
}
|
|
353
457
|
function pt(point) {
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
export type { CallSite } from "./callsite.ts";
|
|
1
2
|
export { createDrawing, DrawingBuilder, sweep, type Arc, type Circle, type Drawing, type DrawingOptions, type DxfVersion, type Entity, type EntityOptions, type Layer, type LayerOptions, type Line, type Linetype, type LinetypeChoice, type LinetypeDefinition, type Lineweight, type Point, type Polyline, type Text, type TextOptions, type Units, } from "./drawing.ts";
|
|
2
3
|
export { toDXF, type DxfOptions } from "./dxf.ts";
|
|
4
|
+
export { fromJSON, toJSON } from "./json.ts";
|
|
3
5
|
export { DrawingError, lint, type Diagnostic, type LintOptions, type LintRule } from "./lint.ts";
|
|
4
6
|
export { CENTER, DASHDOT, DASHED, DOT, HIDDEN, LINEWEIGHTS, PHANTOM } from "./linetypes.ts";
|
|
5
|
-
export
|
|
7
|
+
export { fromPaper, type PaperScale, type PaperUnit } from "./paper.ts";
|
|
8
|
+
export type { Aci, Description, EntityId, LayerColor, LayerLinetypeName, LayerName, LineweightOf, LinetypeName, NewLinetypeName, PaperLength, Pattern, Positive, Ratio, } from "./rules.ts";
|
|
6
9
|
export { toSVG, type SvgOptions } from "./svg.ts";
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export { createDrawing, DrawingBuilder, sweep, } from "./drawing.js";
|
|
2
2
|
export { toDXF } from "./dxf.js";
|
|
3
|
+
export { fromJSON, toJSON } from "./json.js";
|
|
3
4
|
export { DrawingError, lint } from "./lint.js";
|
|
4
5
|
export { CENTER, DASHDOT, DASHED, DOT, HIDDEN, LINEWEIGHTS, PHANTOM } from "./linetypes.js";
|
|
6
|
+
export { fromPaper } from "./paper.js";
|
|
5
7
|
export { toSVG } from "./svg.js";
|
package/dist/json.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Drawing } from "./drawing.ts";
|
|
2
|
+
/**
|
|
3
|
+
* Writes the drawing as canonical JSON: no white space, the keys of every object in code-unit
|
|
4
|
+
* order, and each number in the shortest form that reads back as the same number, with `-0`
|
|
5
|
+
* for negative zero. Two drawings that are equal give the same string, whatever order their
|
|
6
|
+
* keys were set in. A field that is `undefined` is left out.
|
|
7
|
+
*
|
|
8
|
+
* The drawing is written as it is and is not checked: `fromJSON` checks it when it is read.
|
|
9
|
+
* Throws when the drawing holds a value JSON cannot, such as `NaN`.
|
|
10
|
+
*/
|
|
11
|
+
export declare function toJSON(drawing: Drawing): string;
|
|
12
|
+
/**
|
|
13
|
+
* Reads a drawing from JSON, as a string or as the value `JSON.parse` gives, which is returned
|
|
14
|
+
* as the drawing when it passes.
|
|
15
|
+
*
|
|
16
|
+
* It refuses a format version it does not know, a field or an entity kind it does not know, a
|
|
17
|
+
* missing field and a value of the wrong type, and then every error `lint` finds, so a drawing
|
|
18
|
+
* that was read has passed each check the builder makes. It throws a `DrawingError` whose
|
|
19
|
+
* diagnostics name each problem, with the rule `"format"` for the ones `lint` does not look for.
|
|
20
|
+
*/
|
|
21
|
+
export declare function fromJSON(json: unknown): Drawing;
|