TriangleInt triangle_intersection(const vec3 tri_a[3], const vec3 tri_b[3]) {
      if (sat_bbox(tri_a, tri_b)) return TR_INT_NONE;

      sat_t norm1 = sat_normal(tri_a, tri_b);
      sat_t norm2 = sat_normal(tri_b, tri_a);
      if (norm2 == SAT_NONE) return TR_INT_NONE;
      if (norm1 == SAT_NONE) return TR_INT_NONE;

      if (norm2 == SAT_COPLANAR) return TR_INT_NONE;
      if (norm1 == SAT_COPLANAR) return TR_INT_NONE;

      if (norm1 == SAT_COPLANAR) {
        return triangle_intersection_coplanar(tri_a, tri_b);
      }


      size_t ia, ib;
      int sharing = test_sharing<3>(tri_a, tri_b, ia, ib);
      switch (std::abs(sharing)) {
      case 0: {
        // no shared vertices.
        for (size_t i = 0; i < 3; ++i) {
          for (size_t j = 0; j < 3; ++j) {
            if (sat_plane(tri_a, tri_b, i, j)) return TR_INT_NONE;
            if (sat_plane(tri_b, tri_a, i, j)) return TR_INT_NONE;
          }
        }

        return TR_INT_INT;
      }
      case 1: {
        // shared vertex (ia, ib) [but not shared edge, and not coplanar]
        size_t ia0 = ia, ia1 = (ia+1)%3, ia2 = (ia+2)%3;
        size_t ib0 = ib, ib1 = (ib+1)%3, ib2 = (ib+2)%3;

        if (sat_plane(tri_a, tri_b, ia2, ib1, ib2)) return TR_INT_VERT;
        if (sat_plane(tri_a, tri_b, ia2, ib2, ib1)) return TR_INT_VERT;
        if (sat_plane(tri_a, tri_b, ia0, ib1, ib2)) return TR_INT_VERT;
        if (sat_plane(tri_a, tri_b, ia0, ib2, ib1)) return TR_INT_VERT;
        if (sat_plane(tri_b, tri_b, ib2, ia1, ia2)) return TR_INT_VERT;
        if (sat_plane(tri_b, tri_b, ib2, ia2, ia1)) return TR_INT_VERT;
        if (sat_plane(tri_b, tri_b, ib0, ia1, ia2)) return TR_INT_VERT;
        if (sat_plane(tri_b, tri_b, ib0, ia2, ia1)) return TR_INT_VERT;

        return TR_INT_INT;
      }
      case 2: {
        // shared edge (ia, ib) [not coplanar]
        return TR_INT_EDGE;
      }
      case 3: {
        return TR_INT_TRI;
      }
      default: {
        CARVE_FAIL("should not happen.");
      }
      }
    }
    TriangleInt triangle_intersection(const vec2 tri_a[3], const vec2 tri_b[3]) {
      // triangles must be anticlockwise, or colinear.
      CARVE_ASSERT(carve::geom2d::orient2d(tri_a[0], tri_a[1], tri_a[2]) >= 0.0);
      CARVE_ASSERT(carve::geom2d::orient2d(tri_b[0], tri_b[1], tri_b[2]) >= 0.0);

      size_t ia, ib;
      int sharing = test_sharing<2>(tri_a, tri_b, ia, ib);

      switch (std::abs(sharing)) {
      case 0: {
        // no shared vertices.
        if (sat_edge(tri_a, tri_b, 0) < 0) return TR_INT_NONE;
        if (sat_edge(tri_a, tri_b, 1) < 0) return TR_INT_NONE;
        if (sat_edge(tri_a, tri_b, 2) < 0) return TR_INT_NONE;
        if (sat_edge(tri_b, tri_a, 0) < 0) return TR_INT_NONE;
        if (sat_edge(tri_b, tri_a, 1) < 0) return TR_INT_NONE;
        if (sat_edge(tri_b, tri_a, 2) < 0) return TR_INT_NONE;

        return TR_INT_INT;
      }
      case 1: {
        // shared vertex (ia, ib) [but not shared edge]
        if (sat_edge(tri_a, tri_b, (ia+2)%3, ib) < 0) return TR_INT_VERT;
        if (sat_edge(tri_a, tri_b, ia, ib)       < 0) return TR_INT_VERT;
        if (sat_edge(tri_b, tri_a, (ib+2)%3, ia) < 0) return TR_INT_VERT;
        if (sat_edge(tri_b, tri_a, ib, ia)       < 0) return TR_INT_VERT;

        return TR_INT_INT;
      }
      case 2: {
        // shared edge (ia, ib) -> (ia + 1, ib + (sharing > 0) ? +1 : -1)
        size_t pa = (ia + 2) % 3;
        size_t pb = (ib + (sharing == +1 ? 2 : 1)) % 3;

        double sa = orient2d_exact(tri_a[ia], tri_a[(ia+1)%3], tri_a[pa]);
        double sb = orient2d_exact(tri_a[ia], tri_a[(ia+1)%3], tri_b[pb]);

        if (sa * sb < 0.0) {
          return TR_INT_EDGE;
        }

        return TR_INT_INT;
      }
      case 3: {
        return TR_INT_TRI;
      }
      default: {
        CARVE_FAIL("should not happen.");
      }
      }
    }
    TriangleIntType triangle_intersection_exact(const vec2 tri_a[3], const vec2 tri_b[3]) {
      // triangles must be anticlockwise, or colinear.
      CARVE_ASSERT(orient2d_exact(tri_a[0], tri_a[1], tri_a[2]) >= 0.0);
      CARVE_ASSERT(orient2d_exact(tri_b[0], tri_b[1], tri_b[2]) >= 0.0);

      int curr = +1;
      for (size_t i = 0; curr != -1 && i < 3; ++i) {
        curr = std::min(curr, sat_edge(tri_a, tri_b, i));
      }
      for (size_t i = 0; curr != -1 && i < 3; ++i) {
        curr = std::min(curr, sat_edge(tri_b, tri_a, i));
      }
      switch (curr) {
      case -1: return TR_TYPE_NONE;
      case  0: return TR_TYPE_TOUCH;
      case +1: return TR_TYPE_INT;
      default: CARVE_FAIL("should not happen.");
      }
    }
