.NET

## .NET

Member
Posts: 4
Registered: ‎09-11-2013
Message 1 of 8 (1,111 Views)

# how to drawing a line tangent to two circles in C#?

1111 Views, 7 Replies
09-11-2013 09:54 AM

how to drawing a line tangent to two circles in C#?

*Expert Elite*
Posts: 1,979
Registered: ‎04-29-2006
Message 2 of 8 (1,096 Views)

# Re : how to drawing a line tangent to two circles in C#?

09-11-2013 10:54 AM in reply to: ydsoo8
*Expert Elite*
Posts: 4,705
Registered: ‎10-12-2006
Message 3 of 8 (1,090 Views)

# Re: how to drawing a line tangent to two circles in C#?

09-11-2013 11:48 AM in reply to: ydsoo8

Break out the math (all that work in high school you never thought would be useful... who knew).  A line that is tangent to a circle will be at 90 degrees to the radius of that circle.  For a line to be tangent to two circles, you need to find a line that is perpendicular to the radius of each circle.  Under this cirumstance, the radii of both circles will be parallel.  From there its just a matter of having a line extending from each circles center to its respective radius, using the same angle for each, and the tangent line will connect them.

----------------------------------
If you are going to fly by the seat of your pants, expect friction burns.
A good question will be halfway to a good answer.

Member
Posts: 4
Registered: ‎09-11-2013
Message 4 of 8 (1,059 Views)

# Re: how to drawing a line tangent to two circles in C#?

09-12-2013 09:34 AM in reply to: dgorsman

LINE <enter>

TAN <enter>

Pick the first point

TAN <enter>

Pick the second point

<enter>

*Expert Elite*
Posts: 1,979
Registered: ‎04-29-2006
Message 5 of 8 (1,005 Views)

# Re : how to drawing a line tangent to two circles in C#?

09-14-2013 01:18 AM in reply to: ydsoo8

Hi,

Here's an extension method for the CicularArc3d class.

It returns a List of LineSegment3d for the tangents between two circles (maybe 0, 2 or 4).

An exception is thrown if the circles do not lie on the same plane.

```using System;
using System.Collections.Generic;

namespace TangentsToCircle
{
public static class Tangents
{
public static List<LineSegment3d> GetTangentsToCircle(this CircularArc3d circle, CircularArc3d other)
{
// check if circles lies on the same plane
Vector3d normal = circle.Normal;
Plane plane = new Plane(Point3d.Origin, normal);
double elev1 = circle.Center.TransformBy(Matrix3d.WorldToPlane(plane)).Z;
double elev2 = other.Center.TransformBy(Matrix3d.WorldToPlane(plane)).Z;
if (!(normal.IsParallelTo(other.Normal) &&
Math.Abs(elev1 - elev2) < Tolerance.Global.EqualPoint))

List<LineSegment3d> result = new List<LineSegment3d>();

// check if a circle is not inside the other
double dist = circle.Center.DistanceTo(other.Center);
return result;

CircularArc3d tmp1, tmp2;
Point3d center;
Point3d[] inters;
Vector3d vec, vec1, vec2;

// external tangents
{
center = circle.Center;
normal = circle.Normal;
vec = other.Center - center;
Line3d perp = new Line3d(center, vec.CrossProduct(normal));
inters = circle.IntersectWith(perp);
if (inters != null)
{
}
}
else
{
{
tmp1 = circle;
circle = other;
other = tmp1;
}
center = circle.Center;
normal = circle.Normal;
vec = other.Center - center;
tmp2 = new CircularArc3d(center + vec / 2.0, normal, dist / 2.0);
inters = tmp1.IntersectWith(tmp2);
if (inters != null)
{
vec1 = (inters[0] - center).GetNormal();
vec2 = (inters[1] - center).GetNormal();
}
}

// crossing tangents
{
tmp1 = new CircularArc3d(center + vec * ratio, normal, dist * ratio);
inters = circle.IntersectWith(tmp1);
if (inters != null)
{
vec1 = (inters[0] - center).GetNormal();
vec2 = (inters[1] - center).GetNormal();
}
}
return result;
}
}
}```

A test command:

