Author Topic: Fillet  (Read 1155 times)

0 Members and 1 Guest are viewing this topic.

Offline (gile)

  • C#
  • *
  • Posts: 87
  • Karma: +8/-0
  • Gender: Male
    • prefered language: F
    • Prog expertise: Good
    • View Profile
Fillet
« on: December 24, 2012, 05:05:41 PM »
Hi,

Here're two extension methods for the Polyline class to add an arc (fillet) at the specified vertex or at each vertex.

The main difference between C#, VB and F# is the way to loop for each vertex while adding some new ones: with C# a 'for' loop can be used because the condition is checked at each loop, this must be done with a 'while' loop in VB and the 'natural' way in F# is a tail recursive function.

C#
Code: [Select]
    public static class Extensions
    {
        // Adds an arc (fillet) at each vertex, if able.
        public static void FilletAll(this Polyline pline, double radius)
        {
            int i = pline.Closed ? 0 : 1;
            for (int j = 0; j < pline.NumberOfVertices - i; j += 1 + pline.FilletAt(j, radius))
            { }
        }

       // Adds an arc (fillet) at the specified vertex. Returns 1 if the operation succeeded, 0 if it failed.
        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 > 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;
        }

        // Evaluates if the points are clockwise.
        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;
        }
    }

VB
Code: [Select]
    Module Extensions

        ' Adds an arc (fillet) at each polyline vertex, if able.
        <System.Runtime.CompilerServices.Extension> _
        Public Sub FilletAll(pline As Polyline, radius As Double)
            Dim i As Integer = If(pline.Closed, 0, 1)
            While i < If(pline.Closed, pline.NumberOfVertices, pline.NumberOfVertices - 1)
                i += 1 + pline.FilletAt(i, radius)
            End While
        End Sub

        ' Adds an arc (fillet) at the specified vertex. Retuns 1 if the operation succeeded, 0 if it failed.
        <System.Runtime.CompilerServices.Extension> _
        Public Function FilletAt(pline As Polyline, index As Integer, radius As Double) As Integer
            Dim prev As Integer = If(index = 0 AndAlso pline.Closed, pline.NumberOfVertices - 1, index - 1)
            If pline.GetSegmentType(prev) <> SegmentType.Line OrElse _
                pline.GetSegmentType(index) <> SegmentType.Line Then
                Return 0
            End If
            Dim seg1 As LineSegment2d = pline.GetLineSegment2dAt(prev)
            Dim seg2 As LineSegment2d = pline.GetLineSegment2dAt(index)
            Dim vec1 As Vector2d = seg1.StartPoint - seg1.EndPoint
            Dim vec2 As Vector2d = seg2.EndPoint - seg2.StartPoint
            Dim angle As Double = (Math.PI - vec1.GetAngleTo(vec2)) / 2.0
            Dim dist As Double = radius * Math.Tan(angle)
            If dist > seg1.Length OrElse dist > seg2.Length Then
                Return 0
            End If
            Dim pt1 As Point2d = seg1.EndPoint + vec1.GetNormal() * dist
            Dim pt2 As Point2d = seg2.StartPoint + vec2.GetNormal() * dist
            Dim bulge As Double = Math.Tan(angle / 2.0)
            If Clockwise(seg1.StartPoint, seg1.EndPoint, seg2.EndPoint) Then
                bulge = -bulge
            End If
            pline.AddVertexAt(index, pt1, bulge, 0.0, 0.0)
            pline.SetPointAt(index + 1, pt2)
            Return 1
        End Function

        ' Evaluates if the points are clockwise.
        Private Function Clockwise(p1 As Point2d, p2 As Point2d, p3 As Point2d) As Boolean
            Return ((p2.X - p1.X) * (p3.Y - p1.Y) - (p2.Y - p1.Y) * (p3.X - p1.X)) < 0.00000001
        End Function

    End Module

F#
Code: [Select]
// Evaluates if the points are clockwise.
let clockwise (p1: Point2d) (p2: Point2d) (p3: Point2d) =
    ((p2.X - p1.X) * (p3.Y - p1.Y) - (p2.Y - p1.Y) * (p3.X - p1.X)) < 1e-8

type Polyline with
    // Adds an arc (fillet) at the specified vertex. Retuns 1 if the operation succeeded, 0 if it failed.
    member pl.FilletAt index radius =
        let prev = (if index = 0 && pl.Closed then pl.NumberOfVertices else index) - 1
        if pl.GetSegmentType(prev) <> SegmentType.Line ||
            pl.GetSegmentType(index) <> SegmentType.Line then
            0
        else
            let seg1 = pl.GetLineSegment2dAt(prev)
            let seg2 = pl.GetLineSegment2dAt(index)
            let vec1 = seg1.StartPoint - seg1.EndPoint
            let vec2 = seg2.EndPoint - seg2.StartPoint
            let angle = (Math.PI - vec1.GetAngleTo(vec2)) / 2.
            let dist = radius * tan(angle)
            if dist > seg1.Length || dist > seg2.Length then
                0
            else
                let pt1 = seg1.EndPoint + vec1.GetNormal() * dist
                let pt2 = seg2.StartPoint + vec2.GetNormal() * dist
                let mutable bulge = tan(angle / 2.)
                if clockwise pt1 seg1.EndPoint pt2 then bulge <- -bulge
                pl.AddVertexAt(index, pt1, bulge, 0., 0.)
                pl.SetPointAt(index + 1, pt2)
                1

    // Adds an arc (fillet) at each vertex, if able.
    member pl.FilletAll radius =
        let rec loop i =
            if i < pl.NumberOfVertices then
                loop (i + 1 + pl.FilletAt i radius)
        loop (if pl.Closed then 0 else 1)
