using System;
using Autodesk.AutoCAD.Geometry;
namespace GeometryExtensions
{
/// <summary>
/// Tangent type enum
/// </summary>
[Flags]
public enum TangentType { Inner = 1, Outer = 2 }
/// <summary>
/// Provides extension methods for the Point2d structure.
/// </summary>
public static class Point2dExtensions
{
/// <summary>
/// Converts a 2d point into a 3d point according to the plane defined by
/// the specified normal vector and elevation.
/// </summary>
/// <param name="pt">The instance to which the method applies.</param>
/// <param name="normal">The normal vector of the plane which the point lies on.</param>
/// <param name="elevation">The elevation of the plane which the point lies on.</param>
/// <returns>The corresponding 3d point</returns>
public static Point3d Convert3d(this Point2d pt, Vector3d normal, double elevation)
{
return new Point3d
(pt
.X, pt
.Y, elevation
).TransformBy(Matrix3d
.PlaneToWorld(normal
)); }
}
/// <summary>
/// Provides extension methods for the CircularArc3d class.
/// </summary>
public static class CircularArc3dExtensions
{
/// <summary>
/// Returns the tangents between the active CircularArc3d instance complete circle and a point.
/// </summary>
/// <remarks>
/// Tangents start points are on the object to which this method applies, end points on the point passed as argument.
/// Tangents are always returned in the same order: the tangent on the left side of the line from the circular arc center
/// to the point before the one on the right side.
/// </remarks>
/// <param name="arc">The object to which this method applies.</param>
/// <param name="pt">The Point3d to which tangents are searched</param>
/// <returns>An array of LineSegement3d representing the tangents (2) or null if there is none.</returns>
/// <exception cref="Autodesk.AutoCAD.Runtime.exception">eNonCoplanarGeometry is thrown if the objects do not lies on the same plane.</exception>
public static LineSegment3d[] GetTangentsTo(this CircularArc3d arc, Point3d pt)
{
// check if arc and point lies on the plane
Vector3d normal = arc.Normal;
Matrix3d WCS2OCS = Matrix3d.WorldToPlane(normal);
double elevation = arc.Center.TransformBy(WCS2OCS).Z;
if (Math.Abs(elevation - pt.TransformBy(WCS2OCS).Z) < Tolerance.Global.EqualPoint)
throw new Autodesk
.AutoCAD.Runtime.Exception( Autodesk.AutoCAD.Runtime.ErrorStatus.NonCoplanarGeometry);
Plane plane
= new Plane
(Point3d
.Origin, normal
); Matrix3d OCS2WCS = Matrix3d.PlaneToWorld(plane);
CircularArc2d ca2d
= new CircularArc2d
(arc
.Center.Convert2d(plane
), arc
.Radius); LineSegment2d[] lines2d = ca2d.GetTangentsTo(pt.Convert2d(plane));
if (lines2d == null)
return null;
LineSegment3d
[] result
= new LineSegment3d
[lines2d
.Length]; for (int i = 0; i < lines2d.Length; i++)
{
LineSegment2d ls2d = lines2d[i];
//Point3d p1 = new Point3d(ls2d.StartPoint.X, ls2d.StartPoint.Y, elevation);
//Point3d p2 = new Point3d(ls2d.EndPoint.X, ls2d.EndPoint.Y, elevation);
//result[i] = new LineSegment3d(p1.TransformBy(OCS2WCS), p2.TransformBy(OCS2WCS));
result
[i
] = new LineSegment3d
(ls2d
.StartPoint.Convert3d(normal, elevation
), ls2d
.EndPoint.Convert3d(normal, elevation
)); }
return result;
}
/// <summary>
/// Returns the tangents between the active CircularArc3d instance complete circle and another one.
/// </summary>
/// <remarks>
/// Tangents start points are on the object to which this method applies, end points on the one passed as argument.
/// Tangents are always returned in the same order: outer tangents before inner tangents, and for both,
/// the tangent on the left side of the line from this circular arc center to the other one before the one on the right side.
/// </remarks>
/// <param name="arc">The object to which this method applies.</param>
/// <param name="other">The CircularArc3d to which searched for tangents.</param>
/// <param name="flags">An enum value specifying which type of tangent is returned.</param>
/// <returns>An array of LineSegment3d representing the tangents (maybe 2 or 4) or null if there is none.</returns>
/// <exception cref="Autodesk.AutoCAD.Runtime.exception">eNonCoplanarGeometry is thrown if the objects do not lies on the same plane.</exception>
public static LineSegment3d[] GetTangentsTo(this CircularArc3d arc, CircularArc3d other, TangentType flags)
{
// check if circles lies on the same plane
Vector3d normal = arc.Normal;
Matrix3d WCS2OCS = Matrix3d.WorldToPlane(normal);
double elevation = arc.Center.TransformBy(WCS2OCS).Z;
if (!(normal.IsParallelTo(other.Normal) &&
Math.Abs(elevation - other.Center.TransformBy(WCS2OCS).Z) < Tolerance.Global.EqualPoint))
throw new Autodesk
.AutoCAD.Runtime.Exception( Autodesk.AutoCAD.Runtime.ErrorStatus.NonCoplanarGeometry);
Plane plane
= new Plane
(Point3d
.Origin, normal
); Matrix3d OCS2WCS = Matrix3d.PlaneToWorld(plane);
CircularArc2d ca2d1
= new CircularArc2d
(arc
.Center.Convert2d(plane
), arc
.Radius); CircularArc2d ca2d2
= new CircularArc2d
(other
.Center.Convert2d(plane
), other
.Radius); LineSegment2d[] lines2d = ca2d1.GetTangentsTo(ca2d2, flags);
if (lines2d == null)
return null;
LineSegment3d
[] result
= new LineSegment3d
[lines2d
.Length]; for (int i = 0; i < lines2d.Length; i++)
{
LineSegment2d ls2d = lines2d[i];
//Point3d p1 = new Point3d(ls2d.StartPoint.X, ls2d.StartPoint.Y, elevation);
//Point3d p2 = new Point3d(ls2d.EndPoint.X, ls2d.EndPoint.Y, elevation);
//result[i] = new LineSegment3d(p1.TransformBy(OCS2WCS), p2.TransformBy(OCS2WCS));
result
[i
] = new LineSegment3d
(ls2d
.StartPoint.Convert3d(normal, elevation
), ls2d
.EndPoint.Convert3d(normal, elevation
)); }
return result;
}
}
/// <summary>
/// Provides extension methods for the CircularArc2d class.
/// </summary>
public static class CircularArc2dExtensions
{
/// <summary>
/// Returns the tangents between the active CircularArc2d instance complete circle and a point.
/// </summary>
/// <remarks>
/// Tangents start points are on the object to which this method applies, end points on the point passed as argument.
/// Tangents are always returned in the same order: the tangent on the left side of the line from the circular arc center
/// to the point before the one on the right side.
/// </remarks>
/// <param name="arc">The object to which this method applies.</param>
/// <param name="pt">The Point2d to which tangents are searched</param>
/// <returns>An array of LineSegement2d representing the tangents (2) or null if there is none.</returns>
public static LineSegment2d[] GetTangentsTo(this CircularArc2d arc, Point2d pt)
{
// check if the point is inside the circle
Point2d center = arc.Center;
if (pt.GetDistanceTo(center) <= arc.Radius)
return null;
Vector2d vec = center.GetVectorTo(pt) / 2.0;
CircularArc2d tmp
= new CircularArc2d
(center
+ vec, vec
.Length); Point2d[] inters = arc.IntersectWith(tmp);
if (inters == null)
return null;
LineSegment2d
[] result
= new LineSegment2d
[2]; Vector2d v1 = inters[0] - center;
Vector2d v2 = inters[1] - center;
int i = vec.X * v1.Y - vec.Y - v1.X > 0 ? 0 : 1;
int j = i ^ 1;
result
[i
] = new LineSegment2d
(inters
[0], pt
); result
[j
] = new LineSegment2d
(inters
[1], pt
); return result;
}
/// <summary>
/// Returns the tangents between the active CircularArc2d instance complete circle and another one.
/// </summary>
/// <remarks>
/// Tangents start points are on the object to which this method applies, end points on the one passed as argument.
/// Tangents are always returned in the same order: outer tangents before inner tangents, and for both,
/// the tangent on the left side of the line from this circular arc center to the other one before the one on the right side.
/// </remarks>
/// <param name="arc">The object to which this method applies.</param>
/// <param name="other">The CircularArc2d to which searched for tangents.</param>
/// <param name="flags">An enum value specifying which type of tangent is returned.</param>
/// <returns>An array of LineSegment2d representing the tangents (maybe 2 or 4) or null if there is none.</returns>
public static LineSegment2d[] GetTangentsTo(this CircularArc2d arc, CircularArc2d other, TangentType flags)
{
// check if a circle is inside the other
double dist = arc.Center.GetDistanceTo(other.Center);
if (dist - Math.Abs(arc.Radius - other.Radius) <= Tolerance.Global.EqualPoint)
return null;
// check if circles overlap
bool overlap = arc.Radius + other.Radius >= dist;
if (overlap && flags == TangentType.Inner)
return null;
CircularArc2d tmp1, tmp2;
Point2d[] inters;
Vector2d vec1, vec2, vec = other.Center - arc.Center;
int i, j;
LineSegment2d
[] result
= new LineSegment2d
[(int)flags
== 3 && !overlap
? 4 : 2];
// outer tangents
if (flags.HasFlag(TangentType.Outer))
{
if (arc.Radius == other.Radius)
{
Line2d perp
= new Line2d
(arc
.Center, vec
.GetPerpendicularVector()); inters = arc.IntersectWith(perp);
if (inters == null)
return null;
vec1 = (inters[0] - arc.Center).GetNormal();
vec2 = (inters[1] - arc.Center).GetNormal();
i = vec.X * vec1.Y - vec.Y - vec1.X > 0 ? 0 : 1;
j = i ^ 1;
result
[i
] = new LineSegment2d
(inters
[0], inters
[0] + vec
); result
[j
] = new LineSegment2d
(inters
[1], inters
[1] + vec
); }
else
{
Point2d center = arc.Radius < other.Radius ? other.Center : arc.Center;
tmp1
= new CircularArc2d
(center, Math
.Abs(arc
.Radius - other
.Radius)); tmp2
= new CircularArc2d
(arc
.Center + vec
/ 2.0, dist
/ 2.0); inters = tmp1.IntersectWith(tmp2);
if (inters == null)
return null;
vec1 = (inters[0] - center).GetNormal();
vec2 = (inters[1] - center).GetNormal();
i = vec.X * vec1.Y - vec.Y - vec1.X > 0 ? 0 : 1;
j = i ^ 1;
result
[i
] = new LineSegment2d
(arc
.Center + vec1
* arc
.Radius, other
.Center + vec1
* other
.Radius); result
[j
] = new LineSegment2d
(arc
.Center + vec2
* arc
.Radius, other
.Center + vec2
* other
.Radius); }
}
// inner tangents
if (flags.HasFlag(TangentType.Inner) && !overlap)
{
double ratio = (arc.Radius / (arc.Radius + other.Radius)) / 2.0;
tmp1
= new CircularArc2d
(arc
.Center + vec
* ratio, dist
* ratio
); inters = arc.IntersectWith(tmp1);
if (inters == null)
return null;
vec1 = (inters[0] - arc.Center).GetNormal();
vec2 = (inters[1] - arc.Center).GetNormal();
i = vec.X * vec1.Y - vec.Y - vec1.X > 0 ? 2 : 3;
j = i == 2 ? 3 : 2;
result
[i
] = new LineSegment2d
(arc
.Center + vec1
* arc
.Radius, other
.Center + vec1
.Negate() * other
.Radius); result
[j
] = new LineSegment2d
(arc
.Center + vec2
* arc
.Radius, other
.Center + vec2
.Negate() * other
.Radius); }
return result;
}
}
}