Resources Numerical accuracy Calculation policy

DWG object values written and read

Carries the coordinates, angles, widths and scales of drawing objects between the drawing and the DWG file's object records.

How the precision is kept

The encoders and decoders pass 64-bit doubles between the drawing model and the bit stream without scaling or rounding. The few values that are computed are arc angles brought into [0, 2π), a linetype's pattern length as a sum, a text direction from a rotation, and a block content matrix. A value that is not a finite number is written as a default, usually 0.

Open item. Report when a non-finite value is replaced by a default on write instead of writing the default silently.

Calculations under this policy

15 calculations in 3 source files. The code is read from the released app when this page loads. It is the calculation people run, not a copy kept for this page.

dwg/objects-write.js CadCraft 0.8.2 64-bit 11 calculations Reviewed

Tests None found by the review

Guard on every written measure

64-bit

Decides what is written when a value is missing or not a number.

A value that is not a finite number (NaN, an infinity, or not a number at all) is replaced by the default, which is 0 unless the caller gives another. Finite values pass through unchanged.

  • dwg/objects-write.js
  • line 218
  • CadCraft 0.8.2
const num = (v, d = 0) => (Number.isFinite(v) ? v : d);

Arc angles brought into one turn

64-bit

Brings an angle into the range from 0 up to 2π before it is written.

r = a mod 2π; r + 2π when r < 0

The remainder by the 64-bit value of 2π is computed without rounding; adding 2π to a negative remainder rounds once. An angle that is not a number becomes 0.

  • dwg/objects-write.js
  • line 217
  • CadCraft 0.8.2
const rad = (a) => { const t = 2 * Math.PI; let r = (+a || 0) % t; if (r < 0) r += t; return r; };

Arc

64-bit

Writes an arc's center, radius and two angles.

Center and radius are written as given. The two angles go through rad(), so they are stored in [0, 2π).

  • dwg/objects-write.js
  • lines 546 to 549
  • CadCraft 0.8.2
ENC.ARC = (w, r) => {
  entityCommon(w, r); w.P3(r.center); w.BD(num(r.radius)); w.BT(num(r.thickness)); BEx(w, r.extrusion);
  w.BD(rad(r.startAngle)); w.BD(rad(r.endAngle));
};

Line

64-bit

Writes a line's two end points.

Start coordinates are written as doubles and end coordinates with the bit writer's default-double call against the start value. The z values are left out when both are 0. No arithmetic is done on the coordinates here.

  • dwg/objects-write.js
  • lines 536 to 543
  • CadCraft 0.8.2
ENC.LINE = (w, r) => {
  entityCommon(w, r);
  const a = P(r.start), b = P(r.end), flat = !a.z && !b.z;
  w.B(flat ? 1 : 0);
  w.RD(a.x); w.DD(b.x, a.x); w.RD(a.y); w.DD(b.y, a.y);
  if (!flat) { w.RD(a.z); w.DD(b.z, a.z); }
  w.BT(num(r.thickness)); BEx(w, r.extrusion);
};

Polyline vertices

64-bit

Writes the vertices of a lightweight polyline.

The first vertex is written as doubles and each later one with the default-double call against the vertex before it. Bulges and widths follow as doubles. No arithmetic is done on them here.

  • dwg/objects-write.js
  • lines 753 to 754
  • CadCraft 0.8.2
w.RD2(num(v[0].x), num(v[0].y));
for (let k = 1; k < v.length; k++) { w.DD(num(v[k].x), num(v[k - 1].x)); w.DD(num(v[k].y), num(v[k - 1].y)); }

Vertex widths

64-bit

Writes the start and end width at a polyline vertex.

equal widths: one value, −|width|

When the start and end widths are equal and not zero, one value is written with a negative sign, the format's short form. The comparison is exact equality.

  • dwg/objects-write.js
  • lines 723 to 724
  • CadCraft 0.8.2
