/* * divide and conquer delaunay */ private void del_divide_and_conquer(DelaunaySegment del, int start, int end) { DelaunaySegment left = new DelaunaySegment(); DelaunaySegment right = new DelaunaySegment(); int i, n; n = (end - start + 1); if (n > 3) { i = (n / 2) + (n & 1); left.points = del.points; right.points = del.points; del_divide_and_conquer(left, start, start + i - 1); del_divide_and_conquer(right, start + i, end); del_link(del, left, right); } else if (n == 3) { del_init_tri(del, start); } else if (n == 2) { del_init_seg(del, start); } }
/* * find the lower tangent between the two delaunay, going from left to right (returns the left half edge) */ private Halfedge del_get_lower_tangent(DelaunaySegment left, DelaunaySegment right) { Point2d pl; Point2d pr; Halfedge right_d; Halfedge left_d; Halfedge new_ld, new_rd; int sl, sr; left_d = left.rightmost_he; right_d = right.leftmost_he; do { pl = left_d.prev.pair.vertex; pr = right_d.pair.vertex; if ((sl = classify_point_seg(left_d.vertex, right_d.vertex, pl)) == ON_RIGHT) { left_d = left_d.prev.pair; } if ((sr = classify_point_seg(left_d.vertex, right_d.vertex, pr)) == ON_RIGHT) { right_d = right_d.pair.next; } } while (sl == ON_RIGHT || sr == ON_RIGHT); /* create the 2 halfedges */ new_ld = new Halfedge(); new_rd = new Halfedge(); /* setup new_gd and new_dd */ new_ld.vertex = left_d.vertex; new_ld.pair = new_rd; new_ld.prev = left_d.prev; left_d.prev.next = new_ld; new_ld.next = left_d; left_d.prev = new_ld; new_rd.vertex = right_d.vertex; new_rd.pair = new_ld; new_rd.prev = right_d.prev; right_d.prev.next = new_rd; new_rd.next = right_d; right_d.prev = new_rd; return(new_ld); }
/* * link the 2 delaunay together */ private void del_link(DelaunaySegment result, DelaunaySegment left, DelaunaySegment right) { Point2d u; Point2d v, ml, mr; Halfedge basis; Debug.Assert(left.points == right.points); /* save the most right point and the most left point */ ml = left.leftmost_he.vertex; mr = right.rightmost_he.vertex; basis = del_get_lower_tangent(left, right); u = basis.next.pair.vertex; v = basis.pair.prev.pair.vertex; while (del_classify_point(basis, u) == ON_LEFT || del_classify_point(basis, v) == ON_LEFT) { basis = del_valid_link(basis); u = basis.next.pair.vertex; v = basis.pair.prev.pair.vertex; } right.rightmost_he = mr.he; left.leftmost_he = ml.he; /* TODO: this part is not needed, and can be optimized */ while (del_classify_point(right.rightmost_he, right.rightmost_he.prev.pair.vertex) == ON_RIGHT) { right.rightmost_he = right.rightmost_he.prev; } while (del_classify_point(left.leftmost_he, left.leftmost_he.prev.pair.vertex) == ON_RIGHT) { left.leftmost_he = left.leftmost_he.prev; } result.leftmost_he = left.leftmost_he; result.rightmost_he = right.rightmost_he; result.points = left.points; result.start_point = left.start_point; result.end_point = right.end_point; }
/* * build the faces for all the halfedge */ private void del_build_faces(DelaunaySegment del) { int i; Halfedge curr; del.num_faces = 0; del.faces = null; /* build external face first */ build_halfedge_face(del, del.rightmost_he.pair); for (i = del.start_point; i <= del.end_point; i++) { curr = del.points[i].he; do { build_halfedge_face(del, curr); curr = curr.next; } while (curr != del.points[i].he); } }
private void build_halfedge_face(DelaunaySegment del, Halfedge d) { Halfedge curr; /* test if the halfedge has already a pointing face */ if (d.face != null) { return; } /* TODO: optimize this */ /* copy the points */ Face[] new_faces = new Face[del.num_faces + 1]; Debug.Assert(null != new_faces && (del.num_faces == 0 || null != del.faces)); for (int i = 0; i < del.num_faces; i++) { new_faces[i] = del.faces[i]; } new_faces[del.num_faces] = new Face(); del.faces = new_faces; Face f = del.faces[del.num_faces]; curr = d; f.he = d; f.num_verts = 0; do { curr.face = f; (f.num_verts)++; curr = curr.pair.prev; } while (curr != d); (del.num_faces)++; }
/* * initialize delaunay segment */ private int del_init_seg(DelaunaySegment del, int start) { Halfedge d0; Halfedge d1; Point2d pt0; Point2d pt1; /* init delaunay */ del.start_point = start; del.end_point = start + 1; /* setup pt0 and pt1 */ pt0 = del.points[start]; pt1 = del.points[start + 1]; /* allocate the halfedges and setup them */ d0 = new Halfedge(); d1 = new Halfedge(); d0.vertex = pt0; d1.vertex = pt1; d0.next = d0.prev = d0; d1.next = d1.prev = d1; d0.pair = d1; d1.pair = d0; pt0.he = d0; pt1.he = d1; del.rightmost_he = d1; del.leftmost_he = d0; return(0); }
/* */ private Delaunay2d delaunay2d_from(Del_Point2d[] points, int num_points) { Delaunay2d res = null; DelaunaySegment del = new DelaunaySegment(); int i, j, fbuff_size = 0; int[] faces = null; /* allocate the points */ del.points = new Point2d[num_points]; Debug.Assert(null != del.points); /* copy the points */ for (i = 0; i < num_points; i++) { del.points[i] = new Point2d(); del.points[i].idx = i; del.points[i].x = points[i].x; del.points[i].y = points[i].y; } Array.Sort(del.points); if (num_points >= 3) { del_divide_and_conquer(del, 0, num_points - 1); del_build_faces(del); fbuff_size = 0; for (i = 0; i < del.num_faces; i++) { fbuff_size += del.faces[i].num_verts + 1; } faces = new int[fbuff_size]; Debug.Assert(null != faces); j = 0; for (i = 0; i < del.num_faces; i++) { Halfedge curr; faces[j] = del.faces[i].num_verts; j++; curr = del.faces[i].he; do { faces[j] = curr.vertex.idx; j++; curr = curr.pair.prev; } while (curr != del.faces[i].he); } del.faces = null; del.points = null; } res = new Delaunay2d(); Debug.Assert(null != res); res.num_points = num_points; res.points = new Del_Point2d[num_points]; Debug.Assert(null != res.points); for (int loop = 0; loop < num_points; loop++) { res.points[loop] = (Del_Point2d)points[loop].Clone(); } res.num_faces = del.num_faces; res.faces = faces; return(res); }
/* * initialize delaunay triangle */ private int del_init_tri(DelaunaySegment del, int start) { Halfedge d0; Halfedge d1; Halfedge d2, d3, d4, d5; Point2d pt0; Point2d pt1, pt2; /* initiate delaunay */ del.start_point = start; del.end_point = start + 2; /* setup the points */ pt0 = del.points[start]; pt1 = del.points[start + 1]; pt2 = del.points[start + 2]; /* allocate the 6 halfedges */ d0 = new Halfedge(); d1 = new Halfedge(); d2 = new Halfedge(); d3 = new Halfedge(); d4 = new Halfedge(); d5 = new Halfedge(); if (classify_point_seg(pt0, pt2, pt1) == ON_LEFT) /* first case */ { /* set halfedges points */ d0.vertex = pt0; d1.vertex = pt2; d2.vertex = pt1; d3.vertex = pt2; d4.vertex = pt1; d5.vertex = pt0; /* set points halfedges */ pt0.he = d0; pt1.he = d2; pt2.he = d1; /* next and next -1 setup */ d0.next = d5; d0.prev = d5; d1.next = d3; d1.prev = d3; d2.next = d4; d2.prev = d4; d3.next = d1; d3.prev = d1; d4.next = d2; d4.prev = d2; d5.next = d0; d5.prev = d0; /* set halfedges pair */ d0.pair = d3; d3.pair = d0; d1.pair = d4; d4.pair = d1; d2.pair = d5; d5.pair = d2; del.rightmost_he = d1; del.leftmost_he = d0; } else /* 2nd case */ { /* set halfedges points */ d0.vertex = pt0; d1.vertex = pt1; d2.vertex = pt2; d3.vertex = pt1; d4.vertex = pt2; d5.vertex = pt0; /* set points halfedges */ pt0.he = d0; pt1.he = d1; pt2.he = d2; /* next and next -1 setup */ d0.next = d5; d0.prev = d5; d1.next = d3; d1.prev = d3; d2.next = d4; d2.prev = d4; d3.next = d1; d3.prev = d1; d4.next = d2; d4.prev = d2; d5.next = d0; d5.prev = d0; /* set halfedges pair */ d0.pair = d3; d3.pair = d0; d1.pair = d4; d4.pair = d1; d2.pair = d5; d5.pair = d2; del.rightmost_he = d2; del.leftmost_he = d0; } return(0); }
//void Triangulator::_recursiveTriangulatePolygon(const DelaunaySegment& cuttingSeg, std::vector<int> inputPoints, DelaunayTriangleBuffer& tbuffer, const PointList& pointList) const void _recursiveTriangulatePolygon(DelaunaySegment cuttingSeg, std_vector <int> inputPoints, DelaunayTriangleBuffer tbuffer, PointList pointList) { if (inputPoints.size() == 0) { return; } if (inputPoints.size() == 1) { Triangle t2 = new Triangle(pointList); //t.setVertices(cuttingSeg.i1, cuttingSeg.i2, inputPoints.begin()); t2.setVertices(cuttingSeg.i1, cuttingSeg.i2, inputPoints.front()); t2.makeDirectIfNeeded(); tbuffer.push_back(t2); return; } // Find a point which, when associated with seg.i1 and seg.i2, builds a Delaunay triangle //std::vector<int>::iterator currentPoint = inputPoints.begin(); int currentPoint_pos = inputPoints.begin(); int currentPoint = inputPoints.front(); bool found = false; while (!found) { bool isDelaunay = true; //Circle c=new Circle(pointList[*currentPoint], pointList[cuttingSeg.i1], pointList[cuttingSeg.i2]); Circle c = new Circle(pointList[currentPoint], pointList[cuttingSeg.i1], pointList[cuttingSeg.i2]); //for (std::vector<int>::iterator it = inputPoints.begin(); it!=inputPoints.end(); ++it) int idx = -1; foreach (var it in inputPoints) { idx++; //if (c.isPointInside(pointList[*it]) && (*it != *currentPoint)) if (c.isPointInside(pointList[it]) && (it != currentPoint)) { isDelaunay = false; currentPoint = it; currentPoint_pos = idx; break; } } if (isDelaunay) { found = true; } } // Insert current triangle Triangle t = new Triangle(pointList); //t.setVertices(*currentPoint, cuttingSeg.i1, cuttingSeg.i2); t.setVertices(currentPoint, cuttingSeg.i1, cuttingSeg.i2); t.makeDirectIfNeeded(); tbuffer.push_back(t); // Recurse //DelaunaySegment newCut1=new DelaunaySegment(cuttingSeg.i1, *currentPoint); //DelaunaySegment newCut2=new DelaunaySegment(cuttingSeg.i2, *currentPoint); DelaunaySegment newCut1 = new DelaunaySegment(cuttingSeg.i1, currentPoint); DelaunaySegment newCut2 = new DelaunaySegment(cuttingSeg.i2, currentPoint); //std_vector<int> set1=new std_vector<int>(inputPoints.begin(), currentPoint); //std_vector<int> set2=new std_vector<int>(currentPoint+1, inputPoints.end()); std_vector <int> set1 = new std_vector <int>(); set1.assign(inputPoints.ToArray(), inputPoints.begin(), currentPoint_pos); std_vector <int> set2 = new std_vector <int>(); set2.assign(inputPoints.ToArray(), currentPoint_pos + 1, inputPoints.end()); if (!set1.empty()) { _recursiveTriangulatePolygon(newCut1, set1, tbuffer, pointList); } if (!set2.empty()) { _recursiveTriangulatePolygon(newCut2, set2, tbuffer, pointList); } }
void _addConstraints(ref DelaunayTriangleBuffer tbuffer, PointList pl, std_vector <int> segmentListIndices) { std_vector <DelaunaySegment> segList = new std_vector <DelaunaySegment>(); //Utils::log("a co"); //for (DelaunayTriangleBuffer::iterator it = tbuffer.begin(); it!=tbuffer.end();it++) // Utils::log(it->debugDescription()); // First, list all the segments that are not already in one of the delaunay triangles //for (std::vector<int>::const_iterator it2 = segmentListIndices.begin(); it2 != segmentListIndices.end(); it2++) for (int i = 0; i < segmentListIndices.Count; i++) { //int i1 = *it2; int i1 = segmentListIndices[i]; //it2++; i++; //int i2 = *it2; int i2 = segmentListIndices[i]; bool isAlreadyIn = false; //for (DelaunayTriangleBuffer::iterator it = tbuffer.begin(); it!=tbuffer.end(); ++it) foreach (var it in tbuffer) { if (it.containsSegment(i1, i2)) { isAlreadyIn = true; break; } } // only do something for segments not already in DT if (!isAlreadyIn) { segList.push_back(new DelaunaySegment(i1, i2)); } } // Re-Triangulate according to the new segments //for (std::vector<DelaunaySegment>::iterator itSeg=segList.begin(); itSeg!=segList.end(); itSeg++) for (int ii = segList.Count - 1; ii >= 0; ii--) { DelaunaySegment itSeg = segList[ii]; //Utils::log("itseg " + StringConverter::toString(itSeg->i1) + "," + StringConverter::toString(itSeg->i2) + " " + StringConverter::toString(pl[itSeg->i1]) + "," + StringConverter::toString(pl[itSeg->i2])); // Remove all triangles intersecting the segment and keep a list of outside edges std_set <DelaunaySegment> segments = new std_set <DelaunaySegment>(); Segment2D seg1 = new Segment2D(pl[itSeg.i1], pl[itSeg.i2]); //for (DelaunayTriangleBuffer::iterator itTri = tbuffer.begin(); itTri!=tbuffer.end(); ) for (int jj = tbuffer.Count - 1; jj >= 0; jj--) { Triangle itTri = tbuffer.getElement(jj).Value; bool isTriangleIntersected = false; bool isDegenerate = false; int degenIndex; for (int i = 0; i < 3; i++) { //Early out if 2 points are in fact the same if (itTri.i[i] == itSeg.i1 || itTri.i[i] == itSeg.i2 || itTri.i[(i + 1) % 3] == itSeg.i1 || itTri.i[(i + 1) % 3] == itSeg.i2) { if (itTri.isDegenerate()) { if (itTri.i[i] == itSeg.i1 || itTri.i[(i + 1) % 3] == itSeg.i1) { degenIndex = itSeg.i1; } else if (itTri.i[i] == itSeg.i2 || itTri.i[(i + 1) % 3] == itSeg.i2) { degenIndex = itSeg.i2; } isTriangleIntersected = true; isDegenerate = true; } else { continue; } } Segment2D seg2 = new Segment2D(itTri.p(i), itTri.p((i + 1) % 3)); if (seg1.intersects(seg2)) { isTriangleIntersected = true; break; } } if (isTriangleIntersected) { //if (isDegenerate) //Utils::log("degen " + itTri->debugDescription()); for (int k = 0; k < 3; k++) { DelaunaySegment d1 = new DelaunaySegment(itTri.i[k], itTri.i[(k + 1) % 3]); if (segments.find(d1) != segments.end()) { segments.erase(d1); } else if (segments.find(d1.inverse()) != segments.end()) { segments.erase(d1.inverse()); } else { segments.insert(d1); } } //itTri=tbuffer.erase(itTri); tbuffer.erase(jj); } //else // itTri++; } // Divide the list of points (coming from remaining segments) in 2 groups : "above" and "below" std_vector <int> pointsAbove = new std_vector <int>(); std_vector <int> pointsBelow = new std_vector <int>(); int pt = itSeg.i1; bool isAbove = true; while (segments.size() > 0) { //find next point //for (std::set<DelaunaySegment>::iterator it = segments.begin(); it!=segments.end(); ++it) DelaunaySegment[] segments_all = segments.get_allocator(); for (int i = 0; i < segments_all.Length; ++i) { DelaunaySegment it = segments_all[i];//segments.find(i,true); if (it.i1 == pt || it.i2 == pt) { //Utils::log("next " + StringConverter::toString(pt)); if (it.i1 == pt) { pt = it.i2; } else { pt = it.i1; } segments.erase(it); if (pt == itSeg.i2) { isAbove = false; } else if (pt != itSeg.i1) { if (isAbove) { pointsAbove.push_back(pt); } else { pointsBelow.push_back(pt); } } break; } } } // Recursively triangulate both polygons _recursiveTriangulatePolygon(itSeg, pointsAbove, tbuffer, pl); _recursiveTriangulatePolygon(itSeg.inverse(), pointsBelow, tbuffer, pl); } // Clean up segments outside of multishape if (mRemoveOutside) { if (mMultiShapeToTriangulate != null && mMultiShapeToTriangulate.isClosed()) { //for (DelaunayTriangleBuffer::iterator it = tbuffer.begin(); it!=tbuffer.end();) for (int i = tbuffer.Count - 1; i >= 0; i--) { Triangle it = tbuffer.getElement(i).Value; bool isTriangleOut = !mMultiShapeToTriangulate.isPointInside(it.getMidPoint()); if (isTriangleOut) { //it = tbuffer.erase(it); tbuffer.erase(i); } //else // ++it; } } else if (mShapeToTriangulate != null && mShapeToTriangulate.isClosed()) { //for (DelaunayTriangleBuffer::iterator it = tbuffer.begin(); it!=tbuffer.end();) for (int i = tbuffer.Count - 1; i >= 0; i--) { Triangle it = tbuffer.getElement(i).Value; bool isTriangleOut = !mShapeToTriangulate.isPointInside(it.getMidPoint()); if (isTriangleOut) { //it = tbuffer.erase(it); tbuffer.erase(i); } //else // ++it; } } } }
// //ORIGINAL LINE: void delaunay(List<Ogre::Vector2>& pointList, LinkedList<Triangle>& tbuffer) const void delaunay(PointList pointList, ref DelaunayTriangleBuffer tbuffer) { // Compute super triangle or insert manual super triangle if (mManualSuperTriangle != null) { float maxTriangleSize = 0.0f; //for (PointList::iterator it = pointList.begin(); it!=pointList.end(); ++it) foreach (Vector2 it in pointList) { maxTriangleSize = max(maxTriangleSize, Math.Abs(it.x)); maxTriangleSize = max(maxTriangleSize, Math.Abs(it.y)); } pointList.push_back(new Vector2(-3f * maxTriangleSize, -3f * maxTriangleSize)); pointList.push_back(new Vector2(3f * maxTriangleSize, -3f * maxTriangleSize)); pointList.push_back(new Vector2(0.0f, 3 * maxTriangleSize)); int maxTriangleIndex = pointList.size() - 3; Triangle superTriangle = new Triangle(pointList); superTriangle.i[0] = maxTriangleIndex; superTriangle.i[1] = maxTriangleIndex + 1; superTriangle.i[2] = maxTriangleIndex + 2; tbuffer.push_back(superTriangle); } // Point insertion loop for (int i = 0; i < pointList.size() - 3; i++) { //Utils::log("insert point " + StringConverter::toString(i)); //std::list<std::list<Triangle>::iterator> borderlineTriangles; std_list <Triangle> borderlineTriangles = new std_list <Triangle>(); // Insert 1 point, find all triangles for which the point is in circumcircle Vector2 p = pointList[i]; //std::set<DelaunaySegment> segments; std_set <DelaunaySegment> segments = new std_set <DelaunaySegment>(); IEnumerator <Triangle> et = tbuffer.GetEnumerator(); //for (DelaunayTriangleBuffer::iterator it = tbuffer.begin(); it!=tbuffer.end();) List <Triangle> need_erase = new List <Triangle>(); while (et.MoveNext()) { Triangle it = et.Current; Triangle.InsideType isInside = it.isPointInsideCircumcircle(p); if (isInside == Triangle.InsideType.IT_INSIDE) { if (!it.isDegenerate()) { //Utils::log("tri insie" + it->debugDescription()); for (int k = 0; k < 3; k++) { DelaunaySegment d1 = new DelaunaySegment(it.i[k], it.i[(k + 1) % 3]); if (segments.find(d1) != segments.end()) { segments.erase(d1); } else if (segments.find(d1.inverse()) != segments.end()) { segments.erase(d1.inverse()); } else { segments.insert(d1); } } } //it=tbuffer.erase(it); need_erase.Add(it); } else if (isInside == Triangle.InsideType.IT_BORDERLINEOUTSIDE) { //Utils::log("tri borer " + it->debugDescription()); borderlineTriangles.push_back(it); //++it; } else { //++it; } } //do delete foreach (var v in need_erase) { tbuffer.Remove(v); } // Robustification of the standard algorithm : if one triangle's circumcircle was borderline against the new point, // test whether that triangle is intersected by new segments or not (normal situation : it should not) // If intersected, the triangle is considered having the new point in its circumc std_set <DelaunaySegment> copySegment = segments; IEnumerator <Triangle> be = borderlineTriangles.GetEnumerator(); //for (std::list<std::list<Triangle>::iterator>::iterator itpTri = borderlineTriangles.begin(); itpTri!=borderlineTriangles.end(); itpTri++ ) while (be.MoveNext()) { Triangle itpTri = be.Current; //DelaunayTriangleBuffer::iterator itTri = *itpTri; Triangle itTri = itpTri; bool triRemoved = false; //for (std::set<DelaunaySegment>::iterator it = copySegment.begin(); it!=copySegment.end() && !triRemoved; ++it) IEnumerator <DelaunaySegment> cse = copySegment.GetEnumerator(); while (cse.MoveNext() && !triRemoved) { DelaunaySegment it = cse.Current; bool isTriangleIntersected = false; for (int k = 0; k < 2; k++) { int i1 = (k == 0) ? it.i1 : it.i2; int i2 = i; for (int l = 0; l < 3; l++) { //Early out if 2 points are in fact the same if (itTri.i[l] == i1 || itTri.i[l] == i2 || itTri.i[(l + 1) % 3] == i1 || itTri.i[(l + 1) % 3] == i2) { continue; } Segment2D seg2 = new Segment2D(itTri.p(l), itTri.p((l + 1) % 3)); Segment2D seg1 = new Segment2D(pointList[i1], pointList[i2]); if (seg1.intersects(seg2)) { isTriangleIntersected = true; break; } } } if (isTriangleIntersected) { if (!itTri.isDegenerate()) { //Utils::log("tri inside" + itTri->debugDescription()); for (int m = 0; m < 3; m++) { DelaunaySegment d1 = new DelaunaySegment(itTri.i[m], itTri.i[(m + 1) % 3]); if (segments.find(d1) != segments.end()) { segments.erase(d1); } else if (segments.find(d1.inverse()) != segments.end()) { segments.erase(d1.inverse()); } else { segments.insert(d1); } } } //tbuffer.erase(itTri); need_erase.Clear(); need_erase.Add(itTri); triRemoved = true; } } } //do delete foreach (var v in need_erase) { tbuffer.Remove(v); } // Find all the non-interior edges IEnumerator <DelaunaySegment> seg_ie = segments.GetEnumerator(); //for (std::set<DelaunaySegment>::iterator it = segments.begin(); it!=segments.end(); ++it) while (seg_ie.MoveNext()) { DelaunaySegment it = seg_ie.Current; //Triangle dt(&pointList); Triangle dt = new Triangle(pointList); dt.setVertices(it.i1, it.i2, i); dt.makeDirectIfNeeded(); //Utils::log("Add tri " + dt.debugDescription()); tbuffer.push_back(dt); } } // NB : Don't remove super triangle here, because all outer triangles are already removed in the addconstraints method. // Uncomment that code if delaunay triangulation ever has to be unconstrained... /*TouchSuperTriangle touchSuperTriangle(maxTriangleIndex, maxTriangleIndex+1,maxTriangleIndex+2); * tbuffer.remove_if(touchSuperTriangle); * pointList.pop_back(); * pointList.pop_back(); * pointList.pop_back();*/ }