Generating C# code for selected AutoCAD polylines using .NET

Here's an idea I've been playing around with for some time: say you want to capture geometry as code for pasting back into your application, how do you do it? For instance, sometimes you might want to model geometry using AutoCAD and then capture it as code for later generation at runtime.

I have a specific example in mind, of course: I have some boundary loops that can be used to generate the outer shell of a space shuttle using the surfacing capabilities introduced in AutoCAD 2010. I'd really like to use code to store these loops and – in due course – generate the various surfaces using these loops to define the "lofts". It may even be that the loops don't have to be database-resident, but we'll see that when we come to the actual surface generation.

Here are the loops:

Our polyline loopsTo give you an idea of what I'm aiming for, here are the surfaces created by lofting them together:

Our shuttle shell

Here's some simple C# code that places C# code you can later use to generate the selected polylines onto the clipboard, ready for pasting into a code project (whether the same or a different one):

using Autodesk.AutoCAD.ApplicationServices;

using Autodesk.AutoCAD.Runtime;

using Autodesk.AutoCAD.DatabaseServices;

using Autodesk.AutoCAD.EditorInput;

using Autodesk.AutoCAD.Geometry;

using System.Text;

using System;

 

namespace CopyCodeForEntities

{

  public class Commands

  {

    // 0 - Counter

    // 1 - Vertex count

    // 2,3,4 - Normal - Vector3d(X,Y,Z)

 

    const string header =

      "Polyline pl{0} = new Polyline({1});\r\n" +

      "pl{0}.Normal = new Vector3d({2},{3},{4});\r\n";

 

    // 0 - Counter

    // 1 - Index

    // 2,3 - Point(X,Y)

    // 4 - Bulge

    // 5 - Start width

    // 6 - End width

 

    const string vert =

      "pl{0}.AddVertexAt({1}, new Point2d({2}, {3})," +

      "{4}, {5}, {6});\r\n";

 

    // 0 - Counter

 

    const string closed = "pl{0}.Closed = true;\r\n";

 

    // 0 - Counter

    // 1 - Elevation

 

    const string elev = "pl{0}.Elevation = {1};\r\n";

 

    // 0 - Counter

 

    const string footer =

      "btr.AppendEntity(pl{0});\r\n" +

      "tr.AddNewlyCreatedDBObject(pl{0}, true);\r\n\r\n";

 

    [CommandMethod("CC")]

    public void CopyCode()

    {

      Editor ed =

        Application.DocumentManager.MdiActiveDocument.Editor;

      Database db =

        HostApplicationServices.WorkingDatabase;

      Transaction tr =

        db.TransactionManager.StartTransaction();

      using (tr)

      {

        // Build a filter list so that only

        // polyline are selected

 

        TypedValue[] filList = new TypedValue[1] {

            new TypedValue((int)DxfCode.Start, "LWPOLYLINE")

          };

        SelectionFilter filter =

          new SelectionFilter(filList);

        PromptSelectionOptions opts =

          new PromptSelectionOptions();

        opts.MessageForAdding = "Select polylines: ";

        PromptSelectionResult res =

          ed.GetSelection(opts, filter);

 

        // Do nothing if selection is unsuccessful

 

        if (res.Status != PromptStatus.OK)

          return;

 

        SelectionSet selSet = res.Value;

        ObjectId[
] ids = selSet.GetObjectIds();

        StringBuilder sb = new StringBuilder();

        for (int i=0; i < ids.Length; i++)

        {

          Polyline pl =

            (Polyline)tr.GetObject(

              ids[i],

              OpenMode.ForRead

            );

 

          sb.AppendFormat(

            header, i, pl.NumberOfVertices,

            ZeroIfTiny(pl.Normal.X),

            ZeroIfTiny(pl.Normal.Y),

            ZeroIfTiny(pl.Normal.Z)

          );

 

          if (!NearZero(pl.Elevation))

          {

            sb.AppendFormat(elev, i, pl.Elevation);

          }

 

          for (int j = 0; j < pl.NumberOfVertices; j++)

          {

            Point2d pt = pl.GetPoint2dAt(j);

            double bul = pl.GetBulgeAt(j),

                  sWid = pl.GetStartWidthAt(j),

                  eWid = pl.GetEndWidthAt(j);

            sb.AppendFormat(vert, i, j, pt.X, pt.Y, bul, sWid, eWid);

          }

 

          if (pl.Closed)

          {

            sb.AppendFormat(closed, i);

          }

 

          sb.AppendFormat(

            footer,

            i

          );

        }

 

        ed.WriteMessage(

          "\nCopied C# code for {0} polylines to the clipboard.",

          ids.Length

        );

        System.Windows.Forms.Clipboard.SetText(sb.ToString());

        tr.Commit();

      }

    }

 

