Works out what DIST, AREA, MEASUREGEOM, ID and LIST print: distances, Δx and Δy, angles, areas, perimeters, running area totals and coordinates; and the unit factor EXPORTPOINTS applies to the points it writes.
How the precision is kept
Each result is computed in 64-bit from the picked points or the stored object: Math.hypot for distances, atan2 for angles, geom.js polygonMeasure and entityArea for areas. The numbers are rounded only when they are written, by the drawing's length and area formats or, for plain angles and ratios, to 4 decimals.
Calculations under this policy
8 calculations in 1 source file. 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.
d = √(Δx² + Δy²); angle = atan2(Δy, Δx), normalized to 0…360°
One Math.hypot, two subtractions and one atan2 on the picked points. Δx and Δy are along the world axes. The angle is handed to units.js tAngle with the drawing's angle format.
The sums are geom.js polygonMeasure on the picked points as stored. It multiplies absolute coordinates, so far from the origin the products are large and nearly cancel and the area keeps fewer digits than the coordinates. The absolute value is taken, so the picking direction does not matter; a point within 1e-12 of the previous one is skipped.
commands.js
lines 3720 to 3735
CadCraft 0.8.2
/** Area and perimeter of points picked one by one (Enter closes the outline). → { area, perimeter } | null */
async function areaByPoints(c, first) {
const pts = [first];
for (;;) {
c.preview((cur) => [{ type: 'poly', closed: true, pts: [...pts, cur].map((p) => ({ x: p.x, y: p.y, b: 0 })) }]);
const p = await c.point(t('cmd.area.next'), { base: pts[pts.length - 1], none: true, options: pts.length > 1 ? [opt('U', t('opt.undo'))] : [] });
if (!p) break;
if (p.option === 'U') { pts.pop(); continue; }
if (dist(p, pts[pts.length - 1]) > 1e-12) pts.push({ x: p.x, y: p.y });
}
c.preview(null);
if (pts.length < 3) { c.print(t('cmd.area.few')); return null; }
const m = polygonMeasure(pts);
c.print(t('cmd.area.result', { area: fmtArea(c, Math.abs(m.area)), perimeter: fmtLen(c, m.perimeter) }));
return { area: Math.abs(m.area), perimeter: m.perimeter };
}
Area and perimeter of an object
64-bit
What AREA prints for a picked circle, polyline, ellipse or hatch.
The area and perimeter are geom.js entityArea for the object, written with the drawing's area and length formats. An object with no area prints a message and adds nothing to a running total.
The total AREA keeps while areas are added and subtracted.
total = Σ ± areaᵢ
A plain running sum in 64-bit: each area is added or subtracted in the order picked. Subtracting an area nearly equal to the total leaves a small difference with fewer correct digits. The total is written with the drawing's area format.
commands.js
lines 3747 to 3748
CadCraft 0.8.2
let mode = null, total = 0; // null, 'add' or 'sub' (a running total)
const tally = (a) => { if (a === null || !mode) return; total += mode === 'add' ? a : -a; c.print(t('cmd.area.total', { area: fmtArea(c, total) })); };
Angle at a vertex, of an arc, or between two lines
64-bit
The angle MEASUREGEOM prints.
angle = |atan2(b − v) − atan2(a − v)|, folded to 0…180°; arc: its sweep, with 0 read as 360°
The angle is the difference of two atan2 results, so a very small angle carries the absolute rounding of both, a few 10⁻¹⁶ radians. For two lines the vertex is their intersection from geom.js intersect and each leg points to the line's end farther from it. The result is printed in degrees with at most 4 decimals.
commands.js
lines 3799 to 3814
CadCraft 0.8.2
ang = Math.abs(normAngle(angleOf(v, b) - angleOf(v, a))); if (ang > Math.PI) ang = TAU - ang;
} else {
const e = c.doc.get(pick.id);
if (e.type === 'arc') ang = normAngle(e.a1 - e.a0) || TAU;
else if (e.type === 'line') {
const q = await c.entity(t('cmd.mea.line2'));
const f = c.doc.get(q.id);
if (f.type !== 'line') { c.print(t('cmd.mea.line2.only')); continue; }
// between the two lines, each toward its end farther from where they meet
const x = intersect({ k: 'L', ...e }, { k: 'L', ...f }, true, true)[0];
if (!x) { c.print(t('cmd.chamfer.err.parallel')); continue; }
const far = (l) => (dist(x, { x: l.x1, y: l.y1 }) > dist(x, { x: l.x2, y: l.y2 }) ? { x: l.x1, y: l.y1 } : { x: l.x2, y: l.y2 });
ang = Math.abs(normAngle(angleOf(x, far(f)) - angleOf(x, far(e)))); if (ang > Math.PI) ang = TAU - ang;
} else { c.print(t('cmd.mea.angle.only')); continue; }
}
c.print(t('cmd.mea.angle', { a: fmt(deg(ang)) }));
Coordinates of a point
64-bit
What ID prints for a picked point.
The picked point is converted to the user coordinate system by ucs.js toUcs and written with the drawing's length format. With no user coordinate system the stored coordinates are printed as they are.
commands.js
lines 3823 to 3826
CadCraft 0.8.2
def('ID', [], async (c) => {
const p = await c.point(t('cmd.id.specify_point'));
const q = toUcs(activeUcs(c.doc), p); // in the UCS (DR.8)
c.print(t('cmd.id.result', { x: fmtLen(c, q.x), y: fmtLen(c, q.y) }));
One object in a line of text
64-bit
The description LIST prints and assistants read for an object: its points, length, radius and angles.
line length = √(Δx² + Δy²); angles in degrees = radians × 180 ÷ π
Lengths and coordinates are written in the drawing's length format, or with at most 4 decimals when no units are passed. Arc angles, an ellipse's ratio, a viewport's scale and a construction line's angle are written with at most 4 decimals. A polyline is described by its number of vertices only.
The factor between drawing units and the units of the point file EXPORTPOINTS writes.
factor = mm per unit ÷ 1000 (meters), ÷ 304.8 (feet), or 1 (drawing units)
The factor is ptexport.js UNIT_FACTOR of the drawing's millimeters per unit: one division by 1000 or by 304.8, both exact definitions. Meters and feet are offered only when the drawing's unit is known. The points and the text of the file are made by ptexport.js collectPoints and formatPoints, which are not in this file.
Distance by Math.hypot; angle between legs as a difference of two atan2 results; area of picked points by the shoelace sum in geom.js polygonMeasure.
Number types
64-bit numbers; printed text is rounded by the drawing's formats.
Tolerances
1e-12 drawing units, absolute: a point picked on top of the last one is not added to an area outline.
Limits
Plain angles and ratios in these messages are printed with at most 4 decimals. The shoelace sum uses absolute coordinates, so far from the origin an area keeps fewer digits than the coordinates do. AREA's running total adds and subtracts in the order picked.
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.