【JS实现】检测空间三角形相交算法(Devillers & Guigue算法)

本代码参考了C++版本,改写成js版本
判断空间三角形是否相交:
//Devillers & Guigue算法-判断2个三角形是否相交
class JudgeTwoTrianglesIntersect {
  constructor() { }

  //主函数  
  run (tri1, tri2) {

    //设tri1所在的平面为p1,tri2所在的平面为p2  
    let p1Tri2Vertex1 = this.getVector4Det(tri1.vertex_1, tri1.vertex_2, tri1.vertex_3, tri2.vertex_1)
    let p1Tri2Vertex2 = this.getVector4Det(tri1.vertex_1, tri1.vertex_2, tri1.vertex_3, tri2.vertex_2)
    let p1Tri2Vertex3 = this.getVector4Det(tri1.vertex_1, tri1.vertex_3, tri1.vertex_3, tri2.vertex_3)

    if (p1Tri2Vertex1 > 0 && p1Tri2Vertex2 > 0 && p1Tri2Vertex3 > 0) {
      return 'NONINTERSECT'
    }

    if (p1Tri2Vertex1 < 0 && p1Tri2Vertex2 < 0 && p1Tri2Vertex3 < 0) {
      return 'NONINTERSECT'
    }

    if (p1Tri2Vertex1 == 0 && p1Tri2Vertex2 == 0 && p1Tri2Vertex3 == 0) {
      if (this.triangleIntersertInSamePlane(tri1, tri2)) {
        return 'INTERSECT'
      }
      else {
        return 'NONINTERSECT'
      }
    }

    if (p1Tri2Vertex1 == 0 && p1Tri2Vertex2 * p1Tri2Vertex3 > 0) {
      if (this.isPointWithinTriangle(tri1, tri2.vertex_1)) {
        return 'INTERSECT'
      }
      else {
        return 'NONINTERSECT'
      }
    } else if (p1Tri2Vertex2 == 0 && p1Tri2Vertex1 * p1Tri2Vertex3 > 0) {
      if (this.isPointWithinTriangle(tri1, tri2.vertex_2)) {
        return 'INTERSECT'
      } else {
        return 'NONINTERSECT'
      }
    } else if (p1Tri2Vertex3 == 0 && p1Tri2Vertex1 * p1Tri2Vertex2 > 0) {
      if (this.isPointWithinTriangle(tri1, tri2.vertex_3)) {
        return 'INTERSECT'
      }
      else {
        return 'NONINTERSECT'
      }
    }

    let p2Tri1Vertex1 = this.getVector4Det(tri2.vertex_1, tri2.vertex_2, tri2.vertex_3, tri1.vertex_1)
    let p2Tri1Vertex2 = this.getVector4Det(tri2.vertex_1, tri2.vertex_2, tri2.vertex_3, tri1.vertex_2)
    let p2Tri1Vertex3 = this.getVector4Det(tri2.vertex_1, tri2.vertex_2, tri2.vertex_3, tri1.vertex_3)

    if (p2Tri1Vertex1 > 0 && p2Tri1Vertex2 > 0 && p2Tri1Vertex3 > 0) {
      return 'NONINTERSECT'
    }

    if (p2Tri1Vertex1 < 0 && p2Tri1Vertex2 < 0 && p2Tri1Vertex3 < 0) {
      return 'NONINTERSECT'
    }

    if (p2Tri1Vertex1 == 0 && p2Tri1Vertex2 * p2Tri1Vertex3 > 0) {
      if (this.isPointWithinTriangle(tri2, tri1.vertex_1)) {
        return 'INTERSECT'
      }
      else {
        return 'NONINTERSECT'
      }
    }

    if (p2Tri1Vertex2 == 0 && p2Tri1Vertex1 * p2Tri1Vertex3 > 0) {
      if (this.isPointWithinTriangle(tri2, tri1.vertex_2)) {
        return 'INTERSECT'
      }
      else {
        return 'NONINTERSECT'
      }
    }

    if (p2Tri1Vertex3 == 0 && p2Tri1Vertex1 * p2Tri1Vertex2 > 0) {
      if (this.isPointWithinTriangle(tri2, tri1.vertex_3)) {
        return 'INTERSECT'
      }
      else {
        return 'NONINTERSECT'
      }
    }

    let tri1_a = tri1.vertex_1
    let tri1_b = tri1.vertex_2
    let tri1_c = tri1.vertex_3

    let tri2_a = tri2.vertex_1
    let tri2_b = tri2.vertex_2
    let tri2_c = tri2.vertex_3

    let m
    let im
    if (p2Tri1Vertex2 * p2Tri1Vertex3 >= 0 && p2Tri1Vertex1 != 0) {
      if (p2Tri1Vertex1 < 0) {
        m = tri2_b
        tri2_b = tri2_c
        tri2_c = m

        im = p1Tri2Vertex2
        p1Tri2Vertex2 = p1Tri2Vertex3
        p1Tri2Vertex3 = im
      }
    } else if (p2Tri1Vertex1 * p2Tri1Vertex3 >= 0 && p2Tri1Vertex2 != 0) {
      m = tri1_a
      tri1_a = tri1_b
      tri1_b = tri1_c
      tri1_c = m

      if (p2Tri1Vertex2 < 0) {
        m = tri2_b
        tri2_b = tri2_c
        tri2_c = m

        im = p1Tri2Vertex2
        p1Tri2Vertex2 = p1Tri2Vertex3
        p1Tri2Vertex3 = im
      }
    } else if (p2Tri1Vertex1 * p2Tri1Vertex2 >= 0 && p2Tri1Vertex3 != 0) {
      m = tri1_a
      tri1_a = tri1_c
      tri1_c = tri1_b
      tri1_b = m
      if (p2Tri1Vertex3 < 0) {
        m = tri2_b
        tri2_b = tri2_c
        tri2_c = m

        im = p1Tri2Vertex2
        p1Tri2Vertex2 = p1Tri2Vertex3
        p1Tri2Vertex3 = im
      }
    }

    if (p1Tri2Vertex2 * p1Tri2Vertex3 >= 0 && p1Tri2Vertex1 != 0) {
      if (p1Tri2Vertex1 < 0) {
        m = tri1_b
        tri1_b = tri1_c
        tri1_c = m
      }
    } else if (p1Tri2Vertex1 * p1Tri2Vertex3 >= 0 && p1Tri2Vertex2 != 0) {
      m = tri2_a
      tri2_a = tri2_b
      tri2_b = tri2_c
      tri2_c = m
      if (p1Tri2Vertex2 < 0) {
        m = tri1_b
        tri1_b = tri1_c
        tri1_c = m
      }
    } else if (p1Tri2Vertex1 * p1Tri2Vertex2 >= 0 && p1Tri2Vertex3 != 0) {
      m = tri2_a
      tri2_a = tri2_c
      tri2_c = tri2_b
      tri2_b = m
      if (p1Tri2Vertex3 < 0) {
        m = tri1_b
        tri1_b = tri1_c
        tri1_c = m
      }
    }

    if (this.getVector4Det(tri1_a, tri1_b, tri2_a, tri2_b) <= 0 && this.getVector4Det(tri1_a, tri1_c, tri2_c, tri2_a) <= 0) {
      return 'INTERSECT'
    }
    else {
      return 'NONINTERSECT'
    }
  }

