/// <summary> /// Calculates the points of intersection between 2 CircleF objects</summary> /// <param name="c1">First CircleF</param> /// <param name="c2">Second CircleF</param> /// <param name="p1">First intersection point</param> /// <param name="p2">Second intersection point</param> /// <returns>True iff there are 1 or 2 intersection points; false if there are none or an infinite number</returns> public static bool Intersect(CircleF c1, CircleF c2, ref Vec2F p1, ref Vec2F p2) { Vec2F v1 = c2.Center - c1.Center; double d = v1.Length; const double EPS = 1.0e-6; if (d < EPS || d > c1.Radius + c2.Radius) { return(false); } v1 *= (float)(1 / d); Vec2F v2 = v1.Perp; double cos = (d * d + c1.Radius * c1.Radius - c2.Radius * c2.Radius) / (2 * c1.Radius * d); double sin = Math.Sqrt(1 - cos * cos); Vec2F t1 = Vec2F.Mul(v1, (float)(c1.Radius * cos)); Vec2F t2 = Vec2F.Mul(v2, (float)(c1.Radius * sin)); p1 = c1.Center + t1 + t2; p2 = c1.Center + t1 - t2; return(true); // From http://mathforum.org/library/drmath/view/51710.html // First, let C1 and C2 be the centers of the Circlefs with radii r1 and // r2, and let d be the distance between C1 and C2. // // Now let V1 be the unit vector from C1 to C2, and let V2 be the unit // vector perpendicular to V1. // // Also let V3 be the vector from C1 to one of the intersection points. // // Finally, let A be the angle between V1 and V3. // // From the law of cosines we know that // // r2^2 = r1^2 + d^2 - 2*r1*d*cos(A) // // With this equation we can solve for 'A'. // // The intersection points will be // // C1 + [r1*cos(A)]*V1 + [r1*sin(A)]*V2 // // C1 + [r1*cos(A)]*V1 - [r1*sin(A)]*V2 // a simple unit test // CircleF test1 = new CircleF(new Vec2F(-0.5f, 0), 1); // CircleF test2 = new CircleF(new Vec2F(0.5f, 0), 1); // Vec2F result1 = new Vec2F(); // Vec2F result2 = new Vec2F(); // CircleF.Intersect(test1, test2, ref result1, ref result2); }
/// <summary> /// Picks the specified curve</summary> /// <param name="curve">Curve</param> /// <param name="p">Picking point</param> /// <param name="tolerance">Pick tolerance</param> /// <param name="hitPoint">Hit point</param> /// <returns>True if curve found; false otherwise</returns> public static bool Pick(BezierCurve2F curve, Vec2F p, float tolerance, ref Vec2F hitPoint) { Queue <BezierCurve2F> curves = new Queue <BezierCurve2F>(); curves.Enqueue(curve); float dMin = float.MaxValue; Vec2F closestPoint = new Vec2F(); while (curves.Count > 0) { BezierCurve2F current = curves.Dequeue(); // project p onto segment connecting curve endpoints Seg2F seg = new Seg2F(current.P1, current.P4); Vec2F projection = Seg2F.Project(seg, p); float d = Vec2F.Distance(p, projection); // reject - point not near enough to segment, expanded by curve "thickness" float flatness = current.Flatness; if (d - flatness > tolerance) { continue; } // accept - point within tolerance of curve if (flatness <= tolerance) { if (d < dMin) { dMin = d; closestPoint = projection; } } else { BezierCurve2F left, right; current.Subdivide(0.5f, out left, out right); curves.Enqueue(left); curves.Enqueue(right); } } if (dMin < tolerance) { hitPoint = closestPoint; return(true); } return(false); }
/// <summary> /// Projects a point onto a circle</summary> /// <param name="p">Point to project</param> /// <param name="c">Circle to project onto</param> /// <param name="projection">Projected point</param> /// <returns>True iff projection is well defined</returns> public static bool Project(Vec2F p, CircleF c, ref Vec2F projection) { Vec2F d = Vec2F.Sub(p, c.Center); float length = d.Length; bool wellDefined = false; if (length > 0.000001f * c.Radius) { wellDefined = true; float scale = c.Radius / length; projection = Vec2F.Add(c.Center, Vec2F.Mul(d, scale)); } return(wellDefined); }
/// <summary> /// Determines if 2 rays intersect, and if so, returns the ray parameters of the intersection point</summary> /// <param name="r1">Ray 1</param> /// <param name="r2">Ray 2</param> /// <param name="t1">Returned ray parameter 1</param> /// <param name="t2">Returned ray parameter 2</param> /// <returns>True iff the rays intersect (are not parallel)</returns> public static bool Intersect(Ray2F r1, Ray2F r2, ref double t1, ref double t2) { double denom = (r2.Direction.Y * r1.Direction.X - r2.Direction.X * r1.Direction.Y); if (Math.Abs(denom) < 0.000001f) { return(false); } Vec2F d = Vec2F.Sub(r1.Origin, r2.Origin); t1 = (r2.Direction.X * d.Y - r2.Direction.Y * d.X) / denom; t2 = (r1.Direction.X * d.Y - r1.Direction.Y * d.X) / denom; return(true); }
/// <summary> /// Evaluates the curve on parameter t [0,1]</summary> /// <param name="t">Parameter</param> /// <returns>Point on the curve at the given parameter</returns> public Vec2F Evaluate(float t) { // use de Casteljau constuction float oneMinusT = 1.0f - t; Vec2F s11 = P1 * oneMinusT + P2 * t; Vec2F s12 = P2 * oneMinusT + P3 * t; Vec2F s13 = P3 * oneMinusT + P4 * t; Vec2F s21 = s11 * oneMinusT + s12 * t; Vec2F s22 = s12 * oneMinusT + s13 * t; Vec2F s31 = s21 * oneMinusT + s22 * t; return(s31); }
/// <summary> /// Extends box to contain the given point</summary> /// <param name="p">Point</param> /// <returns>Extended box</returns> public Box2F Extend(Vec2F p) { if (m_empty) { Min = Max = p; m_empty = false; } else { Min.X = Math.Min(Min.X, p.X); Min.Y = Math.Min(Min.Y, p.Y); Max.X = Math.Max(Max.X, p.X); Max.Y = Math.Max(Max.Y, p.Y); } return(this); }
/// <summary> /// Subdivides the Bezier curve into two equivalent Bezier curves</summary> /// <param name="t">Parameter of subdivision point</param> /// <param name="left">Left or "less than or equal to t" parameter side</param> /// <param name="right">Right or "greater than or equal to t" parameter side</param> public void Subdivide(float t, out BezierCurve2F left, out BezierCurve2F right) { // use de Casteljau constuction float oneMinusT = 1.0f - t; Vec2F s11 = P1 * oneMinusT + P2 * t; Vec2F s12 = P2 * oneMinusT + P3 * t; Vec2F s13 = P3 * oneMinusT + P4 * t; Vec2F s21 = s11 * oneMinusT + s12 * t; Vec2F s22 = s12 * oneMinusT + s13 * t; Vec2F s31 = s21 * oneMinusT + s22 * t; left = new BezierCurve2F(P1, s11, s21, s31); right = new BezierCurve2F(s31, s22, s13, P4); }
private static Vector2 FindIntersection2D(Vector2 ptA, float mA, Vector2 ptB, float mB) { // High School algebra alert! // ptA.Y = mA * ptA.X + cA // cA = ptA.Y - mA * ptA.X var cA = ptA.Y - mA * ptA.X; var cB = ptB.Y - mB * ptB.X; // y = mA * x + cA // y = mB * x + cB // mA * x + cA = mB * x + cB // x * (mA - mB) = cB - cA // x = (cB - cA) / (mA - mB) float x = (cB - cA) / (mA - mB); float y = mA * x + cA; System.Diagnostics.Debug.Assert(Math.Abs((mB * x + cB) - y) < 0.001f); return(new Vector2(x, y)); }
/// <summary> /// Constructs the circle containing 3 points</summary> /// <param name="p1">Point 1</param> /// <param name="p2">Point 2</param> /// <param name="p3">Point 3</param> public CircleF(Vec2F p1, Vec2F p2, Vec2F p3) { Vec2F o1 = Vec2F.Add(p1, p2); o1 *= 0.5f; Vec2F o2 = Vec2F.Add(p3, p2); o2 *= 0.5f; Vec2F d1 = Vec2F.Sub(p2, p1); d1 = d1.Perp; Vec2F d2 = Vec2F.Sub(p3, p2); d2 = d2.Perp; double t1 = 0; double t2 = 0; if (Ray2F.Intersect(new Ray2F(o1, d1), new Ray2F(o2, d2), ref t1, ref t2)) { Center = o1 + d1 * (float)t1; Radius = Vec2F.Distance(Center, p1); } else { Center = new Vec2F(float.PositiveInfinity, float.PositiveInfinity); Radius = float.PositiveInfinity; } }
/// <summary> /// Constructor with min and max</summary> /// <param name="min">Minima of extents</param> /// <param name="max">Maxima of extents</param> public Box2F(Vec2F min, Vec2F max) { Min = min; Max = max; m_empty = false; }
/// <summary> /// Constructor</summary> /// <param name="origin">Ray origin</param> /// <param name="direction">Ray direction</param> public Ray2F(Vec2F origin, Vec2F direction) { Origin = origin; Direction = direction; }
private static Vector2 FindIntersection2D(Vector2 ptA, float mA, Vector2 ptB, float mB) { // High School algebra alert! // ptA.Y = mA * ptA.X + cA // cA = ptA.Y - mA * ptA.X var cA = ptA.Y - mA * ptA.X; var cB = ptB.Y - mB * ptB.X; // y = mA * x + cA // y = mB * x + cB // mA * x + cA = mB * x + cB // x * (mA - mB) = cB - cA // x = (cB - cA) / (mA - mB) float x = (cB - cA) / (mA - mB); float y = mA * x + cA; System.Diagnostics.Debug.Assert(Math.Abs((mB * x + cB) - y) < 0.001f); return new Vector2(x, y); }
/// <summary> /// Constructor using end points</summary> /// <param name="p1">First endpoint</param> /// <param name="p2">Second endpoint</param> public Seg2F(Vec2F p1, Vec2F p2) { P1 = p1; P2 = p2; }
/// <summary> /// Constructor using 2D vector</summary> /// <param name="v">2D vector</param> public Vec3F(Vec2F v) { X = v.X; Y = v.Y; Z = 1; }
/// <summary> /// Determines if point is inside circle</summary> /// <param name="p">Point</param> /// <returns>True iff point is inside circle</returns> public bool Contains(Vec2F p) { Vec2F d = Vec2F.Sub(p, Center); return(d.LengthSquared < (Radius * Radius)); }
/// <summary> /// Constructor with center and radius</summary> /// <param name="center">Center point</param> /// <param name="radius">Radius</param> public CircleF(Vec2F center, float radius) { Center = center; Radius = radius; }