cd514d72d6
Introduces constant width freedraw mode, keeping the original variable mode as default. --------- Signed-off-by: Mark Tolmacs <mark@lazycat.hu> Co-authored-by: dwelle <5153846+dwelle@users.noreply.github.com>
106 lines
2.3 KiB
TypeScript
106 lines
2.3 KiB
TypeScript
export type Point = [x: number, y: number, r: number];
|
|
|
|
export function add([ax, ay, ar]: Point, [bx, by, br]: Point): Point {
|
|
return [ax + bx, ay + by, ar + br];
|
|
}
|
|
|
|
export function sub([ax, ay, ar]: Point, [bx, by, br]: Point): Point {
|
|
return [ax - bx, ay - by, ar - br];
|
|
}
|
|
|
|
export function smul([x, y, r]: Point, s: number): Point {
|
|
return [x * s, y * s, r * s];
|
|
}
|
|
|
|
export function norm([x, y, r]: Point): Point {
|
|
return [x / Math.sqrt(x ** 2 + y ** 2), y / Math.sqrt(x ** 2 + y ** 2), r];
|
|
}
|
|
|
|
export function rot([x, y, r]: Point, rad: number): Point {
|
|
return [
|
|
Math.cos(rad) * x - Math.sin(rad) * y,
|
|
Math.sin(rad) * x + Math.cos(rad) * y,
|
|
r,
|
|
];
|
|
}
|
|
|
|
export function plerp(a: Point, b: Point, t: number): Point {
|
|
return add(a, smul(sub(b, a), t));
|
|
}
|
|
|
|
export function lerp(a: number, b: number, t: number): number {
|
|
return a + (b - a) * t;
|
|
}
|
|
|
|
export function angle(p: Point, p1: Point, p2: Point) {
|
|
return (
|
|
Math.atan2(p2[1] - p[1], p2[0] - p[0]) -
|
|
Math.atan2(p1[1] - p[1], p1[0] - p[0])
|
|
);
|
|
}
|
|
|
|
export function normAngle(a: number) {
|
|
return Math.atan2(Math.sin(a), Math.cos(a));
|
|
}
|
|
|
|
export function mag([x, y]: Point) {
|
|
return Math.sqrt(x ** 2 + y ** 2);
|
|
}
|
|
|
|
export function dist([ax, ay]: Point, [bx, by]: Point): number {
|
|
return Math.sqrt((bx - ax) ** 2 + (by - ay) ** 2);
|
|
}
|
|
|
|
export function getCircleAndPerpendicularLineIntersectionsAtPoint(
|
|
point: Point,
|
|
direction: Point,
|
|
radius: number,
|
|
): [Point, Point] {
|
|
return [
|
|
add(point, smul(norm(rot(direction, Math.PI / 2)), radius)),
|
|
add(point, smul(norm(rot(direction, -Math.PI / 2)), radius)),
|
|
];
|
|
}
|
|
|
|
export function runLength(ps: Point[]): number {
|
|
if (ps.length < 2) {
|
|
return 0;
|
|
}
|
|
|
|
let len = 0;
|
|
|
|
for (let i = 1; i <= ps.length - 1; i++) {
|
|
len += dist(ps[i - 1], ps[i]);
|
|
}
|
|
|
|
len += dist(ps[ps.length - 2], ps[ps.length - 1]);
|
|
|
|
return len;
|
|
}
|
|
|
|
export const clamp = (v: number, min: number, max: number) =>
|
|
Math.max(min, Math.min(max, v));
|
|
|
|
export function distancePointToSegment(p3: Point, p1: Point, p2: Point) {
|
|
const sMag = dist(p1, p2);
|
|
|
|
if (sMag === 0) {
|
|
return dist(p3, p1);
|
|
}
|
|
|
|
const u = clamp(
|
|
((p3[0] - p1[0]) * (p2[0] - p1[0]) + (p3[1] - p1[1]) * (p2[1] - p1[1])) /
|
|
sMag ** 2,
|
|
0,
|
|
1,
|
|
);
|
|
|
|
const pi: Point = [
|
|
p1[0] + u * (p2[0] - p1[0]),
|
|
p1[1] + u * (p2[1] - p1[1]),
|
|
p3[2],
|
|
];
|
|
|
|
return dist(pi, p3);
|
|
}
|