    bool NearZero(double x)

    {

      return (Math.Abs(x) <= Tolerance.Global.EqualPoint);

    }

 

    double ZeroIfTiny(double x)

    {

      return (NearZero(x) ? 0.0 : x);

    }

 

    [CommandMethod("PP")]

    public void CreatePolylines()

    {

      Editor ed =

        Application.DocumentManager.MdiActiveDocument.Editor;

      Database db =

        HostApplicationServices.WorkingDatabase;

      Transaction tr =

        db.TransactionManager.StartTransaction();

      using (tr)

      {

        BlockTable bt =

          (BlockTable)tr.GetObject(

            db.BlockTableId,

            OpenMode.ForRead

          );

        BlockTableRecord btr =

          (BlockTableRecord)tr.GetObject(

            bt[BlockTableRecord.ModelSpace],

            OpenMode.ForWrite

          );

 

        // Paste clipboard contents here

 

 

        tr.Commit();

      }

    }

  }

}

When we run the CC command and select the contents of the above drawing, we get code that creates a number of Polyline objects on the clipboard (which can then be pasted at the suggested place in the PP command's implementation). Here's one chosen at random:

Polyline pl36 = new Polyline(7);

pl36.Normal = new Vector3d(0,1,0);

pl36.Elevation = -2.4299;

pl36.AddVertexAt(0, new Point2d(8.02783179363428, -0.295849333699189),0.354906273868793,
0, 0);

pl36.AddVertexAt(1, new Point2d(7.45736328194178, -0.759462222753096),0.0609649900714751, 0, 0);

pl36.AddVertexAt(2, new Point2d(7.45188160777544, -1.53173175298703),0.146023464679161, 0, 0);

pl36.AddVertexAt(3, new Point2d(8.00466801537898, -1.71147878611281),0, 0, 0);

pl36.AddVertexAt(4, new Point2d(8.04787604781722, -1.71152639257886),0.146023464679161, 0, 0);

pl36.AddVertexAt(5, new Point2d(8.60105720218827, -1.53299791082467),0.0609649900714751, 0, 0);

pl36.AddVertexAt(6, new Point2d(8.59727730725114, -0.760718176224785),0.354906273868755, 0, 0);

pl36.Closed = true;

btr.AppendEntity(pl36);

tr.AddNewlyCreatedDBObject(pl36, true);

 

Clearly there are some objects needed by this code, such as btr and tr. The PP command sets these up appropriately. If the code gets pasted in and we use this command, we see our Polyline loop get recreated appropriately:

Our recreated loopsThere may be things that I'm missing, but this worked well with the geometry I needed to capture. We'll see later on whether we can then use that to programmatically generate surfaces.

6 responses to “Generating C# code for selected AutoCAD polylines using .NET”

  1. Hi Kean,

    would that work for 3D solids and Surfaces(Meshes, NURBS, analytic etc...)?
    A simple code example would be then very helpful!

    Thanks

  2. Hi Konstantin,

    With this approach you have to encode the information needed to generate the object in a string. For certain objects capturing and reusing this is hard, which is why I chose to focus on Polylines (as on the one hand they're popular, they're relatively simple and can be used to build more complex objects, such as surfaces).

    Regards,

    Kean

  3. John Holmes; ADN #3908 Avatar
    John Holmes; ADN #3908

    This is the best tool created so far! I think with a little work, this could be the plug-in of the month. Just add a few other types, and we are in bussiness! Thanks Kean

    HomeBoy Out

  4. Wooow!!! Ingenious code!!!

    THX Kean

  5. Hello kean
    I want to know how to find a polyline equation in order to calculate an intersection

  6. There is no equation, as such: you need to open the Polyline entity and call IntersectWith(), passing in the entity to check against.

    Kean

Leave a Reply to John Holmes; ADN #3908 Cancel reply

Your email address will not be published. Required fields are marked *