You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

333 lines
9.0 KiB
JavaScript

const { abs, sqrt, sin, cos, max, min, PI } = Math
/**
* @description Clone an object or array
* @param {Object|Array} object Cloned object
* @param {Boolean} recursion Whether to use recursive cloning
* @return {Object|Array} Clone object
*/
export function deepClone (object, recursion = false) {
if (!object) return object
const { parse, stringify } = JSON
if (!recursion) return parse(stringify(object))
const clonedObj = object instanceof Array ? [] : {}
if (object && typeof object === 'object') {
for (let key in object) {
if (object.hasOwnProperty(key)) {
if (object[key] && typeof object[key] === 'object') {
clonedObj[key] = deepClone(object[key], true)
} else {
clonedObj[key] = object[key]
}
}
}
}
return clonedObj
}
/**
* @description Eliminate line blur due to 1px line width
* @param {Array} points Line points
* @return {Array} Line points after processed
*/
export function eliminateBlur (points) {
return points.map(([x, y]) => [parseInt(x) + 0.5, parseInt(y) + 0.5])
}
/**
* @description Check if the point is inside the circle
* @param {Array} point Postion of point
* @param {Number} rx Circle x coordinate
* @param {Number} ry Circle y coordinate
* @param {Number} r Circle radius
* @return {Boolean} Result of check
*/
export function checkPointIsInCircle (point, rx, ry, r) {
return getTwoPointDistance(point, [rx, ry]) <= r
}
/**
* @description Get the distance between two points
* @param {Array} point1 point1
* @param {Array} point2 point2
* @return {Number} Distance between two points
*/
export function getTwoPointDistance ([xa, ya], [xb, yb]) {
const minusX = abs(xa - xb)
const minusY = abs(ya - yb)
return sqrt(minusX * minusX + minusY * minusY)
}
/**
* @description Check if the point is inside the polygon
* @param {Array} point Postion of point
* @param {Array} points The points that makes up a polyline
* @return {Boolean} Result of check
*/
export function checkPointIsInPolygon (point, polygon) {
let counter = 0
const [x, y] = point
const pointNum = polygon.length
for (let i = 1, p1 = polygon[0]; i <= pointNum; i++) {
const p2 = polygon[i % pointNum]
if (x > min(p1[0], p2[0]) && x <= max(p1[0], p2[0])) {
if (y <= max(p1[1], p2[1])) {
if (p1[0] !== p2[0]) {
const xinters = (x - p1[0]) * (p2[1] - p1[1]) / (p2[0] - p1[0]) + p1[1]
if (p1[1] === p2[1] || y <= xinters) {
counter++
}
}
}
}
p1 = p2
}
return counter % 2 === 1
}
/**
* @description Check if the point is inside the sector
* @param {Array} point Postion of point
* @param {Number} rx Sector x coordinate
* @param {Number} ry Sector y coordinate
* @param {Number} r Sector radius
* @param {Number} startAngle Sector start angle
* @param {Number} endAngle Sector end angle
* @param {Boolean} clockWise Whether the sector angle is clockwise
* @return {Boolean} Result of check
*/
export function checkPointIsInSector (point, rx, ry, r, startAngle, endAngle, clockWise) {
if (!point) return false
if (getTwoPointDistance(point, [rx, ry]) > r) return false
if (!clockWise) [startAngle, endAngle] = deepClone([endAngle, startAngle])
const reverseBE = startAngle > endAngle
if (reverseBE) [startAngle, endAngle] = [endAngle, startAngle]
const minus = endAngle - startAngle
if (minus >= PI * 2) return true
const [x, y] = point
const [bx, by] = getCircleRadianPoint(rx, ry, r, startAngle)
const [ex, ey] = getCircleRadianPoint(rx, ry, r, endAngle)
const vPoint = [x - rx, y - ry]
let vBArm = [bx - rx, by - ry]
let vEArm = [ex - rx, ey - ry]
const reverse = minus > PI
if (reverse) [vBArm, vEArm] = deepClone([vEArm, vBArm])
let inSector = isClockWise(vBArm, vPoint) && !isClockWise(vEArm, vPoint)
if (reverse) inSector = !inSector
if (reverseBE) inSector = !inSector
return inSector
}
/**
* @description Determine if the point is in the clockwise direction of the vector
* @param {Array} vArm Vector
* @param {Array} vPoint Point
* @return {Boolean} Result of check
*/
function isClockWise (vArm, vPoint) {
const [ax, ay] = vArm
const [px, py] = vPoint
return -ay * px + ax * py > 0
}
/**
* @description Check if the point is inside the polyline
* @param {Array} point Postion of point
* @param {Array} polyline The points that makes up a polyline
* @param {Number} lineWidth Polyline linewidth
* @return {Boolean} Result of check
*/
export function checkPointIsNearPolyline (point, polyline, lineWidth) {
const halfLineWidth = lineWidth / 2
const moveUpPolyline = polyline.map(([x, y]) => [x, y - halfLineWidth])
const moveDownPolyline = polyline.map(([x, y]) => [x, y + halfLineWidth])
const polygon = [...moveUpPolyline, ...moveDownPolyline.reverse()]
return checkPointIsInPolygon(point, polygon)
}
/**
* @description Check if the point is inside the rect
* @param {Array} point Postion of point
* @param {Number} x Rect start x coordinate
* @param {Number} y Rect start y coordinate
* @param {Number} width Rect width
* @param {Number} height Rect height
* @return {Boolean} Result of check
*/
export function checkPointIsInRect ([px, py], x, y, width, height) {
if (px < x) return false
if (py < y) return false
if (px > x + width) return false
if (py > y + height) return false
return true
}
/**
* @description Get the coordinates of the rotated point
* @param {Number} rotate Degree of rotation
* @param {Array} point Postion of point
* @param {Array} origin Rotation center
* @param {Array} origin Rotation center
* @return {Number} Coordinates after rotation
*/
export function getRotatePointPos (rotate = 0, point, origin = [0, 0]) {
if (!point) return false
if (rotate % 360 === 0) return point
const [x, y] = point
const [ox, oy] = origin
rotate *= PI / 180
return [
(x - ox) * cos(rotate) - (y - oy) * sin(rotate) + ox,
(x - ox) * sin(rotate) + (y - oy) * cos(rotate) + oy
]
}
/**
* @description Get the coordinates of the scaled point
* @param {Array} scale Scale factor
* @param {Array} point Postion of point
* @param {Array} origin Scale center
* @return {Number} Coordinates after scale
*/
export function getScalePointPos (scale = [1, 1], point, origin = [0, 0]) {
if (!point) return false
if (scale === 1) return point
const [x, y] = point
const [ox, oy] = origin
const [xs, ys] = scale
const relativePosX = x - ox
const relativePosY = y - oy
return [
relativePosX * xs + ox,
relativePosY * ys + oy
]
}
/**
* @description Get the coordinates of the scaled point
* @param {Array} translate Translation distance
* @param {Array} point Postion of point
* @return {Number} Coordinates after translation
*/
export function getTranslatePointPos (translate, point) {
if (!translate || !point) return false
const [x, y] = point
const [tx, ty] = translate
return [x + tx, y + ty]
}
/**
* @description Get the distance from the point to the line
* @param {Array} point Postion of point
* @param {Array} lineBegin Line start position
* @param {Array} lineEnd Line end position
* @return {Number} Distance between point and line
*/
export function getDistanceBetweenPointAndLine (point, lineBegin, lineEnd) {
if (!point || !lineBegin || !lineEnd) return false
const [x, y] = point
const [x1, y1] = lineBegin
const [x2, y2] = lineEnd
const a = y2 - y1
const b = x1 - x2
const c = y1 * (x2 - x1) - x1 * (y2 - y1)
const molecule = abs(a * x + b * y + c)
const denominator = sqrt(a * a + b * b)
return molecule / denominator
}
/**
* @description Get the coordinates of the specified radian on the circle
* @param {Number} x Circle x coordinate
* @param {Number} y Circle y coordinate
* @param {Number} radius Circle radius
* @param {Number} radian Specfied radian
* @return {Array} Postion of point
*/
export function getCircleRadianPoint (x, y, radius, radian) {
return [x + cos(radian) * radius, y + sin(radian) * radius]
}
/**
* @description Get the points that make up a regular polygon
* @param {Number} x X coordinate of the polygon inscribed circle
* @param {Number} y Y coordinate of the polygon inscribed circle
* @param {Number} r Radius of the polygon inscribed circle
* @param {Number} side Side number
* @param {Number} minus Radian offset
* @return {Array} Points that make up a regular polygon
*/
export function getRegularPolygonPoints (rx, ry, r, side, minus = PI * -0.5) {
const radianGap = PI * 2 / side
const radians = new Array(side).fill('').map((t, i) => i * radianGap + minus)
return radians.map(radian => getCircleRadianPoint(rx, ry, r, radian))
}
export default {
deepClone,
eliminateBlur,
checkPointIsInCircle,
checkPointIsInPolygon,
checkPointIsInSector,
checkPointIsNearPolyline,
getTwoPointDistance,
getRotatePointPos,
getScalePointPos,
getTranslatePointPos,
getCircleRadianPoint,
getRegularPolygonPoints,
getDistanceBetweenPointAndLine
}