```using System.Collections.Generic;

[assembly: CommandClass(typeof(TangentsToCircle.CommandMethods))]

namespace TangentsToCircle
{
public class CommandMethods
{
[CommandMethod("Test", CommandFlags.Modal)]
public void Test()
{
Document doc = Application.DocumentManager.MdiActiveDocument;
Database db = doc.Database;
Editor ed = doc.Editor;

PromptEntityOptions peo = new PromptEntityOptions("\nSelect a circle: ");
peo.SetRejectMessage("Only a circle.");
PromptEntityResult per = ed.GetEntity(peo);
if (per.Status != PromptStatus.OK)
return;
ObjectId id1 = per.ObjectId;

peo.Message = "\nSelect another circle: ";
ObjectId id2;
while (true)
{
per = ed.GetEntity(peo);
if (per.Status != PromptStatus.OK)
return;
id2 = per.ObjectId;
if (id1 == id2)
ed.WriteMessage("\nThe second circle is the same as the first one.");
else break;
}

try
{
using (Transaction tr = db.TransactionManager.StartTransaction())
{
CircularArc3d ca1 = new CircularArc3d(c1.Center, c1.Normal, c1.Radius);
CircularArc3d ca2 = new CircularArc3d(c2.Center, c2.Normal, c2.Radius);
List<LineSegment3d> lines = ca1.GetTangentsToCircle(ca2);
BlockTableRecord btr =
(BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite);
foreach (LineSegment3d l in lines)
{
Line line = new Line(l.StartPoint, l.EndPoint);
btr.AppendEntity(line);
}
tr.Commit();
}
}
{
ed.WriteMessage("\n" + exn.Message);
}
catch (System.Exception exn)
{
ed.WriteMessage("\n" + exn.Message);
}
}
}
}
```

Gilles Chanteau
*Expert Elite*
Posts: 1,979
Registered: ‎04-29-2006
Message 6 of 8 (990 Views)

# Re : how to drawing a line tangent to two circles in C#?

09-14-2013 09:40 AM in reply to: _gile

A better (more robust and xml documented) implementation.

```using System;

namespace GeometryExtensions
{
/// <summary>
/// Tangent type enum
/// </summary>
[Flags]
public enum TangentType { Inner = 1, Outer = 2 }

/// <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 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 theme 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;
double elev1 = arc.Center.TransformBy(Matrix3d.WorldToPlane(normal)).Z;
double elev2 = other.Center.TransformBy(Matrix3d.WorldToPlane(normal)).Z;
if (!(normal.IsParallelTo(other.Normal) &&
Math.Abs(elev1 - elev2) < Tolerance.Global.EqualPoint))

// check if a circle is inside the other
double dist = arc.Center.DistanceTo(other.Center);
return null;

// check if circles overlap
if (overlap && flags == TangentType.Inner)
return null;

CircularArc3d tmp1, tmp2;
Point3d[] inters;
Vector3d vec1, vec2, vec = other.Center - arc.Center;
int i, j;
LineSegment3d[] result = new LineSegment3d[(int)flags == 3 && !overlap ? 4 : 2];

// outer tangents
if (flags.HasFlag(TangentType.Outer))
{
{
Line3d perp = new Line3d(arc.Center, vec.CrossProduct(normal));
inters = arc.IntersectWith(perp);
vec1 = (inters[0] - arc.Center).GetNormal();
vec2 = (inters[1] - arc.Center).GetNormal();
i = vec.GetAngleTo(vec1, normal) < vec.GetAngleTo(vec2, normal) ? 0 : 1;
j = i ^ 1;
result[i] = new LineSegment3d(inters[0], inters[0] + vec);
result[j] = new LineSegment3d(inters[1], inters[1] + vec);
}
else
{
tmp2 = new CircularArc3d(arc.Center + vec / 2.0, normal, dist / 2.0);
inters = tmp1.IntersectWith(tmp2);
vec1 = (inters[0] - center).GetNormal();
vec2 = (inters[1] - center).GetNormal();
i = vec.GetAngleTo(vec1, normal) < vec.GetAngleTo(vec2, normal) ? 0 : 1;
j = i ^ 1;
result[i] = new LineSegment3d(arc.Center + vec1 * arc.Radius, other.Center + vec1 * other.Radius);
result[j] = new LineSegment3d(arc.Center + vec2 * arc.Radius, other.Center + vec2 * other.Radius);
}
}

// inner tangents
if (flags.HasFlag(TangentType.Inner) && !overlap)
{
tmp1 = new CircularArc3d(arc.Center + vec * ratio, normal, dist * ratio);
inters = arc.IntersectWith(tmp1);
vec1 = (inters[0] - arc.Center).GetNormal();
vec2 = (inters[1] - arc.Center).GetNormal();
i = vec.GetAngleTo(vec1, normal) < vec.GetAngleTo(vec2, normal) ? 2 : 3;
j = i == 2 ? 3 : 2;
result[i] = new LineSegment3d(arc.Center + vec1 * arc.Radius, other.Center + vec1.Negate() * other.Radius);
result[j] = new LineSegment3d(arc.Center + vec2 * arc.Radius, other.Center + vec2.Negate() * other.Radius);
}
return result;
}

/// <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 theme plane.</exception>
public static LineSegment3d[] GetTangentsTo(this CircularArc3d arc, Point3d pt)
{
// check if circle and point lies on the plane
Vector3d normal = arc.Normal;
double elev1 = arc.Center.TransformBy(Matrix3d.WorldToPlane(normal)).Z;
double elev2 = pt.TransformBy(Matrix3d.WorldToPlane(normal)).Z;
if (Math.Abs(elev1 - elev2) < Tolerance.Global.EqualPoint)

// check if the point is inside the circle
Point3d center = arc.Center;
return null;

Vector3d vec = pt.GetVectorTo(center) / 2.0;
CircularArc3d tmp = new CircularArc3d(pt + vec, arc.Normal, vec.Length);
Point3d[] inters = arc.IntersectWith(tmp);
LineSegment3d[] result = new LineSegment3d[2];
int i = vec.GetAngleTo(inters[0] - center, normal) < vec.GetAngleTo(inters[1] - center, normal) ? 0 : 1;
int j = i ^ 1;
result[i] = new LineSegment3d(inters[0], pt);
result[j] = new LineSegment3d(inters[1], pt);
return result;
}
}
}
```

