コード例 #1
0
        /// <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);
        }
コード例 #2
0
        /// <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);
        }
コード例 #3
0
        /// <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));
        }
コード例 #4
0
        /// <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);
            }
        }
コード例 #5
0
ファイル: LineD.cs プロジェクト: prepare/Tektosyne.NET
        /// <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));
        }
コード例 #6
0
        /// <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));
        }