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/dist/rules.js CHANGED
@@ -23,6 +23,8 @@ const COLOR = "A layer color must be an AutoCAD Color Index integer from 1 to 25
23
23
  const POSITIVE = "This value must be greater than 0";
24
24
  const WRITTEN_AS_ZERO = "This value is written as 0, because tsdraft writes 6 decimals. Use 0.000001 or more";
25
25
  const TOO_LARGE = "This value must be under 1e100, because AutoCAD reads only two exponent digits from DXF and would take 1e+100 for 1e+10";
26
+ const PAPER_LENGTH = "The paper length must be greater than 0";
27
+ const RATIO = "The ratio must be greater than 0";
26
28
  const ARC_LOST = "An arc of nearly a full turn cannot be written: its angles are the same once rounded to 6 decimals, so AutoCAD would hold an arc of no length. Draw a circle instead";
27
29
  const TEXT_CONTROL = "Text must be a single line with no control characters";
28
30
  const TEXT_PRIVATE_USE = "Text cannot contain the private-use characters U+E800 to U+E8FF, because AutoCAD replaces them when it reads DXF";
@@ -41,6 +43,9 @@ const PATTERN_WRITTEN_AS_ZERO = "A dash or gap of this length is written as 0, b
41
43
  const PATTERN_TOTAL = "A linetype pattern's lengths, added up without their signs, must be under 1e100. The total is written too, and AutoCAD reads only two exponent digits from DXF";
42
44
  const PATTERN_TYPE = "A linetype's pattern must be an array of finite numbers";
43
45
  const DESCRIPTION_LENGTH = "A linetype description cannot be longer than 249 characters, because AutoCAD cuts it there when it reads DXF";
46
+ const ID_EMPTY = "An id cannot be empty";
47
+ const ID_CHARS = "An id can only use the letters A to Z and a to z, digits, ., - and _";
48
+ const ID_LENGTH = "An id cannot be longer than 64 characters";
44
49
  const TEXT_PERCENT = 'AutoCAD reads "%%" in text as the start of a control code: it draws "%%d" as a degree sign and "%%%" as one percent sign. The text is stored as written';
45
50
  /** Returns what is wrong with a layer name for a DXF version, or undefined when it is valid. */
