private double arcAngle; // angle in radians internal Arc(Circle circle, ICoordinate p1, ICoordinate midPt, ICoordinate p2) { this.circle = circle; this.p1 = p1; this.p2 = p2; p1Angle = circle.GetAngle(p1); // See if this arc covers the whole circle if (p1.Equals2D(p2)) { p2Angle = TWO_PI + p1Angle; arcAngle = TWO_PI; } else { p2Angle = circle.GetAngle(p2); double midPtAngle = circle.GetAngle(midPt); // determine the direction double ccDegrees = SubtractAngles(p1Angle, midPtAngle) + SubtractAngles(midPtAngle, p2Angle); if (ccDegrees < TWO_PI) { clockwise = false; arcAngle = ccDegrees; } else { clockwise = true; arcAngle = TWO_PI - ccDegrees; } } }
/// <summary> /// Compares two <see cref="Coordinate" />s for their relative position along a segment /// lying in the specified <see cref="Octant" />. /// </summary> /// <param name="octant"></param> /// <param name="p0"></param> /// <param name="p1"></param> /// <returns> /// -1 if node0 occurs first, or /// 0 if the two nodes are equal, or /// 1 if node1 occurs first. /// </returns> public static int Compare(Octants octant, ICoordinate p0, ICoordinate p1) { // nodes can only be equal if their coordinates are equal if (p0.Equals2D(p1)) return 0; int xSign = RelativeSign(p0.X, p1.X); int ySign = RelativeSign(p0.Y, p1.Y); switch (octant) { case Octants.Zero: return CompareValue(xSign, ySign); case Octants.One: return CompareValue(ySign, xSign); case Octants.Two: return CompareValue(ySign, -xSign); case Octants.Three: return CompareValue(-xSign, ySign); case Octants.Four: return CompareValue(-xSign, -ySign); case Octants.Five: return CompareValue(-ySign, -xSign); case Octants.Six: return CompareValue(-ySign, xSign); case Octants.Seven: return CompareValue(xSign, -ySign); } Assert.ShouldNeverReachHere("invalid octant value: " + octant); return 0; }
/// <summary> /// Initializes a new instance of the <see cref="SegmentNode"/> class. /// </summary> /// <param name="segString"></param> /// <param name="coord"></param> /// <param name="segmentIndex"></param> /// <param name="segmentOctant"></param> public SegmentNode(SegmentString segString, ICoordinate coord, int segmentIndex, Octants segmentOctant) { this.segString = segString; this.Coordinate = new Coordinate(coord); this.SegmentIndex = segmentIndex; this.segmentOctant = segmentOctant; isInterior = !coord.Equals2D(segString.GetCoordinate(segmentIndex)); }
/// <summary> /// Initializes a new instance of the <see cref="SegmentNode"/> class. /// </summary> /// <param name="segString"></param> /// <param name="coord"></param> /// <param name="segmentIndex"></param> /// <param name="segmentOctant"></param> public SegmentNode(SegmentString segString, ICoordinate coord, int segmentIndex, Octants segmentOctant) { this.segString = segString; this.Coordinate = new Coordinate(coord); this.SegmentIndex = segmentIndex; this.segmentOctant = segmentOctant; isInterior = !coord.Equals2D(segString.GetCoordinate(segmentIndex)); }
/// <summary> /// Adds nodes for any collapsed edge pairs /// which are pre-existing in the vertex list. /// </summary> /// <param name="collapsedVertexIndexes"></param> private void FindCollapsesFromExistingVertices(IList collapsedVertexIndexes) { for (int i = 0; i < edge.Count - 2; i++) { ICoordinate p0 = edge.GetCoordinate(i); ICoordinate p1 = edge.GetCoordinate(i + 1); ICoordinate p2 = edge.GetCoordinate(i + 2); if (p0.Equals2D(p2)) // add base of collapse as node { collapsedVertexIndexes.Add(i + 1); } } }
/// <summary> /// /// </summary> /// <param name="pt"></param> /// <param name="snapPts"></param> /// <returns></returns> private ICoordinate FindSnapForVertex(ICoordinate pt, ICoordinate[] snapPts) { foreach (ICoordinate coord in snapPts) { // if point is already equal to a src pt, don't snap if (pt.Equals2D(coord)) { return(null); } if (pt.Distance(coord) < snapTolerance) { return(coord); } } return(null); }
private Arc(Circle circle, ICoordinate p1, ICoordinate p2, bool isClockwise) { this.p1 = p1; this.p2 = p2; clockwise = isClockwise; p1Angle = circle.GetAngle(p1); if (p1.Equals2D(p2)) { p2Angle = TWO_PI + p1Angle; } else { p2Angle = circle.GetAngle(p2); } DetermineArcAngle(); }
/// <summary> /// </summary> /// <param name="obj"></param> /// <returns> /// -1 this SegmentNode is located before the argument location, or /// 0 this SegmentNode is at the argument location, or /// 1 this SegmentNode is located after the argument location. /// </returns> public int CompareTo(object obj) { SegmentNode other = (SegmentNode)obj; if (SegmentIndex < other.SegmentIndex) { return(-1); } if (SegmentIndex > other.SegmentIndex) { return(1); } if (Coordinate.Equals2D(other.Coordinate)) { return(0); } return(SegmentPointComparator.Compare(segmentOctant, Coordinate, other.Coordinate)); }
private void DetermineArcAngle() { double diff; if (p1.Equals2D(p2)) { diff = TWO_PI; } else if (clockwise) { diff = p1Angle - p2Angle; } else { diff = p2Angle - p1Angle; } arcAngle = NormalizeAngle(diff); }
/// <summary> /// Compares two <see cref="Coordinate" />s for their relative position along a segment /// lying in the specified <see cref="Octant" />. /// </summary> /// <param name="octant"></param> /// <param name="p0"></param> /// <param name="p1"></param> /// <returns> /// -1 if node0 occurs first, or /// 0 if the two nodes are equal, or /// 1 if node1 occurs first. /// </returns> public static int Compare(Octants octant, ICoordinate p0, ICoordinate p1) { // nodes can only be equal if their coordinates are equal if (p0.Equals2D(p1)) { return(0); } int xSign = RelativeSign(p0.X, p1.X); int ySign = RelativeSign(p0.Y, p1.Y); switch (octant) { case Octants.Zero: return(CompareValue(xSign, ySign)); case Octants.One: return(CompareValue(ySign, xSign)); case Octants.Two: return(CompareValue(ySign, -xSign)); case Octants.Three: return(CompareValue(-xSign, ySign)); case Octants.Four: return(CompareValue(-xSign, -ySign)); case Octants.Five: return(CompareValue(-ySign, -xSign)); case Octants.Six: return(CompareValue(-ySign, xSign)); case Octants.Seven: return(CompareValue(xSign, -ySign)); } Assert.ShouldNeverReachHere("invalid octant value: " + octant); return(0); }
/// <summary> /// /// </summary> /// <param name="intPt"></param> /// <param name="segmentIndex"></param> public void AddIntersection(ICoordinate intPt, int segmentIndex) { int normalizedSegmentIndex = segmentIndex; // normalize the intersection point location int nextSegIndex = normalizedSegmentIndex + 1; if (nextSegIndex < pts.Length) { ICoordinate nextPt = pts[nextSegIndex]; // Normalize segment index if intPt falls on vertex // The check for point equality is 2D only - Z values are ignored if (intPt.Equals2D(nextPt)) { normalizedSegmentIndex = nextSegIndex; } } // Add the intersection point to edge intersection list. SegmentNode ei = nodeList.Add(intPt, normalizedSegmentIndex); }
/// <summary> /// /// </summary> /// <param name="splitEdges"></param> private void CheckSplitEdgesCorrectness(IList splitEdges) { ICoordinate[] edgePts = edge.Coordinates; // check that first and last points of split edges are same as endpoints of edge SegmentString split0 = (SegmentString)splitEdges[0]; ICoordinate pt0 = split0.GetCoordinate(0); if (!pt0.Equals2D(edgePts[0])) { throw new Exception("bad split edge start point at " + pt0); } SegmentString splitn = (SegmentString)splitEdges[splitEdges.Count - 1]; ICoordinate[] splitnPts = splitn.Coordinates; ICoordinate ptn = splitnPts[splitnPts.Length - 1]; if (!ptn.Equals2D(edgePts[edgePts.Length - 1])) { throw new Exception("bad split edge end point at " + ptn); } }
/// <summary> /// /// </summary> /// <param name="intPt"></param> /// <param name="segmentIndex"></param> public void AddIntersection(ICoordinate intPt, int segmentIndex) { var normalizedSegmentIndex = segmentIndex; // normalize the intersection point location var nextSegIndex = normalizedSegmentIndex + 1; if(nextSegIndex < pts.Length) { var nextPt = pts[nextSegIndex]; // Normalize segment index if intPt falls on vertex // The check for point equality is 2D only - Z values are ignored if (intPt.Equals2D(nextPt)) normalizedSegmentIndex = nextSegIndex; } // Add the intersection point to edge intersection list. var ei = nodeList.Add(intPt, normalizedSegmentIndex); }
/// <summary> /// /// </summary> /// <param name="pt"></param> /// <param name="snapPts"></param> /// <returns></returns> private ICoordinate FindSnapForVertex(ICoordinate pt, ICoordinate[] snapPts) { foreach (ICoordinate coord in snapPts) { // if point is already equal to a src pt, don't snap if (pt.Equals2D(coord)) return null; if (pt.Distance(coord) < snapTolerance) return coord; } return null; }
/// <summary> /// Computes whether a ring defined by an array of <see cref="Coordinate" />s is oriented counter-clockwise. /// The list of points is assumed to have the first and last points equal. /// This will handle coordinate lists which contain repeated points. /// This algorithm is only guaranteed to work with valid rings. /// If the ring is invalid (e.g. self-crosses or touches), /// the computed result may not be correct. /// </summary>> /// <param name="ring"></param> /// <returns></returns> public static bool IsCCW(ICoordinate[] ring) { // # of points without closing endpoint int nPts = ring.Length - 1; // find highest point ICoordinate hiPt = ring[0]; int hiIndex = 0; for (int i = 1; i <= nPts; i++) { ICoordinate p = ring[i]; if (p.Y > hiPt.Y) { hiPt = p; hiIndex = i; } } // find distinct point before highest point int iPrev = hiIndex; do { iPrev = iPrev - 1; if (iPrev < 0) { iPrev = nPts; } }while (ring[iPrev].Equals2D(hiPt) && iPrev != hiIndex); // find distinct point after highest point int iNext = hiIndex; do { iNext = (iNext + 1) % nPts; }while (ring[iNext].Equals2D(hiPt) && iNext != hiIndex); ICoordinate prev = ring[iPrev]; ICoordinate next = ring[iNext]; /* * This check catches cases where the ring contains an A-B-A configuration of points. * This can happen if the ring does not contain 3 distinct points * (including the case where the input array has fewer than 4 elements), * or it contains coincident line segments. */ if (prev.Equals2D(hiPt) || next.Equals2D(hiPt) || prev.Equals2D(next)) { return(false); } int disc = ComputeOrientation(prev, hiPt, next); /* * If disc is exactly 0, lines are collinear. There are two possible cases: * (1) the lines lie along the x axis in opposite directions * (2) the lines lie on top of one another * * (1) is handled by checking if next is left of prev ==> CCW * (2) will never happen if the ring is valid, so don't check for it * (Might want to assert this) */ bool isCCW = false; if (disc == 0) { // poly is CCW if prev x is right of next x isCCW = (prev.X > next.X); } else { // if area is positive, points are ordered CCW isCCW = (disc > 0); } return(isCCW); }
private double arcAngle; // angle in radians #endregion Fields #region Constructors internal Arc(Circle circle, ICoordinate p1, ICoordinate midPt, ICoordinate p2) { this.circle = circle; this.p1 = p1; this.p2 = p2; p1Angle = circle.GetAngle(p1); // See if this arc covers the whole circle if (p1.Equals2D(p2)) { p2Angle = TWO_PI + p1Angle; arcAngle = TWO_PI; } else { p2Angle = circle.GetAngle(p2); double midPtAngle = circle.GetAngle(midPt); // determine the direction double ccDegrees = SubtractAngles(p1Angle, midPtAngle) + SubtractAngles(midPtAngle, p2Angle); if (ccDegrees < TWO_PI) { clockwise = false; arcAngle = ccDegrees; } else { clockwise = true; arcAngle = TWO_PI - ccDegrees; } } }
private Arc(Circle circle, ICoordinate p1, ICoordinate p2, bool isClockwise) { this.p1 = p1; this.p2 = p2; clockwise = isClockwise; p1Angle = circle.GetAngle(p1); if (p1.Equals2D(p2)) { p2Angle = TWO_PI + p1Angle; } else { p2Angle = circle.GetAngle(p2); } DetermineArcAngle(); }