Fillet in .net

Fillet in .net

Yonas89
Collaborator Collaborator
2,686 Views
3 Replies
Message 1 of 4

Fillet in .net

Yonas89
Collaborator
Collaborator

Can someone please share a snippet of .net code for creating a fillet between line and polyline as it's shown in this picture below?

 

Fillet.JPG

 

 

0 Likes
Accepted solutions (1)
2,687 Views
3 Replies
Replies (3)
Message 2 of 4

Anonymous
Not applicable

This is a math problem rather than anything else.

 

A few time ago I was working in a soil engineering project from which we had to do several interactions using circles to find the rupture surface of a soil mass.

 

The project was a Frankenstein of "Math exchange" algorithms and self produced codes but it worked.

 

I just extracted a part of it and did a little adaptation to fit your needings:

 

        [CommandMethod("CircleTangentToCircleAndLine")]
        public static void DrawCircles()
        {
            Document doc = Application.DocumentManager.MdiActiveDocument;
            Editor ed = doc.Editor;
            Database db = doc.Database;

            ObjectId Circleid = ObjectId.Null, Lineid = ObjectId.Null;
            bool isok = false; ;
            while (!isok)
            {
                var selection = ed.GetSelection(new PromptSelectionOptions() { MessageForAdding = "Select only a circle and a line!" });
                if (selection.Status == PromptStatus.Cancel) return;
                if (selection.Value.GetObjectIds().Count() > 2) continue;

                using (Transaction tr = db.TransactionManager.StartTransaction())
                {
                    foreach (ObjectId n in selection.Value.GetObjectIds())
                    {
                        if (n.GetObject(OpenMode.ForRead) as Autodesk.AutoCAD.DatabaseServices.Line != null) Lineid = n;
                        if (n.GetObject(OpenMode.ForRead) as Autodesk.AutoCAD.DatabaseServices.Circle != null) Circleid = n;
                    }
                    tr.Commit();
                }
                if (Circleid != ObjectId.Null && Lineid != ObjectId.Null) isok = true;
            }
            var radius = ed.GetDistance(new PromptDistanceOptions("Enter Radius of the Tangent."));
            if (radius.Status == PromptStatus.Cancel) return;

            using (Transaction tr = db.TransactionManager.StartTransaction())
            {
                var line = Lineid.GetObject(OpenMode.ForRead) as Autodesk.AutoCAD.DatabaseServices.Line;
                var circle = Circleid.GetObject(OpenMode.ForRead) as Autodesk.AutoCAD.DatabaseServices.Circle;

                Line Offset = (new Line(new Point(line.StartPoint.X, line.StartPoint.Y),new Point(line.EndPoint.X, line.EndPoint.Y))).Offset(radius.Value);
                Circle WholeCircle = new Circle(circle.Center.X, circle.Center.Y, circle.Radius + radius.Value);

                KeyValuePair<Point, Point> Interseptions = new KeyValuePair<Point, Point>();
                if (!Offset.TryGetCircleIntersection(WholeCircle, out Interseptions))
                {
                    ed.WriteMessage("No fillet found!\n"); return;
                }

                var currentlayout = LayoutManager.Current.GetLayoutId(LayoutManager.Current.CurrentLayout);
                var btr = (currentlayout.GetObject(OpenMode.ForRead) as Layout).BlockTableRecordId.GetObject(OpenMode.ForWrite) as BlockTableRecord;
                using (var newCircle1 = new Autodesk.AutoCAD.DatabaseServices.Circle())
                using (var newCircle2 = new Autodesk.AutoCAD.DatabaseServices.Circle())
                {
                    newCircle1.Center = new Autodesk.AutoCAD.Geometry.Point3d(Interseptions.Key.x, Interseptions.Key.y, 0);
                    newCircle2.Center = new Autodesk.AutoCAD.Geometry.Point3d(Interseptions.Value.x, Interseptions.Value.y, 0);

                    newCircle1.Radius = radius.Value;
                    newCircle2.Radius = radius.Value;

                    btr.AppendEntity(newCircle1);
                    tr.AddNewlyCreatedDBObject(newCircle1, true);
                    btr.AppendEntity(newCircle2);
                    tr.AddNewlyCreatedDBObject(newCircle2, true);
                }

                tr.Commit();
            }
        }
        internal class Point
        {
            public double x, y;
            public Point(double px, double py)
            {
                x = px;
                y = py;
            }
            public Point sub(Point p2)
            {
                return new Point(x - p2.x, y - p2.y);
            }
            public Point add(Point p2)
            {
                return new Point(x + p2.x, y + p2.y);
            }
            public double distance(Point p2)
            {
                return Math.Sqrt((x - p2.x) * (x - p2.x) + (y - p2.y) * (y - p2.y));
            }
            public Point normal()
            {
                var length = Math.Sqrt(x * x + y * y);
                return new Point(x / length, y / length);
            }
            public Point scale(double s)
            {
                return new Point(x * s, y * s);
            }
            public static Point operator -(Point p1, Point p2)
            {
                return new Point(p1.x - p2.x, p1.y - p2.y);
            }
        };
        internal class Line
        {
            public Point p1, p2;
            public Line(Point p1, Point p2)
            {
                this.p1 = p1;
                this.p2 = p2;
            }
            public Line Offset(double distance)
            {
                var L = Math.Sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y));

                var offsetPixels = distance;

                // This is the second line
                var x1p = p1.x + offsetPixels * (p2.y - p1.y) / L;
                var x2p = p2.x + offsetPixels * (p2.y - p1.y) / L;
                var y1p = p1.y + offsetPixels * (p1.x - p2.x) / L;
                var y2p = p2.y + offsetPixels * (p1.x - p2.x) / L;

                return new Line(new Point(x1p, y1p ), new Point(x2p, y2p));
            }
            public bool TryGetCircleIntersection(Circle c, out KeyValuePair<Point,Point> Interseptions)
            {
                // compute the euclidean distance between A and B
                var LAB = Math.Sqrt(Math.Pow(p2.x - p1.x, 2) + (Math.Pow(p2.y - p1.y, 2)));

                // compute the direction vector D from A to B
                var Dx = (p2.x - p1.x) / LAB;
                var Dy = (p2.y - p1.y) / LAB;

                // Now the line equation is x = Dx*t + Ax, y = Dy*t + Ay with 0 <= t <= 1.

                // compute the value t of the closest point to the circle center (Cx, Cy)
                var t = Dx * (c.x - p1.x) + Dy * (c.y - p1.y);

                // This is the projection of C on the line from A to B.

                // compute the coordinates of the point E on line and closest to C
                var Ex = t * Dx + p1.x;
                var Ey = t * Dy + p1.y;

                // compute the euclidean distance from E to C
                var LEC = Math.Sqrt(Math.Pow(Ex - c.x, 2) + Math.Pow(Ey - c.y, 2));

                // test if the line intersects the circle
                if (LEC < c.r)
                {
                    // compute distance from t to circle intersection point
                    var dt = Math.Sqrt(Math.Pow(c.r, 2) - Math.Pow(LEC, 2));

                    // compute first intersection point
                    var Fx = (t - dt) * Dx + p1.x;
                    var Fy = (t - dt) * Dy + p1.y;

                    // compute second intersection point
                    var Gx = (t + dt) * Dx + p1.x;
                    var Gy = (t + dt) * Dy + p1.y;

                    Interseptions = new KeyValuePair<Point, Point>(new Point(Fx, Fy), new Point(Gx, Gy));
                    return true;
                }
                // else test if the line is tangent to circle
                else if (LEC == c.r)
                {
                    Interseptions = new KeyValuePair<Point, Point>(new Point(Ex, Ey), new Point(Ex, Ey));
                    return true;
                }
                // tangent point to circle is E
                else
                {
                    Interseptions = new KeyValuePair<Point, Point>(new Point(double.NaN, double.NaN), new Point(double.NaN, double.NaN));
                    return false;
                }
                // line doesn't touch circle
            }
        }
        internal class Circle
        {
            public double x, y, r, left;
            public Circle(double cx, double cy, double cr)
            {
                x = cx;
                y = cy;
                r = cr;
                left = x - r;
            }
        };

