Shows an object's length, angle, area, circumference, chord and sweep, and changes the object when one of them is typed.
How the precision is kept
Each property is a closed-form 64-bit expression of the object's stored numbers, and each edit solves that expression back for one stored number. Angles are shown in degrees and stored in radians. Several objects show one value only when their values agree within a relative 1e-9.
Calculations under this policy
10 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.
Length comes from curveLength and area and perimeter from entityArea, both of geom.js; this function only takes the absolute value of the area. An open polyline has no area.
propdefs.js
lines 134 to 144
CadCraft 0.8.2
/** Length, area and perimeter of an object, where they mean something. */
export function measure(e) {
const out = {};
const L = curveLength(e);
if (L !== null && L !== undefined) out.length = L;
if (['circle', 'poly', 'ellipse', 'hatch'].includes(e.type) && (e.type !== 'poly' || e.closed)) {
const a = entityArea(e);
if (a) { out.area = Math.abs(a.area); out.perimeter = a.perimeter; }
}
return out;
}
Centroid of a closed polyline
64-bit
The point a closed polyline is scaled about when its area is typed.
a = Σ (xᵢyⱼ − xⱼyᵢ); c = Σ (pᵢ + pⱼ)(xᵢyⱼ − xⱼyᵢ) ÷ 3a
Shoelace sums over absolute coordinates, so a small polygon far from the origin subtracts large nearly equal products. Bulges are ignored, as the comment says. When |a| < 1e-12, an absolute limit, the mean of the corners is used instead.
propdefs.js
lines 146 to 153
CadCraft 0.8.2
/** The centroid of a closed polyline's corners (bulges ignored: close enough to scale about). */
function polyCentre(e) {
let a = 0, cx = 0, cy = 0;
const p = e.pts, n = p.length;
for (let i = 0; i < n; i++) { const u = p[i], v = p[(i + 1) % n], c = u.x * v.y - v.x * u.y; a += c; cx += (u.x + v.x) * c; cy += (u.y + v.y) * c; }
if (Math.abs(a) < 1e-12) return { x: p.reduce((s, q) => s + q.x, 0) / n, y: p.reduce((s, q) => s + q.y, 0) / n };
return { x: cx / (3 * a), y: cy / (3 * a) };
}
Scaling a polyline to a typed area
64-bit
Resizes a closed polyline about its centroid so its area becomes the value typed.
k = √(A ÷ A₀); p′ = c + (p − c)·k
One division and one square root for k, then each vertex is moved in 64-bit; segment widths are multiplied by k. A polyline with no area is refused.
propdefs.js
lines 154 to 160
CadCraft 0.8.2
/** A closed polyline scaled about its centre so its area becomes A (P.8). */
function polyToArea(e, A) {
const A0 = Math.abs(entityArea(e)?.area || 0);
if (!(A0 > 0)) throw new PropError('props.err.noarea');
const k = Math.sqrt(A / A0), c = polyCentre(e);
return { ...e, pts: e.pts.map((q) => ({ ...q, x: c.x + (q.x - c.x) * k, y: c.y + (q.y - c.y) * k, ...(q.sw ? { sw: q.sw * k } : {}), ...(q.ew ? { ew: q.ew * k } : {}) })) };
}
Line: length and angle
64-bit
Shows a line's length, angle and deltas, and moves its end point when length or angle is typed.
L = |p₂ − p₁|; new end = p₁ + L·(cos a, sin a)
Typing a length recomputes the end point from atan2 of the current direction and then its cosine and sine, so the direction passes through an angle and back instead of being scaled; the start point stays.
Shows an arc's angles, sweep, length, chord and segment area, and changes the arc when one is typed.
θ = (a₁ − a₀) mod 2π; length = rθ; chord = 2r·sin(θ ÷ 2); area = r²(θ − sin θ) ÷ 2
A sweep that wraps to exactly zero is read as a full turn. A typed sweep of 360 − 1e-9 degrees or more is refused, and a typed chord is refused when |sin(θ ÷ 2)| ≤ 1e-9. For a very shallow arc θ − sin θ subtracts two nearly equal numbers and the segment area loses digits.
Shows an ellipse's major and minor axis, ratio, axis angle and start and end angles, and changes them when typed.
major = |m|; minor = |m| × ratio; ratio = minor ÷ major
The major axis is rescaled by dividing its vector by its own length and multiplying by the new one. A typed minor axis may exceed the major by at most 1e-12, an absolute allowance. A typed axis angle rebuilds the vector from cosine and sine.
Decides whether a field shows one value or 'varies' for a selection.
|a − b| ≤ 1e-9 × max(1, |a|, |b|)
The tolerance is relative above 1 and absolute (1e-9) below it. The value shown is the first object's; the others may differ from it by up to that amount.
propdefs.js
line 493
CadCraft 0.8.2
const near = (a, b) => (typeof a === 'number' && typeof b === 'number' ? Math.abs(a - b) <= 1e-9 * Math.max(1, Math.abs(a), Math.abs(b)) : a === b || (typeof a === 'string' && typeof b === 'string' && /^#/.test(a) && a.toLowerCase() === b.toLowerCase()));
props.jsCadCraft 0.8.264-bit1 calculationReviewed
TestsNone found by the review
Text held by a number field
64-bit
Chooses whether a field holds the formatted text (12'-6") or the plain number.
formatted text kept when |read(text) − value| ≤ 1e-9 × max(1, |value|)
The formatted text is used only when reading it back gives the value within a relative 1e-9; otherwise the field holds the number itself, so a value that the format would round is not replaced by its rounded text.
props.js
lines 213 to 220
CadCraft 0.8.2
/** The text a number field holds: the drawing's own format (12'-6", 1/2") when reading it back gives the same number, else the plain number. */
const numText = (def, value) => {
if (value === VARIES || !Number.isFinite(value) || (def.kind !== 'len' && def.kind !== 'ang')) return value === VARIES ? null : value;
const txt = show(def, value);
let back = null;
try { back = read(def, txt); } catch { back = null; }
return back && back.ok && Math.abs(back.value - value) <= 1e-9 * Math.max(1, Math.abs(value)) ? txt : value;
};
In detail
Method
Closed forms: circle C = 2πr, A = πr²; arc chord 2r·sin(θ ÷ 2), segment area r²(θ − sin θ) ÷ 2; polygon centroid by the shoelace sums; scale to an area by √(A ÷ A₀).
Number types
64-bit numbers.
Tolerances
Values agree within 1e-9 × max(1, |a|, |b|). Full arc refused at sweep ≥ 360 − 1e-9 degrees. Chord edit refused when |sin(θ ÷ 2)| ≤ 1e-9. Minor axis may exceed major by 1e-12 (absolute). Polygon area sum under 1e-12 (absolute) falls back to the mean of the corners. Uniform scale when factors agree within 1e-12.
Limits
The centroid used to scale a polyline ignores bulges. The arc segment area loses digits for very shallow arcs.
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.