.NET
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Circle by point and two tangents

25 REPLIES 25
SOLVED
Reply
Message 1 of 26
FRFR1426
3564 Views, 25 Replies

Circle by point and two tangents

Ok guys, some geometry to finish the week. I've got one point, one line and one arc as input. I would like to find the center of the red circle to draw it.

 

tangents.png

In AutoCAD, I use Circle by three points. I select the first point at the bottom, then a point on the line with a tangent osnap and finally one point on the arc with a tangent osnap. And boom, I've got the circle. But now, I need to do this with the API. Using the CIRCLE command is not an option, as I need to feed it with tangent osnap input.

Maxence DELANNOY
Manager
Add-ins development for Autodesk software products
http://wiip.fr
25 REPLIES 25
Message 2 of 26
alexisgacia
in reply to: FRFR1426

Hi,

 

did you find a solution for this one? I'm having this kind of problem.

Message 3 of 26
hgasty1001
in reply to: FRFR1426

Hi,

You have a 3 points defined circle, so you can calculate the radius and center by solving an equation system. If you don't want to go with the math involve, you can check this link: 3 points defined circle 

 

-gnb

Message 4 of 26
FRFR1426
in reply to: alexisgacia

I don't remember (it was 6 years ago). But the answer of @hgasty1001  is the way to go. You have three unknown: the center C of the circle to draw (Cx, Cy) and its radius (r).

 

The first input (the point) give you a first equation: (x – Cx)² + (y -Cy)² = r²

The other inputs are two tangents: you need to translate these constraints into 2 additional equations in order to have a system that you can solve. But I must admit I have no clue how we can do it.

Maxence DELANNOY
Manager
Add-ins development for Autodesk software products
http://wiip.fr
Message 5 of 26
FRFR1426
in reply to: FRFR1426

One interesting thing to note is that when you draw the circle this way in AutoCAD, it does not display a preview.

Maxence DELANNOY
Manager
Add-ins development for Autodesk software products
http://wiip.fr
Message 6 of 26
doaiena
in reply to: FRFR1426

In AutoCAD, you can find the center of a circle, defined by 3 points in a relatively straightforward manner.

We have 3 points /p1, p2, p3/ and we need to find the center of the circle, that is defined by those points.

1 - Draw the line p1 p2
2 - Draw the line p2 p3
3 - Draw a line L1 that is perpendicular to p1 p2 and is passing through the mid point of p1 p2

4 - Draw a line L2 that is perpendicular to p2 p3 and is passing through the mid point of p2 p3

5 - The intersection point of L1 and L2 is the center of your circle

This can be easily coded, rather than solving equations.

3pCircle.jpg

Message 7 of 26
_gile
in reply to: FRFR1426

Hi,

 

Sorry for non French speakers but knowing that @FRFR1426  is French I allow myself to give this link to a page in French which shows a geometric construction:

https://debart.pagesperso-orange.fr/seconde/contruc_cercle.html#ch7
I had started trying to do something with the arcs passing through a point and tangent to two straight segments but I did not have time to go further.
http://cadxp.com/topic/46483-creer-un-arc-tangent-a-deux-elements-et-qui-passe-par-un-point-donne/pa...

 



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 8 of 26
alexisgacia
in reply to: doaiena

Thanks for the reply.

 

What I'm planning is to create a function similar to the command Circle (Tan Tan Radius).

 

 

Message 9 of 26
alexisgacia
in reply to: FRFR1426

Hi,

 

I found a solution but in C#. I tried to convert it in VB but failed to do so due to some errors. 

 

https://forums.autodesk.com/t5/net/fillet-in-net/m-p/6814302#M51688

 

If you can manage to convert it in VB please share.

 

Thanks

Message 10 of 26
_gile
in reply to: FRFR1426

It looks that there's something wrong with the Screencast pasting.

Only the upper second one is related to this topic.



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 11 of 26
_gile
in reply to: alexisgacia

Hi @alexisgacia,

 

Circle by Tangent Tangent Radius is much more simple to implement.

You can offset both curves (or segment for polylines) with the radius value, the intersections of offseted curves (if any) are the centers of the possible circles.



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 12 of 26
FRFR1426
in reply to: doaiena