A test command which draws a closed polyline  along the outer tangents and the trimmed selected circles.

```        [CommandMethod("JoinCircles", CommandFlags.Modal)]
public void JoinCircles()
{
Document doc = Application.DocumentManager.MdiActiveDocument;
Database db = doc.Database;
Editor ed = doc.Editor;

PromptEntityOptions peo = new PromptEntityOptions("\nSelect a circle: ");
peo.SetRejectMessage("Only a circle.");
PromptEntityResult per = ed.GetEntity(peo);
if (per.Status != PromptStatus.OK)
return;
ObjectId id1 = per.ObjectId;

peo.Message = "\nSelect another circle: ";
ObjectId id2;
while (true)
{
per = ed.GetEntity(peo);
if (per.Status != PromptStatus.OK)
return;
id2 = per.ObjectId;
if (id1 == id2)
ed.WriteMessage("\nThe second circle is the same as the first one.");
else break;
}

try
{
using (Transaction tr = db.TransactionManager.StartTransaction())
{
CircularArc3d ca1 = new CircularArc3d(c1.Center, c1.Normal, c1.Radius);
CircularArc3d ca2 = new CircularArc3d(c2.Center, c2.Normal, c2.Radius);
LineSegment3d[] lines = ca1.GetTangentsTo(ca2, TangentType.Outer);
if (lines != null)
{
BlockTableRecord btr =
(BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite);
Vector3d vec = c1.Center.GetVectorTo(c2.Center);
Plane plane = new Plane(Point3d.Origin, c1.Normal);
double a1 = vec.GetAngleTo(ca1.Center.GetVectorTo(lines[1].StartPoint), ca1.Normal) -
vec.GetAngleTo(ca1.Center.GetVectorTo(lines[0].StartPoint), ca1.Normal);
double a2 = vec.Negate().GetAngleTo(ca2.Center.GetVectorTo(lines[0].EndPoint), ca1.Normal) -
vec.Negate().GetAngleTo(ca2.Center.GetVectorTo(lines[1].EndPoint), ca1.Normal);
Polyline pline = new Polyline(4);
pline.AddVertexAt(0, lines[0].StartPoint.Convert2d(plane), Math.Tan(a1 / 4.0), 0.0, 0.0);
pline.AddVertexAt(2, lines[1].EndPoint.Convert2d(plane), Math.Tan(a2 / 4.0), 0.0, 0.0);
pline.Closed = true;
pline.Normal = c1.Normal;
pline.Elevation = c1.Center.TransformBy(Matrix3d.WorldToPlane(c1.Normal)).Z;
btr.AppendEntity(pline);
c1.Erase();
c2.Erase();
}
tr.Commit();
}
}
catch (System.Exception exn)
{
ed.WriteMessage("\nError: " + exn.Message);
}
}```

Gilles Chanteau
Member
Posts: 4
Registered: ‎09-11-2013
Message 7 of 8 (979 Views)

# Re : how to drawing a line tangent to two circles in C#?

09-15-2013 12:50 AM in reply to: _gile

Thank you for your reply. You may not understand what I mean. Although your code does not realize what I want, but I can change according to your code to achieve.. Then I will put out the modified code.

