How to code Fillet function when use the c# laHow nguage,please look at the code

How to code Fillet function when use the c# laHow nguage,please look at the code

Anonymous
適用対象外
2,999件の閲覧回数
8件の返信
メッセージ1/9

How to code Fillet function when use the c# laHow nguage,please look at the code

Anonymous
適用対象外

I want to use code to implement cad function fillet,now here is my code

public class Class1
    {
        [DllImport("acad.exe", EntryPoint = "acedCmd", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
        private extern static int acedCmd(IntPtr rbp);

        public static ObjectId toModelSpace(Entity ent)
        {
            Database db = HostApplicationServices.WorkingDatabase;
            ObjectId endId;
            using (Transaction trans = db.TransactionManager.StartTransaction())
            {
                BlockTable bt = (BlockTable)trans.GetObject(db.BlockTableId, OpenMode.ForRead);
                BlockTableRecord btr = (BlockTableRecord)trans.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite);
                endId = btr.AppendEntity(ent);
                trans.AddNewlyCreatedDBObject(ent, true);
                trans.Commit();
            }
            return endId;
        }
        
        public static int AcedCmd(Editor ed, ResultBuffer args)
        {
            if (!Application.DocumentManager.IsApplicationContext)
                return acedCmd(args.UnmanagedObject);
            else
                return 0;
        }
        [CommandMethod("ArxCommand")]
        public void ComCommand() {
            Document doc = Application.DocumentManager.MdiActiveDocument;
            Editor ed = doc.Editor;
            
            Line l1 = new Line(new Point3d(),new Point3d(1000,1000,0));
            Line l2 = new Line(new Point3d(0,400,0),new Point3d(500,0,0));
            toModelSpace(l1);
            toModelSpace(l2);
            
            ResultBuffer rb = new ResultBuffer();
            rb.Add(new TypedValue(5005, "_fillet"));
            rb.Add(new TypedValue(5005, "r"));
            rb.Add(new TypedValue(5001, 10));
            rb.Add(new TypedValue(5006, l1.ObjectId));
            rb.Add(new TypedValue(5006, l2.ObjectId));
            rb.Add(new TypedValue());
            AcedCmd(ed, rb);  
        }
        
    }

After the code runs but does not function,We hope to see where a problem write please,thanks!

0 件のいいね
3,000件の閲覧回数
8件の返信
返信 (8)
メッセージ2/9

sergey.utkin
Enthusiast
Enthusiast

Hello

 

I have tested your code and found 2 issues:

 

1) Are you sure you need to use "acad.exe" in your DllImport? It's "accore.dll" since AutoCAD 2013.

 

2) I set the "FILLETRAD" variable first to overwrite the radius and then "_fillet" command w/o resetting the radius

 Autodesk.AutoCAD.ApplicationServices.Application.SetSystemVariable("FILLETRAD", 10.0);
 ResultBuffer rb = new ResultBuffer();
 rb.Add(new TypedValue(5005, "_fillet"));
 rb.Add(new TypedValue(5006, l1.ObjectId));
 rb.Add(new TypedValue(5006, l2.ObjectId));
 AcedCmd(ed, rb);

the code now runs perfectly

メッセージ3/9

_gile
Consultant
Consultant

Hi,

 

Since AutoCAD 2009 (as I recall) instead of P/Invoking acedCmd(), you can use Tony Tanzillo's wrapper for the non-public Editor.RunCommand() method.

Since AutoCAD 2015 you have to use the new Editor.Command() built-in method.

 

using Autodesk.AutoCAD.ApplicationServices;
using System;
using System.Reflection;

namespace Autodesk.AutoCAD.EditorInput
{
    // From Tony Tanzillo
    // https://www.theswamp.org/index.php?topic=43113.msg483306#msg483306
    public static class MyEditorExtensions
    {
        static MethodInfo runCommand = typeof(Editor).GetMethod(
           "RunCommand", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);

        public static PromptStatus Command(this Editor ed, params object[] args)
        {
            if (Application.DocumentManager.IsApplicationContext)
                throw new InvalidOperationException("Invalid execution context for Command()");
            if (ed.Document != Application.DocumentManager.MdiActiveDocument)
                throw new InvalidOperationException("Document is not active");
            return (PromptStatus)runCommand.Invoke(ed, new object[] { args });
        }
    }
}

 