const sw = num(r.startWidth), ew = num(r.endWidth, sw);
if (sw && sw === ew) w.BD(-Math.abs(sw)); else { w.BD(sw); w.BD(ew); }

Linetype pattern length

64-bit

The total length of a linetype's dash pattern.

length = Σ |element length|

The stored pattern length is used when it is a finite number. Otherwise it is the sum of the absolute element lengths, added in order.

  • dwg/objects-write.js
  • line 303
  • CadCraft 0.8.2
w.BD(num(r.patternLength, els.reduce((s, e) => s + Math.abs(num(e.length)), 0)));

Text direction

64-bit

The x-axis direction written for a block of text.

x axis = (cos θ, sin θ, 0)

A stored x axis is written as it is. Without one, the direction is Math.cos and Math.sin of the rotation.

  • dwg/objects-write.js
  • line 628
  • CadCraft 0.8.2
const xa = r.xAxis ? P(r.xAxis) : { x: Math.cos(num(r.rotation)), y: Math.sin(num(r.rotation)), z: 0 };

Block scale

64-bit

Writes the three scale factors of a block reference in the format's compact form.

The short forms are chosen by exact equality: all three scales 1, all three equal, or x equal to 1. The values themselves are written unchanged.

  • dwg/objects-write.js
  • lines 656 to 662
  • CadCraft 0.8.2
function insertScale(w, s) {
  const sx = num(s?.x, 1), sy = num(s?.y, 1), sz = num(s?.z, 1);
  if (sx === 1 && sy === 1 && sz === 1) w.BB(3);
  else if (sx === sy && sx === sz) { w.BB(2); w.RD(sx); }
  else if (sx === 1) { w.BB(1); w.DD(sy, 1); w.DD(sz, 1); }
  else { w.BB(0); w.RD(sx); w.DD(sy, sx); w.DD(sz, sx); }
}

Single-line text

64-bit

Writes a text's insertion point, height, rotation, width factor and oblique angle.

A field equal to its default (elevation 0, rotation 0, oblique 0, width factor 1) is flagged and left out, by exact comparison. A height that is not a finite number is written as 0.2.

  • dwg/objects-write.js
  • lines 584 to 603
  • CadCraft 0.8.2
/** TEXT data (20.4.3 R2000+), also the start of ATTRIB / ATTDEF. */
function textData(w, r) {
  const ins = P(r.insert), al = r.align ? P(r.align) : null;
  const ha = r.hAlign | 0, va = r.vAlign | 0;
  const hasAlign = !!al && (ha || va);
  const elev = num(ins.z), rot = num(r.rotation), wf = num(r.widthFactor, 1), obl = num(r.oblique), gen = r.generation | 0;
  w.RC((elev ? 0 : 1) | (hasAlign ? 0 : 2) | (obl ? 0 : 4) | (rot ? 0 : 8) | (wf !== 1 ? 0 : 0x10) | (gen ? 0 : 0x20) | (ha ? 0 : 0x40) | (va ? 0 : 0x80));
  if (elev) w.RD(elev);
  w.RD2(ins.x, ins.y);
  if (hasAlign) { w.DD(al.x, ins.x); w.DD(al.y, ins.y); }
  BEx(w, r.extrusion); w.BT(num(r.thickness));
  if (obl) w.RD(obl);
  if (rot) w.RD(rot);
  w.RD(num(r.height, 0.2));
  if (wf !== 1) w.RD(wf);
  w.TV(r.value ?? '');
  if (gen) w.BS(gen);
  if (ha) w.BS(ha);
  if (va) w.BS(va);
}

Extension record values

64-bit

Writes the numbers kept in an extension record.

Doubles and points are written as given, with a value that is not a number written as 0. Integer values are truncated with | 0, which also wraps anything beyond 32 bits; 64-bit integers go through Math.trunc and BigInt.

  • dwg/objects-write.js
  • lines 470 to 476
  • CadCraft 0.8.2
