Saturday, December 24, 2022

Getting entity's Bounding Box in UCS

In AutoCAD, the bounding box of an entity is a minimal orthogonal rectangle that enclose the entity. With .NET API, one can get the bounding box of an entity with Entity.GeometricExtents property, which is an Extents3d structure. With COM API, one can call AcadEntity.GetBoundingBox(minPoint, maxPoint) to obtain the bounding box's minimum point (lower-left corner) and maximum point (the upper-right corner).

There was an interesting question asked recently in the AutoCAD VBA discussion forum. The OP wanted to get the bounding box of an entity as it appears in an UCS. Obviously, depending on the transformation of an UCS from WCS (World Coordinate System), the appeared bounding box in current UCS would be different from the Bounding Box calculated with .NET API/COM API, as shown in the pictures below.

The bounding box of an Hatch entity obtained through API (Entity.GeometricExtents, or AcadEntity.GetBoundingBox()), is shown in red:


When an UCS is set current, the appeared bounding box (which was what the OP of aforementioned forum discussion wanted) is shown in yellow:


So, the question here is how to find the corner coordinates of the UCS-appeared bound box in the current UCS.

It turns out, with AutoCAD .NET API support, the calculation is rather easy: simply transform the entity with inverse Matrix3d of the current UCS and then get the transformed entity's GeometricExtents. Following code shows how to (only 2 lines of code in red does the trick!):
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.Runtime;
using CadApp = Autodesk.AutoCAD.ApplicationServices.Application;
 
[assembly: CommandClass(typeof(UcsToWcsTest.MyCommands))]
 
namespace UcsToWcsTest
{
    public class MyCommands
    {
        [CommandMethod("GetUcsBox")]
        public static void RunCommand()
        {
            var dwg = CadApp.DocumentManager.MdiActiveDocument;
            var ed = dwg.Editor;
 
            var entId = SelectEntity(ed);
            if (entId.IsNull)
            {
                ed.WriteMessage("\n*Cancel*");
                return;
            }
 
            GetUcsBoundingBox(entId, ed);
        }
 
        private static ObjectId SelectEntity(Editor ed)
        {
            var res = ed.GetEntity("\nSelect an entity:");
            if (res.Status == PromptStatus.OK)
                return res.ObjectId;
            else
                return ObjectId.Null;
        }
 
        private static void GetUcsBoundingBox(ObjectId entId, Editor ed)
        {
            var db = entId.Database;
            Extents3d bound;
            using (var tran = db.TransactionManager.StartTransaction())
            {
                var space = (BlockTableRecord)tran.GetObject(
                    db.CurrentSpaceId, OpenMode.ForWrite);
                var ent = (Entity)tran.GetObject(entId, OpenMode.ForRead);
 
                using (var entCopy = ent.Clone() as Entity)
                {
                    entCopy.TransformBy(ed.CurrentUserCoordinateSystem.Inverse());
                    bound = entCopy.GeometricExtents;
                }
 
                tran.Commit();
            }
 
            var pt = bound.MinPoint;
            ed.WriteMessage($"\nFirst corner: {PointToString(pt)}");
 
            pt = new Point3d(bound.MinPoint.X,bound.MaxPoint.Y, bound.MinPoint.Z);
            ed.WriteMessage($"\nSecond corner: {PointToString(pt)}");
 
            pt = bound.MaxPoint;
            ed.WriteMessage($"\nThird corner: {PointToString(pt)}");
 
            pt = new Point3d(bound.MaxPoint.X, bound.MinPoint.Y, bound.MinPoint.Z);
            ed.WriteMessage($"\nFourth corner: {PointToString(pt)}");
        }
 
        private static string PointToString(Point3d pt)
        {
            var x = Converter.DistanceToString(pt.X);
            var y= Converter.DistanceToString(pt.Y);
 
            return $"({x}{y})";
        }
    }
}

Noted that I used a copy of the entity for the calculation, so that the real entity is not changed/transformed. Otherwise it would need to be transformed back.

See the video click below for the effect of the code execution.


Since the aforementioned question was asked in AutoCAD VBA forum, then, how can we find out the appeared bounding box in UCS with VBA code? Well, unlike AutoCAD .NET API's support to solve this problem, it is a bit complicated with VBA/AutoCAD COM API, or at least, not straightforward. Because I have posted the VBA code in the VBA discussion forum, go to here to take a look, if interested in:

https://forums.autodesk.com/t5/vba/lisp-code-to-vba-code-conversion/td-p/11561281