Exemplo n.º 4
0
std::vector<std::pair<size_t, size_t> >
carve::triangulate::incorporateHolesIntoPolygon(const std::vector<std::vector<carve::geom2d::P2> > &poly) {
#if 1
  std::vector<std::pair<size_t, size_t> > result;
  std::vector<size_t> hole_indices;
  hole_indices.reserve(poly.size() - 1);
  for (size_t i = 1; i < poly.size(); ++i) {
    hole_indices.push_back(i);
  }

  incorporateHolesIntoPolygon(poly, result, 0, hole_indices);

  return result;

#else
  typedef std::vector<carve::geom2d::P2> loop_t;
  size_t N = poly[0].size();
  //
  // work out how much space to reserve for the patched in holes.
  for (size_t i = 0; i < poly.size(); i++) {
    N += 2 + poly[i].size();
  }

  // this is the vector that we will build the result in.
  std::vector<std::pair<size_t, size_t> > current_f_loop;
  current_f_loop.reserve(N);

  // this is a heap of current_f_loop indices that defines the vertex test order.
  std::vector<size_t> f_loop_heap;
  f_loop_heap.reserve(N);

  // add the poly loop to current_f_loop.
  for (size_t i = 0; i < poly[0].size(); ++i) {
    current_f_loop.push_back(std::make_pair((size_t)0, i));
  }

  if (poly.size() == 1) {
    return current_f_loop;
  }

  std::vector<std::pair<size_t, size_t> > h_loop_min_vertex;

  h_loop_min_vertex.reserve(poly.size() - 1);

  // find the major axis for the holes - this is the axis that we
  // will sort on for finding vertices on the polygon to join
  // holes up to.
  //
  // it might also be nice to also look for whether it is better
  // to sort ascending or descending.
  // 
  // another trick that could be used is to modify the projection
  // by 90 degree rotations or flipping about an axis. just as
  // long as we keep the carve::geom3d::Vector pointers for the
  // real data in sync, everything should be ok. then we wouldn't
  // need to accomodate axes or sort order in the main loop.

  // find the bounding box of all the holes.
  double min_x, min_y, max_x, max_y;
  min_x = max_x = poly[1][0].x;
  min_y = max_y = poly[1][0].y;
  for (size_t i = 1; i < poly.size(); ++i) {
    const loop_t &hole = poly[i];
    for (size_t j = 0; j < hole.size(); ++j) {
      min_x = std::min(min_x, hole[j].x);
      min_y = std::min(min_y, hole[j].y);
      max_x = std::max(max_x, hole[j].x);
      max_y = std::max(max_y, hole[j].y);
    }
  }

  // choose the axis for which the bbox is largest.
  int axis = (max_x - min_x) > (max_y - min_y) ? 0 : 1;

  // for each hole, find the minimum vertex in the chosen axis.
  for (size_t i = 1; i < poly.size(); ++i) {
    const loop_t &hole = poly[i];
    size_t best, curr;
    best = 0;
    for (curr = 1; curr != hole.size(); ++curr) {
      if (detail::axisOrdering(hole[curr], hole[best], axis)) {
        best = curr;
      }
    }
    h_loop_min_vertex.push_back(std::make_pair(i, best));
  }

  // sort the holes by the minimum vertex.
  std::sort(h_loop_min_vertex.begin(), h_loop_min_vertex.end(), order_h_loops_2d(poly, axis));

  // now, for each hole, find a vertex in the current polygon loop that it can be joined to.
  for (unsigned i = 0; i < h_loop_min_vertex.size(); ++i) {
    // the index of the vertex in the hole to connect.
    size_t hole_i = h_loop_min_vertex[i].first;
    size_t hole_i_connect = h_loop_min_vertex[i].second;

    carve::geom2d::P2 hole_min = poly[hole_i][hole_i_connect];

    f_loop_heap.clear();
    // we order polygon loop vertices that may be able to be connected
    // to the hole vertex by their distance to the hole vertex
    heap_ordering_2d _heap_ordering(poly, current_f_loop, hole_min, axis);

    const size_t SZ = current_f_loop.size();
    for (size_t j = 0; j < SZ; ++j) {
      // it is guaranteed that there exists a polygon vertex with
      // coord < the min hole coord chosen, which can be joined to
      // the min hole coord without crossing the polygon
      // boundary. also, because we merge holes in ascending
      // order, it is also true that this join can never cross
      // another hole (and that doesn't need to be tested for).
      if (pvert(poly, current_f_loop[j]).v[axis] <= hole_min.v[axis]) {
        f_loop_heap.push_back(j);
        std::push_heap(f_loop_heap.begin(), f_loop_heap.end(), _heap_ordering);
      }
    }

    // we are going to test each potential (according to the
    // previous test) polygon vertex as a candidate join. we order
    // by closeness to the hole vertex, so that the join we make
    // is as small as possible. to test, we need to check the
    // joining line segment does not cross any other line segment
    // in the current polygon loop (excluding those that have the
    // vertex that we are attempting to join with as an endpoint).
    size_t attachment_point = current_f_loop.size();

    while (f_loop_heap.size()) {
      std::pop_heap(f_loop_heap.begin(), f_loop_heap.end(), _heap_ordering);
      size_t curr = f_loop_heap.back();
      f_loop_heap.pop_back();
      // test the candidate join from current_f_loop[curr] to hole_min

      if (!testCandidateAttachment(poly, current_f_loop, curr, hole_min)) {
        continue;
      }

      attachment_point = curr;
      break;
    }

    if (attachment_point == current_f_loop.size()) {
      CARVE_FAIL("didn't manage to link up hole!");
    }

    patchHoleIntoPolygon_2d(current_f_loop, attachment_point, hole_i, hole_i_connect, poly[hole_i].size());
  }

  return current_f_loop;
#endif
}