How to apply fillet to lwpolyline with C#?

How to apply fillet to lwpolyline with C#?

Anonymous
Not applicable
4,357 Views
8 Replies
Message 1 of 9

How to apply fillet to lwpolyline with C#?

Anonymous
Not applicable

Hi everyone,

 

I am developing in the following environment.

- Autocad control from external windows applications.

- using Dynamic binding.

- Implement ActiveX in C#

 

And, I want to round the corners with a radius of 0.1 as shown below.

 

fillet.png

How can I apply a fillet to a line drawn with lwpolyline?

Please tell me some example code or how to do this.

Thank you.

0 Likes
Accepted solutions (1)
4,358 Views
8 Replies
Replies (8)
Message 2 of 9

_gile
Consultant
Consultant
Accepted solution

Hi,

 

Here're some extension methods, but they use.NET APIs which have no equivalent with ActiveX.
Writing the same thing with Activex would be a quite big task because ActiveX does not provide such geometrical types.
If using a standalone application is absolutely mandatory you can use these extension methods to build a .NET command which suit your needs, load the dll from your standalone application with SendCommand and launch the command with SendCommand too as shown here.

 

 static class Extensions
    {
        /// <summary>
        /// Adds an arc (fillet), if able, at each polyline vertex.
        /// </summary>
        /// <param name="pline">The instance to which the method applies.</param>
        /// <param name="radius">The arc radius.</param>
        public static void FilletAll(this Polyline pline, double radius)
        {
            int n = pline.Closed ? 0 : 1;
            for (int i = n; i < pline.NumberOfVertices - n; i += 1 + pline.FilletAt(i, radius))
            { }
        }

        /// <summary>
        /// Adds an arc (fillet) at the specified vertex.
        /// </summary>
        /// <param name="pline">The instance to which the method applies.</param>
        /// <param name="index">The index of the vertex.</param>
        /// <param name="radius">The arc radius.</param>
        /// <returns>1 if the operation succeeded, 0 if it failed.</returns>
        public static int FilletAt(this Polyline pline, int index, double radius)
        {
            int prev = index == 0 && pline.Closed ? pline.NumberOfVertices - 1 : index - 1;
            if (pline.GetSegmentType(prev) != SegmentType.Line ||
                pline.GetSegmentType(index) != SegmentType.Line)
            {
                return 0;
            }
            LineSegment2d seg1 = pline.GetLineSegment2dAt(prev);
            LineSegment2d seg2 = pline.GetLineSegment2dAt(index);
            Vector2d vec1 = seg1.StartPoint - seg1.EndPoint;
            Vector2d vec2 = seg2.EndPoint - seg2.StartPoint;
            double angle = (Math.PI - vec1.GetAngleTo(vec2)) / 2.0;
            double dist = radius * Math.Tan(angle);
            if (dist == 0.0 || dist > seg1.Length || dist > seg2.Length)
            {
                return 0;
            }
            Point2d pt1 = seg1.EndPoint + vec1.GetNormal() * dist;
            Point2d pt2 = seg2.StartPoint + vec2.GetNormal() * dist;
            double bulge = Math.Tan(angle / 2.0);
            if (Clockwise(seg1.StartPoint, seg1.EndPoint, seg2.EndPoint))
            {
                bulge = -bulge;
            }
            pline.AddVertexAt(index, pt1, bulge, 0.0, 0.0);
            pline.SetPointAt(index + 1, pt2);
            return 1;
        }

        /// <summary>
        /// Evaluates if the points are clockwise.
        /// </summary>
        /// <param name="p1">First point.</param>
        /// <param name="p2">Second point</param>
        /// <param name="p3">Third point</param>
        /// <returns>true if points are clockwise, false otherwise.</returns>
        private static bool Clockwise(Point2d p1, Point2d p2, Point2d p3)
        {
            return ((p2.X - p1.X) * (p3.Y - p1.Y) - (p2.Y - p1.Y) * (p3.X - p1.X)) < 1e-8;
        }
    }


Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 3 of 9

