예제 #1
0
파일: Intersect.cs 프로젝트: biorpg/RT.Util
        /// <summary>
        ///     Finds the points of intersection between a line and a circle. The results are two lambdas along the line, one
        ///     for each point, or NaN if there is no intersection.</summary>
        public static void LineWithCircle(ref EdgeD line, ref CircleD circle,
                                          out double lambda1, out double lambda2)
        {
            // The following expressions come up a lot in the solution, so simplify using them.
            double dx = line.End.X - line.Start.X;
            double dy = line.End.Y - line.Start.Y;
            double ax = -line.Start.X + circle.Center.X;
            double ay = -line.Start.Y + circle.Center.Y;

            // Solve simultaneously for l:
            // Eq of a line:    x = sx + l * dx
            //                  y = sy + l * dy
            // Eq of a circle:  (x - cx)^2 + (y - cy)^2 = r^2
            //
            // Eventually we get a standard quadratic equation in l with the
            // following coefficients:
            double a = dx * dx + dy * dy;
            double b = -2 * (ax * dx + ay * dy);
            double c = ax * ax + ay * ay - circle.Radius * circle.Radius;

            // Now just solve the quadratic eqn...
            double D = b * b - 4 * a * c;

            if (D < 0)
            {
                lambda1 = lambda2 = double.NaN;
            }
            else
            {
                double sqrtD = Math.Sqrt(D);
                lambda1 = (-b + sqrtD) / (2 * a);
                lambda2 = (-b - sqrtD) / (2 * a);
            }
        }
예제 #2
0
 private void AssertRayWithCircle(double frX, double frY, double toX, double toY, double cX, double cY, double cR, double expL1, double expL2)
 {
     EdgeD ray = new EdgeD(frX, frY, toX, toY);
     CircleD cir = new CircleD(cX, cY, cR);
     double l1, l2;
     Intersect.RayWithCircle(ref ray, ref cir, out l1, out l2);
     Assert.AreEqual(expL1, l1, 0.001);
     Assert.AreEqual(expL2, l2, 0.001);
 }
예제 #3
0
 private void AssertLineWithCircle(double frX, double frY, double toX, double toY, double cX, double cY, double cR, double expL1, double expL2)
 {
     EdgeD lin = new EdgeD(frX, frY, toX, toY);
     CircleD cir = new CircleD(cX, cY, cR);
     double l1, l2;
     Intersect.LineWithCircle(ref lin, ref cir, out l1, out l2);
     Assert.AreEqual(MinTO(expL1, expL2), MinTO(l1, l2), 0.001);
     Assert.AreEqual(MaxTO(expL1, expL2), MaxTO(l1, l2), 0.001);
 }
예제 #4
0
        private void AssertRayWithCircle(double frX, double frY, double toX, double toY, double cX, double cY, double cR, double expL1, double expL2)
        {
            EdgeD   ray = new EdgeD(frX, frY, toX, toY);
            CircleD cir = new CircleD(cX, cY, cR);
            double  l1, l2;

            Intersect.RayWithCircle(ref ray, ref cir, out l1, out l2);
            Assert.AreEqual(expL1, l1, 0.001);
            Assert.AreEqual(expL2, l2, 0.001);
        }
예제 #5
0
        private void AssertLineWithCircle(double frX, double frY, double toX, double toY, double cX, double cY, double cR, double expL1, double expL2)
        {
            EdgeD   lin = new EdgeD(frX, frY, toX, toY);
            CircleD cir = new CircleD(cX, cY, cR);
            double  l1, l2;

            Intersect.LineWithCircle(ref lin, ref cir, out l1, out l2);
            Assert.AreEqual(MinTO(expL1, expL2), MinTO(l1, l2), 0.001);
            Assert.AreEqual(MaxTO(expL1, expL2), MaxTO(l1, l2), 0.001);
        }
