October 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  










« New APIs in AutoCAD 2012 | Main | Displaying transient AutoCAD points that respect PDMODE using .NET »

March 25, 2011

Generating a mesh for a 3D solid using AutoCAD’s Brep API from .NET

Thanks for Balaji Ramamoorthy – who recently joined our team in India – and Adam Nagy for helping generate the code for this post.

There are lots of reasons people might want to tessellate a 3D solid in AutoCAD to generate a mesh. The code in today’s post uses the Boundary Representation (Brep) API in AutoCAD to do just that, generating a set of 3D faces.

A few points about the implementation:

  • I’ve only made a small number of settings to control the mesh generation: more are available for you to experiment with.
  • It should be simple enough to generate a SubDMesh, rather than a set of faces, but that’s left as an exercise for the reader.
  • I only tested with a very simple solid (a sphere). The settings may need tweaking depending on the complexity of the solid you’re meshing.

Here’s the C# code:

using Autodesk.AutoCAD.ApplicationServices;

using Autodesk.AutoCAD.BoundaryRepresentation;

using Autodesk.AutoCAD.DatabaseServices;

using Autodesk.AutoCAD.EditorInput;

using Autodesk.AutoCAD.Geometry;

using Autodesk.AutoCAD.Runtime;

using AcDb = Autodesk.AutoCAD.DatabaseServices;

 

namespace MeshSolid

{

  public class MeshCreator

  {

    // Mesh a selected solid

 

    [CommandMethod("SOLMESH")]

    static public void MeshFromSolid()

    {

      Document doc =

        Application.DocumentManager.MdiActiveDocument;

      Database db = doc.Database;

      Editor ed = doc.Editor;

 

      // Ask the user to select a solid

 

      PromptEntityOptions peo =

        new PromptEntityOptions("Select a 3D solid");

      peo.SetRejectMessage("\nA 3D solid must be selected.");

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

      PromptEntityResult per = ed.GetEntity(peo);

 

      if (per.Status != PromptStatus.OK)

        return;

 

      Transaction tr =

        db.TransactionManager.StartTransaction();

      using (tr)

      {

        BlockTable bt =

          (BlockTable)tr.GetObject(

            db.BlockTableId,

            OpenMode.ForRead,

            false

          );

        BlockTableRecord btr =

          (BlockTableRecord)tr.GetObject(

            bt[BlockTableRecord.ModelSpace],

            OpenMode.ForWrite,

            false

          );

 

        Solid3d sol =

          tr.GetObject(

            per.ObjectId,

            OpenMode.ForRead

          ) as Solid3d;

 

        // Calculate the approximate size of our solid

 

        double length =

          sol.GeometricExtents.MinPoint.GetVectorTo(

            sol.GeometricExtents.MaxPoint

          ).Length;

 

        try

        {

          using (Brep brp = new Brep(sol))

          {

            // Create and set our mesh control object

 

            using (Mesh2dControl mc = new Mesh2dControl())

            {

              // These settings seem extreme, but only result

              // in ~500 faces for a sphere (during my testing,

              // anyway). Other control settings are available

 

              mc.MaxNodeSpacing = length / 10000;

              mc.MaxSubdivisions = 100000000;

 

              // Create a mesh filter object

 

              using (Mesh2dFilter mf = new Mesh2dFilter())

              {

                // Use it to map our control settings to the Brep

 

                mf.Insert(brp, mc);

 

                // Generate a mesh using the filter

 

                using (Mesh2d m = new Mesh2d(mf))

                {

                  // Extract individual faces from the mesh data

 

                  foreach (Element2d e in m.Element2ds)

                  {

                    Point3dCollection pts = new Point3dCollection();

                    foreach (Node n in e.Nodes)

                    {

                      pts.Add(n.Point);

                      n.Dispose();

                    }

                    e.Dispose();

 

                    // A face could be a triangle or a quadrilateral

                    // (the Booleans indicate the edge visibility)

 

                    AcDb.Face face = null;

                    if (pts.Count == 3)

                      face =

                        new AcDb.Face(

                          pts[0], pts[1], pts[2],

                          true, true, true, true

                        );

                    else if (pts.Count == 4)

                      face =

                        new AcDb.Face(

                          pts[0], pts[1], pts[2], pts[3],

                          true, true, true, true

                        );

 

                    // If we have a valid face, add it to the

                    // database and the transaction

 

                    if (face != null)

                    {

                      // Make each face yellow for visibility

 

                      face.ColorIndex = 2;

 

                      btr.AppendEntity(face);

                      tr.AddNewlyCreatedDBObject(face, true);

                    }

                  }

                }

              }

            }

          }

          tr.Commit();

        }

        catch (System.Exception ex)

        {

          ed.WriteMessage("Exception: {0}", ex.Message);

        }

      }

    }

  }

}

 

Here’s what happens when we run the SOLMESH command, selecting a simple sphere (which I later moved to the side, for comparison):

Meshed version of a sphere

And now with the realistic visual style set, just to show we have opacity:

And now in the realistic visual style

Update:

As mentioned in the comments, the code was randomly crashing on larger models. Viru Aithal and Balaji Ramamoorthy tracked down the problem, which is due to a common issue with AutoCAD objects: the enumerators for Mesh2dElement2dCollection and Element2dNodeCollection both return newly-created objects (a Node and an Element2d, respectively) which need to be disposed of before the .NET Finalizer (which works on a background thread) attempts to do so for you. Two lines (with calls to n.Dispose() and e.Dispose()) have been added to the above code to address this issue.

blog comments powered by Disqus

Feed/Share

10 Random Posts