Member
Posts: 4
Registered: ‎09-11-2013
Message 8 of 8 (970 Views)

# Re : how to drawing a line tangent to two circles in C#?

09-15-2013 02:41 AM in reply to: _gile
```struct lineDist
{
public Line  l;
public double dist;
}
[CommandMethod("tt")]
static public void test()
{
Point3d pickedP1;
Point3d pickedP2;

Document doc = Application.DocumentManager.MdiActiveDocument;
Database db = doc.Database;
Editor ed = doc.Editor;

PromptEntityOptions peo = new PromptEntityOptions("\nSelect a circle: ");
peo.SetRejectMessage("Only a circle.");
PromptEntityResult per = ed.GetEntity(peo);
if (per.Status != PromptStatus.OK)
return;
ObjectId id1 = per.ObjectId;
pickedP1 = per.PickedPoint;

peo.Message = "\nSelect another circle: ";
ObjectId id2;
while (true)
{
per = ed.GetEntity(peo);
if (per.Status != PromptStatus.OK)
return;
id2 = per.ObjectId;
pickedP2 = per.PickedPoint;
if (id1 == id2)
ed.WriteMessage("\nThe second circle is the same as the first one.");
else break;
}

try
{
using (Transaction tr = db.TransactionManager.StartTransaction())
{
CircularArc3d ca1 = new CircularArc3d(c1.Center, c1.Normal, c1.Radius);
CircularArc3d ca2 = new CircularArc3d(c2.Center, c2.Normal, c2.Radius);
List<LineSegment3d> lines = ydsCommands.GetTangentsToCircle(ca1, ca2);
BlockTableRecord btr =
(BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite);
List<lineDist> ld = new List<lineDist>(lines.Count);
double dist = 0;
lineDist ldx = new lineDist();
foreach (LineSegment3d l in lines)
{
Line line = new Line(l.StartPoint, l.EndPoint);
dist = line.GetClosestPointTo(pickedP1, true).DistanceTo(pickedP1) + line.GetClosestPointTo(pickedP2, true).DistanceTo(pickedP2);
ldx.l = line;
ldx.dist = dist;
}
foreach (lineDist d in ld)
{
if (d.dist < ldx.dist)
{
ldx = d;
}
}
btr.AppendEntity(ldx.l);
tr.Commit();
}
}
{
ed.WriteMessage("\n" + exn.Message);
}
catch (System.Exception exn)
{
ed.WriteMessage("\n" + exn.Message);
}
}
public static List<LineSegment3d> GetTangentsToCircle(CircularArc3d circle, CircularArc3d other)
{
// check if circles lies on the same plane
Vector3d normal = circle.Normal;
Plane plane = new Plane(Point3d.Origin, normal);
double elev1 = circle.Center.TransformBy(Matrix3d.WorldToPlane(plane)).Z;
double elev2 = other.Center.TransformBy(Matrix3d.WorldToPlane(plane)).Z;
if (!(normal.IsParallelTo(other.Normal) &&
Math.Abs(elev1 - elev2) < Tolerance.Global.EqualPoint))

List<LineSegment3d> result = new List<LineSegment3d>();

// check if a circle is not inside the other
double dist = circle.Center.DistanceTo(other.Center);
return result;

CircularArc3d tmp1, tmp2;
Point3d center;
Point3d[] inters;
Vector3d vec, vec1, vec2;

// external tangents
{
center = circle.Center;
normal = circle.Normal;
vec = other.Center - center;
Line3d perp = new Line3d(center, vec.CrossProduct(normal));
inters = circle.IntersectWith(perp);
if (inters != null)
{
}
}
else
{
{
tmp1 = circle;
circle = other;
other = tmp1;
}
center = circle.Center;
normal = circle.Normal;
vec = other.Center - center;
tmp2 = new CircularArc3d(center + vec / 2.0, normal, dist / 2.0);
inters = tmp1.IntersectWith(tmp2);
if (inters != null)
{
vec1 = (inters[0] - center).GetNormal();
vec2 = (inters[1] - center).GetNormal();
}
}

// crossing tangents
{
tmp1 = new CircularArc3d(center + vec * ratio, normal, dist * ratio);
inters = circle.IntersectWith(tmp1);
if (inters != null)
{
vec1 = (inters[0] - center).GetNormal();
vec2 = (inters[1] - center).GetNormal();
}
}
return result;
}```

### You are not logged in.

Announcements
Welcome to the new Autodesk Community!
If this is your first visit, click here to get started and make the most of the Community. Let us know what you think of the new experience in the Community Feedback Forum.