With both upper methods, you do not need anymore to build a ResultBuffer, just pass .NET typed objects as arguments

 

doc.Database.Filletrad = 10.0;
ed.Command("_.fillet", l1.ObjectId, l2.ObjectId);

 

Note the _FILLET command also accepts points as arguments, in your case (crossing lines) this allows to specify the line side you want to keep:

 

doc.Database.Filletrad = 10.0;
ed.Command("_.fillet", new Point3d(1000, 1000, 0), new Point3d(0, 400, 0));

 



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

メッセージ4/9

Anonymous
適用対象外

以圆角半径为偏移距离分别偏移已知圆和直线,得到辅助圆和辅助直线,其交点即为圆角弧的圆心.以此点为圆心,以圆角半径为半径画第二个辅助圆,其与已知圆和已知直线的交点(其实是切点)即为圆角弧的起点和端点.画好圆角弧后删除辅助直线和两个辅助圆即可.
按不同的偏移方向,最多可能得到8个圆角弧的圆心,具体哪个合适,要根据具体情况判断.

0 件のいいね
メッセージ5/9

Anonymous
適用対象外

but my cad version is 2007,how to release this function

0 件のいいね
メッセージ6/9

_gile
Consultant
Consultant

itxingqing a écrit :

but my cad version is 2007,how to release this function


If so you cannot use the upper methods, you need to P/Invoke acedCmd as you do or build some wrapper to get it more friendly:

 

        [System.Security.SuppressUnmanagedCodeSecurity]
        [DllImport("acad.exe", EntryPoint = "acedCmd",
            CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
        extern static private int acedCmd(IntPtr resbuf);

        /// <summary>
        /// Call an AutoCAD command (works synchronously).
        /// </summary>
        /// <param name="args">The command name followed by command inputs.</param>
        public static void Command(params object[] args)
        {
            ResultBuffer resbuf = new ResultBuffer();
            foreach (object obj in args)
            {
                switch (obj.GetType().Name)
                {
                    case "String":
                        resbuf.Add(new TypedValue(5005, obj)); break;
                    case "Int16":
                        resbuf.Add(new TypedValue(5003, obj)); break;
                    case "Int32":
                        resbuf.Add(new TypedValue(5010, obj)); break;
                    case "Double":
                        resbuf.Add(new TypedValue(5001, obj)); break;
                    case "Point2d":
                        resbuf.Add(new TypedValue(5002, obj)); break;
                    case "Point3d":
                        resbuf.Add(new TypedValue(5009, obj)); break;
                    case "ObjectId":
                        resbuf.Add(new TypedValue(5006, obj)); break;
                    case "ObjectId[]":
                        foreach (ObjectId id in (ObjectId[])obj)
                            resbuf.Add(new TypedValue(5006, id));
                        break;
                    case "ObjectIdCollection":
                        foreach (ObjectId id in (ObjectIdCollection)obj)
                            resbuf.Add(new TypedValue(5006, id));
                        break;
                    case "SelectionSetDelayMarshalled":
                    case "SelectionSetFullyMarshalled":
                        resbuf.Add(new TypedValue(5007, obj)); break;
                    default:
                        throw new InvalidOperationException("Unsupported type in Command() method");
                }
            }
            acedCmd(resbuf.UnmanagedObject);
        }

Then, just do:

 

doc.Database.Filletrad = 10.0;
Command("_.fillet", l1.ObjectId, l2.ObjectId);

or:

 

doc.Database.Filletrad = 10.0;
Command("_.fillet", new Point3d(1000, 1000, 0), new Point3d(0, 400, 0));

 

 



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

0 件のいいね
メッセージ7/9

Anonymous
適用対象外

yes , thanks i have try your answer ,it is work ,thank you very much

0 件のいいね
メッセージ8/9

Anonymous
適用対象外

Old thread, but hopefully you can help. Sometimes when i use the ObjectId method it fillets the wrong end of my line. But when I use the point method, if there are overlapping lines it could fillet the wrong line at the specified point. Do you know if there is a way to include both ObjectIds and points as arguments to ensure the correct objectId and correct side are filleted?

0 件のいいね
メッセージ9/9

_gile
Consultant
Consultant

Hi,

 

Assuming you know the polyline geometry, instead of calling the native command and simulate user inputs, it may be easier to compute the fillet.

You can use these extensions methods from the GeometryExtensions library.

 

    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

0 件のいいね