Getting the extents of an AutoCAD group using .NET

Here's an interesting question that came in from Nick Gilbert via a blog comment:

Is there a simple way to get the geometric extents of the group?

As discussed in this previous post, the Group object in AutoCAD presents itself as a collection of geometric objects (or entities, in ObjectARX-parlance). But a Group, in itself, isn't a geometric object: it's an aggregator of objects that are.

So there isn't – as Nick is clearly aware – a simple GeometricExtents property of the Group object. But we can access the contents of the Group and combine their various GeometricExtents, returning an overall Extents3d for the group.

There's a few ways I could have done this… I ended up implementing an extension method on the Transaction class to get the extents of an array of ObjectIds. This seemed the most likely to be reusable for other situations. Then we just call the extension method with the selected Group's contents and report the results.

Here's the C# code:

using Autodesk.AutoCAD.ApplicationServices;

using Autodesk.AutoCAD.DatabaseServices;

using Autodesk.AutoCAD.EditorInput;

using Autodesk.AutoCAD.Runtime;

using System.Collections.Generic;

 

namespace GroupExtents

{

  public static class TransactionExtensions

  {

    // A simple extension method that aggregates the extents of any entities

    // passed in (via their ObjectIds)

 

    public static Extents3d GetExtents(this Transaction tr, ObjectId[] ids)

    {

      var ext = new Extents3d();

      foreach (var id in ids)

      {

        var ent = tr.GetObject(id, OpenMode.ForRead) as Entity;

        if (ent != null)

        {

          ext.AddExtents(ent.GeometricExtents);

        }

      }

      return ext;

    }

  }

 

  public class Commands

  {

    [CommandMethod("GE")]

    public void GroupExtents()

    {

      var doc = Application.DocumentManager.MdiActiveDocument;

      var db = doc.Database;

      var ed = doc.Editor;

 

      using (var tr = db.TransactionManager.StartTransaction())

      {

        // Get the group dictionary from the drawing

 

        var gd =

          (DBDictionary)tr.GetObject(db.GroupDictionaryId, OpenMode.ForRead);

 

        if (gd.Count == 0)

        {

          ed.WriteMessage("\nNo groups found in drawing.");

        }

        else

        {

          // List the groups in the drawing with an index

 

          var groupNames = new List<string>(gd.Count);

 

          ed.WriteMessage("\nGroups:");

          int i = 0;

          foreach (var entry in gd)

          {

            i++;

       &
#160;    ed.WriteMessage("\n{0}. {1}", i, entry.Key);

            groupNames.Add(entry.Key);

 

          }

 

          // Ask the user to select a group number

 

          var pio = new PromptIntegerOptions("\nEnter group index");

          pio.AllowNegative = false;

          pio.AllowZero = false;

          pio.DefaultValue = 1;

          pio.LowerLimit = 1;

          pio.UpperLimit = i;

 

          var pir = ed.GetInteger(pio);

          if (pir.Status == PromptStatus.OK)

          {

            // Get the selected group

 

            var grp =

              tr.GetObject(

                (ObjectId)gd[groupNames[pir.Value-1]], OpenMode.ForRead

              ) as Group;

 

            if (grp != null)

            {

              // Call our extension method to get the extents of the group's

              // referenced objects

 

              var ext = tr.GetExtents(grp.GetAllEntityIds());

 

              // Print the information for the user

 

              ed.WriteMessage(

                "\nGroup's extents are from {0} to {1}.",

                ext.MinPoint, ext.MaxPoint

              );

            }

          }

        }

 

        // Commit the transaction

 

        tr.Commit();

      }

    }

  }

}

 

I tested it with a fairly large 3D drawing (this contains 6,635 spheres) and there was no perceived delay for the calculation.

Apollonian packing grouped together

Here's what was reported at the command-line:

Command: GE

Groups:

1. *A1

Enter group index <1>: 1

Group's extents are from (-0.997431989660447,-0.997431989660447,-0.997431989660447) to (0.997431989660447,0.997431989660447,0.997431989660447).

 

Which for this model is correct (to save people the effort of hand-verifying the results ;-).

