/// <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, Coordinate coord, int segmentIndex, OctantDirection segmentOctant) { Coordinate = new Coordinate(coord); SegmentIndex = segmentIndex; _segmentOctant = segmentOctant; _isInterior = !coord.Equals2D(segString.GetCoordinate(segmentIndex)); }
public void Equals2D_ReturnsTrueForCoordinateWithTheSameOrdinates() { Coordinate target = new Coordinate(xCoordinate, yCoordinate, zCoordinate, mValue); Coordinate other = new Coordinate(xCoordinate, yCoordinate, zCoordinate, mValue); Assert.True(target.Equals2D(other)); }
public void Equals2D_ReturnsFalseForCoordinateWithDifferentXYOrdinates() { Coordinate target = new Coordinate(xCoordinate, yCoordinate, zCoordinate, mValue); Coordinate other = new Coordinate(xCoordinate + 1, yCoordinate + 1, zCoordinate, mValue); Assert.False(target.Equals2D(other)); }
/// <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, Coordinate p0, Coordinate 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, Coordinate 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(INodableSegmentString segString, Coordinate coord, int segmentIndex, Octants segmentOctant) { Coord = null; _segString = segString; Coord = new Coordinate(coord.X, coord.Y, coord.Z); SegmentIndex = segmentIndex; _segmentOctant = segmentOctant; _isInterior = !coord.Equals2D(segString.Coordinates[segmentIndex]); }
private static void PreciseCoordinateTester(IPrecisionModel pm, double x1, double y1, double x2, double y2) { var p = new Coordinate(x1, y1); pm.MakePrecise(p); var pPrecise = new Coordinate(x2, y2); Assert.IsTrue(p.Equals2D(pPrecise), "Expected {0}, but got {1}", pPrecise, p); }
public void TestEquals() { Coordinate c1 = new Coordinate(1, 2, 3); const string s = "Not a coordinate"; Assert.IsFalse(c1.Equals(s)); Coordinate c2 = new Coordinate(1, 2, 3); Assert.IsTrue(c1.Equals2D(c2)); Coordinate c3 = new Coordinate(1, 22, 3); Assert.IsFalse(c1.Equals2D(c3)); }
/// <summary> /// Computes the "edge distance" of an intersection point p along a segment. /// The edge distance is a metric of the point along the edge. /// The metric used is a robust and easy to compute metric function. /// It is not equivalent to the usual Euclidean metric. /// It relies on the fact that either the x or the y ordinates of the /// points in the edge are unique, depending on whether the edge is longer in /// the horizontal or vertical direction. /// NOTE: This function may produce incorrect distances /// for inputs where p is not precisely on p1-p2 /// (E.g. p = (139,9) p1 = (139,10), p2 = (280,1) produces distanct 0.0, which is incorrect. /// My hypothesis is that the function is safe to use for points which are the /// result of rounding points which lie on the line, but not safe to use for truncated points. /// </summary> public static double ComputeEdgeDistance(Coordinate p, Coordinate p0, Coordinate p1) { var dx = Math.Abs(p1.X - p0.X); var dy = Math.Abs(p1.Y - p0.Y); var dist = -1.0; // sentinel value if (p.Equals(p0)) dist = 0.0; else if (p.Equals(p1)) { dist = dx > dy ? dx : dy; } else { double pdx = Math.Abs(p.X - p0.X); double pdy = Math.Abs(p.Y - p0.Y); dist = dx > dy ? pdx : pdy; // <FIX>: hack to ensure that non-endpoints always have a non-zero distance if (dist == 0.0 && ! p.Equals2D(p0)) dist = Math.Max(pdx, pdy); } Assert.IsTrue(!(dist == 0.0 && ! p.Equals(p0)), "Bad distance calculation"); return dist; }
private Arc(Circle circle, Coordinate p1, Coordinate 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(); }
private double arcAngle; // angle in radians internal Arc(Circle circle, Coordinate p1, Coordinate midPt, Coordinate 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> /// Tests whether either intersection point is an interior point of the specified input segment. /// </summary> /// <returns> /// <c>true</c> if either intersection point is in the interior of the input segment. /// </returns> public virtual bool IsInteriorIntersection(int inputLineIndex) { Coordinate ptI; for (int i = 0; i < (int)_result; i++) { ptI = new Coordinate(_intPt[i]); if (!(ptI.Equals2D(_inputLines[inputLineIndex, 0]) || ptI.Equals2D(_inputLines[inputLineIndex, 1]))) return true; } return false; }
/// <summary> /// Calculates distance between a point and a line AB /// </summary> /// <param name="c">The coordinate to compute the distance for.</param> /// <param name="a">One point of the line.</param> /// <param name="b">Another point of the line.</param> /// <param name="mode">LineMode value that specifies whether AB should be treated as infinite line or as line segment.</param> /// <returns> The distance from C to line AB in coordinate's units.</returns> public double CalculateDistance(Coordinate c, Coordinate a, Coordinate b, LineMode mode) { if (a.Equals2D(b)) { return this.CalculateDistance(c, a); } double deltaX = b.X - a.X; double deltaY = b.Y - a.Y; if (mode == LineMode.LineSegment) { /* Let P be the point of perpendicular projection of C on AB. The parameter r, which indicates P's position along AB, is computed by the dot product of AC and AB divided by the square of the length of AB: AC dot AB r = --------- ||AB||^2 r has the following meaning: r=0 P = A r=1 P = B r<0 P is on the backward extension of AB r>1 P is on the forward extension of AB 0<r<1 P is interior to AB */ double r = ((c.X - a.X) * deltaX + (c.Y - a.Y) * deltaY) / (deltaX * deltaX + deltaY * deltaY); if (r <= 0.0) { return this.CalculateDistance(c, a); } if (r >= 1.0) { return this.CalculateDistance(c, b); } } /* Use another parameter s to indicate the location along PC, with the following meaning: s<0 C is left of AB s>0 C is right of AB s=0 C is on AB (principialy the same as r - only use perpendicular vector) Compute s as follows: (Ay-Cy)(Bx-Ax)-(Ax-Cx)(By-Ay) s = ----------------------------- L^2 */ double s = ((a.Y - c.Y) * deltaX - (a.X - c.X) * deltaY) / (deltaX * deltaX + deltaY * deltaY); /* Then the distance from C to P = |s|*L. */ return Math.Abs(s) * Math.Sqrt(deltaX * deltaX + deltaY * deltaY); }
private static Octants SafeOctant(Coordinate p0, Coordinate p1) { if (p0.Equals2D(p1)) return Octants.Zero; return Octant.GetOctant(p0, p1); }
/// <summary> /// /// </summary> /// <param name="intPt"></param> /// <param name="segmentIndex"></param> public void AddIntersection(Coordinate 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); }
private static bool IsSnapped(Coordinate v, Coordinate p0, Coordinate p1) { if (v.Equals2D(p0)) return true; if (v.Equals2D(p1)) return true; var seg = new LineSegment(p0, p1); var dist = seg.Distance(v); if (dist < SnapTolerance / 2.05) return false; return true; }
/// <summary> /// Zooms the map to fit a bounding box /// </summary> /// <remarks> /// NOTE: If the aspect ratio of the box and the aspect ratio of the mapsize /// isn't the same, the resulting map-envelope will be adjusted so that it contains /// the bounding box, thus making the resulting envelope larger! /// </remarks> /// <param name="bbox"></param> public void ZoomToBox(Envelope bbox) { if (bbox != null && !bbox.IsNull) { //Ensure aspect ratio var resX = Size.Width == 0 ? double.MaxValue : bbox.Width / Size.Width; var resY = Size.Height == 0 ? double.MaxValue : bbox.Height / Size.Height; var zoom = bbox.Width; if (resY > resX && resX > 0) { zoom *= resY / resX; } var center = new Coordinate(bbox.Centre); zoom = _mapViewportGuard.VerifyZoom(zoom, center); var changed = false; if (zoom != _zoom) { _zoom = zoom; changed = true; } if (!center.Equals2D(_center)) { _center = center; changed = true; } if (changed && MapViewOnChange != null) MapViewOnChange(); } }
public void TestEquals2D() { Coordinate c1 = new Coordinate(1, 2, 3); Coordinate c2 = new Coordinate(1, 2, 3); Assert.IsTrue(c1.Equals2D(c2)); Coordinate c3 = new Coordinate(1, 22, 3); Assert.IsFalse(c1.Equals2D(c3)); }
public void Equals2D_ReturnsTrueForNaNCoordinates() { Coordinate target = new Coordinate(double.NaN, double.NaN, double.NaN, double.NaN); Coordinate other = new Coordinate(double.NaN, double.NaN, double.NaN, double.NaN); Assert.True(target.Equals2D(other)); }
/// <summary> /// /// </summary> /// <param name="pt"></param> /// <param name="snapPts"></param> /// <returns></returns> private Coordinate FindSnapForVertex(Coordinate pt, Coordinate[] snapPts) { foreach (Coordinate 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 IsCounterClockwise(IList<Coordinate> ring) { // # of points without closing endpoint int nPts = ring.Count - 1; // find highest point Coordinate hiPt = ring[0]; int hiIndex = 0; for (int i = 1; i <= nPts; i++) { Coordinate 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); Coordinate prev = new Coordinate(ring[iPrev]); Coordinate next = new Coordinate(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, new Coordinate(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; 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; }
/// <summary> /// Add an EdgeIntersection for intersection intIndex. /// An intersection that falls exactly on a vertex of the edge is normalized /// to use the higher of the two possible segmentIndexes. /// </summary> /// <param name="li"></param> /// <param name="segmentIndex"></param> /// <param name="geomIndex"></param> /// <param name="intIndex"></param> public virtual void AddIntersection(LineIntersector li, int segmentIndex, int geomIndex, int intIndex) { Coordinate intPt = new Coordinate(li.GetIntersection(intIndex)); int normalizedSegmentIndex = segmentIndex; double dist = li.GetEdgeDistance(geomIndex, intIndex); // normalize the intersection point location int nextSegIndex = normalizedSegmentIndex + 1; if (nextSegIndex < Points.Count) { Coordinate nextPt = Points[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; dist = 0.0; } // Add the intersection point to edge intersection list. EdgeIntersectionList.Add(intPt, normalizedSegmentIndex, dist); } }
public void TestEquals2DWithinTolerance() { Coordinate c = new Coordinate(100.0, 200.0, 50.0); Coordinate aBitOff = new Coordinate(100.1, 200.1, 50.0); Assert.IsTrue(c.Equals2D(aBitOff, 0.2)); }