Exploding AutoCAD objects using .NET

Time to go back to basics. I realised recently – on receiving this comment – that I hadn't specifically covered the nature of Entity.Explode() in a post (even if it's been used in a few of them, over the years).

Entity.Explode() is one of those tricky methods: it's actually a faux-ami with the AutoCAD command of the same name, in a very similar way to Database.Purge(). The way in which Explode() and Purge() differ from their "equivalent" commands is that they're non-destructive: they don't actually result in a change to the AutoCAD drawing database. Explode() populates a DBObjectCollection with the results of the explode operation, and it's then up to the caller of the function to add them to the appropriate location (typically the current – i.e. model- or paper- – space). Purge() checks a list of objects for ones that can safely be erased, and then it's up to the caller of the function to do so.

So there is some occasional – and understandable – confusion around Explode() or Purge() not working as expected.

Here's a simple approach to mimicking the behaviour of the EXPLODE command programmatically: after calling Entity.Explode() on each selected entity, we optionally erase the original entities and add the results of the Explode() to the drawing.

There's a lot this code doesn't do – such as check for entities that did not actually generate any exploded results (and so probably shouldn't be erased) or output some feedback on what is happening to the command-line – but the principle should be clear enough.

Here's the C# code:

using Autodesk.AutoCAD.ApplicationServices;

using Autodesk.AutoCAD.DatabaseServices;

using Autodesk.AutoCAD.EditorInput;

using Autodesk.AutoCAD.Runtime;

 

namespace Explosion

{

  public class Commands

  {

    [CommandMethod("EXP", CommandFlags.UsePickSet)]

    public void ExplodeEntities()

    {

      Document doc =

          Application.DocumentManager.MdiActiveDocument;

      Database db = doc.Database;

      Editor ed = doc.Editor;

 

      // Ask user to select entities

 

      PromptSelectionOptions pso =

        new PromptSelectionOptions();

      pso.MessageForAdding = "\nSelect objects to explode: ";

      pso.AllowDuplicates = false;

      pso.AllowSubSelections = true;

      pso.RejectObjectsFromNonCurrentSpace = true;

      pso.RejectObjectsOnLockedLayers = false;

 

      PromptSelectionResult psr = ed.GetSelection(pso);

      if (psr.Status != PromptStatus.OK)

        return;

 

      // Check whether to erase the original(s)

 

      bool eraseOrig = false;

 

      if (psr.Value.Count > 0)

      {

        PromptKeywordOptions pko =

          new PromptKeywordOptions("\nErase original objects?");

        pko.AllowNone = true;

        pko.Keywords.Add("Yes");

        pko.Keywords.Add("No");

        pko.Keywords.Default = "No";

 

        PromptResult pkr = ed.GetKeywords(pko);

        if (pkr.Status != PromptStatus.OK)

          return;

 

        eraseOrig = (pkr.StringResult == "Yes");

      }

 

      Transaction tr =

        db.TransactionManager.StartTransaction();

      using (tr)

      {

        // Collect our exploded objects in a single collection

 

        DBObjectCollection objs = new DBObjectCollection();

 

        // Loop through the selected objects

 

        foreach (SelectedObject so in psr.Value)

        {

          // Open one at a time

 

          Entity ent =

            (Entity)tr.GetObject(

              so.ObjectId,

              OpenMode.ForRead

            );

 

          // Explode the object into our collection

 

          ent.Explode(objs);

 

          // Erase the original, if requested

 

          if (eraseOrig)

          {

            ent.UpgradeOpen();

            ent.Erase();

          }

        }

 

        // Now open the current space in order to

        // add our resultant objects

 

        BlockTableRecord btr =

          (BlockTableRecord)tr.GetObject(

            db.CurrentSpaceId,

            OpenMode.ForWrite

          );

 

        // Add each one of them to the current space

        // and to the transaction

 

        foreach (DBObject obj in objs)

        {

          Entity ent = (Entity)obj;

          btr.AppendEntity(ent);

          tr.AddNewlyCreatedDBObject(ent, true);

        }

 

        // And then we commit

 

        tr.Commit();

      }

    }

  }

} 

The results of calling the EXP command should be obvious enough – and do not result in anything very visually stimulating – so I'll finish the post here.

23 responses to “Exploding AutoCAD objects using .NET”

  1. Hi Kean,
    example is really good cause it´s short as possible. I wonder is there any example to explode ex. Civil 3D Pipe or Struct objects and what kind of objects expode returns? I know that "they" are quite complex objects, but I just like to know. =) Keep on a good site...

  2. Hi Veli,

    I don't use Civil 3D, myself, but hopefully someone else will comment on how well the approach works with its objects.

    Regards,

    Kean

  3. Could you expand this article to handle MTEXT? It would be nice to contrast the Explode and ExplodeFragments methods.

  4. Hi Mike,

    Certainly. The currently implementation does explode MText to DBText, but I can certainly add something to show how to use ExplodeFragments().

    Cheers,

    Kean

  5. amitsaluja@hotmail.com Avatar
    amitsaluja@hotmail.com

    I am looking for some sample to convert autocad files to pdf using C#/.NET

  6. You should take a look at the DWF/PDF Batch Publish Plugin of the Month on Autodesk Labs.

    It makes use of AutoCAD, but then that's a requirement to create quality PDFs from DWG files.

    Kean

  7. Kean,

    A thread over at theswamp had me looking at the differences in behavior between Entity.Explode() and the explode command when the entity is a blockreference. In that case, the user's block has an mtext [not an attribute] with a field. The explode command preserves that field, whereas Entity.Explode() does not.

    Noting that difference, I checked one more item. Geometric constraints in a block reference are preserved by the explode command, but they aren't with Entity.Explode().

    In short, it looks like block references need to be special-cased. Using BlockReference.ExplodeToOwnerSpace() corrects these issues [and I assume DeepCloning the objects from the BTR would, too.]

    -drg

  8. Interesting - thanks, Dan.

    Kean

  9. Hello Kean,

    I've got a question. I want to change a 3d model of a basic structure (Line, Arc, ...) for this I use the command explode. How could it get?

  10. Hi Joaquin,

    Sorry - I'm not sure I understand the question.

    Can you explain a little more?

    Thanks,

    Kean

  11. yes.
    - I mean that your program whit Solid3d, it can get Regions. But I'm trying to get from a Solid3d: lines, arcs, circles and splines. Is that using the explode command several times can be achieved, but I don't know how to get it. This example is helpful, but I can not be done for what I need.

    I'm beginners and your blog has helped me a lot. It's amazing.

    Thank you very much.

  12. A recursive function to explode entities is what's needed.

    This post may be of help:

    keanw.com/2011/02/gathering-points-defining-2d-autocad-geometry-using-net.html

    Kean

  13. Thank you for the code.Great Job.

  14. Matthijs ter Woord Avatar
    Matthijs ter Woord

    Very interesting read. This helped me a lot converting an old VBA-based application to .NET.
    One problem we have though, is that the .Explode call on a BlockReference is not stable. Are there any things to keep in mind?
    (For my issue, see forums.autodesk.com/t5/AutoCAD-Architecture/Unstable-BlockReference-Explode/m-p/3688024)

  15. Hi Matthijs,

    I see you're trying to Explode() an AutoCAD Architecture object. I can imagine that might be view-dependent - I'm not sure how thos entities are implemented, these days.

    I've never had issues with exploding BlockReference objects, from my side.

    Regards,

    Kean

  16. Luigi Ballotta Avatar
    Luigi Ballotta

    Hi Kean, very useful post.
    By the way, once I deleted the block reference using myBlockRef.UpgradeOpen()and myBlockRef.Erase(), there's a way to remove the block from the Block Table of the DB document? There is the necessity to do that, or I can leave the block in the Block Table?

    Thank you

    Luigi

  17. Kean Walmsley Avatar
    Kean Walmsley

    Hi Luigi,

    Sure - you can get the BlockTableRecord from the BlockReference, open it and Erase() it. I suggestpassing it through Database.Purge() first, to make sure there aren't any other references to it.

    But you can also just leave the block there, if you don't mind the extra space it takes up on disk. It won't be visible until it's inserted into the model- or paperspace.

    Regards,

    Kean

  18. Hello Kean, apparently exploding entities through the Entity.Explode() method does not work for mirrored entities, because those have a non-uniform scaling (mirroring applies a negative scale on one of the axes). However, the command line "X" command Works on those mirrored entities. Are there any workarounds for this through the API?

  19. Hi Samir,

    I don't recall, off the top of my head. Have you tried asking either ADN or on the discussion groups?

    Regards,

    Kean

  20. Hi, is there a way to insert a block in a drawing already exploded, similar to the way the INSERT "*blockname" command line would work?

    1. Hi Patrick,

      BlockReference.ExplodeToOwnerSpace() should do what you want (you may need to add a block to the drawing first). I'll try to cover this in a post soon.

      Regards,

      Kean

    2. And here it is... keanw.com/2014/09/exploding-nested-autocad-blocks-using-net.html

      It doesn't exactly cover what you wanted, as yet - just let me know if you want the drawing import piece added in a follow-up post.

      Kean

      1. Thanks Kean, in the meantime I found another solution for my problem, but I keep in mind what you posted here.

Leave a Reply

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