Anonymous
Not applicable

Hi Gilles,

Thank you for your help every time.

 

From your explanation I understood that:

1. Build Extensions as a dll.

2. Load the above dll in my application and use it as a SendCommend.

 

I'm implementing dynamic binding because I want the application to work on multiple versions of AutoCAD.

 

I'm not sure, but I expect the Extensions code to be implemented with the same dynamic binding.

Is it correct? If yes, please help me how to modify the above code.

 

Thanks!

0 Likes
Message 4 of 9

_gile
Consultant
Consultant

Sorry but I do not fully understand what you try ti do.

The first question you have to ask you is: "Do I absolutely need to build a standalone application instead of an AutoCAD plugin (a DLL to be netloaded in AutoCAD) ?" Because, most of the times, using a standalone application (which requires the using of the COM API) makes things more complicated.



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

0 Likes
Message 5 of 9

Anonymous
Not applicable

Thanks for the quick advice.

 

I'm sorry for my lack of understanding.

 

It is very complicated to apply fillet to lwpolyline with ActiveX, and there is a way to SendCommend with .NET API by netload dll as above.

As I can't give up the independent application structure, I have no choise but to use ActiveX.

 

Thank you very much for the answer.

0 Likes
Message 6 of 9

_gile
Consultant
Consultant

Here's a simple example of a .NET plugin defining a command to fillet the selected polylines you can build following this exercice first.

While netloaded in AutoCAD, the FILLETPOLYLINE command can be called as any other native command.

using System;

using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.Runtime;

using AcAp = Autodesk.AutoCAD.ApplicationServices.Core.Application;

[assembly: CommandClass(typeof(FilletPolylineSample.Commands))]

namespace FilletPolylineSample
{
    public class Commands
    {
        [CommandMethod("FILLETPOLYLINE")]
        public static void Test()
        {
            var doc = AcAp.DocumentManager.MdiActiveDocument;
            var db = doc.Database;
            var ed = doc.Editor;

            // prompt the user to select polylines
            var filter = new SelectionFilter(new[] { new TypedValue(0, "LWPOLYLINE") });
            var selection = ed.GetSelection(filter);
            if (selection.Status != PromptStatus.OK)
                return;

            // prompt the user to specify the radius
            var pdo = new PromptDistanceOptions("\nSpecify the radius: ");
            pdo.DefaultValue = (double)AcAp.GetSystemVariable("FILLETRAD");
            var pdr = ed.GetDistance(pdo);
            if (pdr.Status != PromptStatus.OK)
                return;
            AcAp.SetSystemVariable("FILLETRAD", pdr.Value);

            // start a transaction to open and fillet the selected polylines
            using (var tr = db.TransactionManager.StartTransaction())
            {
                foreach (SelectedObject selectedObject in selection.Value)
                {
                    var pline = (Polyline)tr.GetObject(selectedObject.ObjectId, OpenMode.ForWrite);
                    pline.FilletAll(pdr.Value);
                    tr.Commit();
                }
            }
        }
    }

    static class Extensions
    {
        /// <summary>
        /// Adds an arc (fillet), if able, at each polyline vertex.
        /// </summary>
        /// <param name="pline">The instance to which the method applies.</param>
        /// <param name="radius">The arc radius.</param>
        public static void FilletAll(this Polyline pline, double radius)
        {
            int n = pline.Closed ? 0 : 1;
            for (int i = n; i < pline.NumberOfVertices - n; i += 1 + pline.FilletAt(i, radius))
            { }
        }

