File size: 2,390 Bytes
c3e4a23
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
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);
}