void split_faces(union tree *left_tree, union tree *right_tree, const struct bn_tol *tol) { struct soup_s *l, *r; unsigned long int i, j; RT_CK_TREE(left_tree); RT_CK_TREE(right_tree); l = (struct soup_s *)left_tree->tr_d.td_r->m_p; r = (struct soup_s *)right_tree->tr_d.td_r->m_p; SOUP_CKMAG(l); SOUP_CKMAG(r); /* this is going to be big and hairy. Has to walk both meshes finding * all intersections and split intersecting faces so there are edges at * the intersections. Initially going to be O(n^2), then change to use * space partitioning (binning? octree? kd?). */ for (i=0;i<l->nfaces;i++) { struct face_s *lf = l->faces+i, *rf = NULL; int ret; for (j=0;j<r->nfaces;j++) { rf = r->faces+j; /* quick bounding box test */ if (lf->min[X]>rf->max[X] || lf->max[X]>lf->max[X] || lf->min[Y]>rf->max[Y] || lf->max[Y]>lf->max[Y] || lf->min[Z]>rf->max[Z] || lf->max[Z]>lf->max[Z]) continue; /* two possibly overlapping faces found */ ret = split_face(l, i, r, j, tol); if (ret&0x1) i--; if (ret&0x2) j--; } } }
// This routine assumes the sentinel points have already been added, and processes points in order GEODE_NEVER_INLINE static Ref<MutableTriangleTopology> deterministic_exact_delaunay(RawField<const Perturbed2,VertexId> X, const bool validate) { const int n = X.size()-3; const auto mesh = new_<MutableTriangleTopology>(); IntervalScope scope; // Initialize the mesh to a Delaunay triangle containing the sentinels at infinity. mesh->add_vertices(n+3); mesh->add_face(vec(VertexId(n+0),VertexId(n+1),VertexId(n+2))); if (self_check) { mesh->assert_consistent(); assert_delaunay("self check 0: ",mesh,X); } // The randomized incremental construction algorithm uses the history of the triangles // as the acceleration structure. Specifically, we maintain a BSP tree where the nodes // are edge tests (are we only the left or right of an edge) and the leaves are triangles. // There are two operations that modify this tree: // // 1. Split: A face is split into three by the insertion of an interior vertex. // 2. Flip: An edge is flipped, turning two triangles into two different triangles. // // The three starts out empty, since one triangle needs zero tests. Array<Node> bsp; // All BSP nodes including leaves bsp.preallocate(3*n); // The minimum number of possible BSP nodes Field<Vector<int,2>,FaceId> face_to_bsp; // Map from FaceId to up to two BSP leaf points (2*node+(right?0:1)) face_to_bsp.flat.preallocate(2*n+1); // The exact maximum number of faces face_to_bsp.flat.append_assuming_enough_space(vec(0,-1)); // By the time we call set_links, node 0 will be valid if (self_check) check_bsp(*mesh,bsp,face_to_bsp,X); // Allocate a stack to simulate recursion when flipping non-Delaunay edges. // Invariant: if edge is on the stack, the other edges of face(edge) are Delaunay. // Since halfedge ids change during edge flips in a corner mesh, we store half edges as directed vertex pairs. Array<Tuple<HalfedgeId,Vector<VertexId,2>>> stack; stack.preallocate(8); // Insert all vertices into the mesh in random order, maintaining the Delaunay property for (const auto i : range(n)) { const VertexId v(i); check_interrupts(); // Search through the BSP tree to find the containing triangle const auto f0 = bsp_search(bsp,X,v); const auto vs = mesh->vertices(f0); // Split the face by inserting the new vertex and update the BSP tree accordingly. mesh->split_face(f0,v); const auto e0 = mesh->halfedge(v), e1 = mesh->left(e0), e2 = mesh->right(e0); assert(mesh->dst(e0)==vs.x); const auto f1 = mesh->face(e1), f2 = mesh->face(e2); const int base = bsp.extend(3,uninit); set_links(bsp,face_to_bsp[f0],base); bsp[base+0] = Node(vec(v,vs.x),base+2,base+1); bsp[base+1] = Node(vec(v,vs.y),~f0.id,~f1.id); bsp[base+2] = Node(vec(v,vs.z),~f1.id,~f2.id); face_to_bsp[f0] = vec(2*(base+1)+0,-1); face_to_bsp.flat.append_assuming_enough_space(vec(2*(base+1)+1,2*(base+2)+0)); face_to_bsp.flat.append_assuming_enough_space(vec(2*(base+2)+1,-1)); if (self_check) { mesh->assert_consistent(); check_bsp(*mesh,bsp,face_to_bsp,X); } // Fix all non-Delaunay edges stack.copy(vec(tuple(mesh->next(e0),vec(vs.x,vs.y)), tuple(mesh->next(e1),vec(vs.y,vs.z)), tuple(mesh->next(e2),vec(vs.z,vs.x)))); if (self_check) assert_delaunay("self check 1: ",mesh,X,Tuple<>(),true); while (stack.size()) { const auto evs = stack.pop(); auto e = mesh->vertices(evs.x)==evs.y ? evs.x : mesh->halfedge(evs.y.x,evs.y.y); if (e.valid() && !is_delaunay(*mesh,X,e)) { // Our mesh is linearly embedded in the plane, so edge flips are always safe assert(mesh->is_flip_safe(e)); e = mesh->unsafe_flip_edge(e); GEODE_ASSERT(is_delaunay(*mesh,X,e)); // Update the BSP tree for the triangle flip const auto f0 = mesh->face(e), f1 = mesh->face(mesh->reverse(e)); const int node = bsp.append(Node(mesh->vertices(e),~f1.id,~f0.id)); set_links(bsp,face_to_bsp[f0],node); set_links(bsp,face_to_bsp[f1],node); face_to_bsp[f0] = vec(2*node+1,-1); face_to_bsp[f1] = vec(2*node+0,-1); if (self_check) { mesh->assert_consistent(); check_bsp(*mesh,bsp,face_to_bsp,X); } // Recurse to successor edges to e const auto e0 = mesh->next(e), e1 = mesh->prev(mesh->reverse(e)); stack.extend(vec(tuple(e0,mesh->vertices(e0)), tuple(e1,mesh->vertices(e1)))); if (self_check) assert_delaunay("self check 2: ",mesh,X,Tuple<>(),true); } } if (self_check) { mesh->assert_consistent(); assert_delaunay("self check 3: ",mesh,X); } } // Remove sentinels for (int i=0;i<3;i++) mesh->erase_last_vertex_with_reordering(); // If desired, check that the final mesh is Delaunay if (validate) assert_delaunay("delaunay validate: ",mesh,X); // Return the mesh with the sentinels removed return mesh; }
HEFace VoronoiDiagram::split_faces(const Point& p) { HEFace newface = hedi::add_face( FaceProps( HEEdge(), p, NONINCIDENT ), g ); fgrid->add_face( g[newface] ); BOOST_FOREACH( HEFace f, incident_faces ) { split_face(newface, f); // each INCIDENT face is split into two parts: newface and f }
PST_Edge* PST_Edge::split_face( PST_Point* start, PST_Point* end, PST_Face* face ) { if( !start->edge_ || !end->edge_ ) return 0; if( ! face ) { PST_Edge* start_edge = start->edge(); do { PST_Face* curr_face = start_edge->forward()->face(); PST_Face* rev_face = start_edge->reverse()->face(); while( true ) { PST_CoEdge* coe = curr_face->coedge_; do { if( coe->other( end ) ) { if( face ) return 0; face = curr_face; curr_face = rev_face; break; } coe = coe->next(); } while( coe != curr_face->coedge_ ); if( curr_face == rev_face ) break; else curr_face = rev_face; } start_edge = start_edge->next(start); } while( start_edge != start->edge() ); if( !face ) return 0; } PST_CoEdge* start_coedge = 0; PST_CoEdge* end_coedge = 0; PST_CoEdge* coedge = face->coedge_; do { if( coedge->end_point() == start ) { start_coedge = coedge; break; } coedge = coedge->next(); } while( coedge != face->coedge_ ); if( ! start_coedge ) return 0; coedge = start_coedge->next(); PST_CoEdge* stop = start_coedge; do { if( coedge->end_point() == start ) { start_coedge = coedge; } if( coedge->end_point() == end ) { end_coedge = coedge; break; } coedge = coedge->next(); } while( coedge != stop ); if( ! end_coedge ) return 0; return split_face( start_coedge, end_coedge ); }