  //判断同一平面的直线和三角形是否相交 
  lineTriangleIntersertInSamePlane (tri, f1, f2) {
    const p1 = {}
    const p2 = {}
    const p3 = {}
    const p4 = {}

    p1.x = f1[0]
    p1.y = f1[1]

    p2.x = f2[0]
    p2.y = f2[1]

    p3.x = tri.vertex_1[0]
    p3.y = tri.vertex_1[1]

    p4.x = tri.vertex_2[0]
    p4.y = tri.vertex_2[1]

    if (this.segmentsIntersert(p1, p2, p3, p4)) {
      return true
    }

    p3.x = tri.vertex_2[0]
    p3.y = tri.vertex_2[1]

    p4.x = tri.vertex_3[0]
    p4.y = tri.vertex_3[1]

    if (this.segmentsIntersert(p1, p2, p3, p4)) {
      return true
    }

    p3.x = tri.vertex_1[0]
    p3.y = tri.vertex_1[1]

    p4.x = tri.vertex_3[0]
    p4.y = tri.vertex_3[1]

    if (this.segmentsIntersert(p1, p2, p3, p4)) {
      return true
    }

    return false
  }

  //判断线段p1p2与线段p3p4是否相交
  segmentsIntersert (p1, p2, p3, p4) {
    const d1 = this.direction(p3, p4, p1)
    const d2 = this.direction(p3, p4, p2)
    const d3 = this.direction(p1, p2, p3)
    const d4 = this.direction(p1, p2, p4)

    if (d1 * d2 < 0 && d3 * d4 < 0) {
      return 1
    }
    else if (d1 == 0 && this.onSegment(p3, p4, p1) == 1) {
      return 1
    }
    else if (d2 == 0 && this.onSegment(p3, p4, p2) == 1) {
      return 1
    }
    else if (d3 == 0 && this.onSegment(p1, p2, p3) == 1) {
      return 1
    }
    else if (d4 == 0 && this.onSegment(p1, p2, p4) == 1) {
      return 1
    }
    return 0
  }

