コード例 #1
0
ファイル: Circle2D.cs プロジェクト: urbrioche/DecimalMath
        /// <summary>
        /// Returns circles that are tangent to another circle and line.
        /// </summary>
        /// <param name="circle">Circle that returned circles should be tangent to.</param>
        /// <param name="line">Line that returned circles should be tangent to.</param>
        /// <param name="radius">Radius of new circles.</param>
        /// <returns>External tangent circles are listed first.</returns>
        public static  Circle2D[] FromTangentTangentRadius(Circle2D circle, LineSeg2D line, decimal radius)
        {
            List <Circle2D> orbits = new List <Circle2D>();

            LineSeg2D[]     parallels      = new LineSeg2D[2];
            List <Point2D>  centerPoints   = new List <Point2D>();
            List <Circle2D> tangentCircles = new List <Circle2D>();

            orbits.Add(new Circle2D(circle.X, circle.Y, circle.Radius + radius));
            if (radius < circle.Radius)
            {
                orbits.Add(new Circle2D(circle.X, circle.Y, circle.Radius - radius));
            }

            parallels = line.GetParallels(radius);


            foreach (Circle2D orbit in orbits)
            {
                for (int i = 0; i <= 1; i++)
                {
                    centerPoints.AddRange(orbit.GetIntersect(parallels[i]));
                }
            }


            for (int i = 0; i <= centerPoints.Count - 1; i++)
            {
                tangentCircles.Add(new Circle2D(centerPoints[i].X, centerPoints[i].Y, radius));
            }

            return(tangentCircles.ToArray());
        }
コード例 #2
0
ファイル: Circle2D.cs プロジェクト: urbrioche/DecimalMath
        /// <summary>
        /// Returns circles that are tangent to two lines.
        /// </summary>
        /// <param name="lineA">First line.</param>
        /// <param name="lineB">Second line.</param>
        /// <param name="radius">Radius of the tangent circles.</param>
        /// <returns>Will return no circles if the two lines are parallel or the same line.</returns>
        public static  Circle2D[] FromTangentTangentRadius(LineSeg2D lineA, LineSeg2D lineB, decimal radius)
        {
            List <LineSeg2D>   parallels      = new List <LineSeg2D>();
            Nullable <Point2D> centerPoint    = default(Nullable <Point2D>);
            List <Point2D>     centerPoints   = new List <Point2D>();
            List <Circle2D>    tangentCircles = new List <Circle2D>();

            parallels.AddRange(lineA.GetParallels(radius));
            parallels.AddRange(lineB.GetParallels(radius));


            for (int i = 0; i <= parallels.Count - 2; i++)
            {
                for (int j = i + 1; j <= parallels.Count - 1; j++)
                {
                    centerPoint = parallels[i].GetIntersect(parallels[j], true);
                    if (centerPoint.HasValue)
                    {
                        centerPoints.Add(centerPoint.Value);
                    }
                }
            }


            for (int i = 0; i <= centerPoints.Count - 1; i++)
            {
                tangentCircles.Add(new Circle2D(centerPoints[i].X, centerPoints[i].Y, radius));
            }

            return(tangentCircles.ToArray());
        }
コード例 #3
0
        public static Arc2D FromLinesCircle(LineSeg2D line1, LineSeg2D line2, Circle2D c, int decimals = -1)
        {
            Point2D[] intersects1 = null;
            Point2D[] intersects2 = null;

            intersects1 = c.GetIntersect(line1, decimals);
            intersects2 = c.GetIntersect(line2, decimals);

            throw new NotImplementedException();
        }
コード例 #4
0
ファイル: Point2D.cs プロジェクト: urbrioche/DecimalMath
        public static bool PointsAreColinear(decimal x1, decimal y1, decimal x2, decimal y2, decimal x3, decimal y3)
        {
            var l1        = new LineSeg2D(x1, y1, x2, y2);
            var l2        = new LineSeg2D(x2, y2, x3, y3);
            var intersect = l1.GetIntersect(l2, true);

            // Points won't have an intersection if they are parallel. If they
            // are parallel and share a point, then they are colinear.
            return(!intersect.HasValue);
        }