16 responses to “Getting the extents of an AutoCAD group using .NET”

  1. Thanks so much Kean! Glad you brought up that there was no delay in the calculation too because I wrote a method which got the geometric extents by manually checking every point of every entity in a group for the overall min and max points. It worked but it ran wicked slowly. This was exactly what I needed

    1. James Maeding Avatar

      Nick,
      I have a similar situation where I need to find entities that cross each other. I calc the bounding box of each and store in a list, then sort the list to avoid doing the expensive geometry crossing test if not needed. All I am getting at is if you make a list of x and y's, then sort and get low and high numbers, that is generally fast enough for any use. Use linq OrderBy method when sorting as it seems a couple times faster than other methods. Having said that, I expect the built in acad stuff to be faster but then you are tied to an acad session for your program.

  2. James Maeding Avatar

    Hi Kean, Civil engineers commonly need to find what you could call a shrink wrap boundary, which is the convex hull for areas where lines and arcs do not form a closed shape, but where they do, it "sticks" to the shape. Civil3d has a command to do that, but I need to do it in memory so I can compare if a block sticks out of a given trim boundary. I would essentially be looking for blocks that are trimmed into nothing. Is there some API for dealing with items crossing or being inside boundaries (line-arc closed shapes)? Not sure if the brep is made for that, or maybe an API from the GIS end of things.
    thanks

  3. Kean Walmsley Avatar

    Hi James,

    The closest I've come, myself, is with this post:

    keanw.com/2011/02/creating-the-smallest-possible-circle-around-2d-autocad-geometry-using-net.html

    I don't have much direct experience with convex hull algorithms, but I've at least heard of the Graham scan:

    1
    1

    Maybe there's some way to combine the point collection code in my post with a convex hull algorithm implemented in OpenCV (for instance)?

    Kean

  4. And there is no error in determining extents?
    var ext = new Extents3d();
    This add a point zero. It gives an error result if there are no objects in zero.

    1. What kind of error, and under what circumstances? If there are no objects, then having "zero" extents would seem valid...

      Kean

      1. Extents3d is a structure. new Extents3d() init points 0. Sorry, can't better explain.

        1. But where's the error?

          Kean

          1. My bad (.

            private static Extents3d ext;
            public static Extents3d GetExtents(this Transaction tr, ObjectId[] ids)
            {
            foreach (var id in ids)
            {
            var ent = tr.GetObject(id, OpenMode.ForRead) as Entity;
            if (ent != null)
            {
            ext.AddExtents(ent.GeometricExtents);
            }
            }
            return ext;
            }

            Group's extents are from (0,0,0) to (5541.35909948649,4595.91169076275,0).

          2. My bad (;
            Other way get error with point 0.0 - field Extents3d w/o new ():

            private static Extents3d ext;
            public static Extents3d GetExtents(this Transaction tr, ObjectId[] ids)
            {
            foreach (var id in ids)
            {
            var ent = tr.GetObject(id, OpenMode.ForRead) as Entity;
            if (ent != null)
            {
            ext.AddExtents(ent.GeometricExtents);
            }
            }
            return ext;
            }

            Get example: Group's extents are from (0,0,0) to (5541.35,4595.91,0).

            1. Can you please tell me what error is generated and by which piece of code?

              Kean

  5. Hello Kean,
    I am using ReadDwgFile method to read all blockreferences inside modelspace and looping through all block references. When I use ent.geometricsextents in that loop for valid block, it gives "eInvalidExtents error". But If I use in active document by selecting same block it does not give any error. Can you please explain this behavior? and if you can guide me to solve this problem?

    thanks,
    Pankaj

    1. Hi Pankaj,

      It's probably because side databases don't have the display list available.

      Either way, this is really a support question: please post it to the discussion group, as someone there will certainly be able to help.

      Regards,

      Kean

      1. Thanks Kean, as always you are so quick on replying.
        I don't know How you can manage that from your busy schedule!

  6. Edgar Itriago Avatar

    Hello Kean, instead of Get the group dictionary to the entire groups from the drawing, Can you get the existing groups on a particular layout only¿ just may iterating it. thanks, I hope that you can guide me to solve this question please

    1. Kean Walmsley Avatar

      Hi Edgar,

      Yes - you'll need to include just the groups for a particular BlockTableRecord. It shouldn't be too hard to work out (if you have trouble, try posting to the AutoCAD .NET forum - someone there will be able to help).

      Regards.

      Kean

Leave a Reply to Vildar Cancel reply

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