Kean Walmsley

July 2009

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  

Twitter Updates

    follow me on Twitter



    « Autodesk API technology learning resources & webcasts | Main | F# takes an important step closer to official release »

    September 01, 2008

    Traversing a 3D solid's brep using AutoCAD 2009's new .NET API

    In a recent webcast, Gopinath Taget, from our DevTech Americas team, showed how to use the Brep API from a .NET application: something that was made possible in AutoCAD 2009. The Brep API in AutoCAD allows you to traverse the boundary representation of a Solid3d object. Without going into specifics - as this isn't really an area of AutoCAD I've had much reason to use, over the years - I went ahead and took the sample Gopi showed in his webcast and modified it for the purposes of this blog.

    The following C# code traverses the Brep of a selected Solid3d, dumping the information to the command-line. It uses the technique shown in this previous post to retrieve the type of solid we're dealing with via COM.

    using Autodesk.AutoCAD.ApplicationServices;

    using Autodesk.AutoCAD.DatabaseServices;

    using Autodesk.AutoCAD.EditorInput;

    using Autodesk.AutoCAD.Runtime;

    using Autodesk.AutoCAD.BoundaryRepresentation;

    using Autodesk.AutoCAD.Interop.Common;

    using BrFace =

      Autodesk.AutoCAD.BoundaryRepresentation.Face;

     

    namespace BRepTraversal

    {

      public class Commands

      {

        [CommandMethod("TBR")]

        static public void TraverseBRep()

        {

          Document doc =

            Application.DocumentManager.MdiActiveDocument;

          Database db = doc.Database;

          Editor ed = doc.Editor;

     

          Transaction tr =

            db.TransactionManager.StartTransaction();

          using (tr)

          {

            try

            {

              // Prompt for selection of a solid to be traversed

     

              PromptEntityOptions prEntOpt =

                new PromptEntityOptions(

                  "\nSelect a 3D solid:"

                );

              prEntOpt.SetRejectMessage(

                "\nMust be a 3D solid."

              );

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

     

              PromptEntityResult prEntRes =

                ed.GetEntity(prEntOpt);

              ObjectId[] objIds = { prEntRes.ObjectId };

     

              Solid3d sol =

                (Solid3d)tr.GetObject(

                  prEntRes.ObjectId,

                  OpenMode.ForRead

                );

     

              // Use COM to get the solid's type

     

              Acad3DSolid oSol = (Acad3DSolid)sol.AcadObject;

              ed.WriteMessage(

                "\nSolid type: {0}",

                oSol.SolidType

              );

              oSol = null;

     

              // Build the BRep topology object to traverse

     

              Brep brp = new Brep(sol);

              using (brp)

              {

                int cmpCnt = 0;

     

                // Get all the Complexes which are primary BRep

                // elements and represent a conceptual topological

                // entity of connected shell boundaries.

     

                foreach (Complex cmp in brp.Complexes)

                {

                  ed.WriteMessage(

                    "\n  Complex number {0}",

                    ++cmpCnt

                  );

     

                  // Get all the shells within a complex. Shells

                  // are secondary BRep entities that correspond

                  // to a collection of neighboring surfaces on a

                  // solid

     

                  int shlCnt = 0;

     

                  foreach (Shell shl in cmp.Shells)

                  {

                    ed.WriteMessage(

                      "\n    Shell number {0} [{1}]",

                      ++shlCnt,

                      shl.ShellType

                    );

     

                    // Get all the faces in a shell. Faces are

                    // primary BRep topological entities that

                    // directly correspond to face subentities on

                    // AutoCAD entities like solid, region and body

     

                    int fceCnt = 0;

     

                    foreach (BrFace fce in shl.Faces)

                    {

                      ed.WriteMessage(

                        "\n      Face number {0}",

                        ++fceCnt

                      );

     

                      // Get all the boundary loops within a face

                      // (Secondary BRep entities and no corresponding

                      // AutoCAD entities/subentities)

     

                      try

                      {

                        int lpCnt = 0;

     

                        foreach (BoundaryLoop lp in fce.Loops)

                        {

                          ed.WriteMessage(

                            "\n        Loop number {0} [{1}]",

                            ++lpCnt,

                            lp.LoopType

                          );

     

                          // Get all the Edges in a loop (Edges are

                          // primary BRep entities and correspond to

                          // Geometric entities). Output the

     

                          int edgCnt = 0;

     

                          foreach (Edge edg in lp.Edges)

                          {

                            ed.WriteMessage(

                              "\n          Edge number {0}: " +

                              "\n          Vertex 1: {1}" +

                              "\n          Vertex 2: {2}",

                              ++edgCnt,

                              edg.Vertex1.Point,

                              edg.Vertex2.Point

                            );

                          }

                        }

                      }

                      catch

                      {

                        ed.WriteMessage(

                          "\n        Problem getting loops/edges:" +

                          " object is probably unbounded " +

                          "(e.g. a sphere or a torus)."

                        );

                      }

                    }

                  }

                }

              }

              tr.Commit();

            }

            catch (System.Exception ex)

            {

              ed.WriteMessage(

                "\nException during traversal: {0}",

                ex.Message

              );

            }

          }

        }

      }

    }

    Here's what happens when we run the TBR command against a cylinder:

    Command:  TBR

    Select a 3D solid:

    Solid type: Cylinder

      Complex number 1

        Shell number 1 [ShellExterior]

          Face number 1

            Loop number 1 [LoopWinding]

              Edge number 1:

              Vertex 1: (15.0005647466013,4.38174252440491,0)

              Vertex 2: (15.0005647466013,4.38174252440491,0)

            Loop number 2 [LoopWinding]

              Edge number 1:

              Vertex 1: (15.0005647466013,4.38174252440491,5.78051528967497)

              Vertex 2: (15.0005647466013,4.38174252440491,5.78051528967497)

          Face number 2

            Loop number 1 [LoopExterior]

              Edge number 1:

              Vertex 1: (15.0005647466013,4.38174252440491,0)

              Vertex 2: (15.0005647466013,4.38174252440491,0)

          Face number 3

            Loop number 1 [LoopExterior]

              Edge number 1:

              Vertex 1: (15.0005647466013,4.38174252440491,5.78051528967497)

              Vertex 2: (15.0005647466013,4.38174252440491,5.78051528967497)

    One point to note: in the above code I've used a try-catch block around the code to get the loops of a face and the edges of a loop. This is because the GetEnumerator() call on either a loop or an edge (which gets called implicitly by the foreach statement to loop through their respective enumerable objects) can throw an exception in the case where an object is unbounded. Here's an excerpt from the .NET reference material (currently residing in the ObjectARX Reference, which is part of the ObjectARX SDK):

    A topological object may be unbounded (that is, it may have no lower dimensional bounding topology) only in the following cases:

    • A closed surface, which is intrinsically bounded in both the u and v directions (such as a full torus or sphere), is represented by a face that has no loop boundaries.
    • A closed curve, which is intrinsically bounded (such as a full circle or ellipse), is represented by an edge that has coincident start and end vertices.

    For example, here's what happens when we run the TBR command against a torus:

    Command:  TBR

    Select a 3D solid:

    Solid type: Torus

      Complex number 1

        Shell number 1 [ShellExterior]

          Face number 1

            Problem getting loops/edges: object is probably unbounded (e.g. a sphere or a torus).

    So we have, at least, detected the case, even if we have to deal with the overhead of catching an exception rather than checking a property for the number of Loops (something I couldn't work out how to do).

    TrackBack

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

    Listed below are links to weblogs that reference Traversing a 3D solid's brep using AutoCAD 2009's new .NET API:

    Comments

    I've tried to run this code in a loop with some solids.(23/25 3dsolids in a drawing)


    It's runs 10/12 times and then autocad crash.
    Exception Memory error read/write. Very strange.
    OK...if I don't touch the vertex ..it's run without errors.

    the structure is:

    1) Current Drawing with some solids
    2) Selectionset
    3) Foreach objectid in selectionset
    {
    traverserSolid(SolidID)
    }

    Have You the same problem if You run this ?

    so...If You want try take this:


    1) extrude a simple pline (4 edges)
    2) copy it to obtain other solids.(10,12 is not really matter)

    [CommandMethod("ll")]
    public void ll()
    {
    Autodesk.AutoCAD.ApplicationServices.Document d = (Autodesk.AutoCAD.ApplicationServices.Document)Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;

    // obtain your solids from drawing..
    List lstid = B2UtilCad.B2Editor.b2Editor.GetAllObjectsByType(d,"3DSOLID");

    loop...
    for(int i = 0;i <2000;i++)
    {
    d.Editor.WriteMessage(i.ToString()+ "\n");
    foreach(ObjectId id in lstid)
    CommonCAD.ArredilBRep.keansol(d, id);


    }
    }
    and then crash :(

    public static void keansol(Document doc, ObjectId idsolido)
    {
    Database db = doc.Database;
    Transaction tr = db.TransactionManager.StartTransaction();
    Editor ed = doc.Editor;

    using (tr)
    {

    try
    {
    Solid3d sol = (Solid3d)tr.GetObject(idsolido, OpenMode.ForRead);

    // Use COM to get the solid's type
    Acad3DSolid oSol = (Acad3DSolid)sol.AcadObject;
    // Build the BRep topology object to traverse
    Brep brp = new Brep(sol);
    int cmpCnt = 0;

    // Get all the Complexes which are primary BRep

    // elements and represent a conceptual topological

    // entity of connected shell boundaries.

    foreach (Complex cmp in brp.Complexes)
    {

    int shlCnt = 0;

    foreach (Shell shl in cmp.Shells)
    {

    int fceCnt = 0;

    foreach (BrFace fce in shl.Faces)
    {
    // Get all the boundary loops within a face
    // (Secondary BRep entities and no corresponding
    // AutoCAD entities/subentities)

    try
    {

    int lpCnt = 0;

    foreach (BoundaryLoop lp in fce.Loops)
    {

    // Get all the Edges in a loop (Edges are

    // primary BRep entities and correspond to

    // Geometric entities). Output the

    int edgCnt = 0;

    foreach (Edge edg in lp.Edges)
    {

    Point3d p1 = edg.Vertex1.Point;
    ed.WriteMessage(p1.ToString());

    }

    }

    }

    catch
    {

    ed.WriteMessage(
    "\n Problem getting loops/edges:" +

    " object is probably unbounded " +

    "(e.g. a sphere or a torus)."

    );

    }

    }

    }

    }


    tr.Commit();
    }
    catch (System.Exception ex)
    {

    ed.WriteMessage(

    "\nException during traversal: {0}",

    ex.Message

    );

    }

    }
    }

    ok...that's work. loop 2000 times...with 40 3dsolids.


    public static IList PrendiVerticiSolido(Document d, ObjectId idSolido)
    {

    IList ptResult = new List(0);
    try
    {
    using (DocumentLock l = d.LockDocument())
    {
    using (Transaction tr = d.Database.TransactionManager.StartTransaction())
    {
    using (Solid3d sol = tr.GetObject(idSolido, OpenMode.ForRead) as Solid3d)
    {

    //System.Diagnostics.Trace.WriteLine(sol.Handle);
    if (sol == null)
    {
    tr.Abort();
    return ptResult;
    }

    if (sol.IsNull)
    {
    sol.UpgradeOpen();
    sol.Erase();
    tr.Commit();
    sol.Dispose();
    return ptResult;
    }

    using (Brep brp = new Brep(sol))
    {
    BrepComplexEnumerator colcom = brp.Complexes.GetEnumerator();
    do
    {
    Complex complex = colcom.Current;
    ComplexShellEnumerator shllenum = complex.Shells.GetEnumerator();
    do
    {
    Shell shll = shllenum.Current;
    ShellFaceEnumerator shfaceenum = shll.Faces.GetEnumerator();
    do
    {
    Autodesk.AutoCAD.BoundaryRepresentation.Face f = shfaceenum.Current;

    FaceLoopEnumerator flpenum = f.Loops.GetEnumerator();

    do
    {
    BoundaryLoop lp = flpenum.Current;

    LoopEdgeEnumerator edgeenum = lp.Edges.GetEnumerator();
    if (edgeenum != null)
    {
    do
    {

    Edge edge = edgeenum.Current;
    if (edge != null)
    {
    using (Autodesk.AutoCAD.BoundaryRepresentation.Vertex v = edge.Vertex1)
    {
    Point3d p = v.Point;
    if (!ptResult.Contains(p))
    ptResult.Add(p);
    }
    using (Autodesk.AutoCAD.BoundaryRepresentation.Vertex v2 = edge.Vertex2)
    {
    Point3d p2 = v2.Point;
    if (!ptResult.Contains(p2))
    ptResult.Add(p2);
    }
    edge.Dispose();
    edge = null;
    }
    }
    while (edgeenum.MoveNext());
    edgeenum.Dispose();
    edgeenum = null;
    }


    lp.Dispose();
    lp = null;
    }
    while (flpenum.MoveNext());
    flpenum.Dispose();
    flpenum = null;


    f.Dispose();
    f = null;
    }
    while (shfaceenum.MoveNext());
    shfaceenum.Dispose();
    shfaceenum = null;


    shll.Dispose();
    shll = null;
    }
    while (shllenum.MoveNext());
    shllenum.Dispose();
    shllenum = null;


    complex.Dispose();
    complex = null;
    }
    while (colcom.MoveNext());
    colcom.Dispose();

    }
    tr.Commit();
    }
    }

    }
    }
    catch (Autodesk.AutoCAD.BoundaryRepresentation.Exception exp)
    {
    System.Diagnostics.Trace.WriteLine("PrendiVerticiSolido errore " + exp.Message);
    //throw new System.Exception("PrendiVerticiSolido errore " + exp.Message);

    }
    catch (System.Exception ex)
    {
    System.Diagnostics.Trace.WriteLine("PrendiVerticiSolido errore " + ex.Message);
    // throw new System.Exception("PrendiVerticiSolido errore " + ex.Message);
    }
    return ptResult;
    }

    it's a bit slow, but it runs without any error.
    maybe the better way is to write in C++/Arx and then get the resultbuffer as list result.
    But I'll write this another time...now it's midnight!

    Thanks for input, Giuliano.

    From my own tests I found two key omissions from the original code:

    1) We need to set oSol to null to make sure the COM object is released.
    2) We need to Dispose of the brp object (which you have done in your code, but I'd forgotten to do). I'll do this with a using block.

    Adding these operations allows the code to run repeatedly (I tested up to 40K iterations) without problem.

    I've updated the code in the post to reflect these omissions.

    Regards,

    Kean

    ok..as soon as possible i will try..
    all my dispose is just to verify the crash problem..


    Verify your Comment

    Previewing your Comment

    This is only a preview. Your comment has not yet been posted.

    Working...
    Your comment could not be posted. Error type:
    Your comment has been posted. Post another comment

    The letters and numbers you entered did not match the image. Please try again.

    As a final step before posting your comment, enter the letters and numbers you see in the image below. This prevents automated programs from posting comments.

    Having trouble reading this image? View an alternate.

    Working...

    Post a comment

    Feed & Share

    Search