예제 #6
0
파일: CircleD.cs 프로젝트: biorpg/RT.Util
        /// <summary>
        ///     Given this circle and another circle, tries to find a third and fourth circle with a given target radius such
        ///     that the new circles are both tangent to the first two.</summary>
        /// <param name="other">
        ///     The other circle.</param>
        /// <param name="targetRadius">
        ///     Target radius for output circles.</param>
        /// <returns>
        ///     The two output circles if they exist. If the input circles are further apart than twice the target radius, the
        ///     desires circles do not exist and null is returned.</returns>
        public (CircleD, CircleD)? FindTangentCircles(CircleD other, double targetRadius)
        {
            double a = ((Center.X - other.Center.X) * (Center.X - other.Center.X)) /
                       ((other.Center.Y - Center.Y) * (other.Center.Y - Center.Y)) + 1;
            double t = Center.Y - (Radius * Radius - other.Radius * other.Radius + other.Center.X * other.Center.X
                                   - Center.X * Center.X + other.Center.Y * other.Center.Y - Center.Y * Center.Y
                                   + 2 * targetRadius * (Radius - other.Radius)) / (other.Center.Y - Center.Y) / 2;
            double b = -2 * Center.X - 2 * t * (Center.X - other.Center.X) / (other.Center.Y - Center.Y);
            double c = Center.X * Center.X - (Radius + targetRadius) * (Radius + targetRadius) + t * t;

            double q = b * b - 4 * a * c;

            // At this point, Q < 0 means the circles are too far apart, so no solution
            if (q < 0)
            {
                return(null);
            }

            double s  = Math.Sqrt(q);
            double xa = (-b + s) / (2 * a);
            double xb = (-b - s) / (2 * a);

            // These Sqrts should succeed, i.e. their parameter should never be < 0
            double ya = Math.Sqrt(-other.Center.X * other.Center.X - xa * xa + targetRadius * targetRadius
                                  + 2 * other.Center.X * xa + other.Radius * other.Radius + 2 * other.Radius * targetRadius);
            double yb = Math.Sqrt(-Center.X * Center.X - xb * xb + targetRadius * targetRadius
                                  + 2 * Center.X * xb + Radius * Radius + 2 * Radius * targetRadius);

            if (Math.Sign(Center.X - other.Center.X) != Math.Sign(Center.Y - other.Center.Y))
            {
                ya += other.Center.Y;
                yb  = Center.Y - yb;
            }
            else
            {
                ya  = other.Center.Y - ya;
                yb += Center.Y;
            }

            return(new CircleD(xa, ya, targetRadius), new CircleD(xb, yb, targetRadius));
        }
예제 #7
0
파일: Intersect.cs 프로젝트: biorpg/RT.Util
        /// <summary>
        ///     Finds the points of intersection between a ray and a circle. The resulting lambdas along the ray are sorted in
        ///     ascending order, so the "first" intersection is always in lambda1 (if any). Lambda may be NaN if there is no
        ///     intersection (or no "second" intersection).</summary>
        public static void RayWithCircle(ref EdgeD ray, ref CircleD circle,
                                         out double lambda1, out double lambda2)
        {
            LineWithCircle(ref ray, ref circle, out lambda1, out lambda2);

            // Sort the two values in ascending order, with NaN last,
            // while resetting negative values to NaNs
            if (lambda1 < 0)
            {
                lambda1 = double.NaN;
            }
            if (lambda2 < 0)
            {
                lambda2 = double.NaN;
            }
            if (lambda1 > lambda2 || double.IsNaN(lambda1))
            {
                double temp = lambda1;
                lambda1 = lambda2;
                lambda2 = temp;
            }
        }
예제 #8
0
        /// <summary>
        ///     Given this circle and another circle, tries to find a third and fourth circle with a given target radius such
        ///     that the new circles are both tangent to the first two.</summary>
        /// <param name="other">
        ///     The other circle.</param>
        /// <param name="targetRadius">
        ///     Target radius for output circles.</param>
        /// <returns>
        ///     The two output circles if they exist. If the input circles are further apart than twice the target radius, the
        ///     desires circles do not exist and null is returned.</returns>
        public Tuple<CircleD, CircleD> FindTangentCircles(CircleD other, double targetRadius)
        {
            double a = ((Center.X - other.Center.X) * (Center.X - other.Center.X)) /
                       ((other.Center.Y - Center.Y) * (other.Center.Y - Center.Y)) + 1;
            double t = Center.Y - (Radius * Radius - other.Radius * other.Radius + other.Center.X * other.Center.X
                                   - Center.X * Center.X + other.Center.Y * other.Center.Y - Center.Y * Center.Y
                                   + 2 * targetRadius * (Radius - other.Radius)) / (other.Center.Y - Center.Y) / 2;
            double b = -2 * Center.X - 2 * t * (Center.X - other.Center.X) / (other.Center.Y - Center.Y);
            double c = Center.X * Center.X - (Radius + targetRadius) * (Radius + targetRadius) + t * t;

            double q = b * b - 4 * a * c;
            // At this point, Q < 0 means the circles are too far apart, so no solution
            if (q < 0) return null;

            double s = Math.Sqrt(q);
            double xa = (-b + s) / (2 * a);
            double xb = (-b - s) / (2 * a);

            // These Sqrts should succeed, i.e. their parameter should never be < 0
            double ya = Math.Sqrt(-other.Center.X * other.Center.X - xa * xa + targetRadius * targetRadius
                                  + 2 * other.Center.X * xa + other.Radius * other.Radius + 2 * other.Radius * targetRadius);
            double yb = Math.Sqrt(-Center.X * Center.X - xb * xb + targetRadius * targetRadius
                                  + 2 * Center.X * xb + Radius * Radius + 2 * Radius * targetRadius);

            if (Math.Sign(Center.X - other.Center.X) != Math.Sign(Center.Y - other.Center.Y))
            {
                ya += other.Center.Y;
                yb = Center.Y - yb;
            }
            else
            {
                ya = other.Center.Y - ya;
                yb += Center.Y;
            }

            return Tuple.Create(new CircleD(xa, ya, targetRadius), new CircleD(xb, yb, targetRadius));
        }