コード例 #5
0
ファイル: Circle2D.cs プロジェクト: urbrioche/DecimalMath
        /// <summary>
        /// Returns a line segment with the coordinates of a chord centered on
        /// the given angle with the given length.
        /// </summary>
        /// <param name="angleThroughCenter">The angle that passes through the
        /// center of the chord, in degrees.</param>
        /// <param name="chordLength">The length of the chord.</param>
        public LineSeg2D GetChord(decimal angleThroughCenter, decimal chordLength)
        {
            LineSeg2D l = default(LineSeg2D);
            decimal   a = 0m;

            // Create a right triangle where one side is half the chord length,
            // the other side is from the center of the circle to the midpoint
            // of the chord, and the hypotenuse is from the center of the circle
            // to the chord start point
            a = RightTriangle.GetAngleFromOppSideHyp(0.5m * chordLength, _radius);

            l.Pt1 = PointAt(angleThroughCenter - a);
            l.Pt2 = PointAt(angleThroughCenter + a);

            return(l);
        }
コード例 #6
0
ファイル: Circle2D.cs プロジェクト: urbrioche/DecimalMath
        /// <summary>
        /// Create a circle from two points.
        /// </summary>
        /// <param name="pt1">A point.</param>
        /// <param name="pt2">A point.</param>

        public Circle2D(Point2D pt1, Point2D pt2)
        {
            _center = default(Point2D);
            _radius = 0m;

            LineSeg2D l = new LineSeg2D(pt1, pt2);

            if (l.IsZeroLength())
            {
                throw new Exception("Can't create circle from two points that are the same point!");
            }

            Center = l.MidPoint;
            Radius = l.Length / 2m;

            if (Radius == 0)
            {
                throw new Exception("Can't create circle from two points that are the same point!");
            }
        }
コード例 #7
0
ファイル: Circle2D.cs プロジェクト: urbrioche/DecimalMath
        /// <summary>
        /// Gets a line segment of length 1 starting at the point on the circle
        /// and extending either clockwise or counterclockwise.
        /// </summary>
        /// <param name="degrees">An angle.</param>
        public LineSeg2D TangentAt(decimal degrees, decimal length, bool clockwise)
        {
            var       r   = RightTriangleAbstract.FromTwoSides(_radius, length);
            decimal   rad = DecimalEx.ToRad(degrees + r.AngleA);
            LineSeg2D ret = default(LineSeg2D);

            Debug.Assert(r.AngleA == RightTriangle.GetAngleFromSides(length, _radius));
            Debug.Assert(r.Hypotenuse == RightTriangle.GetHypFromSides(length, _radius));

            ret.Pt1 = PointAt(degrees);
            if (!clockwise)
            {
                ret.Pt2 = new Point2D(_center.X + r.Hypotenuse * DecimalEx.Cos(rad), _center.Y + r.Hypotenuse * DecimalEx.Sin(rad));
            }
            else
            {
                ret.Pt2 = new Point2D(ret.Pt1.X - (_center.X + r.Hypotenuse * DecimalEx.Cos(rad) - ret.Pt1.X), ret.Pt1.Y - (_center.Y + r.Hypotenuse * DecimalEx.Sin(rad) - ret.Pt1.Y));
            }

            return(ret);
        }