case 'pt3': o.RD(+v?.x || 0); o.RD(+v?.y || 0); o.RD(+v?.z || 0); break;
case 'pt2': o.RD(+v?.x || 0); o.RD(+v?.y || 0); break;
case 'dbl': o.RD(+v || 0); break;
case 'i8': case 'bool': o.RC(v | 0); break;
case 'i16': o.RS(v | 0); break;
case 'i32': o.RL(v | 0); break;
case 'i64': o.RLL(typeof v === 'bigint' ? v : BigInt(Math.trunc(+v || 0))); break;
dwg/mleader-write.js CadCraft 0.8.2 64-bit 1 calculation Reviewed

Tests None found by the review

Block content matrix of a multileader

64-bit

The 4 × 4 matrix written for a multileader's block content when the model gives none.

M = translate(l) · rotate_z(θ) · scale(s)

cos and sin of the rotation are taken once and multiplied by the x and y scales; the location fills the last column. A matrix of 16 numbers supplied with the content is written instead, unchanged.

  • dwg/mleader-write.js
  • lines 153 to 158
  • CadCraft 0.8.2
/** 20.4.86: rotation, then scale, then translation (normal Z) — for a block content given without its matrix. */
function blockMatrix(b) {
  const s = P(b.scale, { x: 1, y: 1, z: 1 }), r = num(b.rotation), l = P(b.location);
  const cs = Math.cos(r), sn = Math.sin(r);
  return [cs * s.x, -sn * s.y, 0, l.x, sn * s.x, cs * s.y, 0, l.y, 0, 0, s.z, l.z, 0, 0, 0, 1];
}
dwg/objects-read.js CadCraft 0.8.2 64-bit 3 calculations Reviewed

Tests None found by the review

Line read

64-bit

Reads a line's two end points.

Start coordinates are read as doubles and end coordinates with the bit reader's default-double call against the start value; z is 0 when the record flags it as absent. No arithmetic is done on the values here.

  • dwg/objects-read.js
  • lines 454 to 457
  • CadCraft 0.8.2
const sx = r.RD(), ex = r.DD(sx), sy = r.RD(), ey = r.DD(sy);
let sz = 0, ez = 0;
if (!zzero) { sz = r.RD(); ez = r.DD(sz); }
rec.start = { x: sx, y: sy, z: sz }; rec.end = { x: ex, y: ey, z: ez };

Polyline vertices read

64-bit

Reads the vertices of a lightweight polyline.

The first vertex is read as doubles and each later one with the default-double call against the vertex before it. Values are stored as read.

  • dwg/objects-read.js
  • lines 1109 to 1111
  • CadCraft 0.8.2
let x = r.RD(), y = r.RD();
vs[0] = { x, y, bulge: 0, startWidth: 0, endWidth: 0 };
for (let i = 1; i < n; i++) { x = r.DD(x); y = r.DD(y); vs[i] = { x, y, bulge: 0, startWidth: 0, endWidth: 0 }; }

North direction of older geolocation data

64-bit

Turns the north angle of a version 1 geolocation object into a direction vector.

north = (sin a, cos a)

Version 1 objects store north as an angle in radians clockwise from (0, 1), which is turned into a vector with Math.sin and Math.cos. Later versions store the vector, and it is read as it is.

  • dwg/objects-read.js
  • line 1173
  • CadCraft 0.8.2
rec.north = { x: Math.sin(a), y: Math.cos(a) };

In detail

Method
Field-by-field encoding and decoding after the ODA Open Design Specification for .dwg files v5.4.1 (cited in the file headers); compact forms chosen by exact equality with the format's default values.
Number types
64-bit doubles for coordinates and measures; integers for flags and counts; BigInt for 64-bit integer values.
Tolerances
None: defaults are detected by exact equality (0, 1, or the previous coordinate).
Limits
A non-finite value is replaced by a default without a report from these functions. Integer group values are truncated to 32 bits with | 0.

Approach: 64-bit, direct

Every value is a 64-bit number from input to result. Formulas are closed form or a direct solve, and every comparison uses a stated tolerance.

Every policy that takes this approach