The few places where the DWG writer changes a number on its way into the file: lineweights, ellipse axes, ray directions, the drawing extents and the defaults of a light.
How the precision is kept
Coordinates, radii and angles are passed to the encoder as the 64-bit numbers the model holds. A lineweight is replaced by the nearest of the 24 values the format can store. The extents written to the header are a box of defining points, not of the drawn shapes. One default angle of a light is a constant written to 10 digits.
Open item. Write the default falloff angle of a light as π ÷ 4 + 5π ÷ 180 computed in 64-bit, instead of adding the 10-digit constant 0.0872664626.
Calculations under this policy
5 calculations in 2 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.
Picks the stored lineweight that is nearest to the one the layer or object has.
index = the i with the smallest |table[i] − lineweight|
The table holds 24 whole numbers of hundredths of a millimeter. The comparison is strict, so a value exactly halfway between two entries takes the smaller one. A lineweight that is not in the table is changed to the nearest entry in the file.
dwg/write.js
lines 40 to 47
CadCraft 0.8.2
export function lwIndex(lw) {
if (lw === undefined || lw === null || lw === -1) return 29;
if (lw === -2) return 30;
if (lw === -3 || lw < 0) return 31;
let best = 0;
for (let i = 1; i < LW_TABLE.length; i++) if (Math.abs(LW_TABLE[i] - lw) < Math.abs(LW_TABLE[best] - lw)) best = i;
return best;
}
Ellipse with a ratio above 1
64-bit
Rewrites an ellipse whose minor axis is longer than its major axis so the file holds a ratio of 1 or less.
major′ = (−m_y, m_x) × ratio; ratio′ = 1 ÷ ratio; start and end − π ÷ 2
The new axis is the old one turned a quarter turn and multiplied by the ratio, and the ratio is replaced by its reciprocal. Both parameters lose π ÷ 2, a rounded 64-bit constant. An ellipse with a ratio of 1 or less is written unchanged.
dwg/write.js
lines 385 to 390
CadCraft 0.8.2
case 'ELLIPSE': {
if (!okP(e.center) || !okP(e.majorAxis) || !(e.ratio > 0)) return null;
let { majorAxis: maj, ratio, startParam: s = 0, endParam: en = 2 * Math.PI } = e;
if (ratio > 1) { maj = { x: -maj.y * ratio, y: maj.x * ratio, z: num(maj.z) * ratio }; ratio = 1 / ratio; s -= Math.PI / 2; en -= Math.PI / 2; }
return [{ ...c('ELLIPSE'), center: P3(e.center), majorAxis: P3(maj), ratio, startParam: s, endParam: en, extrusion: e.extrusion }];
}
Ray and construction line direction
64-bit
Writes the direction of a ray or construction line as a unit vector.
d′ = d ÷ |d|
The length is one Math.hypot call and each component is divided by it. A direction of zero length leaves the object out of the file.
dwg/write.js
lines 395 to 400
CadCraft 0.8.2
case 'RAY': case 'XLINE': {
if (!okP(e.start) || !okP(e.direction)) return null;
const d = P3(e.direction), n = Math.hypot(d.x, d.y, d.z);
if (!(n > 0)) return null;
return [{ ...c(type), start: P3(e.start), direction: { x: d.x / n, y: d.y / n, z: d.z / n } }]; // a unit vector, as DXF 11 is
}
Drawing extents
64-bit
The lower left and upper right corner of model space written to the file header.
min and max of x, y, z over the defining points
Minimum and maximum only, so nothing is rounded. The box is not tight: an arc counts as its whole circle, an ellipse as a circle of its major radius in x and y, text and block references as their insertion point, and a hatch by its polyline loops only. When the model's header already has valid extents they are written as they are.
dwg/write.js
lines 642 to 662
CadCraft 0.8.2
let minX = Infinity, minY = Infinity, minZ = Infinity, maxX = -Infinity, maxY = -Infinity, maxZ = -Infinity;
const g = (p, r = 0) => { if (!p || !fin(p.x, p.y)) return; const z = num(p.z); minX = Math.min(minX, p.x - r); maxX = Math.max(maxX, p.x + r); minY = Math.min(minY, p.y - r); maxY = Math.max(maxY, p.y + r); minZ = Math.min(minZ, z); maxZ = Math.max(maxZ, z); };
const he = hdrIn.EXTMIN, hx = hdrIn.EXTMAX;
if (he && hx && fin(he.x, he.y, hx.x, hx.y) && he.x <= hx.x && he.y <= hx.y && Math.abs(he.x) < 1e19) { g(he); g(hx); }
else for (const list of MS.ents) {
const r = list[0];
switch (r.t) {
case 'LINE': g(r.start); g(r.end); break;
case 'CIRCLE': case 'ARC': g(r.center, r.radius); break;
case 'ELLIPSE': g(r.center, Math.hypot(r.majorAxis.x, r.majorAxis.y)); break;
case 'LWPOLYLINE': for (const p of r.vertices) g({ x: p.x, y: p.y, z: r.elevation }); break;
case 'POLYLINE_2D': case 'POLYLINE_3D': case 'POLYLINE_PFACE': case 'POLYLINE_MESH': for (const v of list.slice(1)) if (v.point) g(v.point); break;
case 'POINT': g(r.position); break;
case 'TEXT': case 'MTEXT': case 'INSERT': case 'MINSERT': g(r.insert); break;
case 'SOLID': case 'TRACE': case '3DFACE': for (const p of r.points) g(p); break;
case 'HATCH': for (const l of r.loops) { if (l.polyline) for (const p of l.polyline.vertices) g(p); } break;
case 'SPLINE': for (const p of r.control.length ? r.control : r.fit) g(p); break;
default: if (r.defPoint) g(r.defPoint);
}
}
if (Number.isFinite(minX)) ext = { min: { x: minX, y: minY, z: minZ }, max: { x: maxX, y: maxY, z: maxZ } };
The hotspot and falloff angles written for a light that has none of its own.
hotspot = π ÷ 4; falloff = π ÷ 4 + 0.0872664626
The falloff default adds 0.0872664626, which is 5 degrees in radians written to 10 digits; the 64-bit value of 5π ÷ 180 is 0.08726646259971647. The other fields of a light and of a solid are written as the model holds them.
Nearest table entry for lineweights; an axis swap for an ellipse whose ratio is above 1; a unit vector for ray and construction line directions; minimum and maximum of defining points for the extents.
Number types
64-bit numbers. Lineweights are whole hundredths of a millimeter.
Tolerances
The header's own extents are used only when they are finite, ordered and below 1e19 in size.
Limits
A lineweight that is not in the format's table is written as the nearest one. The computed extents take an arc as its whole circle, an ellipse as a circle of its major radius, and text and block references as their insertion point only.
Approach: 64-bit, stated limit
The arithmetic is 64-bit, but the method or the data sets the accuracy: a solver tolerance, a mesh, a step size, a datum shift, a table. The limit is stated where the result is shown.