« Last Edit: December 25, 2012, 06:49:59 PM by (gile) »

Offline fixo

  • Full Member
  • ***
  • Posts: 135
  • Karma: +4/-0
  • Gender: Male
    • prefered language: C
    • Prog expertise: Good
    • View Profile
Re: Fillet
« Reply #1 on: December 17, 2013, 07:26:51 PM »
There is another way if somebody want to use it
Code: [Select]
Imports System
Imports System.Collections.Generic
Imports System.Linq
Imports System.Text
Imports Autodesk.AutoCAD.ApplicationServices
Imports Autodesk.AutoCAD.DatabaseServices
Imports Autodesk.AutoCAD.Runtime
Imports Autodesk.AutoCAD.Geometry
Imports Autodesk.AutoCAD.EditorInput
Imports System.Runtime.InteropServices
<Assembly: CommandClass(GetType(VBTemplate.PlineCommands))>
Namespace VBTemplate


    Public Class PlineCommands
        'ads_queueexpr
        ' A2010
        ' A2014
        <DllImport("acad.exe", CharSet:=CharSet.Unicode, CallingConvention:=CallingConvention.Cdecl, EntryPoint:="ads_queueexpr")> _
        Private Shared Function ads_queueexpr(command As Byte()) As Integer
        End Function
        '<DllImport("accore.dll", CharSet:=CharSet.Unicode, CallingConvention:=CallingConvention.Cdecl, EntryPoint:="ads_queueexpr")> _
       
        <CommandMethod("opl")> _
        Public Shared Sub TestDrawPolyExm()
            Dim doc As Document = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument

            Dim ed As Editor = doc.Editor

            Dim db As Database = HostApplicationServices.WorkingDatabase

            Dim plineId As ObjectId = ObjectId.Null

            Using doclock As DocumentLock = doc.LockDocument()

                doc.TransactionManager.EnableGraphicsFlush(True)

                Dim cme As Object = Autodesk.AutoCAD.ApplicationServices.Application.GetSystemVariable("cmdecho")

                Using tr As Transaction = db.TransactionManager.StartTransaction()
                    ' Select an object

                    Try
                        Dim btr As BlockTableRecord = TryCast(tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite), BlockTableRecord)

                        Dim plan As New Plane(Point3d.Origin, Vector3d.ZAxis)

                        Dim pts As New Point3dCollection()

                        Dim pline As New Polyline()

                        Dim fstPnt As Point3d = ed.GetPoint(vbLf & "Pick first point: ").Value

                        pts.Add(fstPnt)

                        While True
                            Dim ppo As New PromptPointOptions(vbLf & "Pick Next Point (or hit ""Enter"" to Exit): ")

                            ppo.AllowNone = True

                            Dim ppr As PromptPointResult

                            ppo.UseDashedLine = True

                            ppo.UseBasePoint = True

                            ppo.BasePoint = fstPnt

                            ppr = ed.GetPoint(ppo)

                            If (Not (ppr.Status = PromptStatus.OK)) Or (ppr.Status = PromptStatus.None) Then
                                Exit While
                            End If

                            Dim endPnt As Point3d = ppr.Value

                            pts.Add(endPnt)

                            ed.DrawVector(fstPnt, endPnt, 1, True)

                            fstPnt = endPnt
                        End While

                        Dim i As Integer = 0

                        For i = 0 To pts.Count - 1

                            pline.AddVertexAt(i, pts(i).Convert2d(plan), 0, 0, 0)
                        Next
                        pline.Closed = True

                        pline.ColorIndex = 5

                        plineId = btr.AppendEntity(pline)

                        tr.AddNewlyCreatedDBObject(pline, True)

                        tr.TransactionManager.QueueForGraphicsFlush()

                        ed.SetImpliedSelection(New ObjectId() {plineId})

                        Dim res As PromptSelectionResult = ed.SelectImplied()

                        If res.Status <> PromptStatus.OK Then
                            Return
                        End If


                        Autodesk.AutoCAD.ApplicationServices.Application.SetSystemVariable("cmdecho", 0)

                        Dim uEncode As New UnicodeEncoding()

                        ads_queueexpr(uEncode.GetBytes("(command ""_.FILLETRAD""" & """10""" & ")"))
                        ' <--  set fillet radius to your needs
                        uEncode = New UnicodeEncoding()

                        ads_queueexpr(uEncode.GetBytes("(command ""_.FILLET""" & """_P""" & """_L""" & ")"))

                        uEncode = New UnicodeEncoding()

                        ads_queueexpr(uEncode.GetBytes("(command ""_ZOOM"" ""_OB""" & """_L""" & """""" & ") "))

                        Autodesk.AutoCAD.ApplicationServices.Application.SetSystemVariable("cmdecho", cme)

                        ed.UpdateScreen() 'optional

                        doc.TransactionManager.FlushGraphics()

                        tr.Commit()

                        ed.Regen()
                    Catch ex As System.Exception
                        Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage(ex.Message)
                    Finally

                        Autodesk.AutoCAD.ApplicationServices.Application.SetSystemVariable("cmdecho", cme)
                    End Try
                End Using
            End Using
        End Sub
    End Class
