多边形等距缩放算法 + 顺时针判断,js版,即插即用

多边形等距缩放算法 + 顺时针判断,js版,即插即用

    • 一、效果
    • 二、算法
    • 三、代码
      • 使用示例:
      • 缩放多边形代码:
      • 判断顺时针代码:

一、效果

红色为原图,绿色为扩展或者缩小后新图,新图叠加到原图上的效果:
多边形等距缩放算法 + 顺时针判断,js版,即插即用_第1张图片

二、算法

参考:https://blog.csdn.net/sun_and_breeze/article/details/107517088
参考:https://blog.csdn.net/shyjhyp11/article/details/126396170

三、代码

需配合‘顺时针判断’方法一起使用

使用示例:

cosnt path = [
          [59.68109339407744, 131.20728929384967],
          [111.16173120728931, 147.15261958997723],
          [131.66287015945332, 98.40546697038725],
          [76.5375854214123, 92.93849658314352],
        ],
const newPath = scalePolygon(path,10)

缩放多边形代码:

/**
 * 缩放多边形坐标
 * @decoration 需配合顺时针判断方法一起使用
 * @param {number[][]} paths 多边形顶点数组
 * @param {number} extra 外延大小。为正: 向外扩; 为负: 向内缩
 * @return {number[][]} 扩展或缩小后的多边形顶点数组
 */
function scalePolygon(paths, extra) {
  const ps = paths
  // 通过顺时针判断取正值还是负值
  const extra0 = isClockwise(ps) ? -extra : extra

  const norm = (x, y) => Math.sqrt(x * x + y * y)

  const len = ps.length
  const polygon = []
  for (let i = 0; i < len; i++) {
    const point = ps[i]
    const point1 = ps[i === 0 ? len - 1 : i - 1]
    const point2 = ps[i === len - 1 ? 0 : i + 1]

    // 向量PP1
    const vectorX1 = point1[0] - point[0] // 向量PP1 横坐标
    const vectorY1 = point1[1] - point[1] // 向量PP1 纵坐标
    const n1 = norm(vectorX1, vectorY1) // 向量的平方根 为了对向量PP1做单位化
    let vectorUnitX1 = vectorX1 / n1 // 向量单位化 横坐标
    let vectorUnitY1 = vectorY1 / n1 // 向量单位化 纵坐标

    // 向量PP2
    const vectorX2 = point2[0] - point[0] // 向量PP2 横坐标
    const vectorY2 = point2[1] - point[1] // 向量PP2 纵坐标
    const n2 = norm(vectorX2, vectorY2) // 向量的平方根 为了对向量PP1做单位化
    let vectorUnitX2 = vectorX2 / n2 // 向量单位化 横坐标
    let vectorUnitY2 = vectorY2 / n2 // 向量单位化 纵坐标

    // PQ距离
    const vectorLen = -extra0 / Math.sqrt((1 - (vectorUnitX1 * vectorUnitX2 + vectorUnitY1 * vectorUnitY2)) / 2)

    // 根据向量的叉乘积来判断角是凹角还是凸角
    if (vectorX1 * vectorY2 + -1 * vectorY1 * vectorX2 < 0) {
      vectorUnitX2 *= -1
      vectorUnitY2 *= -1
      vectorUnitX1 *= -1
      vectorUnitY1 *= -1
    }

    // PQ的方向
    const vectorX = vectorUnitX1 + vectorUnitX2
    const vectorY = vectorUnitY1 + vectorUnitY2
    const n = vectorLen / norm(vectorX, vectorY)
    const vectorUnitX = vectorX * n
    const vectorUnitY = vectorY * n

    const polygonX = vectorUnitX + point[0]
    const polygonY = vectorUnitY + point[1]

    polygon[i] = [polygonX, polygonY]
  }

  return polygon
}

判断顺时针代码:

/**
 * 判断坐标数组是否顺时针(默认为false)
 * @param {number[][]} paths 坐标二维数组 [[1,0],....]
 * @returns {boolean} 是否顺时针
 */
function isClockwise(paths) {
  // 三个点可以判断矢量是顺时针旋转还是逆时针旋转的,但由于可能存在凹边,所以并不是任意三点都可以正确反映多边形的走向
  // 因此需要取多边形中绝对是凸边的点来判断,
  // 多边形中的极值点(x最大或x最小或y最大或y最小)它与相邻两点构成的边必然是凸边,因此我们先取出多边形中的极值点,再由极值点和其前后两点去判断矢量的走向,从而判断出多边形的走向。
  if (!Array.isArray(paths) || paths.length < 3) {
    return false
  }
  let coords = JSON.parse(JSON.stringify(paths))

  if (coords[0] === coords[coords.length - 1]) {
    coords = coords.slice(0, coords.length - 1)
  }
  coords = coords.reverse()
  let maxXIndex = 0
  let maxX = parseFloat(coords[maxXIndex][0])
  let c1
  let c2
  let c3
  for (let i = 0; i < coords.length; i++) {
    if (parseFloat(coords[i][0]) > maxX) {
      maxX = parseFloat(coords[i][0])
      maxXIndex = i
    }
  }
  if (maxXIndex === 0) {
    c1 = coords[coords.length - 1]
    c2 = coords[maxXIndex]
    c3 = coords[maxXIndex + 1]
  } else if (maxXIndex === coords.length - 1) {
    c1 = coords[maxXIndex - 1]
    c2 = coords[maxXIndex]
    c3 = coords[0]
  } else {
    c1 = coords[maxXIndex - 1]
    c2 = coords[maxXIndex]
    c3 = coords[maxXIndex + 1]
  }
  const x1 = parseFloat(c1[0])
  const y1 = parseFloat(c1[1])
  const x2 = parseFloat(c2[0])
  const y2 = parseFloat(c2[1])
  const x3 = parseFloat(c3[0])
  const y3 = parseFloat(c3[1])
  const s = (x1 - x3) * (y2 - y3) - (x2 - x3) * (y1 - y3)
  return s < 0
}

你可能感兴趣的:(#,JS,算法,javascript)