Edge *Subdivision::locate(const Vec2& x, Edge *start) { Edge *e = start; double t = triArea(x, e->Dest(), e->Org()); if (t>0) { // x is to the right of edge e t = -t; e = e->Sym(); } while (true) { Edge *eo = e->Onext(); Edge *ed = e->Dprev(); double to = triArea(x, eo->Dest(), eo->Org()); double td = triArea(x, ed->Dest(), ed->Org()); if (td>0) // x is below ed if (to>0 || to==0 && t==0) {// x is interior, or origin endpoint startingEdge = e; return e; } else { // x is below ed, below eo t = to; e = eo; } else // x is on or above ed if (to>0) // x is above eo if (td==0 && t==0) { // x is destination endpoint startingEdge = e; return e; } else { // x is on or above ed and above eo t = td; e = ed; } else // x is on or below eo if (t==0 && !leftOf(eo->Dest(), e)) // x on e but subdiv. is to right e = e->Sym(); else if (rand()&1) { // x is on or above ed and t = to; // on or below eo; step randomly e = eo; } else { t = td; e = ed; } } }
static inline void printFace(std::ostream &out, Face *f) { out << "Face: " << f->getID() << std::endl; FaceEdgeIterator eitr(f); Edge *e = NULL; int edge = 0; while ((e = eitr.next())) { out << "Edge " << edge << " with id: " << e->getID() << " from " << e->Org()->pos << " to " << e->Dest()->pos << std::endl; ++edge; } }
Edge *Cell::getOrbitOrg(Edge *edge, Vertex *org) { assert(edge != 0); assert(org != 0); // traverse the Lnext orbit of _edge_ looking for an edge whose origin is // _org_ Edge *scan = edge; do { if (scan->Org() == org) { return scan; } scan = scan->Lnext(); } while (scan != edge); return 0; }
void Subdivision::InsertSite(const Point2d& x) // Inserts a new point into a subdivision representing a Delaunay // triangulation, and fixes the affected edges so that the result // is still a Delaunay triangulation. This is based on the // pseudocode from Guibas and Stolfi (1985) p.120, with slight // modifications and a bug fix. { Edge* e = Locate(x); if ((x == e->Org2d()) || (x == e->Dest2d())) // point is already in return; else if (OnEdge(x, e)) { e = e->Oprev(); DeleteEdge(e->Onext()); } // Connect the new point to the vertices of the containing // triangle (or quadrilateral, if the new point fell on an // existing edge.) Edge* base = MakeEdge(); base->EndPoints(e->Org(), new Point2d(x)); Splice(base, e); startingEdge = base; do { base = Connect(e, base->Sym()); e = base->Oprev(); } while (e->Lnext() != startingEdge); // Examine suspect edges to ensure that the Delaunay condition // is satisfied. do { Edge* t = e->Oprev(); if (RightOf(t->Dest2d(), e) && InCircle(e->Org2d(), t->Dest2d(), e->Dest2d(), x)) { Swap(e); e = e->Oprev(); } else if (e->Onext() == startingEdge) // no more suspect edges return; else // pop a suspect edge e = e->Onext()->Lprev(); } while (TRUE); }
int main() { using cgl::Point2d; using cgl::Edge; using cgl::make_edge; // Point2d *a = new Point2d(0.0, 0.0); // 0 // Point2d *b = new Point2d(1.0, 0.0); // 1 // Point2d *c = new Point2d(1.0, 1.0); // 2 // Point2d *d = new Point2d(0.0, 1.0); // 3 Edge *ea = make_edge(0, 1); Edge *eb = make_edge(ea->Dest(), 2); Edge *ec = make_edge(eb->Dest(), 3); Edge *ed = make_edge(ec->Dest(), 0); Edge *ee = make_edge(eb->Dest(), ea->Org()); // Connect eb to ea->Dest() splice(ea->Sym(), eb); // Connect ec to eb->Dest() splice(eb->Sym(), ec); // Connect ed to ec->Sym() and ea splice(ec->Sym(), ed); splice(ed->Sym(), ea); // Connect ee to ec and ea -- this is equivalent to Delaunay::connect splice(ee, eb->Lnext()); splice(ee->Sym(), ea); // Traverse the convex hull if (ea->Dnext() != eb->Sym()) { std::cerr << "Error: ea->Dnext() != eb->Sym()" << std::endl; return 1; } if (eb->Dnext() != ec->Sym()) { std::cerr << "Error: eb->Dnext() != ec->Sym()" << std::endl; return 1; } if (ec->Dnext() != ed->Sym()) { std::cerr << "Error: ec->Dnext() != ed->Sym()" << std::endl; return 1; } if (ed->Dprev() != ee) { std::cerr << "Error: ed->Dprev() != ee" << std::endl; return 1; } // Cross linkage -- test algebra // Rot / invRot if (ee->Rot()->Org() != ed->invRot()->Org()) { std::cerr << "Error: ee->Rot()->Org() != ed->invRot()->Org()" << std::endl; return 1; } // Sym if (ee->Sym()->Org() != 0) { std::cerr << "Error: ee->Sym()->Org() != a" << std::endl; return 1; } // Onext if (ee->Onext() != eb->Sym()) { std::cerr << "Error: ee->Onext() != eb->Sym()" << std::endl; return 1; } // Oprev if (ee->Oprev() != ec) { std::cerr << "Error: ee->Oprev() != ec" << std::endl; return 1; } // Dnext if (ee->Dnext() != ed) { std::cerr << "Error: ee->Dnext() != ed" << std::endl; return 1; } // Dprev if (ee->Dprev() != ea->Sym()) { std::cerr << "Error: ee->Dprev() != ea->Sym()" << std::endl; return 1; } // Lnext if (ee->Lnext() != ea) { std::cerr << "Error: ee->Lnext() != ea" << std::endl; return 1; } // Lprev if (ee->Lprev() != eb) { std::cerr << "Error: ee->Lprev() != eb" << std::endl; return 1; } // Rnext if (ee->Rnext() != ec->Sym()) { std::cerr << "Error: ee->Rnext() != ec->Sym()" << std::endl; return 1; } // Rprev if (ee->Rprev() != ed->Sym()) { std::cerr << "Error: ee->Rprev() != ed->Sym()" << std::endl; return 1; } // Org / Dest if (ed->Org() != 3) { std::cerr << "Error: ed->Org() != d" << std::endl; return 1; } if (ed->Dest() != 0) { std::cerr << "Error: ed->Dest() != a" << std::endl; return 1; } if (ee->Org() != 2) { std::cerr << "Error: ee->Org() != c" << std::endl; return 1; } if (ee->Dest() != 0) { std::cerr << "Error: ee->Dest() != a" << std::endl; return 1; } // Let the system clean up the memory return 0; }
/*Type of Cut Strategy is defined as: * (useAltCut, useVertical) = (true, true) => Alternating Cuts * (useAltCut, useVertical) = (true, false) => Horizontal Cuts* * (useAltCut, useVertical) = (false, true) => Vertical Cuts * */ void delaunay(Vertex **ppVertices, long numVertices, Edge **ppLe, Edge **ppRe, bool useAltCuts, bool useVertical) { NOT_NULL(ppVertices); if(numVertices == 2) { Edge *pA = Edge::makeEdge(); if(*ppVertices[0] > *ppVertices[1]) { ELEM_SWAP(ppVertices[0], ppVertices[1]); } pA->setOrg(ppVertices[0]); pA->setDest(ppVertices[1]); *ppLe = pA; *ppRe = pA->Sym(); } else if(numVertices == 3) { Edge *pA = Edge::makeEdge(), *pB = Edge::makeEdge(), *pC = 0; if(*ppVertices[0] > *ppVertices[1]) { ELEM_SWAP(ppVertices[0], ppVertices[1]); } if(*ppVertices[1] > *ppVertices[2]) { ELEM_SWAP(ppVertices[1], ppVertices[2]); } Edge::splice(pA->Sym(), pB); pA->setOrg(ppVertices[0]); pA->setDest(ppVertices[1]); pB->setOrg(ppVertices[1]); pB->setDest(ppVertices[2]); REAL ccw = orient2d(ppVertices[0]->Pos(), ppVertices[1]->Pos(), ppVertices[2]->Pos()); if(ccw > 0) { pC = Edge::connect(pB, pA); *ppLe = pA; *ppRe = pB->Sym(); } else if(ccw < 0) { pC = Edge::connect(pB, pA); *ppLe = pC->Sym(); *ppRe = pC; } else { *ppLe = pA; *ppRe = pB->Sym(); } } else { long middle = std::floor(numVertices/2); Edge *pLdo = 0, *pLdi = 0, *pRdi = 0, *pRdo = 0; //These vertices are used for merging a horizontal cut Vertex *pBotMax = 0, //highest vertex of bottom half *pTopMin = 0, //lowest vertex of top half *pMin = 0, //Lexicographically max vertex *pMax = 0; //Lexicographically min vertex //Find median partition by X or Y, depending on whether we're using a vertical cut std::nth_element(ppVertices, ppVertices+middle, ppVertices+numVertices, useVertical ? Vertex::lessX : Vertex::lessY); //Recursive calls delaunay(ppVertices, middle, &pLdo, &pLdi, useAltCuts, useAltCuts ? !useVertical : useVertical); delaunay(ppVertices+middle, numVertices - middle, &pRdi, &pRdo, useAltCuts, useAltCuts ? !useVertical : useVertical); //Modify ldi be highest in bottom half and rdi to be lowest in top half if(!useVertical) { pBotMax = ppVertices[0]; pTopMin = ppVertices[middle]; pMin = (*ppVertices[0] < *ppVertices[middle]) ? ppVertices[0] : ppVertices[middle]; pMax = (*ppVertices[0] > *ppVertices[middle]) ? ppVertices[0] : ppVertices[middle];; for(long i=1; i < middle; i++) { if(*ppVertices[i] < *pMin) { pMin = ppVertices[i]; } else if(*ppVertices[i] > *pMax) { pMax = ppVertices[i]; } if(ppVertices[i]->gtY(*pBotMax)) { pBotMax = ppVertices[i]; } } for(long i=middle+1; i < numVertices; i++) { if(*ppVertices[i] < *pMin) { pMin = ppVertices[i]; } else if(*ppVertices[i] > *pMax) { pMax = ppVertices[i]; } if(ppVertices[i]->ltY(*pTopMin)) { pTopMin = ppVertices[i]; } } pLdi = pBotMax->getCWHullEdge(); pRdi = pTopMin->getCCWHullEdge(); } //Compute the lower common tangent of two sets of vertices while (1) { if(pLdi->leftOf(pRdi->Org())) { pLdi = pLdi->Lnext(); } else if(pRdi->rightOf(pLdi->Org())) { pRdi = pRdi->Rprev(); } else { break; } } // Create a first cross edge pBasel from pRdi.origin to pLdi.origin Edge *pBasel = Edge::connect(pRdi->Sym(), pLdi); if(pLdi->Org() == pLdo->Org()) { pLdo = pBasel->Sym(); } if(pRdi->Org() == pRdo->Org()) { pRdo = pBasel; } //Merging two halves while(1) { //Locate the first Left point pLcand to be encou Edge *pLcand = pBasel->Sym()->Onext(); bool leftFinished = !pLcand->valid(pBasel); if(!leftFinished) { while(incircle(pBasel->Dest()->Pos(), pBasel->Org()->Pos(), pLcand->Dest()->Pos(), pLcand->Onext()->Dest()->Pos()) > 0) { Edge *pT = pLcand->Onext(); Edge::deleteEdge(pLcand); pLcand = pT; } } //Symmetrically locate the first R point to be hit Edge *pRcand = pBasel->Oprev(); bool rightFinished = !pRcand->valid(pBasel); if(!rightFinished) { while(incircle(pBasel->Dest()->Pos(), pBasel->Org()->Pos(), pRcand->Dest()->Pos(), pRcand->Oprev()->Dest()->Pos()) > 0) { Edge *pT = pRcand->Oprev(); Edge::deleteEdge(pRcand); pRcand = pT; } } //both are invalid, pBasel is upper common tangent if(leftFinished && rightFinished) { break; } //the next cross edge is to be connected to either pLcand.dest or pRcand.dest if(leftFinished || (!rightFinished && incircle(pLcand->Dest()->Pos(), pLcand->Org()->Pos(), pRcand->Org()->Pos(), pRcand->Dest()->Pos()) > 0)) { pBasel = Edge::connect(pRcand, pBasel->Sym()); } else { pBasel = Edge::connect(pBasel->Sym(), pLcand->Sym()); } } //Modify pLdo and pRdo if we merging a horizontal cut if(!useVertical) { pLdo = pMin->getCCWHullEdge(); pRdo = pMax->getCWHullEdge(); } *ppLe = pLdo; *ppRe = pRdo; } return; }