util.js 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. const { abs, sqrt, sin, cos, max, min, PI } = Math
  2. /**
  3. * @description Clone an object or array
  4. * @param {Object|Array} object Cloned object
  5. * @param {Boolean} recursion Whether to use recursive cloning
  6. * @return {Object|Array} Clone object
  7. */
  8. export function deepClone (object, recursion = false) {
  9. if (!object) return object
  10. const { parse, stringify } = JSON
  11. if (!recursion) return parse(stringify(object))
  12. const clonedObj = object instanceof Array ? [] : {}
  13. if (object && typeof object === 'object') {
  14. for (let key in object) {
  15. if (object.hasOwnProperty(key)) {
  16. if (object[key] && typeof object[key] === 'object') {
  17. clonedObj[key] = deepClone(object[key], true)
  18. } else {
  19. clonedObj[key] = object[key]
  20. }
  21. }
  22. }
  23. }
  24. return clonedObj
  25. }
  26. /**
  27. * @description Eliminate line blur due to 1px line width
  28. * @param {Array} points Line points
  29. * @return {Array} Line points after processed
  30. */
  31. export function eliminateBlur (points) {
  32. return points.map(([x, y]) => [parseInt(x) + 0.5, parseInt(y) + 0.5])
  33. }
  34. /**
  35. * @description Check if the point is inside the circle
  36. * @param {Array} point Postion of point
  37. * @param {Number} rx Circle x coordinate
  38. * @param {Number} ry Circle y coordinate
  39. * @param {Number} r Circle radius
  40. * @return {Boolean} Result of check
  41. */
  42. export function checkPointIsInCircle (point, rx, ry, r) {
  43. return getTwoPointDistance(point, [rx, ry]) <= r
  44. }
  45. /**
  46. * @description Get the distance between two points
  47. * @param {Array} point1 point1
  48. * @param {Array} point2 point2
  49. * @return {Number} Distance between two points
  50. */
  51. export function getTwoPointDistance ([xa, ya], [xb, yb]) {
  52. const minusX = abs(xa - xb)
  53. const minusY = abs(ya - yb)
  54. return sqrt(minusX * minusX + minusY * minusY)
  55. }
  56. /**
  57. * @description Check if the point is inside the polygon
  58. * @param {Array} point Postion of point
  59. * @param {Array} points The points that makes up a polyline
  60. * @return {Boolean} Result of check
  61. */
  62. export function checkPointIsInPolygon (point, polygon) {
  63. let counter = 0
  64. const [x, y] = point
  65. const pointNum = polygon.length
  66. for (let i = 1, p1 = polygon[0]; i <= pointNum; i++) {
  67. const p2 = polygon[i % pointNum]
  68. if (x > min(p1[0], p2[0]) && x <= max(p1[0], p2[0])) {
  69. if (y <= max(p1[1], p2[1])) {
  70. if (p1[0] !== p2[0]) {
  71. const xinters = (x - p1[0]) * (p2[1] - p1[1]) / (p2[0] - p1[0]) + p1[1]
  72. if (p1[1] === p2[1] || y <= xinters) {
  73. counter++
  74. }
  75. }
  76. }
  77. }
  78. p1 = p2
  79. }
  80. return counter % 2 === 1
  81. }
  82. /**
  83. * @description Check if the point is inside the sector
  84. * @param {Array} point Postion of point
  85. * @param {Number} rx Sector x coordinate
  86. * @param {Number} ry Sector y coordinate
  87. * @param {Number} r Sector radius
  88. * @param {Number} startAngle Sector start angle
  89. * @param {Number} endAngle Sector end angle
  90. * @param {Boolean} clockWise Whether the sector angle is clockwise
  91. * @return {Boolean} Result of check
  92. */
  93. export function checkPointIsInSector (point, rx, ry, r, startAngle, endAngle, clockWise) {
  94. if (!point) return false
  95. if (getTwoPointDistance(point, [rx, ry]) > r) return false
  96. if (!clockWise) [startAngle, endAngle] = deepClone([endAngle, startAngle])
  97. const reverseBE = startAngle > endAngle
  98. if (reverseBE) [startAngle, endAngle] = [endAngle, startAngle]
  99. const minus = endAngle - startAngle
  100. if (minus >= PI * 2) return true
  101. const [x, y] = point
  102. const [bx, by] = getCircleRadianPoint(rx, ry, r, startAngle)
  103. const [ex, ey] = getCircleRadianPoint(rx, ry, r, endAngle)
  104. const vPoint = [x - rx, y - ry]
  105. let vBArm = [bx - rx, by - ry]
  106. let vEArm = [ex - rx, ey - ry]
  107. const reverse = minus > PI
  108. if (reverse) [vBArm, vEArm] = deepClone([vEArm, vBArm])
  109. let inSector = isClockWise(vBArm, vPoint) && !isClockWise(vEArm, vPoint)
  110. if (reverse) inSector = !inSector
  111. if (reverseBE) inSector = !inSector
  112. return inSector
  113. }
  114. /**
  115. * @description Determine if the point is in the clockwise direction of the vector
  116. * @param {Array} vArm Vector
  117. * @param {Array} vPoint Point
  118. * @return {Boolean} Result of check
  119. */
  120. function isClockWise (vArm, vPoint) {
  121. const [ax, ay] = vArm
  122. const [px, py] = vPoint
  123. return -ay * px + ax * py > 0
  124. }
  125. /**
  126. * @description Check if the point is inside the polyline
  127. * @param {Array} point Postion of point
  128. * @param {Array} polyline The points that makes up a polyline
  129. * @param {Number} lineWidth Polyline linewidth
  130. * @return {Boolean} Result of check
  131. */
  132. export function checkPointIsNearPolyline (point, polyline, lineWidth) {
  133. const halfLineWidth = lineWidth / 2
  134. const moveUpPolyline = polyline.map(([x, y]) => [x, y - halfLineWidth])
  135. const moveDownPolyline = polyline.map(([x, y]) => [x, y + halfLineWidth])
  136. const polygon = [...moveUpPolyline, ...moveDownPolyline.reverse()]
  137. return checkPointIsInPolygon(point, polygon)
  138. }
  139. /**
  140. * @description Check if the point is inside the rect
  141. * @param {Array} point Postion of point
  142. * @param {Number} x Rect start x coordinate
  143. * @param {Number} y Rect start y coordinate
  144. * @param {Number} width Rect width
  145. * @param {Number} height Rect height
  146. * @return {Boolean} Result of check
  147. */
  148. export function checkPointIsInRect ([px, py], x, y, width, height) {
  149. if (px < x) return false
  150. if (py < y) return false
  151. if (px > x + width) return false
  152. if (py > y + height) return false
  153. return true
  154. }
  155. /**
  156. * @description Get the coordinates of the rotated point
  157. * @param {Number} rotate Degree of rotation
  158. * @param {Array} point Postion of point
  159. * @param {Array} origin Rotation center
  160. * @param {Array} origin Rotation center
  161. * @return {Number} Coordinates after rotation
  162. */
  163. export function getRotatePointPos (rotate = 0, point, origin = [0, 0]) {
  164. if (!point) return false
  165. if (rotate % 360 === 0) return point
  166. const [x, y] = point
  167. const [ox, oy] = origin
  168. rotate *= PI / 180
  169. return [
  170. (x - ox) * cos(rotate) - (y - oy) * sin(rotate) + ox,
  171. (x - ox) * sin(rotate) + (y - oy) * cos(rotate) + oy
  172. ]
  173. }
  174. /**
  175. * @description Get the coordinates of the scaled point
  176. * @param {Array} scale Scale factor
  177. * @param {Array} point Postion of point
  178. * @param {Array} origin Scale center
  179. * @return {Number} Coordinates after scale
  180. */
  181. export function getScalePointPos (scale = [1, 1], point, origin = [0, 0]) {
  182. if (!point) return false
  183. if (scale === 1) return point
  184. const [x, y] = point
  185. const [ox, oy] = origin
  186. const [xs, ys] = scale
  187. const relativePosX = x - ox
  188. const relativePosY = y - oy
  189. return [
  190. relativePosX * xs + ox,
  191. relativePosY * ys + oy
  192. ]
  193. }
  194. /**
  195. * @description Get the coordinates of the scaled point
  196. * @param {Array} translate Translation distance
  197. * @param {Array} point Postion of point
  198. * @return {Number} Coordinates after translation
  199. */
  200. export function getTranslatePointPos (translate, point) {
  201. if (!translate || !point) return false
  202. const [x, y] = point
  203. const [tx, ty] = translate
  204. return [x + tx, y + ty]
  205. }
  206. /**
  207. * @description Get the distance from the point to the line
  208. * @param {Array} point Postion of point
  209. * @param {Array} lineBegin Line start position
  210. * @param {Array} lineEnd Line end position
  211. * @return {Number} Distance between point and line
  212. */
  213. export function getDistanceBetweenPointAndLine (point, lineBegin, lineEnd) {
  214. if (!point || !lineBegin || !lineEnd) return false
  215. const [x, y] = point
  216. const [x1, y1] = lineBegin
  217. const [x2, y2] = lineEnd
  218. const a = y2 - y1
  219. const b = x1 - x2
  220. const c = y1 * (x2 - x1) - x1 * (y2 - y1)
  221. const molecule = abs(a * x + b * y + c)
  222. const denominator = sqrt(a * a + b * b)
  223. return molecule / denominator
  224. }
  225. /**
  226. * @description Get the coordinates of the specified radian on the circle
  227. * @param {Number} x Circle x coordinate
  228. * @param {Number} y Circle y coordinate
  229. * @param {Number} radius Circle radius
  230. * @param {Number} radian Specfied radian
  231. * @return {Array} Postion of point
  232. */
  233. export function getCircleRadianPoint (x, y, radius, radian) {
  234. return [x + cos(radian) * radius, y + sin(radian) * radius]
  235. }
  236. /**
  237. * @description Get the points that make up a regular polygon
  238. * @param {Number} x X coordinate of the polygon inscribed circle
  239. * @param {Number} y Y coordinate of the polygon inscribed circle
  240. * @param {Number} r Radius of the polygon inscribed circle
  241. * @param {Number} side Side number
  242. * @param {Number} minus Radian offset
  243. * @return {Array} Points that make up a regular polygon
  244. */
  245. export function getRegularPolygonPoints (rx, ry, r, side, minus = PI * -0.5) {
  246. const radianGap = PI * 2 / side
  247. const radians = new Array(side).fill('').map((t, i) => i * radianGap + minus)
  248. return radians.map(radian => getCircleRadianPoint(rx, ry, r, radian))
  249. }
  250. export default {
  251. deepClone,
  252. eliminateBlur,
  253. checkPointIsInCircle,
  254. checkPointIsInPolygon,
  255. checkPointIsInSector,
  256. checkPointIsNearPolyline,
  257. getTwoPointDistance,
  258. getRotatePointPos,
  259. getScalePointPos,
  260. getTranslatePointPos,
  261. getCircleRadianPoint,
  262. getRegularPolygonPoints,
  263. getDistanceBetweenPointAndLine
  264. }