/// <summary> /// A basic strategy for finding split points when nothing extra is known about the geometry of /// the situation. /// </summary> /// <param name="seg">the encroached segment</param> /// <param name="encroachPt">the encroaching point</param> /// <returns>the point at which to split the encroached segment</returns> public Coordinate FindSplitPoint(Segment seg, Coordinate encroachPt) { var lineSeg = seg.LineSegment; var segLen = lineSeg.Length; var midPtLen = segLen / 2; var splitSeg = new SplitSegment(lineSeg); var projPt = ProjectedSplitPoint(seg, encroachPt); /* * Compute the largest diameter (length) that will produce a split segment which is not * still encroached upon by the encroaching point (The length is reduced slightly by a * safety factor) */ var nonEncroachDiam = projPt.Distance(encroachPt) * 2 * 0.8; // .99; var maxSplitLen = nonEncroachDiam; if (maxSplitLen > midPtLen) { maxSplitLen = midPtLen; } splitSeg.MinimumLength = maxSplitLen; splitSeg.SplitAt(projPt); return splitSeg.SplitPoint; }
// public static final String DEBUG_SEG_SPLIT = "C:\\proj\\CWB\\test\\segSplit.jml"; /// <summary> /// Given a set of points stored in the kd-tree and a line segment defined by /// two points in this set, finds a <see cref="Coordinate"/> in the circumcircle of /// the line segment, if one exists. This is called the Gabriel point - if none /// exists then the segment is said to have the Gabriel condition. Uses the /// heuristic of finding the non-Gabriel point closest to the midpoint of the /// segment. /// </summary> /// <param name="seg">the line segment</param> /// <returns> /// A point which is non-Gabriel, /// or null if no point is non-Gabriel /// </returns> private Coordinate FindNonGabrielPoint(Segment seg) { Coordinate p = seg.Start; Coordinate q = seg.End; // Find the mid point on the line and compute the radius of enclosing circle Coordinate midPt = new Coordinate((p.X + q.X) / 2.0, (p.Y + q.Y) / 2.0); double segRadius = p.Distance(midPt); // compute envelope of circumcircle Envelope env = new Envelope(midPt); env.ExpandBy(segRadius); // Find all points in envelope ICollection<KdNode<Vertex>> result = _kdt.Query(env); // For each point found, test if it falls strictly in the circle // find closest point Coordinate closestNonGabriel = null; double minDist = Double.MaxValue; foreach (KdNode<Vertex> nextNode in result) { Coordinate testPt = nextNode.Coordinate; // ignore segment endpoints if (testPt.Equals2D(p) || testPt.Equals2D(q)) continue; double testRadius = midPt.Distance(testPt); if (testRadius < segRadius) { // double testDist = seg.distance(testPt); double testDist = testRadius; if (closestNonGabriel == null || testDist < minDist) { closestNonGabriel = testPt; minDist = testDist; } } } return closestNonGabriel; }
/* * private List findMissingConstraints() { List missingSegs = new ArrayList(); * for (int i = 0; i < segments.size(); i++) { Segment s = (Segment) * segments.get(i); QuadEdge q = subdiv.locate(s.getStart(), s.getEnd()); if * (q == null) missingSegs.add(s); } return missingSegs; } */ private int EnforceGabriel(ICollection<Segment> segsToInsert) { List<Segment> newSegments = new List<Segment>(); int splits = 0; List<Segment> segsToRemove = new List<Segment>(); /* * On each iteration must always scan all constraint (sub)segments, since * some constraints may be rebroken by Delaunay triangle flipping caused by * insertion of another constraint. However, this process must converge * eventually, with no splits remaining to find. */ foreach (Segment seg in segsToInsert) { // System.out.println(seg); Coordinate encroachPt = FindNonGabrielPoint(seg); // no encroachment found - segment must already be in subdivision if (encroachPt == null) continue; // compute split point _splitPt = _splitFinder.FindSplitPoint(seg, encroachPt); ConstraintVertex splitVertex = CreateVertex(_splitPt, seg); /* * Check whether the inserted point still equals the split pt. This will * not be the case if the split pt was too close to an existing site. If * the point was snapped, the triangulation will not respect the inserted * constraint - this is a failure. This can be caused by: * <ul> * <li>An initial site that lies very close to a constraint segment The * cure for this is to remove any initial sites which are close to * constraint segments in a preprocessing phase. * <li>A narrow constraint angle which causing repeated splitting until * the split segments are too small. The cure for this is to either choose * better split points or "guard" narrow angles by cracking the segments * equidistant from the corner. * </ul> */ ConstraintVertex insertedVertex = InsertSite(splitVertex); //Debugging FObermaier //Console.WriteLine("inserted vertex: " + insertedVertex.ToString()); if (!insertedVertex.Coordinate.Equals2D(_splitPt)) { Debug.WriteLine("Split pt snapped to: " + insertedVertex); // throw new ConstraintEnforcementException("Split point snapped to // existing point // (tolerance too large or constraint interior narrow angle?)", // splitPt); } // split segment and record the new halves Segment s1 = new Segment(seg.StartX, seg.StartY, seg.StartZ, splitVertex.X, splitVertex.Y, splitVertex.Z, seg.Data); Segment s2 = new Segment(splitVertex.X, splitVertex.Y, splitVertex.Z, seg.EndX, seg.EndY, seg.EndZ, seg.Data); //Debugging FObermaier //Console.WriteLine("Segment " + seg.ToString() + " splitted to \n\t" + s1.ToString() + "\n\t"+ s2.ToString()); newSegments.Add(s1); newSegments.Add(s2); segsToRemove.Add(seg); splits = splits + 1; } foreach (Segment seg in segsToRemove) { segsToInsert.Remove(seg); } foreach (Segment seg in newSegments) { segsToInsert.Add(seg); } return splits; }
/// <summary> /// Creates a vertex on a constraint segment /// </summary> /// <param name="p">the location of the vertex to create</param> /// <param name="seg">the constraint segment it lies on</param> /// <returns>the new constraint vertex</returns> private ConstraintVertex CreateVertex(Coordinate p, Segment seg) { ConstraintVertex v; if (_vertexFactory != null) v = _vertexFactory.CreateVertex(p, seg); else v = new ConstraintVertex(p); v.IsOnConstraint = true; return v; }
/// <summary> /// Computes the intersection point between this segment and another one. /// </summary> /// <param name="s">a segment</param> /// <returns>the intersection point, or <code>null</code> if there is none</returns> public Coordinate Intersection(Segment s) { return _ls.Intersection(s.LineSegment); }
/// <summary> /// Determines whether two segments are topologically equal. /// I.e. equal up to orientation. /// </summary> /// <param name="s">a segment</param> /// <returns>true if the segments are topologically equal</returns> public bool EqualsTopologically(Segment s) { return _ls.EqualsTopologically(s.LineSegment); }
/// <summary> /// Computes a split point which is the projection of the encroaching point on the segment /// </summary> /// <param name="seg">The segment</param> /// <param name="encroachPt">The enchroaching point</param> /// <returns>A split point on the segment</returns> public static Coordinate ProjectedSplitPoint(Segment seg, Coordinate encroachPt) { LineSegment lineSeg = seg.LineSegment; Coordinate projPt = lineSeg.Project(encroachPt); return projPt; }
/// <summary> /// Gets the midpoint of the split segment /// </summary> public Coordinate FindSplitPoint(Segment seg, Coordinate encroachPt) { Coordinate p0 = seg.Start; Coordinate p1 = seg.End; return new Coordinate((p0.X + p1.X) / 2, (p0.Y + p1.Y) / 2); }