Converts points, directions and angles between a user coordinate system and world coordinates, and sets a coordinate system from points or from an object.
How the precision is kept
A coordinate system is an origin and one angle. Conversions are a rotation by the cosine and sine of that angle and a shift, in 64-bit; angles convert by adding or subtracting the system's angle. A system within 1e-12 of the world is stored as the world.
Calculations under this policy
5 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.
ucs.jsCadCraft 0.8.264-bit5 calculationsReviewed
Tests bin/test-cadcraft.mjs
User coordinates to world
64-bit
A point or direction typed in the user coordinate system, in world coordinates.
x = u.x + pₓ·cos − p_y·sin; y = u.y + pₓ·sin + p_y·cos
One cosine and one sine of the stored angle per call, then two products and a sum per coordinate. A system turned by exactly 90° still has a cosine of about 6e-17, so an axis-aligned point can pick up a term of that size times its other coordinate.
ucs.js
lines 38 to 52
CadCraft 0.8.2
export function vecToWorld(u, v) {
if (!u) return { x: v.x, y: v.y };
const c = Math.cos(u.ang), s = Math.sin(u.ang);
return { x: v.x * c - v.y * s, y: v.x * s + v.y * c };
}
export function vecToUcs(u, v) {
if (!u) return { x: v.x, y: v.y };
const c = Math.cos(u.ang), s = Math.sin(u.ang);
return { x: v.x * c + v.y * s, y: -v.x * s + v.y * c };
}
export function toWorld(u, p) {
if (!u) return { x: p.x, y: p.y };
const w = vecToWorld(u, p);
return { x: u.x + w.x, y: u.y + w.y };
}
World coordinates to user
64-bit
A world point in the user coordinate system, as shown in coordinates and prompts.
d = p − origin; x = dₓ·cos + d_y·sin; y = −dₓ·sin + d_y·cos
The origin is subtracted first, then the difference is rotated back. Going to user coordinates and back is not exact to the last bit: each way rounds its products.
Normalizes a coordinate system and stores it as the world when it is the same.
world when |x| < 1e-12, |y| < 1e-12 and angle within 1e-12 of 0 or 2π
The three limits are absolute. The angle is wrapped into [0, 2π) by the remainder of a division by 2π.
ucs.js
lines 29 to 36
CadCraft 0.8.2
/** null for the world; else the UCS with its angle in [0, 2π). */
export function cleanUcs(v) {
if (!v || typeof v !== 'object') return null;
const x = +v.x, y = +v.y, ang = +v.ang;
if (![x, y, ang].every(Number.isFinite) || Math.abs(x) > 1e15 || Math.abs(y) > 1e15) return null;
const u = { x, y, ang: normAngle(ang) };
return isWorld(u) ? null : u;
}
Coordinate system from an origin and an X-axis point
64-bit
The coordinate system whose X axis runs from one picked point through another.
angle = atan2(p_y − o_y, pₓ − oₓ)
One atan2 of the difference of the two points; points closer than 1e-9 (EPS of geom.js, absolute) are refused.
ucs.js
lines 67 to 71
CadCraft 0.8.2
/** UCS 3point (in 2D): the origin and a point on the positive X axis. */
export function threePoint(o, px) {
if (dist(o, px) < EPS) return null;
return cleanUcs({ x: o.x, y: o.y, ang: Math.atan2(px.y - o.y, px.x - o.x) }) || { x: 0, y: 0, ang: 0 };
}
Coordinate system from a polyline segment
64-bit
The coordinate system at the start of the polyline segment picked, with X along it.
angle = atan2 of (point at 0.001 of the segment − its start)
The direction is a chord over the first thousandth of the segment. For a straight segment that is its direction; for an arc segment it is a short chord, which is close to but not the tangent at the start.
ucs.js
lines 89 to 95
CadCraft 0.8.2
case 'poly': {
const ps = prims(e, blocks);
if (!ps.length) return null;
let best = 0, bd = Infinity;
if (pick) ps.forEach((q, i) => { const d = dist(primPoint(q, primParam(q, pick)), pick); if (d < bd) { bd = d; best = i; } });
const q = ps[best], s = primPoint(q, q.rev ? 1 : 0), t = primPoint(q, q.rev ? 0.999 : 0.001);
return mk(s.x, s.y, Math.atan2(t.y - s.y, t.x - s.x));
In detail
Method
2D rotation and translation; direction by atan2.
Number types
64-bit numbers.
Tolerances
World when |x|, |y| and the angle are each under 1e-12 (absolute). Origin and X-axis point closer than 1e-9 are refused. Origins beyond 1e15 are refused.
Limits
For a polyline arc segment the X axis is the chord over the first thousandth of the segment, not its tangent.
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.