fix: Midpoint controls

Signed-off-by: Mark Tolmacs <mark@lazycat.hu>
This commit is contained in:
Mark Tolmacs
2026-03-23 17:58:26 +00:00
parent 8059518d85
commit 03d46aa62f
+59 -26
View File
@@ -80,8 +80,11 @@ import type {
import type { Drawable, Options } from "roughjs/bin/core";
import type { Point as RoughPoint } from "roughjs/bin/geometry";
const SIMPLE_ROUNDED_ARROW_ENDPOINT_ANGLE_SCALE = 0.3;
const SIMPLE_ROUNDED_ARROW_CP_LENGTH_RATIO = 1 / 4;
const SIMPLE_ROUNDED_ARROW_ENDPOINT_ANGLE_SCALE = 0.25;
// Lerp weight for interior-point tangents: 1 = pure bisector (smooth, no twist possible),
// 0 = chord-aligned (flat). Values between dampen the angle at midpoints without flipping.
const SIMPLE_ROUNDED_ARROW_MIDPOINT_ANGLE_SCALE = 0.9;
const SIMPLE_ROUNDED_ARROW_CP_LENGTH_RATIO = 1 / 3;
export class ShapeCache {
private static rg = new RoughGenerator();
@@ -683,8 +686,16 @@ export const generateLinearCollisionShape = (
bisUx = -inUy;
bisUy = inUx;
}
ptxn[i] = bisUx;
ptyn[i] = bisUy;
const dotMid = bisUx * outUx + bisUy * outUy;
const mx =
(1 - SIMPLE_ROUNDED_ARROW_MIDPOINT_ANGLE_SCALE) * dotMid * outUx +
SIMPLE_ROUNDED_ARROW_MIDPOINT_ANGLE_SCALE * bisUx;
const my =
(1 - SIMPLE_ROUNDED_ARROW_MIDPOINT_ANGLE_SCALE) * dotMid * outUy +
SIMPLE_ROUNDED_ARROW_MIDPOINT_ANGLE_SCALE * bisUy;
const mLen = Math.hypot(mx, my);
ptxn[i] = mx / mLen;
ptyn[i] = my / mLen;
}
// Endpoints: reflect the adjacent interior tangent across the
@@ -1129,11 +1140,16 @@ const debugRoundedArrowControlPoints = (
bisUy = inUx;
}
const bx = bisUx;
const by = bisUy;
const bLen = Math.hypot(bx, by);
txn[i] = bx / bLen;
tyn[i] = by / bLen;
const dotMid = bisUx * outUx + bisUy * outUy;
const mx =
(1 - SIMPLE_ROUNDED_ARROW_MIDPOINT_ANGLE_SCALE) * dotMid * outUx +
SIMPLE_ROUNDED_ARROW_MIDPOINT_ANGLE_SCALE * bisUx;
const my =
(1 - SIMPLE_ROUNDED_ARROW_MIDPOINT_ANGLE_SCALE) * dotMid * outUy +
SIMPLE_ROUNDED_ARROW_MIDPOINT_ANGLE_SCALE * bisUy;
const mLen = Math.hypot(mx, my);
txn[i] = mx / mLen;
tyn[i] = my / mLen;
}
// Endpoints: reflect the adjacent interior tangent across the endpoint's own chord.
@@ -1144,9 +1160,12 @@ const debugRoundedArrowControlPoints = (
const cux = cx / cLen;
const cuy = cy / cLen;
const dot = txn[1] * cux + tyn[1] * cuy;
const eas = SIMPLE_ROUNDED_ARROW_ENDPOINT_ANGLE_SCALE;
const rx = (1 + eas) * dot * cux - eas * txn[1];
const ry = (1 + eas) * dot * cuy - eas * tyn[1];
const rx =
(1 + SIMPLE_ROUNDED_ARROW_ENDPOINT_ANGLE_SCALE) * dot * cux -
SIMPLE_ROUNDED_ARROW_ENDPOINT_ANGLE_SCALE * txn[1];
const ry =
(1 + SIMPLE_ROUNDED_ARROW_ENDPOINT_ANGLE_SCALE) * dot * cuy -
SIMPLE_ROUNDED_ARROW_ENDPOINT_ANGLE_SCALE * tyn[1];
const rLen = Math.hypot(rx, ry);
txn[0] = rx / rLen;
tyn[0] = ry / rLen;
@@ -1158,9 +1177,12 @@ const debugRoundedArrowControlPoints = (
const cux = cx / cLen;
const cuy = cy / cLen;
const dot = txn[n - 2] * cux + tyn[n - 2] * cuy;
const eas = SIMPLE_ROUNDED_ARROW_ENDPOINT_ANGLE_SCALE;
const rx = (1 + eas) * dot * cux - eas * txn[n - 2];
const ry = (1 + eas) * dot * cuy - eas * tyn[n - 2];
const rx =
(1 + SIMPLE_ROUNDED_ARROW_ENDPOINT_ANGLE_SCALE) * dot * cux -
SIMPLE_ROUNDED_ARROW_ENDPOINT_ANGLE_SCALE * txn[n - 2];
const ry =
(1 + SIMPLE_ROUNDED_ARROW_ENDPOINT_ANGLE_SCALE) * dot * cuy -
SIMPLE_ROUNDED_ARROW_ENDPOINT_ANGLE_SCALE * tyn[n - 2];
const rLen = Math.hypot(rx, ry);
txn[n - 1] = rx / rLen;
tyn[n - 1] = ry / rLen;
@@ -1263,11 +1285,16 @@ const generateRoundedSimpleArrowShape = (
bisUy = inUx;
}
const bx = bisUx;
const by = bisUy;
const bLen = Math.hypot(bx, by);
txn[i] = bx / bLen;
tyn[i] = by / bLen;
const dotMid = bisUx * outUx + bisUy * outUy;
const mx =
(1 - SIMPLE_ROUNDED_ARROW_MIDPOINT_ANGLE_SCALE) * dotMid * outUx +
SIMPLE_ROUNDED_ARROW_MIDPOINT_ANGLE_SCALE * bisUx;
const my =
(1 - SIMPLE_ROUNDED_ARROW_MIDPOINT_ANGLE_SCALE) * dotMid * outUy +
SIMPLE_ROUNDED_ARROW_MIDPOINT_ANGLE_SCALE * bisUy;
const mLen = Math.hypot(mx, my);
txn[i] = mx / mLen;
tyn[i] = my / mLen;
}
// Endpoints: reflect the adjacent interior tangent across the endpoint's own chord.
@@ -1280,9 +1307,12 @@ const generateRoundedSimpleArrowShape = (
const cux = cx / cLen;
const cuy = cy / cLen;
const dot = txn[1] * cux + tyn[1] * cuy;
const eas = SIMPLE_ROUNDED_ARROW_ENDPOINT_ANGLE_SCALE;
const rx = (1 + eas) * dot * cux - eas * txn[1];
const ry = (1 + eas) * dot * cuy - eas * tyn[1];
const rx =
(1 + SIMPLE_ROUNDED_ARROW_ENDPOINT_ANGLE_SCALE) * dot * cux -
SIMPLE_ROUNDED_ARROW_ENDPOINT_ANGLE_SCALE * txn[1];
const ry =
(1 + SIMPLE_ROUNDED_ARROW_ENDPOINT_ANGLE_SCALE) * dot * cuy -
SIMPLE_ROUNDED_ARROW_ENDPOINT_ANGLE_SCALE * tyn[1];
const rLen = Math.hypot(rx, ry);
txn[0] = rx / rLen;
tyn[0] = ry / rLen;
@@ -1294,9 +1324,12 @@ const generateRoundedSimpleArrowShape = (
const cux = cx / cLen;
const cuy = cy / cLen;
const dot = txn[n - 2] * cux + tyn[n - 2] * cuy;
const eas = SIMPLE_ROUNDED_ARROW_ENDPOINT_ANGLE_SCALE;
const rx = (1 + eas) * dot * cux - eas * txn[n - 2];
const ry = (1 + eas) * dot * cuy - eas * tyn[n - 2];
const rx =
(1 + SIMPLE_ROUNDED_ARROW_ENDPOINT_ANGLE_SCALE) * dot * cux -
SIMPLE_ROUNDED_ARROW_ENDPOINT_ANGLE_SCALE * txn[n - 2];
const ry =
(1 + SIMPLE_ROUNDED_ARROW_ENDPOINT_ANGLE_SCALE) * dot * cuy -
SIMPLE_ROUNDED_ARROW_ENDPOINT_ANGLE_SCALE * tyn[n - 2];
const rLen = Math.hypot(rx, ry);
txn[n - 1] = rx / rLen;
tyn[n - 1] = ry / rLen;