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.
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π).
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.
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.
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;
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];
}
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.