  //确定与线段p1p2共线的点p是否在线段p1p2上 
  onSegment (p1, p2, p) {
    const max = p1.x > p2.x ? p1.x : p2.x
    const min = p1.x < p2.x ? p1.x : p2.x
    const max1 = p1.y > p2.y ? p1.y : p2.y
    const min1 = p1.y < p2.y ? p1.y : p2.y
    if (p.x >= min && p.x <= max && p.y >= min1 && p.y <= max1) {
      return 1
    }
    else {
      return 0
    }
  }

  //利用叉积计算点p相对线段p1p2的方位
  direction (p1, p2, p) {
    return (p.x - p1.x) * (p2.y - p1.y) - (p2.x - p1.x) * (p.y - p1.y)
  }

  //判断同一平面内的三角形是否相交  
  triangleIntersertInSamePlane (tri1, tri2) {
    if (this.lineTriangleIntersertInSamePlane(tri2, tri1.vertex_1, tri1.vertex_2)) {
      return true;
    }
    else if (this.lineTriangleIntersertInSamePlane(tri2, tri1.vertex_2, tri1.vertex_3)) {
      return true;
    }
    else if (this.lineTriangleIntersertInSamePlane(tri2, tri1.vertex_1, tri1.vertex_3)) {
      return true;
    }
    else {
      const centralPoint1 = this.getCentralPoint(tri1)
      const centralPoint2 = this.getCentralPoint(tri2)

      if (this.isPointWithinTriangle(tri2, centralPoint1) || this.isPointWithinTriangle(tri1, centralPoint2)) {
        return true;
      }
      return false;
    }
  }

  //重心法判断点是否在三角形内部
  isPointWithinTriangle (tri, point) {
    const v0 = this.getVectorDiff(tri.vertex_1, tri.vertex_3)
    const v1 = this.getVectorDiff(tri.vertex_1, tri.vertex_2)
    const v2 = this.getVectorDiff(tri.vertex_1, point)
    const dot00 = this.Dot(v0, v0)
    const dot01 = this.Dot(v0, v1)
    const dot02 = this.Dot(v0, v2)
    const dot11 = this.Dot(v1, v1)
    const dot12 = this.Dot(v1, v2)
    const inverDeno = 1 / (dot00 * dot11 - dot01 * dot01)
    const u = (dot11 * dot02 - dot01 * dot12) * inverDeno
    if (u < 0 || u > 1) {
      return false
    }
    const v = (dot00 * dot12 - dot01 * dot02) * inverDeno;
    if (v < 0 || v > 1) {
      return false
    }
    return u + v <= 1
  }

  //向量内积
  Dot (v1, v2) {
    return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]
  }

  //向量之差
  getVectorDiff (a, b) {
    const aimV = []
    aimV.push(b[0] - a[0])
    aimV.push(b[1] - a[1])
    aimV.push(b[2] - a[2])
    return aimV
  }

  //获取三角形的中心点
  getCentralPoint (tri) {
    const centralPoint = []
    centralPoint.push((tri.vertex_1[0] + tri.vertex_2[0] + tri.vertex_3[0]) / 3)
    centralPoint.push((tri.vertex_1[1] + tri.vertex_2[1] + tri.vertex_3[1]) / 3)
    centralPoint.push((tri.vertex_1[2] + tri.vertex_2[2] + tri.vertex_3[2]) / 3)
    return centralPoint
  }

  //四点行列式 
  getVector4Det (v1, v2, v3, v4) {
    let a = [[], [], []]
    for (let i = 0; i < 3; i++) {
      a[0].push(v1[i] - v4[i])
      a[1].push(v2[i] - v4[i])
      a[2].push(v3[i] - v4[i])
    }

    return a[0][0] * a[1][1] * a[2][2]
      + a[0][1] * a[1][2] * a[2][0]
      + a[0][2] * a[1][0] * a[2][1]
      - a[0][2] * a[1][1] * a[2][0]
      - a[0][1] * a[1][0] * a[2][2]
      - a[0][0] * a[1][2] * a[2][1]
  }
}
exports.JudgeTwoTrianglesIntersect = JudgeTwoTrianglesIntersect

调用方法:

const triA = {
  vertex_1: [ -25.18, -6.06, 205.79 ],
  vertex_2: [ -25.18, -6.07, 205.79 ],
  vertex_3: [ -25.17, -6.07, 205.79 ]
}

const triB = {
  vertex_1: [ -25.17, -6.06, 205.79 ],
  vertex_2: [ -25.17, -6.05, 205.79 ],
  vertex_3: [ -25.18, -6.05, 205.79 ]
}

const intersectSt = new JudgeTwoTrianglesIntersect()
const res = intersectSt.run(triA, triB)
console.log(res)

参考:
[1] https://www.cnblogs.com/lyggqm/p/5977129.html
[2] 《快速空间三角形对相交检测算法》邹益胜

你可能感兴趣的:(算法,算法,javascript,开发语言)