Kean Walmsley


  • About the Author
    Kean on Google+

August 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            








« Sweeping an AutoCAD surface using .NET | Main | Generating fractals inside AutoCAD using F# »

May 26, 2008

Sectioning an AutoCAD solid using .NET

In the last post we saw how to access some of the 3D modeling functionality introduced in AutoCAD 2007. This post continues that theme, by looking at how to section a Solid3d object programmatically inside AutoCAD. Thanks to Wayne Brill, from DevTech Americas, for providing the original code that inspired this post.

Here's the C# code:

using Autodesk.AutoCAD.ApplicationServices;

using Autodesk.AutoCAD.DatabaseServices;

using Autodesk.AutoCAD.EditorInput;

using Autodesk.AutoCAD.Runtime;

using Autodesk.AutoCAD.Geometry;

using System;


namespace SolidSection

{

  public class Commands

  {

    [CommandMethod("SS")]

    public void SectionSolid()

    {

      Document doc =

        Application.DocumentManager.MdiActiveDocument;

      Database db = doc.Database;

      Editor ed = doc.Editor;


      // Ask the user to select an entity to section


      PromptEntityOptions peo =

        new PromptEntityOptions(

          "\nSelect entity to section: "

        );


      peo.SetRejectMessage(

        "\nEntity must be a 3D solid, " +

        "surface, body or region."

      );

      peo.AddAllowedClass(typeof(Solid3d), false);

      peo.AddAllowedClass(

        typeof(Autodesk.AutoCAD.DatabaseServices.Surface),

        false

      );

      peo.AddAllowedClass(typeof(Body), false);

      peo.AddAllowedClass(typeof(Region), false);


      PromptEntityResult per =

        ed.GetEntity(peo);


      if (per.Status != PromptStatus.OK)

        return;


      ObjectId entId =

        per.ObjectId;


      // Ask the user to define a section plane


      Point3dCollection pts =

        new Point3dCollection();


      PromptPointResult ppr =

        ed.GetPoint("\nPick first point for section: ");


      if (ppr.Status != PromptStatus.OK)

        return;


      pts.Add(ppr.Value);


      PromptPointOptions ppo =

        new PromptPointOptions(

          "\nPick end point for section: "

        );

      ppo.BasePoint = ppr.Value;

      ppo.UseBasePoint = true;


      ppr =

        ed.GetPoint(ppo);


      if (ppr.Status != PromptStatus.OK)

        return;


      pts.Add(ppr.Value);


      // Ask what type of section to create


      PromptKeywordOptions pko =

        new PromptKeywordOptions(

          "Enter section type "

        );

      pko.AllowNone = true;

      pko.Keywords.Add("2D");

      pko.Keywords.Add("3D");

      pko.Keywords.Add("Live");

      pko.Keywords.Default = "3D";


      PromptResult pkr =

        ed.GetKeywords(pko);


      if (pkr.Status != PromptStatus.OK)

        return;


      SectionType st;

      if (pkr.StringResult == "2D")

        st = SectionType.Section2d;

      else if (pkr.StringResult == "Live")

        st = SectionType.LiveSection;

      else // pkr.StringResult == "3D"

        st = SectionType.Section3d;


      // Now we're ready to do the real work


      Transaction tr =

        db.TransactionManager.StartTransaction();

      using (tr)

      {

        try

        {

          BlockTable bt =

            (BlockTable)tr.GetObject(

              db.BlockTableId,

              OpenMode.ForRead

            );

          BlockTableRecord ms =

            (BlockTableRecord)tr.GetObject(

              bt[BlockTableRecord.ModelSpace],

              OpenMode.ForWrite

            );


          // Now let's create our section


          Section sec =

            new Section(pts, Vector3d.ZAxis);

          sec.State = SectionState.Plane;


          // The section must be added to the drawing


          ObjectId secId =

            ms.AppendEntity(sec);

          tr.AddNewlyCreatedDBObject(sec, true);


          // Set up some of its direct properties


          sec.SetHeight(

            SectionHeight.HeightAboveSectionLine,

            3.0

          );

          sec.SetHeight(

            SectionHeight.HeightBelowSectionLine,

            1.0

          );


          // ... and then its settings


          SectionSettings ss =

            (SectionSettings)tr.GetObject(

              sec.Settings,

              OpenMode.ForWrite

            );


          // Set our section type


          ss.CurrentSectionType = st;


          // We only set one additional option if "Live"


          if (st == SectionType.LiveSection)

            sec.EnableLiveSection(true);

          else

          {

            // Non-live (i.e. 2D or 3D) settings


            ObjectIdCollection oic =

              new ObjectIdCollection();

            oic.Add(entId);


            ss.SetSourceObjects(st, oic);


            if (st == SectionType.Section2d)

            {

              // 2D-specific settings


              ss.SetVisibility(

                st,

                SectionGeometry.BackgroundGeometry,

                true

              );

              ss.SetHiddenLine(

                st,

                SectionGeometry.BackgroundGeometry,

                false

              );

            }

            else if (st == SectionType.Section3d)

            {

              // 3D-specific settings


              ss.SetVisibility(

                st,

                SectionGeometry.ForegroundGeometry,

                true

              );

            }


            // Finish up the common 2D/3D settings


            ss.SetGenerationOptions(

              st,

              SectionGeneration.SourceSelectedObjects |

              SectionGeneration.DestinationFile

            );

          }


          // Open up the main entity


          Entity ent =

            (Entity)tr.GetObject(

              entId,

              OpenMode.ForRead

            );


          // Generate the section geometry


          Array flEnts, bgEnts, fgEnts, ftEnts, ctEnts;

          sec.GenerateSectionGeometry(

            ent,

            out flEnts,

            out bgEnts,

            out fgEnts,

            out ftEnts,

            out ctEnts

          );


          // Add the geometry to the modelspace

          // (start by combining the various arrays,

          // so we then have one loop, not four)


          int numEnts =

            flEnts.Length + fgEnts.Length +

            bgEnts.Length + ftEnts.Length +

            ctEnts.Length;


          // Create the appropriately-sized array


          Array ents =

          Array.CreateInstance(

            typeof(Entity),

            numEnts

          );


          // Copy across the contents of the

          // various arrays


          int index = 0;

          flEnts.CopyTo(ents, index);

          index += flEnts.Length;

          fgEnts.CopyTo(ents, index);

          index += fgEnts.Length;

          bgEnts.CopyTo(ents, index);

          index += bgEnts.Length;

          ftEnts.CopyTo(ents, index);

          index += ftEnts.Length;

          ctEnts.CopyTo(ents, index);


          // Our single loop to add entities


          foreach (Entity ent2 in ents)

          {

            ms.AppendEntity(ent2);

            tr.AddNewlyCreatedDBObject(ent2, true);

          }


          tr.Commit();

        }

        catch (System.Exception ex)

        {

          ed.WriteMessage(

            "\nException: " + ex.Message

          );

        }

      }

    }

  }

}