End Namespace
C#_________________
Code: [Select]
       //ads_queueexpr
        [DllImport("acad.exe", CharSet = CharSet.Unicode,
CallingConvention = CallingConvention.Cdecl,
EntryPoint = "ads_queueexpr")]// A2010
//        [DllImport("accore.dll", CharSet = CharSet.Unicode,
//CallingConvention = CallingConvention.Cdecl,
//EntryPoint = "ads_queueexpr")]// A2014
        extern static private int ads_queueexpr(byte[] command);
        [CommandMethod("opl")]
        public static void TestDrawPolyExm()
        {
            Document doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;

            Editor ed = doc.Editor;

            Database db = HostApplicationServices.WorkingDatabase;

            ObjectId plineId = ObjectId.Null;

            using (DocumentLock doclock = doc.LockDocument())
            {
                doc.TransactionManager.EnableGraphicsFlush(true);

                object cme = Autodesk.AutoCAD.ApplicationServices.Application.GetSystemVariable("cmdecho");

                using (Transaction tr = db.TransactionManager.StartTransaction())
                {
                    // Select an object

                    try
                    {
                        BlockTableRecord btr = tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite) as BlockTableRecord;

                        Plane plan = new Plane(Point3d.Origin, Vector3d.ZAxis);

                        Point3dCollection pts = new Point3dCollection();

                        Polyline pline = new Polyline();

                        Point3d fstPnt = ed.GetPoint("\nPick first point: ").Value;

                        pts.Add(fstPnt);

                        while (true)
                        {
                            PromptPointOptions ppo = new PromptPointOptions("\nPick Next Point (or hit \"Enter\" to Exit): ");

                            ppo.AllowNone = true;

                            PromptPointResult ppr;

                            ppo.UseDashedLine = true;

                            ppo.UseBasePoint = true;

                            ppo.BasePoint = fstPnt;

                            ppr = ed.GetPoint(ppo);

                            if ((!(ppr.Status == PromptStatus.OK)) | (ppr.Status == PromptStatus.None))
                            {
                                break;
                            }

                            Point3d endPnt = ppr.Value;

                            pts.Add(endPnt);

                            ed.DrawVector(fstPnt, endPnt, 1, true);

                            fstPnt = endPnt;
                        }

                        int i = 0;

                        for (i = 0; i < pts.Count; i++)
                        {
                            pline.AddVertexAt(i, pts[i].Convert2d(plan), 0, 0, 0);

                        }
                        pline.Closed = true;

                        pline.ColorIndex = 5;

                       plineId= btr.AppendEntity(pline);

                        tr.AddNewlyCreatedDBObject(pline, true);

                        tr.TransactionManager.QueueForGraphicsFlush();

                        ed.SetImpliedSelection(new ObjectId[] { plineId });

                        PromptSelectionResult res = ed.SelectImplied();

                        if (res.Status != PromptStatus.OK)
                        {
                            return;
                        }
                       
                       
                        Autodesk.AutoCAD.ApplicationServices.Application.SetSystemVariable("cmdecho", 0);
                     
                        UnicodeEncoding uEncode = new UnicodeEncoding();

                        ads_queueexpr(uEncode.GetBytes("(command \"_.FILLETRAD\"" + "\"10\"" + ")"));// <--  set fillet radius to your needs

                        uEncode = new UnicodeEncoding();

                        ads_queueexpr(uEncode.GetBytes("(command \"_.FILLET\"" + "\"_P\"" + "\"_L\""  + ")"));

                        uEncode = new UnicodeEncoding();

                        ads_queueexpr(uEncode.GetBytes("(command \"_ZOOM\" \"_OB\"" + "\"_L\"" + "\"\"" + ") "));

                        Autodesk.AutoCAD.ApplicationServices.Application.SetSystemVariable("cmdecho", cme);
                       
                        ed.UpdateScreen();

                        doc.TransactionManager.FlushGraphics();

                        tr.Commit();

                        ed.Regen();
                    }
                    catch (System.Exception ex)
                    {
                        Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage(ex.Message);
                    }
                    finally
                    {
                        Autodesk.AutoCAD.ApplicationServices.Application.SetSystemVariable("cmdecho", cme);
               
                    }
                }
            }
        }