Currently, it works only for a line and a circle.

It also will draw two circles, instead of a single fillet:

 

demosntration.gif

 

 

 

Nevertheless, the properties the code extracts from this entities are found in polylines and arcs so the code only needs a little adaptation...

 

If I have more time later I'll improve the code.

 

0 Likes
Message 3 of 4

Anonymous
Not applicable
Accepted solution

@Yonas89 I did some improvements and it became a monster but it's working like a charm:

 

Main Method:

        [CommandMethod("GetTangentArc")]
        public static void GetTangentArc()
        {
            Document doc = Application.DocumentManager.MdiActiveDocument;
            Editor ed = doc.Editor;
            Database db = doc.Database;

            var currentlayout = LayoutManager.Current.GetLayoutId(LayoutManager.Current.CurrentLayout);

            ObjectId CurveId = ObjectId.Null, Lineid = ObjectId.Null;
            Point3d CurveIdPickedPoint = new Point3d(), LineidPickedPoint = new Point3d();
            int segmentIndex = 0;

            Point3d CalculatedFilletCenter = new Point3d();
            double Calculatedradius = 0, Calculatedstartangle = 0, Calculatedendangle = 0;
            ObjectId DrawnArc = ObjectId.Null;

            var peo = new PromptEntityOptions("\nSelect the line");
            peo.SetRejectMessage("\nMust select a line!");
            peo.AddAllowedClass(typeof(Autodesk.AutoCAD.DatabaseServices.Line), true);

            var line_Gotten = ed.GetEntity(peo);
            if (line_Gotten.Status == PromptStatus.Cancel || line_Gotten.Status == PromptStatus.Error || line_Gotten.Status == PromptStatus.None) return;
            ed.WriteMessage(line_Gotten.StringResult + "\n");
            Lineid = line_Gotten.ObjectId;
            LineidPickedPoint = line_Gotten.PickedPoint;

            var peo2 = new PromptEntityOptions("\nSelect the Circle, Arc or Polyline Segment");
            peo2.SetRejectMessage("\nMust select a Circle, an Arc or a Polyline Arc Segment!");
            peo2.AddAllowedClass(typeof(Autodesk.AutoCAD.DatabaseServices.Circle), true);
            peo2.AddAllowedClass(typeof(Autodesk.AutoCAD.DatabaseServices.Polyline), true);
            peo2.AddAllowedClass(typeof(Autodesk.AutoCAD.DatabaseServices.Arc), true);
            
            bool isSelectionOk = false;
            while (!isSelectionOk)
            {
                var SelectedCurve = ed.GetEntity(peo2);
                if (SelectedCurve.Status == PromptStatus.Cancel || SelectedCurve.Status == PromptStatus.Error || SelectedCurve.Status == PromptStatus.None) return;
                CurveId = SelectedCurve.ObjectId;
                CurveIdPickedPoint = SelectedCurve.PickedPoint;

                using (Transaction tr = db.TransactionManager.StartTransaction())
                {
                    var curve = CurveId.GetObject(OpenMode.ForRead) as Curve;
                    if (curve as Polyline != null)
                    {
                        var gottenpl = curve as Polyline;

                        var pointOnPline = gottenpl.GetClosestPointTo(CurveIdPickedPoint, false);
                        segmentIndex = (int)gottenpl.GetParameterAtPoint(pointOnPline);

                        SegmentType type = gottenpl.GetSegmentType(segmentIndex);
                        if (type == SegmentType.Arc)
                        {
                            isSelectionOk = true;
                            ed.WriteMessage(SelectedCurve.StringResult + "\n");
                        }
                        else
                            ed.WriteMessage("\nYou Must Select Either a Polyline(arc segment), an Arc or a Circle\n");
                    }
                    else
                    {
                        isSelectionOk = true;
                        ed.WriteMessage(SelectedCurve.StringResult + "\n");
                    }
                    tr.Commit();
                }
            }

            var radius = ed.GetDistance(new PromptDistanceOptions("Enter Radius of the Tangent."));
            if (radius.Status == PromptStatus.Cancel) return;
            ed.WriteMessage(radius.StringResult + "\n");

            var sidepoint = ed.GetPoint(new PromptPointOptions("Pick your side to draw a fillet"));
            if (sidepoint.Status == PromptStatus.Cancel) return;
            ed.WriteMessage(sidepoint.StringResult + "\n");

            using (Transaction tr = db.TransactionManager.StartTransaction())
            {
                var line = Lineid.GetObject(OpenMode.ForRead) as Autodesk.AutoCAD.DatabaseServices.Line;
                var curve = CurveId.GetObject(OpenMode.ForRead);

                Circle circle = null;
                var isCircle = curve as Autodesk.AutoCAD.DatabaseServices.Circle;
                if (isCircle != null)
                {
                    circle = new Circle(isCircle.Center.X, isCircle.Center.Y, isCircle.Radius);
                }
                var isPoly = curve as Autodesk.AutoCAD.DatabaseServices.Polyline;
                if (isPoly != null)
                {
                    CircularArc2d arc2d = isPoly.GetArcSegment2dAt(segmentIndex);

                    Point2d ep2d = arc2d.EvaluatePoint(arc2d.GetInterval().UpperBound);
                    Point2d cp2d = arc2d.Center;

                    circle = new Circle(cp2d.X, cp2d.Y, arc2d.Radius);
                }
                var isArc = curve as Autodesk.AutoCAD.DatabaseServices.Arc;
                if (isArc != null)
                {
                    circle = new Circle(isArc.Center.X, isArc.Center.Y, isArc.Radius);
                }
                if (circle == null) throw new System.Exception("Shoudn't have happened");

                Line PickedLine = null;
                if (line.StartPoint.DistanceTo(CurveIdPickedPoint) < line.EndPoint.DistanceTo(CurveIdPickedPoint))
                {
                    PickedLine = new Line( new Point(line.EndPoint.X, line.EndPoint.Y), new Point(line.StartPoint.X, line.StartPoint.Y));
                }
                else
                {
                    PickedLine = new Line(new Point(line.StartPoint.X, line.StartPoint.Y), new Point(line.EndPoint.X, line.EndPoint.Y));
                }

                Point SidePoint = new Point(sidepoint.Value.X, sidepoint.Value.Y);

                int LeftSideOffset = PickedLine.isPointToTheLeftSide(SidePoint) ?-1: 1;
                Line Offset = PickedLine.Offset(LeftSideOffset * radius.Value );

                Circle WholeCircle = new Circle(circle.Center.x, circle.Center.y, circle.Radius + radius.Value);

                KeyValuePair<Point, Point> Interseptions = new KeyValuePair<Point, Point>();
                if (!Offset.TryGetCircleIntersection(WholeCircle, out Interseptions))
                {
                    ed.WriteMessage("\nNo fillet found!\n"); return;
                }

                var btr = (currentlayout.GetObject(OpenMode.ForRead) as Layout).BlockTableRecordId.GetObject(OpenMode.ForWrite) as BlockTableRecord;

                Point2d CenterPoint2d = LineidPickedPoint.DistanceTo(Interseptions.Key.Point3d) < LineidPickedPoint.DistanceTo(Interseptions.Value.Point3d) ?
                    Interseptions.Key.Point2d :
                    Interseptions.Value.Point2d;
                Point2d StartPoint2d = circle.intersections(new Circle(CenterPoint2d.X, CenterPoint2d.Y, radius.Value)).Key.Point2d;
                if (double.IsNaN(StartPoint2d.X) || double.IsNaN(StartPoint2d.Y))
                {
                    ed.WriteMessage("\nNo fillet found!\n"); return;
                }

                KeyValuePair<Point, Point> EndPointIntersection = new KeyValuePair<Point, Point>();
                if (!PickedLine.TryGetCircleIntersection(new Circle(CenterPoint2d.X, CenterPoint2d.Y, radius.Value), out EndPointIntersection)) { throw new System.Exception("Shoudn't have happened"); }
                Point2d EndPoint2d = EndPointIntersection.Key.Point2d;

                double endAngle  = 0;
                double startAngle = 0;

                switch (LeftSideOffset)
                {
                    case 1:
                        endAngle = (new Line2d(CenterPoint2d, EndPoint2d)).Direction.Angle;
                        startAngle = (new Line2d(CenterPoint2d, StartPoint2d)).Direction.Angle;
                        break;
                    case -1:
                        endAngle = (new Line2d(CenterPoint2d, StartPoint2d)).Direction.Angle;
                        startAngle = (new Line2d(CenterPoint2d, EndPoint2d)).Direction.Angle;
                        break;
                }

                CalculatedFilletCenter = new Point3d(CenterPoint2d.X, CenterPoint2d.Y, 0);
                Calculatedradius = radius.Value;
                Calculatedstartangle = startAngle;
                Calculatedendangle = endAngle;
                ;
                
                using (Arc arc = new Arc(CalculatedFilletCenter, Calculatedradius, Calculatedstartangle, Calculatedendangle))
                {
                    arc.SetDatabaseDefaults(db);

                    DrawnArc = btr.AppendEntity(arc);
                    tr.AddNewlyCreatedDBObject(arc, true);
                }
                tr.Commit();
            }

            PromptKeywordOptions pso = new PromptKeywordOptions("\nGet outer Arc [Yes/No]: ", "Yes No");
            pso.Keywords.Default = "No";
            pso.AppendKeywordsToMessage = true;

            PromptResult pr = ed.GetKeywords(pso);
            ;
            if (pr.StringResult == "Yes")
            {
                using (var tr = db.TransactionManager.StartTransaction())
                {
                    DrawnArc.GetObject(OpenMode.ForWrite).Erase();

                    using (Arc arc = new Arc(CalculatedFilletCenter, Calculatedradius,  Calculatedendangle, Calculatedstartangle))
                    {
                        var btr = (currentlayout.GetObject(OpenMode.ForRead) as Layout).BlockTableRecordId.GetObject(OpenMode.ForWrite) as BlockTableRecord;
                        arc.SetDatabaseDefaults(db);

                        DrawnArc = btr.AppendEntity(arc);
                        tr.AddNewlyCreatedDBObject(arc, true);
                    }
                    tr.Commit();
                }
            }
        }

