aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--client/src/index.js102
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>