コード例 #8
0
ファイル: Circle2D.cs プロジェクト: urbrioche/DecimalMath
        public static  Circle2D[] FromTwoPointsAndRadius(decimal x1, decimal y1, decimal x2, decimal y2, decimal radius)
        {
            LineSeg2D pointToPoint  = default(LineSeg2D);
            Point2D   midPoint      = default(Point2D);
            Vector2D  vPerpBisector = default(Vector2D);

            if (x1 == x2 && y1 == y2)
            {
                return new Circle2D[] { }
            }
            ;

            pointToPoint            = new LineSeg2D(x1, y1, x2, y2);
            midPoint                = pointToPoint.MidPoint;
            vPerpBisector           = pointToPoint.PerpendicularBisector().GetVectorP1toP2();
            vPerpBisector.Magnitude = RightTriangle.GetSideFromSideHyp(pointToPoint.Length / 2, radius);

            return(new Circle2D[] {
                new Circle2D(midPoint + vPerpBisector, radius),
                new Circle2D(midPoint - vPerpBisector, radius)
            });
        }
コード例 #9
0
ファイル: Circle2D.cs プロジェクト: urbrioche/DecimalMath
        /// <summary>
        /// Creates a circle from three points.
        /// </summary>
        /// <param name="x1">A number.</param>
        /// <param name="y1">A number.</param>
        /// <param name="x2">A number.</param>
        /// <param name="y2">A number.</param>
        /// <param name="x3">A number.</param>
        /// <param name="y3">A number.</param>
        /// <remarks>See http://www.topcoder.com/tc?module=Static&d1=tutorials&d2=geometry2#circle</remarks>

        public Circle2D(decimal x1, decimal y1, decimal x2, decimal y2, decimal x3, decimal y3)
        {
            _radius = 0m;               // to satisfy compiler
            _center = default(Point2D); // to satisfy compiler

            if (Point2D.PointsAreColinear(x1, y1, x2, y2, x3, y3))
            {
                throw new Exception("Can't create circle! Points are colinear.");
            }

            LineSeg2D l1        = new LineSeg2D(x1, y1, x2, y2).PerpendicularBisector();
            LineSeg2D l2        = new LineSeg2D(x2, y2, x3, y3).PerpendicularBisector();
            Point2D?  intersect = l1.GetIntersect(l2, true);

            if (intersect == null)
            {
                // Should never get null here because lines won't be parallel so
                // long as they aren't colinear which we checked for above.
                throw new Exception("Something bad happened. Should have found an intersect so long as points are not colinear.");
            }

            Center = intersect.Value;
            Radius = intersect.Value.DistanceTo(x1, y1);
        }
コード例 #10
0
ファイル: Point2D.cs プロジェクト: urbrioche/DecimalMath
 public decimal DistanceTo(LineSeg2D l, bool treatLineSegmentAsLine = false)
 {
     return(l.DistanceTo(this, treatLineSegmentAsLine));
 }