46
51
  export function layerNameProblem(name, version) {
@@ -84,9 +89,9 @@ function nameProblem(name, messages) {
84
89
  * layers to AutoCAD and one duplicate name to AUDIT: `ΟΔΟΣ` and `οδος`,
85
90
  * `straße` and `strasse`, a composed é and a decomposed one, and a name with
86
91
  * and without a soft hyphen. `layerAuditKey` lists the kinds this covers,
87
- * which are those measured in AutoCAD and not all that AUDIT reports. Names
88
- * that are one layer to AutoCAD are not this function's concern. The rule
89
- * compares two names, so it has no type.
92
+ * which are not all that AUDIT reports. Names that are one layer to AutoCAD
93
+ * are not this function's concern. The rule compares two names, so it has no
94
+ * type.
90
95
  *
91
96
  * @param name - The name of the layer being checked.
92
97
  * @param other - The name of another layer in the drawing.
@@ -217,6 +222,29 @@ export function sizeProblem(value) {
217
222
  return POSITIVE;
218
223
  return written(value) === 0 ? WRITTEN_AS_ZERO : magnitudeProblem(value);
219
224
  }
225
+ /**
226
+ * Returns what is wrong with a paper length, the ratio it plots at, or the
227
+ * length in drawing units they give, or undefined when the result can be
228
+ * written. The result is a size, so it is held to the limits of `sizeProblem`.
229
+ *
230
+ * @param length - The length on paper, which must be a finite number.
231
+ * @param ratio - The scale ratio, which must be a finite number.
232
+ * @param result - The length in drawing units.
233
+ * @returns The problem, or undefined.
234
+ */
235
+ export function paperLengthProblem(length, ratio, result) {
236
+ if (!(length > 0))
237
+ return PAPER_LENGTH;
238
+ if (!(ratio > 0))
239
+ return RATIO;
240
+ if (written(result) === 0) {
241
+ return `The result, ${result} drawing units, is written as 0, because tsdraft writes 6 decimals. Use a longer paper length or a larger ratio`;
242
+ }
243
+ if (isTooLarge(result)) {
244
+ return "The result is 1e100 drawing units or more, and AutoCAD reads only two exponent digits from DXF. Use a shorter paper length or a smaller ratio";
245
+ }
246
+ return undefined;
247
+ }
220
248
  /**
221
249
  * Returns what is wrong with the size of a number a drawing holds, or
222
250
  * undefined when AutoCAD can read it. This goes for every number that is
@@ -258,6 +286,54 @@ export function arcProblem(startAngle, endAngle) {
258
286
  export function arcIsWrittenAsPoint(startAngle, endAngle) {
259
287
  return roundsToOneAngle(startAngle, endAngle) && isShortArc(startAngle, endAngle);
260
288
  }
289
+ /**
290
+ * Returns what is wrong with an entity id, or undefined when it is valid. It takes `unknown` so
291
+ * that `lint` can report a drawing built by hand with an id of the wrong type.
292
+ */
293
+ export function idProblem(id) {
294
+ if (typeof id !== "string")
295
+ return "An id must be a string";
296
+ if (id.length === 0)
297
+ return ID_EMPTY;
298
+ if (!/^[A-Za-z0-9._-]+$/.test(id))
299
+ return ID_CHARS;
300
+ return id.length > 64 ? ID_LENGTH : undefined;
301
+ }
302
+ /** The form two ids are compared in: `Door-1` and `door-1` are one id. */
303
+ export function idKey(id) {
304
+ return id.toLowerCase();
305
+ }
306
+ /**
307
+ * Returns what is wrong with a second entity that has an id the drawing already has. The rule
308
+ * depends on the drawing, so it has no type.
309
+ *
310
+ * @param id - The id of the second entity.
311
+ * @param first - The id as the first entity spells it.
312
+ */
313
+ export function duplicateIdProblem(id, first) {
314
+ const spelled = first === id ? "" : `, as ${JSON.stringify(first)}`;
315
+ return `The id ${JSON.stringify(id)} is already used${spelled}. An id names one entity, and ids are compared without regard to case`;
316
+ }
317
+ /** Returns what is wrong with a drawing's format version, or undefined when tsdraft reads it. */
318
+ export function formatProblem(format) {
319
+ // Negative zero is refused: it would be written as -0, and so change the drawing's JSON.
320
+ if (Object.is(format, 0))
321
+ return undefined;
322
+ return format === undefined
323
+ ? "The drawing has no format version. A Drawing has format: 0"
324
+ : `Unknown Drawing format ${shown(format)}. This version of tsdraft reads format 0`;
325
+ }
326
+ /** A value of any type for a message: a string quoted, a number as it is, anything else by kind. */
327
+ export function shown(value) {
328
+ if (typeof value === "string")
329
+ return JSON.stringify(value);
330
+ if (Object.is(value, -0))
331
+ return "-0";
332
+ if (typeof value === "number" || typeof value === "boolean" || value == null) {
333
+ return String(value);
334
+ }
335
+ return Array.isArray(value) ? "an array" : `a value of type ${typeof value}`;
336
+ }
261
337
  /** Returns what is wrong with a single-line text value, or undefined when it is valid. */
262
338
  export function textProblem(value) {
263
339
  if (hasControl(value))
package/dist/svg.d.ts CHANGED
@@ -12,6 +12,7 @@ export interface SvgOptions {
12
12
  * Renders a preview SVG. Model Y points up, so the drawing group flips Y. Entities on layers
13
13
  * that are off or frozen are left out. Dashed lines are cut into their dashes, scaled by the
14
14
  * drawing's linetype scale, and each line is as wide as its lineweight. Each entity's element
15
- * has its layer in `data-layer` and its index in `drawing.entities` in `data-entity`.
15
+ * has its layer in `data-layer`, its index in `drawing.entities` in `data-entity`, and its `id`,
16
+ * when it has one, in `data-id`.
16
17
  */
17
18
  export declare function toSVG(drawing: Drawing, options?: SvgOptions): string;
package/dist/svg.js CHANGED
@@ -24,7 +24,8 @@ const MAX_DASHES = 10000;
24
24
  * Renders a preview SVG. Model Y points up, so the drawing group flips Y. Entities on layers
25
25
  * that are off or frozen are left out. Dashed lines are cut into their dashes, scaled by the
26
26
  * drawing's linetype scale, and each line is as wide as its lineweight. Each entity's element
27
- * has its layer in `data-layer` and its index in `drawing.entities` in `data-entity`.
27
+ * has its layer in `data-layer`, its index in `drawing.entities` in `data-entity`, and its `id`,
28
+ * when it has one, in `data-id`.
28
29
  */
29
30
  export function toSVG(drawing, options = {}) {
30
31
  // Keyed as AutoCAD looks layers up, so an entity on `wall` takes the color of layer `WALL`.
@@ -73,7 +74,8 @@ function entityToSvg(e, index, pen, stroke) {
73
74
  const width = pen.lineweight === DEFAULT_LINEWEIGHT
74
75
  ? ""
75
76
  : ` stroke-width="${fmt((stroke * Math.max(pen.lineweight, 5)) / DEFAULT_LINEWEIGHT)}"`;
76
- const s = `stroke="${pen.color}"${width} vector-effect="non-scaling-stroke" data-layer="${esc(e.layer)}" data-entity="${index}"`;
77
+ const mark = `data-layer="${esc(e.layer)}" data-entity="${index}"${e.id === undefined ? "" : ` data-id="${esc(e.id)}"`}`;
78
+ const s = `stroke="${pen.color}"${width} vector-effect="non-scaling-stroke" ${mark}`;
77
79
  if (e.kind !== "text" && pen.dashes) {
78
80
  const d = dashedPath(e, pen.dashes);
79
81
  // A pattern with a dot draws it as a round cap on a path of no length.
@@ -109,7 +111,7 @@ function entityToSvg(e, index, pen, stroke) {
109
111
  case "text": {
110
112
  // Undo the group's Y flip so the text reads upright, then apply CAD rotation.
111
113
  const t = `translate(${fmt(e.at[0])} ${fmt(e.at[1])}) scale(1 -1)${e.rotation ? ` rotate(${fmt(-e.rotation)})` : ""}`;
112
- return `<text transform="${t}" font-size="${fmt(e.height)}" font-family="monospace" fill="${pen.color}" stroke="none" data-layer="${esc(e.layer)}" data-entity="${index}">${esc(e.value)}</text>`;
114
+ return `<text transform="${t}" font-size="${fmt(e.height)}" font-family="monospace" fill="${pen.color}" stroke="none" ${mark}>${esc(e.value)}</text>`;
113
115
  }
114
116
  }
115
117
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tsdraft",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "2D CAD drawings as TypeScript: write the drawing in code, export DXF and SVG.",
5
5
  "keywords": [
6
6
  "2d",
@@ -47,7 +47,6 @@
47
47
  "fmt:check": "oxfmt --check",
48
48
  "test": "node --test test/*.test.ts",
49
49
  "test:package": "node scripts/package-smoke.ts",
50
- "test:cad": "node scripts/cad-check.ts",
51
50
  "example": "node examples/site-plan.ts && node examples/outdoor-kitchen.ts",
52
51
  "readme:drawing": "node scripts/readme-drawing.ts",
53
52
  "verify": "pnpm fmt:check && pnpm lint && pnpm check && pnpm test && pnpm test:package"