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

utils.ts 3.9KB

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