diff options
| -rw-r--r-- | client/src/index.js | 102 |
1 files changed, 66 insertions, 36 deletions
diff --git a/client/src/index.js b/client/src/index.js index 7f97349..156e578 100644 --- a/client/src/index.js +++ b/client/src/index.js @@ -10,19 +10,30 @@ import { Bezier } from 'bezier-js'; import css from './ui/css'; -const add = (a, b) => ({ +const add2 = (a, b) => ({ x: a.x + b.x, y: a.y + b.y, }); - -const sub = (a, b) => ({ +const sub2 = (a, b) => ({ x: a.x - b.x, y: a.y - b.y, }); +const mul2 = (a, b) => ({ + x: a.x * (b.x ?? b), + y: a.y * (b.y ?? b), +}); +const len2 = ({ x, y }) => Math.sqrt(x*x + y*y); +const norm2 = (p) => { + const l = len2(p); + return { x: p.x/l, y: p.y/l }; +}; +const dot2 = (a, b) => a.x*b.x + a.y*b.y; -const lerp = (a, b, t) => ({ - x: b.x*t + a.x*(1-t), - y: b.y*t + a.y*(1-t), +const clamp = (min, max, i) => Math.max(min, Math.min(i, max)); +const lerp = (a, b, t) => (b*t + a*(1-t)); +const lerp2 = (a, b, t) => ({ + x: lerp(a.x, b.x, t), + y: lerp(a.y, b.y, t), }); css` @@ -60,11 +71,17 @@ class RectSeg { this.height = +svg.attributes.height.value; } - project({ x, y }) { - return { - x: Math.max(this.x, Math.min(x, this.x + this.width)), - y: Math.max(this.y, Math.min(y, this.y + this.height)), + project(i) { + const p = { + x: this.x + this.width / 2, // clamp(this.x, i.x, this.x + this.width), + y: clamp(this.y, i.y, this.y + this.height), }; + p.d = len2(sub2(p, i)); + return p; + } + + derivative() { + return { x: 0, y: 1 }; } } @@ -118,7 +135,7 @@ const App = () => { } default: - throw new Error(`invalid segment ${seg.type}`); + throw new Error(`invalid segment ${seg.type}`); } last = next; } @@ -130,33 +147,31 @@ const App = () => { }, [svg]); const getClosest = (target) => { - let dist = 999999999 * 999999999; + let seg; let link; - let point; + let point = { d: 999999999 * 999999999 }; for (const newLink of links) { - for (const segment of newLink.segments) { - const newPoint = segment.project(target, newLink.el.attributes.log); - - const d = sub(newPoint, target); - const newDist = d.x*d.x + d.y*d.y; + for (const newSeg of newLink.segments) { + const newPoint = newSeg.project(target, newLink.el.attributes.log); - if (newDist < dist) { - point = newPoint; - dist = newDist; + if (newPoint.d < point.d) { + seg = newSeg; link = newLink; + point = newPoint; } } } - return [point, link, dist]; + return [point, link, seg]; } const [aim, setAim] = useState(null); - const [target, setTarget] = useState({ x: 400, y: 400 }); + const [target, setTarget] = useState({ x: 442, y: 11+87/2 }); const pdown = (e) => { if (aim) return; + e.preventDefault(); setAim({ ...target, @@ -168,33 +183,47 @@ const App = () => { const pmove = (e) => { if (aim?.pointer !== e.pointerId) return; + e.preventDefault(); if (e.type === 'pointerup') return setAim(null); const last = { x: e.clientX, y: e.clientY }; - const delta = inverted ? sub(last, aim.last) : sub(aim.last, last); - - setAim({ ...aim, last, ...add(aim, delta) }); - }; - - useEffect(() => { - if (!aim) return; + const delta = inverted ? sub2(last, aim.last) : sub2(aim.last, last); + const len = len2(delta); + if (!len) return; + + const [nextTarget, link, seg] = getClosest(aim); + + const ALIGN_SPEED = 1; + const MIN_FLOAT_DIST = 30; + const MAX_FLOAT_DIST = 80; + const MIN_FLOAT_SPEED = 0.3; + const MAX_FLOAT_SPEED = 0.8; + + let speed = 1; + const float = clamp(0, 1, (nextTarget.d - MIN_FLOAT_DIST) / MAX_FLOAT_DIST); + if (float == 0) { + const normal = norm2(seg.derivative(nextTarget.t + 1e-15)); + const align = Math.abs(dot2(norm2(delta), normal)); + speed = lerp(MIN_FLOAT_SPEED, ALIGN_SPEED, align); + } else { + speed = lerp(MIN_FLOAT_SPEED, MAX_FLOAT_SPEED, float); + } - const [nextTarget, link] = getClosest(aim); + setAim({ ...aim, last, float, ...add2(aim, mul2(delta, speed)) }); setTarget(nextTarget); for (const el of Array.from(svg.current.querySelectorAll('path, rect'))) { el.classList.toggle('hl', el === link.el); } - }); + }; const [lerped, setLerped] = useState(target); useEffect(() => { - const dest = aim ? lerp(target, aim, 0.8) : target; - setLerped(lerp(lerped, dest, 0.05)); + setLerped(aim ? aim : lerp2(lerped, target, 0.05)); }); const halfScreen = { x: window.innerWidth/2, y: window.innerHeight/2 }; - const offset = sub(halfScreen, lerped); + const offset = sub2(halfScreen, lerped); return ( <article> @@ -247,7 +276,8 @@ const App = () => { <line x1={aim.x} y1={aim.y} x2={target.x} y2={target.y} - stroke="#0f0" stroke-width="1" + stroke="#000" + stroke-width={5 * (1 - aim.float)} /> )} </svg> |
