您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

utils.ts 4.2KB

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