Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

utils.ts 4.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. import { Bounds } from 'types'
  2. import { ease } from 'utils'
  3. import vec from 'utils/vec'
  4. /**
  5. * ## Utils
  6. */
  7. export default class Utils {
  8. static pointsBetween(a: number[], b: number[], steps = 6): number[][] {
  9. return Array.from(Array(steps))
  10. .map((_, i) => ease(i / steps))
  11. .map((t) => [...vec.lrp(a, b, t), (1 - t) / 2])
  12. }
  13. static getRayRayIntersection(
  14. p0: number[],
  15. n0: number[],
  16. p1: number[],
  17. n1: number[]
  18. ): number[] {
  19. const p0e = vec.add(p0, n0),
  20. p1e = vec.add(p1, n1),
  21. m0 = (p0e[1] - p0[1]) / (p0e[0] - p0[0]),
  22. m1 = (p1e[1] - p1[1]) / (p1e[0] - p1[0]),
  23. b0 = p0[1] - m0 * p0[0],
  24. b1 = p1[1] - m1 * p1[0],
  25. x = (b1 - b0) / (m0 - m1),
  26. y = m0 * x + b0
  27. return [x, y]
  28. }
  29. static getCircleTangentToPoint(
  30. A: number[],
  31. r0: number,
  32. P: number[],
  33. side: number
  34. ): number[] {
  35. const B = vec.lrp(A, P, 0.5),
  36. r1 = vec.dist(A, B),
  37. delta = vec.sub(B, A),
  38. d = vec.len(delta)
  39. if (!(d <= r0 + r1 && d >= Math.abs(r0 - r1))) {
  40. return
  41. }
  42. const a = (r0 * r0 - r1 * r1 + d * d) / (2.0 * d),
  43. n = 1 / d,
  44. p = vec.add(A, vec.mul(delta, a * n)),
  45. h = Math.sqrt(r0 * r0 - a * a),
  46. k = vec.mul(vec.per(delta), h * n)
  47. return side === 0 ? vec.add(p, k) : vec.sub(p, k)
  48. }
  49. static shortAngleDist(a0: number, a1: number): number {
  50. const max = Math.PI * 2
  51. const da = (a1 - a0) % max
  52. return ((2 * da) % max) - da
  53. }
  54. static angleDelta(a0: number, a1: number): number {
  55. return this.shortAngleDist(a0, a1)
  56. }
  57. static getSweep(C: number[], A: number[], B: number[]): number {
  58. return this.angleDelta(vec.angle(C, A), vec.angle(C, B))
  59. }
  60. static bez1d(a: number, b: number, c: number, d: number, t: number): number {
  61. return (
  62. a * (1 - t) * (1 - t) * (1 - t) +
  63. 3 * b * t * (1 - t) * (1 - t) +
  64. 3 * c * t * t * (1 - t) +
  65. d * t * t * t
  66. )
  67. }
  68. static getCubicBezierBounds(
  69. p0: number[],
  70. c0: number[],
  71. c1: number[],
  72. p1: number[]
  73. ): Bounds {
  74. // solve for x
  75. let a = 3 * p1[0] - 9 * c1[0] + 9 * c0[0] - 3 * p0[0]
  76. let b = 6 * p0[0] - 12 * c0[0] + 6 * c1[0]
  77. let c = 3 * c0[0] - 3 * p0[0]
  78. let disc = b * b - 4 * a * c
  79. let xl = p0[0]
  80. let xh = p0[0]
  81. if (p1[0] < xl) xl = p1[0]
  82. if (p1[0] > xh) xh = p1[0]
  83. if (disc >= 0) {
  84. const t1 = (-b + Math.sqrt(disc)) / (2 * a)
  85. if (t1 > 0 && t1 < 1) {
  86. const x1 = Utils.bez1d(p0[0], c0[0], c1[0], p1[0], t1)
  87. if (x1 < xl) xl = x1
  88. if (x1 > xh) xh = x1
  89. }
  90. const t2 = (-b - Math.sqrt(disc)) / (2 * a)
  91. if (t2 > 0 && t2 < 1) {
  92. const x2 = Utils.bez1d(p0[0], c0[0], c1[0], p1[0], t2)
  93. if (x2 < xl) xl = x2
  94. if (x2 > xh) xh = x2
  95. }
  96. }
  97. // Solve for y
  98. a = 3 * p1[1] - 9 * c1[1] + 9 * c0[1] - 3 * p0[1]
  99. b = 6 * p0[1] - 12 * c0[1] + 6 * c1[1]
  100. c = 3 * c0[1] - 3 * p0[1]
  101. disc = b * b - 4 * a * c
  102. let yl = p0[1]
  103. let yh = p0[1]
  104. if (p1[1] < yl) yl = p1[1]
  105. if (p1[1] > yh) yh = p1[1]
  106. if (disc >= 0) {
  107. const t1 = (-b + Math.sqrt(disc)) / (2 * a)
  108. if (t1 > 0 && t1 < 1) {
  109. const y1 = Utils.bez1d(p0[1], c0[1], c1[1], p1[1], t1)
  110. if (y1 < yl) yl = y1
  111. if (y1 > yh) yh = y1
  112. }
  113. const t2 = (-b - Math.sqrt(disc)) / (2 * a)
  114. if (t2 > 0 && t2 < 1) {
  115. const y2 = Utils.bez1d(p0[1], c0[1], c1[1], p1[1], t2)
  116. if (y2 < yl) yl = y2
  117. if (y2 > yh) yh = y2
  118. }
  119. }
  120. return {
  121. minX: xl,
  122. minY: yl,
  123. maxX: xh,
  124. maxY: yh,
  125. width: Math.abs(xl - xh),
  126. height: Math.abs(yl - yh),
  127. }
  128. }
  129. static getExpandedBounds(a: Bounds, b: Bounds): Bounds {
  130. const minX = Math.min(a.minX, b.minX),
  131. minY = Math.min(a.minY, b.minY),
  132. maxX = Math.max(a.maxX, b.maxX),
  133. maxY = Math.max(a.maxY, b.maxY),
  134. width = Math.abs(maxX - minX),
  135. height = Math.abs(maxY - minY)
  136. return { minX, minY, maxX, maxY, width, height }
  137. }
  138. static getCommonBounds(...b: Bounds[]): Bounds {
  139. if (b.length < 2) return b[0]
  140. let bounds = b[0]
  141. for (let i = 1; i < b.length; i++) {
  142. bounds = Utils.getExpandedBounds(bounds, b[i])
  143. }
  144. return bounds
  145. }
  146. }