コード例 #11
0
ファイル: Circle2D.cs プロジェクト: urbrioche/DecimalMath
        /// <summary>
        /// Gets the points of intersection between this circle and the line which
        /// contains the given line segment.
        /// </summary>
        /// <param name="l">Line segment used to determine line equation.</param>
        public Point2D[] GetIntersectFast(ref LineSeg2D l)
        {
            decimal[] x      = new decimal[2];
            decimal   m      = 0m;
            decimal   b      = 0m;
            decimal   aCoeff = 0m;
            decimal   bCoeff = 0m;
            decimal   cCoeff = 0m;
            decimal   lineX  = 0m;
            decimal   t      = 0m;
            decimal   p      = 0m;
            decimal   q      = 0m;

            Point2D[] pts = null;



            if (!l.IsVertical)
            {
                // Circle    (x - h) ^ 2 + (y - k) ^ 2 = r ^ 2
                //   Center: (h, k)
                //   Radius: r
                // Line      y = m * x + b

                // (x - h) ^ 2 + (m * x + b - k) ^ 2 = r ^ 2
                // (x - h) * (x - h) + (m * x + b - k) * (m * x + b - k) = r^2
                // (x^2 - 2 * h * x + h^2) + (m^2 * x^2 + 2 * (b - k) * m * x + (b - k)^2 = r^2
                // (m^2 + 1) * x^2 + (2 * (b - k) * m - 2 * h) * x + (h^2 + (b - k)^2 - r^2) = 0

                m = l.Slope;
                b = l.YIntersect;

                aCoeff = DecimalEx.Pow(m, 2) + 1;
                bCoeff = 2 * (b - _center.Y) * m - 2 * _center.X;
                cCoeff = DecimalEx.Pow(_center.X, 2) + DecimalEx.Pow(b - _center.Y, 2) - DecimalEx.Pow(_radius, 2);

                x = DecimalEx.SolveQuadratic(aCoeff, bCoeff, cCoeff);

                if (x.Length == 0)
                {
                    return new Point2D[] { }
                }
                ;

                pts = new Point2D[x.Length];

                for (var i = 0; i <= x.Length - 1; i++)
                {
                    pts[i] = new Point2D(x[i], m * x[i] + b);
                }
            }
            else
            {
                // Circle    (x - h) ^ 2 + (y - k) ^ 2 = r ^ 2
                //   Center: (h, k)
                //   Radius: r
                // Line      x = lineX

                // Got the following from
                //  http://www.sonoma.edu/users/w/wilsonst/Papers/Geometry/circles/T1--2/T1-3-2.html
                //  http://www.sonoma.edu/users/w/wilsonst/Papers/Geometry/circles/default.html

                lineX = l.Pt1.X;
                t     = _radius * _radius - (lineX - _center.X) * (lineX - _center.X);


                if (t < 0)
                {
                    return(new Point2D[] { });
                }
                else
                {
                    p = _center.Y + DecimalEx.Sqrt(t);
                    q = _center.Y - DecimalEx.Sqrt(t);

                    pts    = new Point2D[1];
                    pts[0] = new Point2D(lineX, p);

                    // NOTE that P=Q when t=0
                    if (p != q)
                    {
                        Array.Resize(ref pts, 2);
                        pts[1] = new Point2D(lineX, q);
                    }
                }
            }

            return(pts);
        }
コード例 #12
0
ファイル: Circle2D.cs プロジェクト: urbrioche/DecimalMath
        /// <summary>
        /// Gets the points of intersection between this circle and the line which
        /// contains the given line segment.
        /// </summary>
        /// <param name="l">Line segment used to determine line equation.</param>
        /// <param name="decimals">Determines rounding that should be performed when determining
        /// if line intersection happens on zero, one, or two points. Default is -1 for
        /// no rounding.</param>
        public Point2D[] GetIntersect(LineSeg2D l, int decimals = -1)
        {
            LineSeg2D centToLine              = default(LineSeg2D);
            decimal   centToLineLength        = 0m;
            decimal   centToLineLengthForComp = 0m;
            decimal   radiusForComp           = 0m;

            if (l.IsHorizontal)
            {
                // The center to any horizontal line is a vertical line through
                // the center of the circle.
                centToLine = new LineSeg2D(this.Center, this.Center + new Vector2D(0, 1));
            }
            else
            {
                centToLine = LineSeg2D.FromPointSlope(this.Center, l.PerpendicularSlope);
            }
            centToLine.Pt2   = l.GetIntersect(centToLine, true).Value;
            centToLineLength = centToLine.Length;

            // Get numbers for comparison, rounding if necessary
            centToLineLengthForComp = centToLineLength;
            radiusForComp           = _radius;
            if (decimals >= 0)
            {
                centToLineLengthForComp = centToLineLengthForComp.RoundFromZero(decimals);
                radiusForComp           = radiusForComp.RoundFromZero(decimals);
            }

            // See if line is outside of circle
            if (centToLineLengthForComp > radiusForComp)
            {
                return(new Point2D[] { });
            }

            // See if line is tangent to circle
            if (centToLineLengthForComp == radiusForComp)
            {
                return(new Point2D[] { centToLine.Pt2 });
            }

            // Line must intersect in two places
            Vector2D vCentToChord = default(Vector2D);
            decimal  halfChord    = 0m;

            // Get a vector from the center to the intersecting chord
            vCentToChord = centToLine.GetVectorP1toP2();


            if (vCentToChord.Magnitude == 0)
            {
                Vector2D offsetVector = default(Vector2D);

                // Line goes through circle center


                if (l.IsVertical)
                {
                    // Slope undefined so just go up the length of the radius
                    offsetVector = new Vector2D(0, _radius);
                }
                else
                {
                    offsetVector = new Vector2D(_radius * DecimalEx.Cos(DecimalEx.ATan(l.Slope)), _radius * DecimalEx.Sin(DecimalEx.ATan(l.Slope)));
                }

                return(new Point2D[] {
                    this.Center + offsetVector,
                    this.Center - offsetVector
                });
            }
            else
            {
                Vector2D vChord = default(Vector2D);

                // Get a vector along the chord
                vChord = vCentToChord.GetPerpendicular();

                // Determine the length of half the chord
                halfChord = RightTriangle.GetSideFromSideHyp(centToLineLength, _radius);

                // Set the magnitude of the vector along the chord
                // to be half the chord length
                vChord.Magnitude = halfChord;

                // The two intersecting points are points translated
                // from the center of the circle to the chord (+vCentToChord)
                // and then translated to the ends of the chord (+-vChord)
                return(new Point2D[] {
                    this.Center + vCentToChord + vChord,
                    this.Center + vCentToChord - vChord
                });
            }
        }
