Dynamic input: points from typed distance, angle, X and Y
Works out the point a command receives when a distance, an angle, or X and Y are typed into the fields by the crosshair.
How the precision is kept
A locked field's text is parsed to a 64-bit number and combined with the pointer: a distance and an angle give base + d × (cos a, sin a), and X and Y replace the pointer's coordinates. When Enter sends the point, a field that was not typed is written into the command line as text with 10 decimals, so that part of the point is rounded to 1e-10 before the command reads it.
Open item. Send a field that was not typed to the command line with full precision instead of 10 decimals.
Calculations under this policy
4 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.
The point shown and used while fields are locked: a typed distance or angle from the base point, or a typed X or Y.
polar: p = base + d × (cos a, sin a), a = typed degrees × π ÷ 180 or atan2 of the pointer; xy: (typed X or pointer x, typed Y or pointer y)
A typed distance is used as parsed; the pointer supplies only the part not typed. The angle is converted with × Math.PI ÷ 180 and goes through cosine and sine, so a typed 90 degrees gives an x offset of d × 6.1e-17 and not 0.
dynin.js
lines 47 to 67
CadCraft 0.8.2
apply(cur, base, parseLen, typed = '') {
if (!this.mode || !cur) return cur;
if (this.ucs) { // work in the UCS, hand back a world point
const u = this.ucs;
this.ucs = null;
try { const r = this.apply(toUcs(u, cur), base && toUcs(u, base), parseLen, typed); return r === null || r === undefined ? r : toWorld(u, r); } finally { this.ucs = u; }
}
const s = String(typed ?? '').trim();
if (s && this.locked[this.active] === null) { // what is being typed counts as if it were locked
const ok = this.mode === 'polar' && this.active === 1 ? Number.isFinite(this.parseAngle(s)) : Number.isFinite(parseLen(s));
if (ok) { const keep = this.locked; this.locked = keep.slice(); this.locked[this.active] = s; try { return this.apply(cur, base, parseLen); } finally { this.locked = keep; } }
}
if (!this.any) return cur;
if (this.mode === 'xy') {
return { x: this.locked[0] !== null ? parseLen(this.locked[0]) : cur.x, y: this.locked[1] !== null ? parseLen(this.locked[1]) : cur.y };
}
if (!base) return cur;
const d = this.locked[0] !== null ? parseLen(this.locked[0]) : Math.hypot(cur.x - base.x, cur.y - base.y);
const a = this.locked[1] !== null ? this.parseAngle(this.locked[1]) * Math.PI / 180 : Math.atan2(cur.y - base.y, cur.x - base.x);
return { x: base.x + Math.cos(a) * d, y: base.y + Math.sin(a) * d };
}
Field values while the pointer moves
64-bit
The distance and angle from the base point, or X and Y, that the fields show.
d = √(Δx² + Δy²); a = atan2(Δy, Δx) × 180 ÷ π, brought into 0 to 360
Math.hypot and Math.atan2 of the pointer's offset; a negative angle has 360 added. These are the full 64-bit values; any rounding happens where they are shown or sent.
dynin.js
lines 69 to 77
CadCraft 0.8.2
live(cur, base) {
if (!this.mode || !cur) return [null, null];
if (this.ucs) { cur = toUcs(this.ucs, cur); base = base && toUcs(this.ucs, base); }
if (this.mode === 'xy') return [this.locked[0] ?? cur.x, this.locked[1] ?? cur.y];
if (!base) return [null, null];
let a = Math.atan2(cur.y - base.y, cur.x - base.x) * 180 / Math.PI;
if (a < 0) a += 360;
return [this.locked[0] ?? Math.hypot(cur.x - base.x, cur.y - base.y), this.locked[1] ?? a];
}
Text sent on Enter
64-bit
The coordinate text the command line receives: a relative distance and angle, or an absolute X and Y.
@d<a from the last point; otherwise #x,y with x = base_x + d × cos a, y = base_y + d × sin a
A typed field is sent exactly as typed. A field taken from the pointer is sent through num, rounded to 10 decimals, and the absolute form rounds both coordinates the same way. The command then parses that text, so the point kept in the drawing carries the 1e-10 rounding.
dynin.js
lines 79 to 92
CadCraft 0.8.2
commit(typed, cur, base, { last = base, parseLen = Number } = {}) {
if (!this.mode) return null;
const s = String(typed ?? '').trim();
if (!this.any && this.active === 0) return null; // nothing locked: the command line reads it as always (direct distance…)
// UCS values, as the command line reads #x,y and @d<a (DR.8)
const vals = this.live(cur, base).map((v, i) => (i === this.active && s ? s : typeof v === 'number' ? num(v) : v));
if (vals.some((v) => v === null || v === undefined)) return null;
if (this.mode === 'xy') return vals[0] + ',' + vals[1];
// @ counts from the last point given; when the base is another point, send where it lands instead
if (last && Math.abs(last.x - base.x) < 1e-12 && Math.abs(last.y - base.y) < 1e-12) return '@' + vals[0] + '<' + vals[1];
const d = parseLen(vals[0]), a = this.parseAngle(vals[1]) * Math.PI / 180, b = toUcs(this.ucs, base);
if (!Number.isFinite(d) || !Number.isFinite(a)) return null;
return '#' + num(b.x + Math.cos(a) * d) + ',' + num(b.y + Math.sin(a) * d);
}
Number text for the command line
64-bit
Writes a value taken from the pointer as text.
text = value rounded to 10 decimals, trailing zeros dropped
toFixed(10) is an absolute rounding at 1e-10. At coordinates around 1e6 that is the 17th significant digit; at values around 1e-5 it leaves about 6 significant digits.
dynin.js
line 23
CadCraft 0.8.2
const num = (v) => String(Number(v.toFixed(10)));
In detail
Method
Polar: point = base + d × (cos a, sin a), a in degrees × π ÷ 180. With a user coordinate system the point is taken into that system, worked out, and brought back.
Number types
64-bit numbers; values sent to the command line as decimal text with at most 10 decimals.
Tolerances
The base point counts as the last point when both coordinates agree within 1e-12 (absolute).
Limits
Values taken from the pointer are sent with 10 decimals (absolute), so a drawing whose features are much smaller than one unit keeps fewer significant digits in them.
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.