Nested Classes used for calculation:

        internal class Point
        {
            public double x, y;
            public Point3d Point3d { get { return new Point3d(x, y, 0); } }
            public Point2d Point2d { get { return new Point2d(x, y); } }
            public Point(double px, double py)
            {
                x = px;
                y = py;
            }
            public Point sub(Point p2)
            {
                return new Point(x - p2.x, y - p2.y);
            }
            public Point add(Point p2)
            {
                return new Point(x + p2.x, y + p2.y);
            }
            public double distance(Point p2)
            {
                return Math.Sqrt((x - p2.x) * (x - p2.x) + (y - p2.y) * (y - p2.y));
            }
            public Point normal()
            {
                var length = Math.Sqrt(x * x + y * y);
                return new Point(x / length, y / length);
            }
            public Point scale(double s)
            {
                return new Point(x * s, y * s);
            }
            public static Point operator -(Point p1, Point p2)
            {
                return new Point(p1.x - p2.x, p1.y - p2.y);
            }
        };
        internal class Line
        {
            public Point p1, p2;
            public Line(Point p1, Point p2)
            {
                this.p1 = p1;
                this.p2 = p2;
            }
            public Line Offset(double distance)
            {
                var L = Math.Sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y));

                var offsetPixels = distance;

                // This is the second line
                var x1p = p1.x + offsetPixels * (p2.y - p1.y) / L;
                var x2p = p2.x + offsetPixels * (p2.y - p1.y) / L;
                var y1p = p1.y + offsetPixels * (p1.x - p2.x) / L;
                var y2p = p2.y + offsetPixels * (p1.x - p2.x) / L;

                return new Line(new Point(x1p, y1p ), new Point(x2p, y2p));
            }
            public bool isPointToTheLeftSide(Point c)
            {
                return ((p2.x - p1.x) * (c.y - p1.y) - (p2.y - p1.y) * (c.x - p1.x)) > 0;
            }
            public bool TryGetCircleIntersection(Circle c, out KeyValuePair<Point,Point> Interseptions)
            {
                // compute the euclidean distance between A and B
                var LAB = Math.Sqrt(Math.Pow(p2.x - p1.x, 2) + (Math.Pow(p2.y - p1.y, 2)));

                // compute the direction vector D from A to B
                var Dx = (p2.x - p1.x) / LAB;
                var Dy = (p2.y - p1.y) / LAB;

                // Now the line equation is x = Dx*t + Ax, y = Dy*t + Ay with 0 <= t <= 1.

                // compute the value t of the closest point to the circle center (Cx, Cy)
                var t = Dx * (c.x - p1.x) + Dy * (c.y - p1.y);

                // This is the projection of C on the line from A to B.

                // compute the coordinates of the point E on line and closest to C
                var Ex = t * Dx + p1.x;
                var Ey = t * Dy + p1.y;

                // compute the euclidean distance from E to C
                var LEC = Math.Sqrt(Math.Pow(Ex - c.x, 2) + Math.Pow(Ey - c.y, 2));

                // test if the line intersects the circle
                if (LEC < c.r)
                {
                    // compute distance from t to circle intersection point
                    var dt = Math.Sqrt(Math.Pow(c.r, 2) - Math.Pow(LEC, 2));

                    // compute first intersection point
                    var Fx = (t - dt) * Dx + p1.x;
                    var Fy = (t - dt) * Dy + p1.y;

                    // compute second intersection point
                    var Gx = (t + dt) * Dx + p1.x;
                    var Gy = (t + dt) * Dy + p1.y;

                    Interseptions = new KeyValuePair<Point, Point>(new Point(Fx, Fy), new Point(Gx, Gy));
                    return true;
                }
                // else test if the line is tangent to circle
                else if (Math.Round(LEC,5) == Math.Round(c.r,5))
                {
                    Interseptions = new KeyValuePair<Point, Point>(new Point(Ex, Ey), new Point(Ex, Ey));
                    return true;
                }
                // tangent point to circle is E
                else
                {
                    Interseptions = new KeyValuePair<Point, Point>(new Point(double.NaN, double.NaN), new Point(double.NaN, double.NaN));
                    return false;
                }
                // line doesn't touch circle
            }
        }
        internal class Circle
        {
            public double x, y, r, left;
            public Point Center { get { return new Point(x, y); } }
            public double Radius { get { return r; } }
            public Circle(double cx, double cy, double cr)
            {
                x = cx;
                y = cy;
                r = cr;
                left = x - r;
            }
            public KeyValuePair<Point, Point> intersections(Circle c)
            {
                Point P0 = new Point(x, y);
                Point P1 = new Point(c.x, c.y);
                double d, a, h;
                d = P0.distance(P1);
                a = (r * r - c.r * c.r + d * d) / (2 * d);
                h = Math.Sqrt(r * r - a * a);
                Point P2 = P1.sub(P0).scale(a / d).add(P0);
                double x3, y3, x4, y4;
                x3 = P2.x + h * (P1.y - P0.y) / d;
                y3 = P2.y - h * (P1.x - P0.x) / d;
                x4 = P2.x - h * (P1.y - P0.y) / d;
                y4 = P2.y + h * (P1.x - P0.x) / d;

                return new KeyValuePair<Point, Point>(new Point(x3, y3), new Point(x4, y4));
            }
        };

Demonstration:

 

Message 4 of 4

Yonas89
Collaborator
Collaborator

Thank  you so much  @Anonymous !

0 Likes