December 2014

Sun Mon Tue Wed Thu Fri Sat
  1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31      










« Displaying a dialog on AutoCAD startup using .NET | Main | Integrating Leap Motion and AutoCAD: Introduction »

January 11, 2013

Creating polyline boundaries from 2D AutoCAD solids using .NET

I had this question come in via a blog comment. I would usually suggest asking this type of question on the relevant online forum, but something about it intrigued me:

Now i would like to ask you that to post an item showing how to get the vertexs of 2d solid to create a boundary, could you add one which shows exporting the entity to other format like .shp.?

My first thought was “wow – someone still uses 2D solid objects inside AutoCAD! I wonder what the Solid object’s .NET API is like?”. (Hopefully this comment doesn’t start a flood of hate-mail from die-hard users of the humble SOLID entity… ;-)

But anyway, for those of you who are new(ish) to the product, the reason 3D AutoCAD solids have the DXF name of 3DSOLID and API classes called AcDb3dSolid and Solid3d is as follows: there is such a thing as a 2D solid in the product. It has been around since the very early, pre-3D days and at the time was used to describe an area containing a solid 2D fill.

The object is kinda quirky: it has either three or four vertices when you draw it, and if you only define three then the fourth will be a copy of the third. The you also don’t define the third and fourth vertices of a 4-vertex solid in the order you’d expect: it doesn’t follow in a loop as if creating a closed polyline, for instance. You have to flip them, otherwise you end up with a bow-tie shape.

So all in all it was actually quite fun to get this working. The code I wrote actually could be considered an EXPLODE for 2D solids – as the EXPLODE command doesn’t work with them, interestingly – but I’ve just implemented it as a command that generates polyline boundaries around the existing 2D solid objects.

Here’s the C# code:

using Autodesk.AutoCAD.ApplicationServices;

using Autodesk.AutoCAD.DatabaseServices;

using Autodesk.AutoCAD.EditorInput;

using Autodesk.AutoCAD.Geometry;

using Autodesk.AutoCAD.Runtime;

 

namespace BoundaryExtraction

{

  public class Commands

  {

    [CommandMethod("SB")]

    static public void SolidBoundaries()

    {

      var doc = Application.DocumentManager.MdiActiveDocument;

      var db = doc.Database;

      var ed = doc.Editor;

 

      // Set up our selection to only select 2D solids

 

      var pso = new PromptSelectionOptions();

      pso.MessageForAdding = "\nSelect 2D solid objects";

 

      var sf =

        new SelectionFilter(

          new TypedValue[]

          {

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

          }

        );

 

      var psr = ed.GetSelection(pso, sf);

 

      if (psr.Status != PromptStatus.OK)

        return;

 

      int boundaries = 0;

 

      var tr = doc.TransactionManager.StartTransaction();

      using (tr)

      {

        foreach (SelectedObject so in psr.Value)

        {

          var sol =

            tr.GetObject(so.ObjectId, OpenMode.ForRead) as Solid;

 

          if (sol != null)

          {

            // We'll collect our points from the Solid

 

            var pts = new Point3dCollection();

 

            // Use a flipped indexing scheme: the 3rd & 4th vertices

            // need to be flipped to go clockwise/anti-clockwise

 

            foreach (var s in new short[] { 0, 1, 3, 2 })

            {

              var pt = sol.GetPointAt(s);

 

              // If we have only 3 points then the 4th is a repeat

              // of the 3rd (which in our case means the point in

              // pts with an index of 1, as we flipped the indices)

 

              if (

                s != 2 ||

                pt.DistanceTo(pts[2]) > Tolerance.Global.EqualPoint

              )

              {

                pts.Add(pt);

              }

            }

 

            // We'll add our replacement boundary to the current

            // space

 

            var btr =

              (BlockTableRecord)tr.GetObject(

                db.CurrentSpaceId,

                OpenMode.ForWrite

              );

 

            // We need a plane to define our polyline's 2D points

 

            var plane = new Plane(pts[0], sol.Normal);

 

            // Create the empty polyline in the plane of the solid

 

            var pl = new Polyline(pts.Count);

            pl.Normal = sol.Normal;

 

            // Fill it with 2D points

 

            for (int i = 0; i < pts.Count; i++)

            {

              // Add our converted 2D point to the vertex list

 

              var pt2 = pts[i].Convert2d(plane);

              pl.AddVertexAt(i, pt2, 0, 0, 0);

            }

 

            // Close the polyline

 

            pl.Closed = true;

 

            // Move it so that it overlaps the Solid

 

            pl.TransformBy(

              Matrix3d.Displacement(pts[0].GetAsVector())

            );

 

            // Add it to the current space and transaction

 

            btr.AppendEntity(pl);

            tr.AddNewlyCreatedDBObject(pl, true);

 

            boundaries++;

          }

        }

        tr.Commit();

      }

 

      ed.WriteMessage(

        "\nCreated boundary polylines for {0} 2D solid objects.",

        boundaries

      );

    }

  }

}

Here are some 2D solid objects inside an AutoCAD drawing, created using the SOLID command:

Some 2D solids in AutoCAD

And here are the results of running the SB command and selecting them (with the currently layer’s lineweight and colour set to values that make the boundaries visible):

Now with their polyline boundaries

I’m still not clear what the part of the comment regarding exporting to .shp is about (whether to AutoCAD’s SHP format or to ESRI’s ShapeFile format), but hopefully it’s clear from this code how it’s possible to access and use the Solid object’s vertex information.

blog comments powered by Disqus

Feed/Share

10 Random Posts