Performing Boolean operations on AutoCAD solids using .NET

I looked back and couldn't find a post covering this particular topic and so decided to put something together. It may well have been covered elsewhere (I admit to not having looked) but I felt like throwing some code together, either way. 🙂

To perform a Boolean operation between Solid3d objects inside AutoCAD, you can use Solid3d.BooleanOperation() on the primary Solid3d, passing in the secondary one.

We want to implement commands for Unite (or Union), Intersect (Intersection) and Subtract (Subtraction). Only the last of these needs us to select a primary object explicitly – as the other two operations are associative – but we'll go ahead and implement a generic function that takes a separate primary object as an argument. In the case of Unite and Intersect, we'll simply take the first element of the selected solids and pass that in as the primary object (being sure to remove it from the "secondary" list we pass in).

That's really all there is to it. Here's the C# code that defined USOLS, ISOLS and SSOLS command (for union, intersection and subtraction respectively).

using Autodesk.AutoCAD.ApplicationServices;

using Autodesk.AutoCAD.DatabaseServices;

using Autodesk.AutoCAD.EditorInput;

using Autodesk.AutoCAD.Runtime;

using System.Linq;

 

namespace BooleanSolids

{

  public class Commands

  {

    [CommandMethod("USOLS")]

    static public void UniteSolids()

    {

      var doc = Application.DocumentManager.MdiActiveDocument;

 

      var psr =

        SelectSolids(

          doc.Editor,

          "\nSelect solid objects to unite"

        );

      if (psr.Status != PromptStatus.OK)

        return;

 

      if (psr.Value.Count > 1)

      {

        BooleanSolids(

          doc, psr.Value[0].ObjectId, AllButFirst(psr),

          BooleanOperationType.BoolUnite

        );

      }

    }

 

    [CommandMethod("ISOLS")]

    static public void IntersectSolids()

    {

      var doc = Application.DocumentManager.MdiActiveDocument;

 

      var psr =

        SelectSolids(

          doc.Editor,

          "\nSelect solid objects to intersect"

        );

      if (psr.Status != PromptStatus.OK)

        return;

 

      if (psr.Value.Count > 1)

      {

        BooleanSolids(

          doc, psr.Value[0].ObjectId, AllButFirst(psr),

          BooleanOperationType.BoolIntersect

        );

      }

    }

 

    [CommandMethod("SSOLS")]

    static public void SubtractSolids()

    {

      var doc = Application.DocumentManager.MdiActiveDocument;

      var ed = doc.Editor;

 

      var first = SelectSingleSolid(ed, "\nSelect primary solid");

      if (first == ObjectId.Null)

        return;

 

      var psr = SelectSolids(ed, "\nSelect solids to subtract");

      if (psr.Status != PromptStatus.OK)

        return;

 

      if (psr.Value.Count > 0)

      {

        BooleanSolids(

          doc, first, psr.Value.GetObjectIds(),

          BooleanOperationType.BoolSubtract

        );

      }

    }

 

    private static ObjectId SelectSingleSolid(

      Editor ed, string prompt

    )

    {

      var peo = new PromptEntityOptions(prompt);

      peo.SetRejectMessage("\nMust be a 3D solid");

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

 

      var per = ed.GetEntity(peo);

      if (per.Status != PromptStatus.OK)

        return ObjectId.Null;

 

      return per.ObjectId;

    }

 

    private static ObjectId[] AllButFirst(PromptSelectionResult psr)

    {

      // Use LINQ to skip the first item in the IEnumerable

      // and then return the results as an ObjectId array

 

      return

        psr.Value.Cast<SelectedObject>().Skip(1).

          Select(o => { return o.ObjectId; }).ToArray();

    }

 

    private static PromptSelectionResult SelectSolids(

      Editor ed, string prompt

    )

    {

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

 

      var pso = new PromptSelectionOptions();

      pso.MessageForAdding = prompt;

 

      var sf =

        new SelectionFilter(

          new TypedValue[]

          {

            new TypedValue((int)DxfCode.Start, "3DSOLID")

          }

        );

 

      return ed.GetSelection(pso, sf);

    }

 

    private static void BooleanSolids(

      Document doc, ObjectId first, ObjectId[] others,

      BooleanOperationType op

    )

    {

      var tr = doc.TransactionManager.StartTransaction();

      using (tr)

      {

        var sol =

          tr.GetObject(first, OpenMode.ForWrite) as Solid3d;

 

        if (sol != null)

        {

          foreach (ObjectId id in others)

          {

            var sol2 =

              tr.GetObject(id, OpenMode.ForWrite) as Solid3d;

 

            if (sol2 != null)

            {

              sol.BooleanOperation(op, sol2);

            }

          }

        }

        tr.Commit();

      }

    }

  }

}.

9 responses to “Performing Boolean operations on AutoCAD solids using .NET”

  1. Hi Kean,
    I was aware of the Boolean operations, but it's little jewels like the AllButFirst method that help a great deal to better understand Linq. Thanks, and keep up the good work!

  2. Hi Mark,

    I'm glad you found it useful.

    LINQ has got some really helpful features, many of which come from the world of Functional Programming (I tend to work backwards from what I know I can do in F# ;-).

    Kean

  3. Kean,

    I really like your articles about AutoCAD 3D operations, and this is a great one. Thanks.

    I am wondering 1) if there is any way to retrieve complex 3D objects from Autodesk database using ObjectARx? 2)Could those Boolean operations that used to generate complex 3D objects be retrieved as well?

  4. Kean Walmsley Avatar

    Wei,

    My pleasure.

    You can access all kinds of objects using ObjectARX. And for AcDb3dSolids you can use the BRep API to query their topology.

    The issue you may face is likely to be around the history information that's available: even if recorded this isn't something you're likely to be able to get at, unfortunately. That includes this kind of Boolean operation.

    So while you can access an object's current topology, you won't be able to access or manipulate its history.

    Regards,

    Kean

  5. Richard Pilkington Avatar
    Richard Pilkington

    Hi Kean - Where a Boolean 3D subtract operation results in multiple entities, how can you get the objectIds for each resultant entity?
    The simplest case might be that by subtracting solidA from SolidB that the latter is split into 2 entities.
    Many thanks - Richard

  6. Hi Richard,

    You might try handling ObjectAppended for the duration of the command, much as was done in this post:

    keanw.com/2014/09/exploding-nested-autocad-blocks-using-net.html

    Regards,

    Kean

  7. Richard Pilkington Avatar
    Richard Pilkington

    Thank Kean - Really appreciate your expertise and wisdom !
    Richard

  8. I am working with ObjectARX and new to it. I want to do the boolean operations. Do you have a reference code for ObjectARX?

    1. Hi,

      This isn't a support forum.

      Please post your request to the relevant online discussion group (in this case the one for ObjectARX):

      forums.autodesk.com/

      Kean

Leave a Reply to Richard Pilkington Cancel reply

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