It is not 3 points (which it's easy): it is ONE point and TWO tangents.

Maxence DELANNOY
Manager
Add-ins development for Autodesk software products
http://wiip.fr
Message 13 of 26
FRFR1426
in reply to: hgasty1001

Here is a solution using a solver from the Accord.NET library:

 

 [CommandMethod("CIRCLEPTT")]
public void CircleByPointAndTwoTangents()
{
    Document document = Application.DocumentManager.MdiActiveDocument;
    var cmd = new CircleByPointAndTwoTangentsCommand(document);
    cmd.Execute();
}

 

using System;
using Accord.Math.Differentiation;
using Accord.Math.Optimization;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;

public class CircleByPointAndTwoTangentsCommand
{
    readonly Document document;

    Point3d point;

    Line line;

    Circle circle;

    Point3d targetCircleCenter;

    double targetCircleRadius;

    Circle targetCircle;

    public CircleByPointAndTwoTangentsCommand(Document document)
    {
        this.document = document ?? throw new ArgumentNullException(nameof(document));
    }

    public void Execute()
    {
        using (Transaction transaction =
            document.TransactionManager.StartTransaction())
        {
            PromptPointResult pointResult = document.Editor.GetPoint("\nPoint: ");
            point = pointResult.Value;

            PromptEntityResult lineResult = document.Editor.GetEntity("\nLine: ");
            line = (Line) lineResult.ObjectId.GetObject(OpenMode.ForRead);
            
            PromptEntityResult circleResult = document.Editor.GetEntity("\nCircle: ");
            circle = (Circle) circleResult.ObjectId.GetObject(OpenMode.ForRead);

            // Use CircularArc3d constructor to find an initial guess for the circle by using the 3 picked points.
            using (var arc = new CircularArc3d(point, lineResult.PickedPoint, circleResult.PickedPoint))
            {
                targetCircleCenter = arc.Center;
                targetCircleRadius = arc.Radius;
            }
            targetCircle = new Circle(targetCircleCenter, Vector3d.ZAxis, targetCircleRadius);
            var currentSpace = (BlockTableRecord) transaction.GetObject(document.Database.CurrentSpaceId, 
                OpenMode.ForWrite);
            currentSpace.AppendEntity(targetCircle);
            transaction.AddNewlyCreatedDBObject(targetCircle, add:true);

            const int NUMBER_OF_UNKNOWNS = 3; // Target circle center X, Y and radius.

// I don't remember why I've picked this particular algorithm, may be another one is better
// suited. var algorithm = new BoundedBroydenFletcherGoldfarbShanno(NUMBER_OF_UNKNOWNS, ErrorFunction, Grad(NUMBER_OF_UNKNOWNS, ErrorFunction)); bool minimize = algorithm.Minimize(new[] {targetCircleCenter.X, targetCircleCenter.Y, targetCircleRadius});
if (minimize)
{
targetCircle.Center = targetCircleCenter;
targetCircle.Radius = targetCircleRadius;
} transaction.Commit(); } }
// This function should measure the deviation between the required solution and the current state.
// The returned value must decrease as we get closer to the solution.
// We compute the radius for each of the 3 constraints and we compute the diff with the current
// radius. double ErrorFunction(double[] arg) { targetCircleCenter = new Point3d(arg[0], arg[1], 0); targetCircleRadius = arg[2]; double pointError = targetCircleRadius - targetCircleCenter.DistanceTo(point); Point3d closestPoint = line.GetClosestPointTo(targetCircleCenter, extend: true); Vector3d v = targetCircleCenter.GetVectorTo(closestPoint); double lineError = targetCircleRadius - v.Length; double circleError = targetCircleRadius - targetCircleCenter.DistanceTo(circle.Center) - circle.Radius;
// Squaring the error helps. return pointError * pointError + lineError * lineError + circleError * circleError; }
// Compute the gradient. static Func<double[], double[]> Grad(int nbOfVariables, Func<double[], double> function) { var finiteDifferences = new FiniteDifferences(nbOfVariables, function); return x => finiteDifferences.Gradient(x); } }

 

Maxence DELANNOY
Manager
Add-ins development for Autodesk software products
http://wiip.fr
Message 14 of 26
SEANT61
in reply to: FRFR1426

ScreenCast

 

Adding to a thread that has already been resolved, this post is more for geometric/programmatical curiosity (requiring only AutoCAD APIs). The method leverages the parabola/directrix relationship to find common circles tangent to multiple entities. See attached drawing for illustration.

 

The code could/would benefit from further refinement, and the underlying processing, I believe, would be fast enough o support jigging. For general illustration, though, it should work well enough.

 

See the screen cast for selection sequence.

 

// (C) Copyright 2020 by Sean Tessier 
//
using System;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.EditorInput;


[assembly: CommandClass(typeof(TTP.MyCommands))]

namespace TTP
{

    public class MyCommands
    {

        [CommandMethod("STSCGroup", "TTpT", "TTpTLocal", CommandFlags.Modal)]
        public void OpInit() 
        {
            double initialLen = 100;
            Plane WorldPlane = new Plane();
            Point2d pt;
            Line2d line2;
            CircularArc2d CA2D;
            Point2d CircCen;

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

            PromptPointResult pointResult = ed.GetPoint("\nPoint: ");
            pt = pointResult.Value.Convert2d(WorldPlane);

            using (Transaction tr = doc.TransactionManager.StartTransaction())
            {
                BlockTableRecord curSpace = (BlockTableRecord)tr.GetObject(doc.Database.CurrentSpaceId, OpenMode.ForWrite);
                PromptEntityResult lineResult = ed.GetEntity("\nLine: ");
                Line lineEnt = (Line)lineResult.ObjectId.GetObject(OpenMode.ForRead);
                line2 = new Line2d(lineEnt.StartPoint.Convert2d(WorldPlane), lineEnt.EndPoint.Convert2d(WorldPlane));

                PromptEntityResult circleResult = ed.GetEntity("\nCircle: ");
                Circle circle = (Circle)circleResult.ObjectId.GetObject(OpenMode.ForRead);

                CircCen = circle.Center.Convert2d(WorldPlane);
                CA2D = new CircularArc2d(CircCen, circle.Radius);

                PointOnCurve2d PtLine = line2.GetClosestPointTo(CircCen);
                Vector2d ToFocus = PtLine.Point.GetVectorTo(CircCen);
                double CurrLen = ToFocus.Length;
                Vector2d Normalized = ToFocus.GetNormal();
                CurrLen += CA2D.Radius;

                LineSegment2d PtLineSegment = new LineSegment2d(PtLine.Point, PtLine.Point + (Normalized * CurrLen));

                NurbCurve2d ArcParabFurther = GenVertFocParab(PtLineSegment.MidPoint, CircCen, initialLen);

                CurrLen -= CA2D.Radius * 2;
                PtLineSegment = new LineSegment2d(PtLine.Point, PtLine.Point + (Normalized * CurrLen));
                NurbCurve2d ArcParabNearer = GenVertFocParab(PtLineSegment.MidPoint, CircCen, initialLen);

                PtLine = line2.GetClosestPointTo(pt);
                PtLineSegment = new LineSegment2d(PtLine.Point, pt);

                NurbCurve2d PtParab = GenVertFocParab(PtLineSegment.MidPoint, pt, initialLen);
                
                CurveCurveIntersector2d CCI2D = new CurveCurveIntersector2d(ArcParabFurther, PtParab);
                if (CCI2D.NumberOfIntersectionPoints > 0)
                {
                    for (int i = 0; i < CCI2D.NumberOfIntersectionPoints; i++)
                    {
                        Point2d TTpCen = CCI2D.GetIntersectionPoint(i);
                        ProcessInt(pt, TTpCen, CA2D, tr, curSpace);
                    }
                }

                CCI2D = new CurveCurveIntersector2d(ArcParabNearer, PtParab);
                if (CCI2D.NumberOfIntersectionPoints > 0)
                {
                    for (int i = 0; i < CCI2D.NumberOfIntersectionPoints; i++)
                    {
                        Point2d TTpCen = CCI2D.GetIntersectionPoint(i);
                        ProcessInt(pt, TTpCen, CA2D, tr, curSpace);
                    }
                }
                tr.Commit();
            }
        }


        private static void ProcessInt(Point2d pt, Point2d Intersect, CircularArc2d CA2D, Transaction tr, BlockTableRecord curSpace)
        {
            double NewRad = Intersect.GetDistanceTo(pt);
            Point3d NewCen = new Point3d(Intersect.X, Intersect.Y, 0.0);
            double ToCirc = Intersect.GetDistanceTo(CA2D.Center) + CA2D.Radius;

            Circle Circ = new Circle(NewCen, new Vector3d(0.0, 0.0, 1.0), NewRad);
            Circ.SetDatabaseDefaults();
            curSpace.AppendEntity(Circ);
            tr.AddNewlyCreatedDBObject(Circ, true);
        }


        private NurbCurve2d GenVertFocParab(Point2d Vert, Point2d Focus, double FocOffset)
        {
            KnotCollection KC = new KnotCollection();
            KC.Add(0.0);
            KC.Add(0.0);
            KC.Add(0.0);
            KC.Add(1.0);
            KC.Add(1.0);
            KC.Add(1.0);

            LineSegment2d Pointer = new LineSegment2d(Vert, Focus);
            double Span = Pointer.Length;
            
            Vector2d Dir = Pointer.Direction * FocOffset;
            Vector2d DirPerp = Pointer.Direction.RotateBy(Math.PI/2);
            double HalfWid = GenVal(FocOffset, (Span * 2));
            Point2d MidMouth = Focus + Dir;
            Vector2d Spread = DirPerp * HalfWid;

            Point2dCollection P2DC = new Point2dCollection(3);
            P2DC.Add(MidMouth + Spread);
            P2DC.Add(Vert.ScaleBy(2, MidMouth));
            P2DC.Add(MidMouth - Spread);

            return new NurbCurve2d(2, KC, P2DC, false); ;
        }


        public double GenVal(double HtFromFocus, double FocToDirectrix)
        {
            double Dist = HtFromFocus + FocToDirectrix;
            return Math.Sqrt((Dist * Dist)-(HtFromFocus * HtFromFocus));
        }

    }
}

 

 

 

https://knowledge.autodesk.com/community/screencast/55514c6c-a67d-4904-9df5-cd3d4b9dedc7

 


************************************************************
May your cursor always snap to the location intended.
Message 15 of 26
david.silva
in reply to: SEANT61

Great solution!

Could you please provide some more info on the theory behind it.

Thanks.

Message 16 of 26
david.silva
in reply to: SEANT61

Also, by running your code there's one solution circle missing that results from the intersection between "ParabPoint" and "ParabNear" parabolas.

Message 17 of 26
SEANT61
in reply to: david.silva


@david.silva wrote:

Also, by running your code there's one solution circle missing that results from the intersection between "ParabPoint" and "ParabNear" parabolas.


I can believe that.  My example code was strictly 'proof of concept'.  As a matter of fact, I hardcoded an arbitrary extent for the parabola spline (100 units), so any circle approaching that radius will probably be missed.

 

The general function of the code leverages the Parabola's Focus/Directrix relationship - which is to say that the centers of a circle, tan to two elements, would prescribe a parabola as the radius increases from the minimum.

 

The code does have to determine how to produce the parabola ( behind the scenes as a degree 2 spline in AutoCAD).  To be honest though, the process may be better served by just setting two Vertex/Focus parabolic equation equal to each other.


************************************************************
May your cursor always snap to the location intended.
Message 18 of 26
marko_ribar
in reply to: FRFR1426

I solved it through ALISP though...

BTW. Your code gave me FATAL ERROR...

I am giving kudo to Mr. Sean Tessier for his original approach to this problem...

 

https://en.wikipedia.org/wiki/Problem_of_Apollonius

 

https://youtu.be/YfeLEnhcS18

 

Regards, M.R.

Marko Ribar, d.i.a. (graduated engineer of architecture)
Message 19 of 26
marko_ribar
in reply to: marko_ribar

I saw some lacks in my video on youtube...

Eg. "LLL" drawed only 1 circle and there should be 4... Also "CCL" was wrongly calculated...

 

Here is new link :

https://youtu.be/eKGdBkSzQ6k

 

(If there is only a way to remove previous video... I don't know if this is possible...)

Marko Ribar, d.i.a. (graduated engineer of architecture)
Message 20 of 26
marko_ribar
in reply to: marko_ribar

There were more lacks... Now it should be fine for all cases and it is correctly representing table posted on Wikipedia...

Only problem was CCC - it constructed 6 circles and I manually added 2 more (unknown reasons)...

 

New link :

https://youtu.be/41O8ZItsZaE

 

(Previous 2 links are dead - videos removed by me...)

M.R.

Marko Ribar, d.i.a. (graduated engineer of architecture)

Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums  

Autodesk DevCon in Munich May 28-29th


Autodesk Design & Make Report

”Boost