コード例 #13
0
ファイル: Circle2D.cs プロジェクト: urbrioche/DecimalMath
 /// <summary>
 /// Returns circles that are tangent to another circle and line.
 /// </summary>
 /// <param name="line">Line that returned circles should be tangent to.</param>
 /// <param name="circle">Circle that returned circles should be tangent to.</param>
 /// <param name="radius">Radius of new circles.</param>
 /// <returns>External tangent circles are listed first.</returns>
 public static  Circle2D[] FromTangentTangentRadius(LineSeg2D line, Circle2D circle, decimal radius)
 {
     return(FromTangentTangentRadius(circle, line, radius));
 }
コード例 #14
0
ファイル: Circle2D.cs プロジェクト: urbrioche/DecimalMath
        /// <summary>
        /// Gets the points of intersection between this circle and another circle.
        /// </summary>
        /// <param name="other">The other circle.</param>
        /// <param name="decimals">Determines rounding that should be performed when determining
        /// if line intersection happens on zero, one, or two points. Default is -1 for
        /// no rounding.</param>
        /// <param name="impreciseResult">Which value to return for an imprecise result,
        /// i.e. tangency determined by rounding. If positive, returns the point on the larger circle. If
        /// negative, returns the point on the smaller circle. If 0, returns the midpoint between these
        /// two points.</param>
        public Point2D[] GetIntersect(Circle2D other, int decimals = -1, int impreciseResult = 0)
        {
            Vector2D meToOtherVector = default(Vector2D);

            meToOtherVector = this.Center.GetVectorTo(other.Center);


            if (decimals >= 0)
            {
                // The only intersection decision that can be made to a given precision is whether
                // the two circles are tangent to each other, either internally or externally.


                if (this.IsTangentTo(other, decimals))
                {
                    // If the smaller circle is inside the other, then the smaller is
                    // considered internally tangent to the larger.

                    if ((this.Radius < other.Radius && this.IsInside(other, decimals)) || (other.Radius < this.Radius && other.IsInside(this, decimals)))
                    {
                        // Internal tangent

                        Point2D pointOnLargeCircle = default(Point2D);
                        Point2D pointOnSmallCircle = default(Point2D);

                        // Vectors to the two tangent points are both pointing in the same
                        // direction--from the center of the larger circle towards the
                        // center of the smaller circle. Their magnitude is the same as the
                        // radius of the circle whose center they start from.


                        if (this.Radius > other.Radius)
                        {
                            // Go from center of larger circle, Me, to smaller circle, other.
                            pointOnLargeCircle = this.Center + new Vector2D(meToOtherVector, this.Radius);
                            pointOnSmallCircle = other.Center + new Vector2D(meToOtherVector, other.Radius);
                        }
                        else
                        {
                            // Go from center of larger circle, other, to smaller circle, Me.
                            pointOnLargeCircle = other.Center - new Vector2D(meToOtherVector, other.Radius);
                            pointOnSmallCircle = this.Center - new Vector2D(meToOtherVector, this.Radius);
                        }


                        if (impreciseResult > 0)
                        {
                            // Point on larger
                            return(new Point2D[] { pointOnLargeCircle });
                        }
                        else if (impreciseResult < 0)
                        {
                            // Point on smaller
                            return(new Point2D[] { pointOnSmallCircle });
                        }
                        else
                        {
                            // Split difference
                            LineSeg2D l = new LineSeg2D(pointOnLargeCircle, pointOnSmallCircle);
                            return(new Point2D[] { l.MidPoint });
                        }
                    }
                    else
                    {
                        // External tangent

                        Point2D pointOnMe    = default(Point2D);
                        Point2D pointOnOther = default(Point2D);

                        // Vectors to the two tangent points are simply pointing at the other
                        // circle's center with a magnitude of the radius of the circle whose
                        // center it originates in.
                        pointOnMe    = this.Center + new Vector2D(meToOtherVector, this.Radius);
                        pointOnOther = other.Center - new Vector2D(meToOtherVector, other.Radius);


                        if (impreciseResult > 0)
                        {
                            // Point on larger
                            return(new Point2D[] { (this.Radius > other.Radius ? pointOnMe : pointOnOther) });
                        }
                        else if (impreciseResult < 0)
                        {
                            // Point on smaller
                            return(new Point2D[] { (this.Radius < other.Radius ? pointOnMe : pointOnOther) });
                        }
                        else
                        {
                            if (pointOnMe == pointOnOther)
                            {
                                return(new Point2D[] { pointOnMe });
                            }
                            else
                            {
                                // Split difference
                                return(new Point2D[] { new LineSeg2D(pointOnMe, pointOnOther).MidPoint });
                            }
                        }
                    }
                }
            }

            // Detect situations where two points touch
            decimal  a                 = 0m;
            decimal  h                 = 0m;
            decimal  r2mina2           = 0m;
            Vector2D vToIntersectMidPt = default(Vector2D);
            Vector2D vToIntersect1     = default(Vector2D);

            // The following two equations are from:
            //   http://paulbourke.net/geometry/2circle/
            a = (DecimalEx.Pow(meToOtherVector.Magnitude, 2) - DecimalEx.Pow(other.Radius, 2) + DecimalEx.Pow(this.Radius, 2)) / (2 * meToOtherVector.Magnitude);

            r2mina2 = DecimalEx.Pow(this.Radius, 2) - DecimalEx.Pow(a, 2);

            // No intersection points -- one circle is inside or outside the other
            if (r2mina2 < 0)
            {
                return new Point2D[] { }
            }
            ;

            vToIntersectMidPt           = new Vector2D(this.Center, other.Center);
            vToIntersectMidPt.Magnitude = a;


            if (r2mina2 == 0)
            {
                // Only one intersection point
                return(new Point2D[] { this.Center + vToIntersectMidPt });
            }

            h                       = DecimalEx.Sqrt(r2mina2);
            vToIntersect1           = vToIntersectMidPt.GetPerpendicular();
            vToIntersect1.Magnitude = h;

            return(new Point2D[] {
                this.Center + vToIntersectMidPt + vToIntersect1,
                this.Center + vToIntersectMidPt - vToIntersect1
            });
        }