        /// <summary>
        /// Adds an arc (fillet) at the specified vertex.
        /// </summary>
        /// <param name="pline">The instance to which the method applies.</param>
        /// <param name="index">The index of the vertex.</param>
        /// <param name="radius">The arc radius.</param>
        /// <returns>1 if the operation succeeded, 0 if it failed.</returns>
        public static int FilletAt(this Polyline pline, int index, double radius)
        {
            int prev = index == 0 && pline.Closed ? pline.NumberOfVertices - 1 : index - 1;
            if (pline.GetSegmentType(prev) != SegmentType.Line ||
                pline.GetSegmentType(index) != SegmentType.Line)
            {
                return 0;
            }
            LineSegment2d seg1 = pline.GetLineSegment2dAt(prev);
            LineSegment2d seg2 = pline.GetLineSegment2dAt(index);
            Vector2d vec1 = seg1.StartPoint - seg1.EndPoint;
            Vector2d vec2 = seg2.EndPoint - seg2.StartPoint;
            double angle = (Math.PI - vec1.GetAngleTo(vec2)) / 2.0;
            double dist = radius * Math.Tan(angle);
            if (dist == 0.0 || dist > seg1.Length || dist > seg2.Length)
            {
                return 0;
            }
            Point2d pt1 = seg1.EndPoint + vec1.GetNormal() * dist;
            Point2d pt2 = seg2.StartPoint + vec2.GetNormal() * dist;
            double bulge = Math.Tan(angle / 2.0);
            if (Clockwise(seg1.StartPoint, seg1.EndPoint, seg2.EndPoint))
            {
                bulge = -bulge;
            }
            pline.AddVertexAt(index, pt1, bulge, 0.0, 0.0);
            pline.SetPointAt(index + 1, pt2);
            return 1;
        }

        /// <summary>
        /// Evaluates if the points are clockwise.
        /// </summary>
        /// <param name="p1">First point.</param>
        /// <param name="p2">Second point</param>
        /// <param name="p3">Third point</param>
        /// <returns>true if points are clockwise, false otherwise.</returns>
        private static bool Clockwise(Point2d p1, Point2d p2, Point2d p3)
        {
            return ((p2.X - p1.X) * (p3.Y - p1.Y) - (p2.Y - p1.Y) * (p3.X - p1.X)) < 1e-8;
        }
    }
}

From your external application, you can netload this plugin and launch the FILLETPOLYLINE command with SendCommans. As SendCommand argument is a string (as a .scr file) to pass the command a selection set, you have to first select the polylines you want to fillet by code and use the "_Previous" keyword in the command string.

activeDocument.SendCommand("FILLETPOLYLINE\n_Previous\n\n0.1\n");


Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 7 of 9

iPranavKulkarni
Advocate
Advocate

for rectangle (made by polyline) it is not working for all vertices.

I used like this:

using (Polyline acPoly = new Polyline())
{
acPoly.AddVertexAt(0, new Point2d(0, 0), 0, 0, 0);
acPoly.AddVertexAt(1, new Point2d(width, 0), 0, 0, 0);
acPoly.AddVertexAt(2, new Point2d(width, height), 0, 0, 0);
acPoly.AddVertexAt(3, new Point2d(0, height), 0, 0, 0);
acPoly.AddVertexAt(4, new Point2d(0, 0), 0, 0, 0);
acBlkTblRec.AppendEntity(acPoly);
acPoly.FilletAll(1);
acTrans.AddNewlyCreatedDBObject(acPoly, true);
}

0 Likes
Message 8 of 9

_gile
Consultant
Consultant

Hi,

You should close the polyline instead of setting a 5th point on the first one.

The using statement for the new Polyline is useless as the polyline is always added to the transaction.

var Polyline acPoly = new Polyline();
acPoly.AddVertexAt(0, new Point2d(0, 0), 0, 0, 0);
acPoly.AddVertexAt(1, new Point2d(width, 0), 0, 0, 0);
acPoly.AddVertexAt(2, new Point2d(width, height), 0, 0, 0);
acPoly.AddVertexAt(3, new Point2d(0, height), 0, 0, 0);
acPoly.Closed = true;
acBlkTblRec.AppendEntity(acPoly);
acTrans.AddNewlyCreatedDBObject(acPoly, true);
acPoly.FilletAll(1.0);

 



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 9 of 9

iPranavKulkarni
Advocate
Advocate
Hi, @_gile
Thanks a lot it is working fine.
Yes absolutely right. I am unnecessary adding using statement.
0 Likes