예제 #9
0
파일: Intersect.cs 프로젝트: biorpg/RT.Util
 /// <summary>
 ///     Finds the points of intersection between a line and a circle. The results are two lambdas along the line, one
 ///     for each point, or NaN if there is no intersection.</summary>
 public static void LineWithCircle(EdgeD line, CircleD circle,
                                   out double lambda1, out double lambda2)
 {
     LineWithCircle(ref line, ref circle, out lambda1, out lambda2);
 }
예제 #10
0
파일: CircleD.cs 프로젝트: biorpg/RT.Util
        /// <summary>
        ///     Returns the smallest circle that encloses all the given points. If 1 point is given, a circle of radius 0 is
        ///     returned.</summary>
        /// <param name="points">
        ///     The set of points to circumscribe.</param>
        /// <returns>
        ///     The circumscribed circle.</returns>
        /// <remarks>
        ///     <list type="bullet">
        ///         <item><description>
        ///             Runs in expected O(n) time, randomized.</description></item></list></remarks>
        /// <exception cref="InvalidOperationException">
        ///     The input collection contained zero points.</exception>
        public static CircleD GetCircumscribedCircle(IList <PointD> points)
        {
            // Clone list to preserve the caller's data
            List <PointD> shuffled = new List <PointD>(points).Shuffle();

            // Progressively add points to circle or recompute circle
            // Initially: No boundary points known
            CircleD?circ = null;

            for (int i = 0; i < shuffled.Count; i++)
            {
                PointD p = shuffled[i];
                if (circ == null || !circ.Value.Contains(p))
                {
                    circ = MakeCircleOnePoint(shuffled.GetRange(0, i + 1), p);
                }
            }
            if (circ == null)
            {
                throw new InvalidOperationException("The input collection did not contain any points.");
            }
            return(circ.Value);

            // One boundary point known
            CircleD MakeCircleOnePoint(List <PointD> pts, PointD p)
            {
                CircleD c = new CircleD(p, 0);

                for (int i = 0; i < pts.Count; i++)
                {
                    PointD q = pts[i];
                    if (!c.Contains(q))
                    {
                        if (c.Radius == 0)
                        {
                            c = MakeDiameter(p, q);
                        }
                        else
                        {
                            c = MakeCircleTwoPoints(pts.GetRange(0, i + 1), p, q);
                        }
                    }
                }
                return(c);
            }

            // Two boundary pts known
            CircleD MakeCircleTwoPoints(List <PointD> pts, PointD p, PointD q)
            {
                CircleD crc   = MakeDiameter(p, q);
                CircleD left  = new CircleD(new PointD(0, 0), -1);
                CircleD right = new CircleD(new PointD(0, 0), -1);

                // For each point not in the two-point circle
                PointD pq = q - p;

                foreach (PointD r in pts)
                {
                    if (crc.Contains(r))
                    {
                        continue;
                    }

                    // Form a circumcircle and classify it on left or right side
                    double  cross = Cross(pq, r - p);
                    CircleD c     = MakeCircumcircle(p, q, r);
                    if (c.Radius < 0)
                    {
                        continue;
                    }
                    else if (cross > 0 && (left.Radius < 0 || Cross(pq, c.Center - p) > Cross(pq, left.Center - p)))
                    {
                        left = c;
                    }
                    else if (cross < 0 && (right.Radius < 0 || Cross(pq, c.Center - p) < Cross(pq, right.Center - p)))
                    {
                        right = c;
                    }
                }

                // Select which circle to return
                if (left.Radius < 0 && right.Radius < 0)
                {
                    return(crc);
                }
                else if (left.Radius < 0)
                {
                    return(right);
                }
                else if (right.Radius < 0)
                {
                    return(left);
                }
                else
                {
                    return(left.Radius <= right.Radius ? left : right);
                }
            }

            CircleD MakeDiameter(PointD a, PointD b)
            {
                PointD c = new PointD((a.X + b.X) / 2, (a.Y + b.Y) / 2);

                return(new CircleD(c, Math.Max(c.Distance(a), c.Distance(b))));
            }

            CircleD MakeCircumcircle(PointD a, PointD b, PointD c)
            {
                // Mathematical algorithm from Wikipedia: Circumscribed circle
                double ox = (Math.Min(Math.Min(a.X, b.X), c.X) + Math.Max(Math.Min(a.X, b.X), c.X)) / 2;
                double oy = (Math.Min(Math.Min(a.Y, b.Y), c.Y) + Math.Max(Math.Min(a.Y, b.Y), c.Y)) / 2;
                double ax = a.X - ox, ay = a.Y - oy;
                double bx = b.X - ox, by = b.Y - oy;
                double cx = c.X - ox, cy = c.Y - oy;
                double d = (ax * (by - cy) + bx * (cy - ay) + cx * (ay - by)) * 2;

                if (d == 0)
                {
                    return(new CircleD(new PointD(0, 0), -1));
                }
                double x = ((ax * ax + ay * ay) * (by - cy) + (bx * bx + by * by) * (cy - ay) + (cx * cx + cy * cy) * (ay - by)) / d;
                double y = ((ax * ax + ay * ay) * (cx - bx) + (bx * bx + by * by) * (ax - cx) + (cx * cx + cy * cy) * (bx - ax)) / d;
                PointD p = new PointD(ox + x, oy + y);
                double r = Math.Max(Math.Max(p.Distance(a), p.Distance(b)), p.Distance(c));

                return(new CircleD(p, r));
            }

            // Signed area / determinant thing
            double Cross(PointD p, PointD q)
            {
                return(p.X * q.Y - p.Y * q.X);
            }
        }
