/// <summary> /// Determines the location of the specified <see cref="PointD"/> coordinates relative to /// the specified line segment, assuming they are collinear and given the specified epsilon /// for coordinate comparisons.</summary> /// <param name="a"> /// The <see cref="LineD.Start"/> point of the line segment.</param> /// <param name="b"> /// The <see cref="LineD.End"/> point of the line segment.</param> /// <param name="q"> /// The <see cref="PointD"/> coordinates to examine.</param> /// <param name="epsilon"> /// The maximum absolute value at which intermediate results should be considered zero. /// </param> /// <returns> /// A <see cref="LineLocation"/> value indicating the location of <paramref name="q"/> /// relative to the line segment from <paramref name="a"/> to <paramref name="b"/>. /// </returns> /// <remarks><para> /// <b>LocateCollinear</b> is identical with the <see cref="LineD.LocateCollinear(PointD, /// Double)"/> instance method of the <see cref="LineD"/> structure but takes the start and /// end points of the line segment as explicit parameters. /// </para><para> /// The specified <paramref name="epsilon"/> must be greater than zero, but /// <b>LocateCollinear</b> does not check this condition.</para></remarks> public static LineLocation LocateCollinear(PointD a, PointD b, PointD q, double epsilon) { if (PointD.Equals(q, a, epsilon)) { return(LineLocation.Start); } if (PointD.Equals(q, b, epsilon)) { return(LineLocation.End); } double ax = b.X - a.X, ay = b.Y - a.Y; double bx = q.X - a.X, by = q.Y - a.Y; if (ax * bx < 0 || ay * by < 0) { return(LineLocation.Before); } if (ax * ax + ay * ay < bx * bx + by * by) { return(LineLocation.After); } return(LineLocation.Between); }
/// <summary> /// Returns the half-edge with the same origin and the specified destination, given the /// specified epsilon for coordinate comparisons.</summary> /// <param name="destination"> /// The <see cref="Destination"/> of the half-edge.</param> /// <param name="epsilon"> /// The maximum absolute difference at which coordinates should be considered equal.</param> /// <returns><para> /// The <see cref="SubdivisionEdge"/> with the same <see cref="Origin"/> as the current /// instance, and with the specified <paramref name="destination"/>. /// </para><para>-or-</para><para> /// A null reference if no matching <see cref="SubdivisionEdge"/> was found. /// </para></returns> /// <exception cref="ArgumentOutOfRangeException"> /// <paramref name="epsilon"/> is less than zero.</exception> /// <remarks> /// <b>GetEdgeTo</b> is identical with the basic <see cref="GetEdgeTo(PointD)"/> overload /// but uses the specified <paramref name="epsilon"/> to compare the specified <paramref /// name="destination"/> against existing <see cref="Subdivision.Vertices"/>.</remarks> public SubdivisionEdge GetEdgeTo(PointD destination, double epsilon) { var edge = this; do { var twin = edge._twin; if (PointD.Equals(twin._origin, destination, epsilon)) { return(edge); } edge = twin._next; } while (edge != this); return(null); }
/// <summary> /// Determines whether this instance and a specified <see cref="SubdivisionEdge"/> have the /// same value.</summary> /// <param name="edge"> /// A <see cref="SubdivisionEdge"/> to compare to this instance.</param> /// <returns> /// <c>true</c> if the value of <paramref name="edge"/> is the same as this instance; /// otherwise, <c>false</c>.</returns> /// <remarks><para> /// <b>Equals</b> compares the values all properties of the two <see /// cref="SubdivisionEdge"/> instances to test for value equality. Properties of type <see /// cref="SubdivisionEdge"/> and <see cref="SubdivisionFace"/> are compared using <see /// cref="Object.ReferenceEquals"/>. /// </para><para> /// <b>Equals</b> is intended for unit testing, as any two <see cref="SubdivisionEdge"/> /// instances created during normal operation are never equal.</para></remarks> public bool Equals(SubdivisionEdge edge) { if (Object.ReferenceEquals(edge, this)) { return(true); } if (Object.ReferenceEquals(edge, null)) { return(false); } return(_origin.Equals(edge._origin) && Object.ReferenceEquals(_face, edge._face) && Object.ReferenceEquals(_twin, edge._twin) && Object.ReferenceEquals(_next, edge._next) && Object.ReferenceEquals(_previous, edge._previous)); }
/// <summary> /// Determines whether this instance and a specified <see cref="SubdivisionElement"/> have /// the same value.</summary> /// <param name="element"> /// A <see cref="SubdivisionElement"/> to compare to this instance.</param> /// <returns> /// <c>true</c> if the value of <paramref name="element"/> is the same as this instance; /// otherwise, <c>false</c>.</returns> /// <exception cref="PropertyValueException"> /// <see cref="ElementType"/> is not a valid <see cref="SubdivisionElementType"/> value. /// </exception> /// <remarks> /// <b>Equals</b> compares the values of the <see cref="ElementType"/> property and either /// the <see cref="Edge"/>, <see cref="Face"/>, or <see cref="Vertex"/> property of the two /// <see cref="SubdivisionElement"/> instances to test for value equality.</remarks> public bool Equals(SubdivisionElement element) { if (ElementType != element.ElementType) { return(false); } switch (ElementType) { case SubdivisionElementType.Edge: case SubdivisionElementType.Face: return(Object.ReferenceEquals(_value, element._value)); case SubdivisionElementType.Vertex: return(_vertex.Equals(element._vertex)); default: ThrowHelper.ThrowPropertyValueException( "ElementType", ElementType, Strings.PropertyInvalidValue); return(false); } }
/// <summary> /// Determines whether two specified <see cref="LineD"/> instances have the same value, /// given the specified epsilon.</summary> /// <param name="a"> /// The first <see cref="LineD"/> to compare.</param> /// <param name="b"> /// The second <see cref="LineD"/> to compare.</param> /// <param name="epsilon"> /// The maximum absolute difference at which the coordinates of <paramref name="a"/> and /// <paramref name="b"/> should be considered equal.</param> /// <returns> /// <c>true</c> if the absolute difference between the coordinates of <paramref name="a"/> /// and <paramref name="b"/> is less than or equal to <paramref name="epsilon"/>; otherwise, /// <c>false</c>.</returns> /// <remarks> /// The specified <paramref name="epsilon"/> must be greater than zero, but <b>Equals</b> /// does not check this condition.</remarks> public static bool Equals(LineD a, LineD b, double epsilon) { return(PointD.Equals(a.Start, b.Start, epsilon) && PointD.Equals(a.End, b.End, epsilon)); }
/// <summary> /// Finds the intersection of the specified line segments, given the specified epsilon for /// coordinate comparisons.</summary> /// <param name="a"> /// The <see cref="LineD.Start"/> point of the first line segment.</param> /// <param name="b"> /// The <see cref="LineD.End"/> point of the first line segment.</param> /// <param name="c"> /// The <see cref="LineD.Start"/> point of the second line segment.</param> /// <param name="d"> /// The <see cref="LineD.End"/> point of the second line segment.</param> /// <param name="epsilon"> /// The maximum absolute difference at which coordinates and intermediate results should be /// considered equal. This value is always raised to a minium of 1e-10.</param> /// <returns> /// A <see cref="LineIntersection"/> instance that describes if and how the line segments /// from <paramref name="a"/> to <paramref name="b"/> and from <paramref name="c"/> to /// <paramref name="d"/> intersect.</returns> /// <remarks><para> /// <b>Find</b> is identical with the basic <see cref="Find(PointD, PointD, PointD, /// PointD)"/> overload but uses the specified <paramref name="epsilon"/> to compare /// coordinates and intermediate results. /// </para><para> /// <b>Find</b> always raises the specified <paramref name="epsilon"/> to a minimum of 1e-10 /// because the algorithm is otherwise too unstable, and would initiate multiple recursions /// with a greater epsilon anyway.</para></remarks> public static LineIntersection Find(PointD a, PointD b, PointD c, PointD d, double epsilon) { if (epsilon < 1e-10) { epsilon = 1e-10; } LineLocation first, second; double bax = b.X - a.X, bay = b.Y - a.Y; double dcx = d.X - c.X, dcy = d.Y - c.Y; // compute cross-products for all end points double d1 = (a.X - c.X) * dcy - (a.Y - c.Y) * dcx; double d2 = (b.X - c.X) * dcy - (b.Y - c.Y) * dcx; double d3 = (c.X - a.X) * bay - (c.Y - a.Y) * bax; double d4 = (d.X - a.X) * bay - (d.Y - a.Y) * bax; //Debug.Assert(d1 == c.CrossProductLength(a, d)); //Debug.Assert(d2 == c.CrossProductLength(b, d)); //Debug.Assert(d3 == a.CrossProductLength(c, b)); //Debug.Assert(d4 == a.CrossProductLength(d, b)); // check for collinear (but not parallel) lines if (Math.Abs(d1) <= epsilon && Math.Abs(d2) <= epsilon && Math.Abs(d3) <= epsilon && Math.Abs(d4) <= epsilon) { // find lexicographically first point where segments overlap if (PointDComparerY.CompareExact(c, d) < 0) { first = LocateCollinear(a, b, c, epsilon); if (Contains(first)) { return(new LineIntersection(c, first, LineLocation.Start, LineRelation.Collinear)); } first = LocateCollinear(a, b, d, epsilon); if (Contains(first)) { return(new LineIntersection(d, first, LineLocation.End, LineRelation.Collinear)); } } else { first = LocateCollinear(a, b, d, epsilon); if (Contains(first)) { return(new LineIntersection(d, first, LineLocation.End, LineRelation.Collinear)); } first = LocateCollinear(a, b, c, epsilon); if (Contains(first)) { return(new LineIntersection(c, first, LineLocation.Start, LineRelation.Collinear)); } } // collinear line segments without overlapping points return(new LineIntersection(LineRelation.Collinear)); } // check for divergent lines with end point intersection if (Math.Abs(d1) <= epsilon) { second = LocateCollinear(c, d, a, epsilon); return(new LineIntersection(a, LineLocation.Start, second, LineRelation.Divergent)); } if (Math.Abs(d2) <= epsilon) { second = LocateCollinear(c, d, b, epsilon); return(new LineIntersection(b, LineLocation.End, second, LineRelation.Divergent)); } if (Math.Abs(d3) <= epsilon) { first = LocateCollinear(a, b, c, epsilon); return(new LineIntersection(c, first, LineLocation.Start, LineRelation.Divergent)); } if (Math.Abs(d4) <= epsilon) { first = LocateCollinear(a, b, d, epsilon); return(new LineIntersection(d, first, LineLocation.End, LineRelation.Divergent)); } // compute parameters of line equations double denom = dcx * bay - bax * dcy; if (Math.Abs(denom) <= epsilon) { return(new LineIntersection(LineRelation.Parallel)); } double snum = a.X * dcy - a.Y * dcx - c.X * d.Y + c.Y * d.X; double s = snum / denom; if ((d1 < 0 && d2 < 0) || (d1 > 0 && d2 > 0)) { if (s < 0) { first = LineLocation.Before; } else if (s > 1) { first = LineLocation.After; } else { return(Find(a, b, c, d, 2 * epsilon)); } } else { if (s > 0 && s < 1) { first = LineLocation.Between; } else { return(Find(a, b, c, d, 2 * epsilon)); } } double tnum = c.Y * bax - c.X * bay + a.X * b.Y - a.Y * b.X; double t = tnum / denom; if ((d3 < 0 && d4 < 0) || (d3 > 0 && d4 > 0)) { if (t < 0) { second = LineLocation.Before; } else if (t > 1) { second = LineLocation.After; } else { return(Find(a, b, c, d, 2 * epsilon)); } } else { if (t > 0 && t < 1) { second = LineLocation.Between; } else { return(Find(a, b, c, d, 2 * epsilon)); } } PointD shared = new PointD(a.X + s * bax, a.Y + s * bay); /* * Epsilon comparisons of cross products (or line equation parameters) might miss * epsilon-close end point intersections of very long line segments. We compensate by * directly comparing the computed intersection point against the four end points. */ if (PointD.Equals(a, shared, epsilon)) { first = LineLocation.Start; } else if (PointD.Equals(b, shared, epsilon)) { first = LineLocation.End; } if (PointD.Equals(c, shared, epsilon)) { second = LineLocation.Start; } else if (PointD.Equals(d, shared, epsilon)) { second = LineLocation.End; } return(new LineIntersection(shared, first, second, LineRelation.Divergent)); }