function clamp01(value) { return Math.max(0, Math.min(1, Number(value) || 0)); } export function round3(value) { return Number.parseFloat(Number(value).toFixed(3)); } export function getAngleLabelBasePosition(x1, y1, xm, ym, x2, y2) { const va = { x: Number(x1) - Number(xm), y: Number(y1) - Number(ym) }; const vb = { x: Number(x2) - Number(xm), y: Number(y2) - Number(ym) }; const lenA = Math.hypot(va.x, va.y); const lenB = Math.hypot(vb.x, vb.y); if (lenA <= 1e-6 || lenB <= 1e-6) { return { x: clamp01(xm), y: clamp01(Number(ym) - 0.14) }; } const unit = { x: (va.x / lenA) + (vb.x / lenB), y: (va.y / lenA) + (vb.y / lenB), }; const unitLength = Math.hypot(unit.x, unit.y); const bisector = unitLength <= 1e-6 ? { x: 0, y: -1 } : { x: unit.x / unitLength, y: unit.y / unitLength }; return { x: clamp01(Number(xm) + bisector.x * 0.14), y: clamp01(Number(ym) + bisector.y * 0.14), }; } export function getAngleLabelPosition(points, labelDx = 0, labelDy = 0) { const base = getAngleLabelBasePosition(points.x1, points.y1, points.xm, points.ym, points.x2, points.y2); return { x: clamp01(base.x + (Number(labelDx) || 0)), y: clamp01(base.y + (Number(labelDy) || 0)), }; } export function moveAngleWidget(points, dx, dy) { const nextDx = Number(dx) || 0; const nextDy = Number(dy) || 0; const xs = [points.x1, points.xm, points.x2]; const ys = [points.y1, points.ym, points.y2]; const minX = Math.min(...xs); const maxX = Math.max(...xs); const minY = Math.min(...ys); const maxY = Math.max(...ys); const clampedDx = Math.max(-minX, Math.min(1 - maxX, nextDx)); const clampedDy = Math.max(-minY, Math.min(1 - maxY, nextDy)); return { x1: round3(clamp01(points.x1 + clampedDx)), y1: round3(clamp01(points.y1 + clampedDy)), xm: round3(clamp01(points.xm + clampedDx)), ym: round3(clamp01(points.ym + clampedDy)), x2: round3(clamp01(points.x2 + clampedDx)), y2: round3(clamp01(points.y2 + clampedDy)), }; } export function measureAngleDegrees(x1, y1, xm, ym, x2, y2) { const ax = Number(x1) - Number(xm); const ay = Number(y1) - Number(ym); const bx = Number(x2) - Number(xm); const by = Number(y2) - Number(ym); const lenA = Math.hypot(ax, ay); const lenB = Math.hypot(bx, by); if (lenA <= 1e-12 || lenB <= 1e-12) return 0; const cosTheta = ((ax * bx) + (ay * by)) / (lenA * lenB); const clamped = Math.max(-1, Math.min(1, cosTheta)); return Math.acos(clamped) * (180 / Math.PI); }