예제 #11
0
        /// <summary>
        ///     Finds the points of intersection between a line and a circle. The results are two lambdas along the line, one
        ///     for each point, or NaN if there is no intersection.</summary>
        public static void LineWithCircle(ref EdgeD line, ref CircleD circle,
                                          out double lambda1, out double lambda2)
        {
            // The following expressions come up a lot in the solution, so simplify using them.
            double dx = line.End.X - line.Start.X;
            double dy = line.End.Y - line.Start.Y;
            double ax = -line.Start.X + circle.Center.X;
            double ay = -line.Start.Y + circle.Center.Y;

            // Solve simultaneously for l:
            // Eq of a line:    x = sx + l * dx
            //                  y = sy + l * dy
            // Eq of a circle:  (x - cx)^2 + (y - cy)^2 = r^2
            // 
            // Eventually we get a standard quadratic equation in l with the
            // following coefficients:
            double a = dx * dx + dy * dy;
            double b = -2 * (ax * dx + ay * dy);
            double c = ax * ax + ay * ay - circle.Radius * circle.Radius;

            // Now just solve the quadratic eqn...
            double D = b * b - 4 * a * c;
            if (D < 0)
            {
                lambda1 = lambda2 = double.NaN;
            }
            else
            {
                double sqrtD = Math.Sqrt(D);
                lambda1 = (-b + sqrtD) / (2 * a);
                lambda2 = (-b - sqrtD) / (2 * a);
            }
        }
예제 #12
0
        /// <summary>
        ///     Finds the points of intersection between a ray and a circle. The resulting lambdas along the ray are sorted in
        ///     ascending order, so the "first" intersection is always in lambda1 (if any). Lambda may be NaN if there is no
        ///     intersection (or no "second" intersection).</summary>
        public static void RayWithCircle(ref EdgeD ray, ref CircleD circle,
                                         out double lambda1, out double lambda2)
        {
            LineWithCircle(ref ray, ref circle, out lambda1, out lambda2);

            // Sort the two values in ascending order, with NaN last,
            // while resetting negative values to NaNs
            if (lambda1 < 0) lambda1 = double.NaN;
            if (lambda2 < 0) lambda2 = double.NaN;
            if (lambda1 > lambda2 || double.IsNaN(lambda1))
            {
                double temp = lambda1;
                lambda1 = lambda2;
                lambda2 = temp;
            }
        }
예제 #13
0
 /// <summary>
 ///     Finds the points of intersection between a line and a circle. The results are two lambdas along the line, one
 ///     for each point, or NaN if there is no intersection.</summary>
 public static void LineWithCircle(EdgeD line, CircleD circle,
                                   out double lambda1, out double lambda2)
 {
     LineWithCircle(ref line, ref circle, out lambda1, out lambda2);
 }