To see the results of the various options in the SS command, I created three identical spheres in an empty drawing:

Spheres to Section

I then used the SS command, selecting each sphere in turn and selecting a similar section line for each (as close as I could get without measuring), choosing, of course, a different command option each time (2D, 3D and Live, from left to right):

Sectioned Spheres - Plan

Orbitting this view, we see the section planes for each sphere:

Sectioned Spheres - Orbitting

The objects we've added to the drawing for the two left-hand sections are basic (2D or 3D, depending) geometry. The third, however, includes a section object:

Sectioned Spheres - Live Section

A quick note on the code at the end which adds the various generated geometry to the drawing: in order to avoid having multiple foreach loops (one for each of flEnts, fgEnts, bgEnts, ftEnts & ctEnts), I opted to create an über-array which then gets populated by the contents of each of the other lists. This simple exercise was a pain in C#, as you can see from the code. In fact, having five separate loops could probably be considered less ugly, depending on your perspective. This is the kind of operation that's a breeze in a language like F#, and, with hindsight, I probably should have chosen F# from the beginning for just that reason. Maybe I'll throw an F# version together for comparison's sake.

Update

In AutoCAD 2010, Section.EnableLiveSection(bool) has become a Boolean property. For the above code to work in AutoCAD 2010, change the line containing the call to sec.EnableLiveSection(true) to:

sec.IsLiveSectionEnabled = true;

TrackBack

TrackBack URL for this entry:
http://www.typepad.com/services/trackback/6a00d83452464869e200e5529707438834

Listed below are links to weblogs that reference Sectioning an AutoCAD solid using .NET:

blog comments powered by Disqus

10 Random Posts