/// <summary> /// Updates the grid index to reflect changes to the triangulation. Note that added /// triangles outside the indexed region will force to recompute the whole index /// with the enlarged region /// </summary> /// <param name="updatedTriangles">Changed triangles of the triangulation. This may be added triangles, /// removed triangles or both. All that matter is that they cover the /// changed area. /// </param> public void updateIndex(IEnumerator <Triangle_dt> updatedTriangles) { // Gather the bounding box of the updated area BoundingBox updatedRegion = new BoundingBox(); while (((updatedTriangles.Current != null))) { updatedRegion = updatedRegion.UnionWith(updatedTriangles.Current.BoundingBox); updatedTriangles.MoveNext(); } if ((updatedRegion.isNull)) { return; } // No update... // Bad news - the updated region lies outside the indexed region. // The whole index must be recalculated if ((!indexRegion.contains(updatedRegion))) { init(indexDelaunay, Convert.ToInt32(indexRegion.Width / x_size), Convert.ToInt32(indexRegion.Height / y_size), indexRegion.UnionWith(updatedRegion)); } else { // Find the cell region to be updated Point_dt minInvalidCell = getCellOf(updatedRegion.MinPoint); Point_dt maxInvalidCell = getCellOf(updatedRegion.MaxPoint); // And update it with fresh triangles Triangle_dt adjacentValidTriangle = findValidTriangle(minInvalidCell); updateCellValues((int)minInvalidCell.x, (int)minInvalidCell.y, (int)maxInvalidCell.x, (int)maxInvalidCell.y, adjacentValidTriangle); } }
/// <summary> /// assumes v is NOT an halfplane! returns the next triangle for find. /// </summary> private static Triangle_dt findnext1(Point_dt p, Triangle_dt v) { if ((p.pointLineTest(v.a, v.b) == Point_dt.RIGHT & (!(v.abnext.isHalfplane)))) { return(v.abnext); } if ((p.pointLineTest(v.b, v.c) == Point_dt.RIGHT & (!(v.bcnext.isHalfplane)))) { return(v.bcnext); } if ((p.pointLineTest(v.c, v.a) == Point_dt.RIGHT & (!(v.canext.isHalfplane)))) { return(v.canext); } if ((p.pointLineTest(v.a, v.b) == Point_dt.RIGHT)) { return(v.abnext); } if ((p.pointLineTest(v.b, v.c) == Point_dt.RIGHT)) { return(v.bcnext); } if ((p.pointLineTest(v.c, v.a) == Point_dt.RIGHT)) { return(v.canext); } return(null); }
private Triangle_dt extendOutside(Triangle_dt t, Point_dt p) { if ((p.pointLineTest(t.a, t.b) == Point_dt.ONSEGMENT)) { Triangle_dt dg = new Triangle_dt(t.a, t.b, p); Triangle_dt hp = new Triangle_dt(p, t.b); t.b = p; dg.abnext = t.abnext; dg.abnext.switchneighbors(t, dg); dg.bcnext = hp; hp.abnext = dg; dg.canext = t; t.abnext = dg; hp.bcnext = t.bcnext; hp.bcnext.canext = hp; hp.canext = t; t.bcnext = hp; return(dg); } Triangle_dt ccT = extendcounterclock(t, p); Triangle_dt cT = extendclock(t, p); ccT.bcnext = cT; cT.canext = ccT; startTriangleHull = cT; return(cT.abnext); }
private void startTriangulation(Point_dt p1, Point_dt p2) { Point_dt ps = default(Point_dt); Point_dt pb = default(Point_dt); if ((p1.isLess(p2))) { ps = p1; pb = p2; } else { ps = p2; pb = p1; } firstT = new Triangle_dt(pb, ps); lastT = firstT; Triangle_dt t = new Triangle_dt(ps, pb); firstT.abnext = t; t.abnext = firstT; firstT.bcnext = t; t.canext = firstT; firstT.canext = t; t.bcnext = firstT; firstP = firstT.b; lastP = lastT.a; startTriangleHull = firstT; }
/// <summary> /// Returns the neighbors that shares the given corner and is not the previous triangle. /// </summary> /// <param name="p">The given corner.</param> /// <param name="prevTriangle">The previous triangle.</param> /// <returns>The neighbors that shares the given corner and is not the previous triangle.</returns> public object nextNeighbor(Point_dt p, Triangle_dt prevTriangle) { Triangle_dt neighbor = null; if (_a.Equals(p)) { neighbor = _canext; } else if (_b.Equals(p)) { neighbor = _abnext; } else if (_c.Equals(p)) { neighbor = _bcnext; } if (neighbor.Equals(prevTriangle) | neighbor.isHalfplane) { if (_a.Equals(p)) { neighbor = _abnext; } else if (_b.Equals(p)) { neighbor = _bcnext; } else if (_c.Equals(p)) { neighbor = _canext; } } return(neighbor); }
private Triangle_dt extendInside(Triangle_dt t, Point_dt p) { Triangle_dt h1 = default(Triangle_dt); Triangle_dt h2 = default(Triangle_dt); h1 = treatDegeneracyInside(t, p); if ((h1 != null)) { return(h1); } h1 = new Triangle_dt(t.c, t.a, p); h2 = new Triangle_dt(t.b, t.c, p); t.c = p; t.circumcircle(); h1.abnext = t.canext; h1.bcnext = t; h1.canext = h2; h2.abnext = t.bcnext; h2.bcnext = h1; h2.canext = t; h1.abnext.switchneighbors(t, h1); h2.abnext.switchneighbors(t, h2); t.bcnext = h2; t.canext = h1; return(t); }
public void insertPoint(Point_dt p) { if (_vertices.Contains(p)) { return; } _modCount += 1; updateBoundingBox(p); _vertices.Add(p); Triangle_dt t = insertPointSimple(p); if (t == null) { return; } Triangle_dt tt = t; currT = t; // recall the last point for fast (last) update iterator do { Flip(tt, _modCount); tt = tt.canext; } while (tt.Equals(t) & !tt.isHalfplane); if ((gridIndex != null)) { gridIndex.updateIndex(getLastUpdatedTriangles()); } }
private static Triangle_dt Find(Triangle_dt curr, Point_dt p) { if (p == null) { return(null); } Triangle_dt next_t = default(Triangle_dt); if ((curr.isHalfplane)) { next_t = findnext2(p, curr); if ((next_t == null | next_t.isHalfplane)) { return(curr); } curr = next_t; } while (true) { next_t = findnext1(p, curr); if ((next_t == null)) { return(curr); } if ((next_t.isHalfplane)) { return(next_t); } curr = next_t; } return(null); // Never supposed to get here }
/// <summary> /// finds the triangle the query point falls in, note if out-side of this /// triangulation a half plane triangle will be returned (see contains). the /// search starts from the the start triangle /// </summary> /// <param name="p">Query point</param> /// <param name="start">The triangle the search starts at.</param> /// <returns>The triangle that point <paramref name="p"/> is in.</returns> public Triangle_dt Find(Point_dt p, Triangle_dt start) { if (start == null) { start = startTriangle; } Triangle_dt T = Find(start, p); return(T); }
private void allTriangles(Triangle_dt curr, List <Triangle_dt> front, int mc) { if (((curr != null)) && curr.mc == mc && (!(front.Contains(curr)))) { front.Add(curr); allTriangles(curr.abnext, front, mc); allTriangles(curr.bcnext, front, mc); allTriangles(curr.canext, front, mc); } }
public IEnumerator <Triangle_dt> getLastUpdatedTriangles() { List <Triangle_dt> tmp = new List <Triangle_dt>(); if (this.TrianglesSize() > 1) { Triangle_dt t = currT; allTriangles(t, tmp, this._modCount); } return(tmp.GetEnumerator()); }
public void init(Delaunay_Triangulation delaunay, int xCellCount, int yCellCount, BoundingBox region) { indexDelaunay = delaunay; indexRegion = region; x_size = region.Width / yCellCount; y_size = region.Height / xCellCount; // The grid will hold a trinagle for each cell, so a point (x,y) will lie // in the cell representing the grid partition of region to a // xCellCount on yCellCount grid grid = new Triangle_dt[xCellCount + 1, yCellCount + 1]; Triangle_dt colStartTriangle = indexDelaunay.Find(middleOfCell(0, 0)); updateCellValues(0, 0, xCellCount - 1, yCellCount - 1, colStartTriangle); }
private Triangle_dt treatDegeneracyInside(Triangle_dt t, Point_dt p) { if ((t.abnext.isHalfplane & p.pointLineTest(t.b, t.a) == Point_dt.ONSEGMENT)) { return(extendOutside(t.abnext, p)); } if ((t.bcnext.isHalfplane & p.pointLineTest(t.c, t.b) == Point_dt.ONSEGMENT)) { return(extendOutside(t.bcnext, p)); } if ((t.canext.isHalfplane & p.pointLineTest(t.a, t.c) == Point_dt.ONSEGMENT)) { return(extendOutside(t.canext, p)); } return(null); }
/// <summary> /// assumes v is an halfplane! - returns another (none halfplane) triangle /// </summary> private static Triangle_dt findnext2(Point_dt p, Triangle_dt v) { if ((((v.abnext != null)) & (!(v.abnext.isHalfplane)))) { return(v.abnext); } if ((((v.bcnext != null)) & (!(v.bcnext.isHalfplane)))) { return(v.bcnext); } if ((((v.canext != null)) & (!(v.canext.isHalfplane)))) { return(v.canext); } return(null); }
/// <summary> /// Go over each grid cell and locate a triangle in it to be the cell's /// starting search triangle. Since we only pass between adjacent cells /// we can search from the last triangle found and not from the start. /// Add triangles for each column cells /// </summary> /// <param name="startXCell"></param> /// <param name="startYCell"></param> /// <param name="lastXCell"></param> /// <param name="lastYCell"></param> /// <param name="startTriangle"></param> /// <remarks></remarks> private void updateCellValues(int startXCell, int startYCell, int lastXCell, int lastYCell, Triangle_dt startTriangle) { for (int i = startXCell; i <= lastXCell; i++) { // Find a triangle at the begining of the current column startTriangle = indexDelaunay.Find(middleOfCell(i, startYCell), startTriangle); grid[i, startYCell] = startTriangle; Triangle_dt prevRowTriangle = startTriangle; // Add triangles for the next row cells for (int j = startYCell + 1; j <= lastYCell; j++) { grid[i, j] = indexDelaunay.Find(middleOfCell(i, j), prevRowTriangle); prevRowTriangle = grid[i, j]; } } }
/// <summary> /// finds the triangle the query point falls in, note if out-side of this /// triangulation a half plane triangle will be returned (see contains), the /// search has expected time of O(n^0.5), and it starts form a fixed triangle /// (me.startTriangle). /// </summary> /// <param name="p">Query point</param> /// <returns>The triangle that point <paramref name="p"/> is in.</returns> public Triangle_dt Find(Point_dt p) { // If triangulation has a spatial index try to use it as the starting //triangle Triangle_dt searchTriangle = startTriangle; if ((gridIndex != null)) { Triangle_dt indexTriangle = gridIndex.findCellTriangleOf(p); if ((indexTriangle != null)) { searchTriangle = indexTriangle; } } // Search for the point's triangle starting from searchTriangle return(Find(searchTriangle, p)); }
private void Flip(Triangle_dt t, int mc) { Triangle_dt u = t.abnext; Triangle_dt v = default(Triangle_dt); t.mc = mc; if ((u.isHalfplane | (!(u.circumcircle_contains(t.c))))) { return; } if ((t.a.Equals(u.a))) { v = new Triangle_dt(u.b, t.b, t.c); v.abnext = u.bcnext; t.abnext = u.abnext; } else if ((t.a.Equals(u.b))) { v = new Triangle_dt(u.c, t.b, t.c); v.abnext = u.canext; t.abnext = u.bcnext; } else if ((t.a.Equals(u.c))) { v = new Triangle_dt(u.a, t.b, t.c); v.abnext = u.abnext; t.abnext = u.canext; } else { throw new Exception("Error in flip."); } v.mc = mc; v.bcnext = t.bcnext; v.abnext.switchneighbors(u, v); v.bcnext.switchneighbors(t, v); t.bcnext = v; v.canext = t; t.b = v.a; t.abnext.switchneighbors(u, t); t.circumcircle(); currT = v; Flip(t, mc); Flip(v, mc); }
private Triangle_dt extendclock(Triangle_dt t, Point_dt p) { t.isHalfplane = false; t.c = p; t.circumcircle(); Triangle_dt tbc = t.bcnext; if ((p.pointLineTest(tbc.a, tbc.b) >= Point_dt.RIGHT)) { Triangle_dt nT = new Triangle_dt(p, t.b); nT.abnext = t; t.bcnext = nT; nT.bcnext = tbc; tbc.canext = nT; return(nT); } return(extendclock(tbc, p)); }
private Triangle_dt extendcounterclock(Triangle_dt t, Point_dt p) { t.isHalfplane = false; t.c = p; t.circumcircle(); Triangle_dt tca = t.canext; if ((p.pointLineTest(tca.a, tca.b) >= Point_dt.RIGHT)) { Triangle_dt nT = new Triangle_dt(t.a, p); nT.abnext = t; t.canext = nT; nT.canext = tca; tca.bcnext = nT; return(nT); } return(extendcounterclock(tca, p)); }
public void switchneighbors(Triangle_dt old_t, Triangle_dt new_t) { if (_abnext.Equals(old_t)) { _abnext = new_t; } else if (_bcnext.Equals(old_t)) { _bcnext = new_t; } else if (_canext.Equals(old_t)) { _canext = new_t; } else { Console.WriteLine("Error, switchneighbors can't find Old."); } }
private void initTriangles() { if (_modCount == _modCount2) { return; } if (this.Size() > 2) { _modCount2 = _modCount; List <Triangle_dt> front = new List <Triangle_dt>(); _triangles = new List <Triangle_dt>(); front.Add(this.startTriangle); while ((front.Count > 0)) { Triangle_dt t = front[0]; front.RemoveAt(0); if ((t.mark == false)) { t.mark = true; _triangles.Add(t); if (((t.abnext != null)) && (!t.abnext.mark)) { front.Add(t.abnext); } if (((t.bcnext != null)) && (!t.bcnext.mark)) { front.Add(t.bcnext); } if (((t.canext != null)) && (!t.canext.mark)) { front.Add(t.bcnext); } } } foreach (Triangle_dt aTriangle in _triangles) { aTriangle.mark = false; } } }
/// <summary> /// Return point with x/y and updated Z value (z value is as given by the triangulation) /// </summary> /// <param name="p">Query point (x/y=</param> /// <returns></returns> /// <remarks></remarks> public Point_dt z(Point_dt p) { Triangle_dt t = Find(p); return(t.z(p)); }
/// <summary> /// Search for x/y point within current triangulation /// </summary> /// <param name="x">Query point x coordinate</param> /// <param name="y">Query point y coordinate</param> /// <returns>Return true if x/y is within current triangulation (in its 2D convex hull).</returns> public bool contains(double x, double y) { Triangle_dt tt = Find(new Point_dt(x, y)); return(!tt.isHalfplane); }
/// <summary> /// Search for p within current triangulation /// </summary> /// <param name="p">Query point</param> /// <returns>Return true if p is within current triangulation (in its 2D convex hull).</returns> public bool contains(Point_dt p) { Triangle_dt tt = Find(p); return(!tt.isHalfplane); }
private Triangle_dt insertPointSimple(Point_dt p) { nPoints += 1; if ((!allColinear)) { Triangle_dt t = Find(startTriangle, p); if (t.isHalfplane) { startTriangle = extendOutside(t, p); } else { startTriangle = extendInside(t, p); } return(startTriangle); } if (nPoints == 1) { firstP = p; return(null); } if (nPoints == 2) { startTriangulation(firstP, p); return(null); } switch (p.pointLineTest(firstP, lastP)) { case Point_dt.LEFT: startTriangle = extendOutside(firstT.abnext, p); allColinear = false; break; // TODO: might not be correct. Was : Exit Select break; case Point_dt.RIGHT: startTriangle = extendOutside(firstT, p); allColinear = false; break; // TODO: might not be correct. Was : Exit Select break; case Point_dt.ONSEGMENT: insertCollinear(p, Point_dt.ONSEGMENT); break; // TODO: might not be correct. Was : Exit Select break; case Point_dt.INFRONTOFA: insertCollinear(p, Point_dt.INFRONTOFA); break; // TODO: might not be correct. Was : Exit Select break; case Point_dt.BEHINDB: insertCollinear(p, Point_dt.BEHINDB); break; // TODO: might not be correct. Was : Exit Select break; } return(null); }
private void insertCollinear(Point_dt p, int res) { Triangle_dt t = default(Triangle_dt); Triangle_dt tp = default(Triangle_dt); Triangle_dt u = default(Triangle_dt); switch (res) { case Point_dt.INFRONTOFA: t = new Triangle_dt(firstP, p); tp = new Triangle_dt(p, firstP); t.abnext = tp; tp.abnext = t; t.bcnext = tp; tp.canext = t; t.canext = firstT; firstT.bcnext = t; tp.bcnext = firstT.abnext; firstT.abnext.canext = tp; firstT = t; firstP = p; break; // TODO: might not be correct. Was : Exit Select break; case Point_dt.BEHINDB: t = new Triangle_dt(p, lastP); tp = new Triangle_dt(lastP, p); t.abnext = tp; tp.abnext = t; t.bcnext = lastT; lastT.canext = t; t.canext = tp; tp.bcnext = t; tp.canext = lastT.abnext; lastT.abnext.bcnext = tp; lastT = t; lastP = p; break; // TODO: might not be correct. Was : Exit Select break; case Point_dt.ONSEGMENT: u = firstT; while (p.isGreater(u.a)) { u = u.canext; } t = new Triangle_dt(p, u.b); tp = new Triangle_dt(u.b, p); u.b = p; u.abnext.a = p; t.abnext = tp; tp.abnext = t; t.bcnext = u.bcnext; u.bcnext.canext = t; t.canext = u; u.bcnext = t; tp.canext = u.abnext.canext; u.abnext.canext.bcnext = tp; tp.bcnext = u.abnext; u.abnext.canext = tp; if ((firstT.Equals(u))) { firstT = t; } break; // TODO: might not be correct. Was : Exit Select break; } }