Next point from a typed entry
64-bitThe plan point for a typed absolute point, relative step, length at an angle, or length along the pointer's direction; and a door or window offset from a typed distance.
relative: q = last + (dx, dy); polar: q = last + len × (cos θ, sin θ), θ = degrees × π ÷ 180; along the pointer: q = last + len × (to − last) ÷ |to − last|
Each case is one 64-bit expression on the parsed millimeters. The polar case evaluates Math.cos and Math.sin of the angle in radians, so right angles do not give an exact 0. A typed door distance is measured from the nearer end of the wall face and is refused when the opening would have to move more than 0.5 mm to fit.
function applyTyped(text) {
if (S.tool === 'select') return false;
if (extTool(S.tool)) return !!extTool(S.tool).typed?.(S.tool, text);
if (S.tool === 'door' || S.tool === 'window') {
// a distance from the nearer end of the wall face the preview shows
const d = parseLen(text, S.units), h = S.hover, o = S.open[S.tool];
if (!h || !Number.isFinite(d) || d < 0) { host.status(t(h ? 'model.err.length' : 'model.typed.wall'), true); return false; }
const [e0, e1] = h.ends, nearStart = h.s < (e0 + e1) / 2;
const offset = nearStart ? e0 + d : e1 - d - o.width, fit = fitOffset(h.wall, o.width, offset);
if (fit === null || Math.abs(fit - offset) > 0.5) { host.status(t('model.err.nofit'), true); return false; }
placeHere({ ...h, offset: fit, ok: true }, S.tool);
return true;
}
// 0.6 round 2: a curved wall's arc height (to the side the pointer is on), once its end is set
if (S.tool === 'wall' && S.wall.shape === 'arc' && S.chain && S.arcEnd) {
const h = parseLen(text, S.units), a = S.chain.points[S.chain.points.length - 1], b = S.arcEnd;
if (!Number.isFinite(h)) { host.status(t('model.err.length'), true); return false; }
const p = S.last?.snap?.p, side = p ? Math.sign((b[0] - a[0]) * (p[1] - a[1]) - (b[1] - a[1]) * (p[0] - a[0])) || 1 : 1;
arcPoint(b, bulgeFromHeight(a, b, side * Math.abs(h)));
return true;
}
const v = parseTyped(text, S.units);
if (!v) { host.status(t('model.err.typed'), true); return false; }
const pts = S.chain?.points;
const last = pts ? pts[pts.length - 1] : null;
let q = null;
if (v.kind === 'abs') q = [v.x, v.y];
else if (!last) { host.status(t('model.typed.first'), true); return false; }
else if (v.kind === 'rel') q = [last[0] + v.dx, last[1] + v.dy];
else if (v.kind === 'polar') q = [last[0] + v.len * Math.cos(v.angle * Math.PI / 180), last[1] + v.len * Math.sin(v.angle * Math.PI / 180)];
else {
// a length along the direction the pointer shows (its snapped direction), else along X
const to = S.last?.snap?.p || [last[0] + 1, last[1]];
const d = Math.hypot(to[0] - last[0], to[1] - last[1]) || 1;
q = [last[0] + (to[0] - last[0]) / d * v.len, last[1] + (to[1] - last[1]) / d * v.len];
}
if (S.tool === 'wall') wallPoint(q);
return true;
}