Getting the centroid of an AutoCAD region using .NET

This week we're going to look at an interesting problem: how to create text that fits into a particular space. The scenario was originally presented (to me, anyway) by Alex Fielder, early last year (thanks, Alex!), but it's taken a while for me to get to it. Alex wanted to check for the extents of block attributes overflowing their containers. I may well go ahead and implement that, in due course, but first I wanted to let the user select a space and create some text to fill it.

Let's take a look at how to make this happen. Here's the flow of operations:

  1. The user selects a point
  2. Call Editor.TraceBoundary() to determine the containing space
  3. Call Region.CreateFromCurves() with the resulting geometry
  4. Determine the centroid of the Region
  5. Check whether the centroid is actually inside the Region
  6. If it is, then generate some text to place (we could also have asked the user for this, of course)…
  7. … and then calculate the size of the text such that it fits entirely into the space

Step 7 is probably the most interesting, in that we iteratively adjust the size of the text and test whether its extents fall within the Region. But more on that, later in the week.

For now we have a "simple" problem… step 4. Regions do have this information available – you can access it via the MASSPROP command, for instance – but it's the first time I've used the .NET API to get it. It turned out to be worthy of its own blog post, so here it is.

Here's the C# code:

using Autodesk.AutoCAD.ApplicationServices;

using Autodesk.AutoCAD.DatabaseServices;

using Autodesk.AutoCAD.EditorInput;

using Autodesk.AutoCAD.Geometry;

using Autodesk.AutoCAD.Runtime;

 

namespace RegionalActivities

{

  public static class Extensions

  {

    // Region extensions

 

    ///<summary>

    /// Get the centroid of a Region.

    ///</summary>

    ///<param name="cur">An optional curve used to define the region.</param>

    ///<returns>A nullable Point3d containing the centroid of the Region.</returns>

 

    public static Point3d? GetCentroid(this Region reg, Curve cur = null)

    {

      if (cur == null)

      {

        var idc = new DBObjectCollection();

        reg.Explode(idc);

        if (idc.Count == 0)

          return null;

 

        cur = idc[0] as Curve;

      }

 

      if (cur == null)

        return null;

 

      var cs = cur.GetPlane().GetCoordinateSystem();

      var o = cs.Origin;

      var x = cs.Xaxis;

      var y = cs.Yaxis;

 

      var a = reg.AreaProperties(ref o, ref x, ref y);

      var pl = new Plane(o, x, y);

      return pl.EvaluatePoint(a.Centroid);

    }

  }

 

  public class Commands

  {

    [CommandMethod("COR")]

    public void CentroidOfRegion()

    {

      var doc = Application.DocumentManager.MdiActiveDocument;

      if (doc == null) return;

      var ed = doc.Editor;

 

      var peo = new PromptEntityOptions("\nSelect a region");

      peo.SetRejectMessage("\nMust be a region.");

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

      var per = ed.GetEntity(peo);

      if (per.Status != PromptStatus.OK)

        return;

 

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

      {

        var reg = tr.GetObject(per.ObjectId, OpenMode.ForRead) as Region;

        if (reg != null)

        {

          var pt = reg.GetCentroid();

          ed.WriteMessage("\nCentroid is {0}", pt);

        }

        tr.Commit();

      }

    }

  }

}

 

Some comments about this:

  • To get the "area properties" of a Region – which include the Region's centroid, perimeter, radii of gyration, moments of inertia, etc. – we first need to specify a plane. Region doesn't have an implementation for Entity.GetPlane(), so we have to determine this using some of its boundary geometry. In the upcoming post, we pass in one of the Curves we'd used to generate the Region, but in this post we're going to have to explode the selected Region to get at its boundary.
  • Region.AreaProperties() will reject a plane that isn't well-defined, as per the AutoCAD .NET reference:
    • This function calculates the area properties of the Region. All of the values in the returned RegionAreaProperties struct are in the coordinate system specified by origin, xAxis, and yAxis (which must be in WCS coordinates). The function validates the origin, xAxis, and yAxis parameters to ensure that the axes are of unit length and are perpendicular to each other, and that the axes and origin lie in the same plane as the Region.
  • The specific curve used to define the plane shouldn't matter, too much: as we're using the plane to evaluate and return a Point3d, which curve was chosen is ultimately unimportant (providing it works, of course :-).

To make sure it does work, here's an example of running both the COR and MASSPROP commands, selecting a Region we've created manually on an arbitrary plane in 3D space.

Region

Command: COR

Select a region:

Centroid is (24.8686431313716,-11.7227175502621,7.71521951836058)

 

Command: MASSPROP

Select objects: 1 found

Select objects:

 

----------------   REGIONS   ----------------

 

Area:                    112.3602

Perimeter:               43.1524

Bounding box:         X: 18.2540  --  31.6548

                      Y: -15.4236  --  -8.3921

                      Z: 1.8974  --  14.8993

Centroid:             X: 24.8686

                      Y: -11.7227

                      Z: 7.7152

 

Write analysis to a file? [Yes/No] <N>: N

 

 

Looks good! In the next post we'll see how this integrates into our "space labelling" application.

One response to “Getting the centroid of an AutoCAD region using .NET”

  1. You can also use the normal:

    public static Point3d GetCentroid(this Region reg)
    {
    using (var pl = new Plane(Point3d.Origin, reg.Normal))
    {
    CoordinateSystem3d cs = pl.GetCoordinateSystem();
    Point3d o = cs.Origin;
    Vector3d x = cs.Xaxis;
    Vector3d y = cs.Yaxis;
    RegionAreaProperties a = reg.AreaProperties(ref o, ref x, ref y);
    return pl.EvaluatePoint(a.Centroid);
    }
    }

    1. Thanks, Maxence.

      How do you deal with the situation where there Region isn't on the XY plane? We need a valid origin for the plane...

      Regards,

      Kean

      1. Right, I've just found it does not work...

  2. Hi

    I've typed out the code (I hand typed) above and i seem to be getting an error with the Region extension method: GetCentroid.

    I"m not sure why. I'm sure it's ridiculously simple:

    any help would be much appreciated.

    regards

    Ben

    1. Solved it - looks like Visual Studio was the culprit 🙂

      chrs

      Ben

      1. Great - thanks for letting me know, Ben.

        I seriously need to know why you hand-typed the code, though. I haven't done that since the early 90s! (Brings back some great memories, though.)

        It reminds me of the IT Crowd sketch where Roy asks someone on the other end of the phone: "Are you from the past?" (sorry - I couldn't resist 🙂

        Kean

        1. Yes thank you Keane haha i looked up the sketch on youtube

          Q: Why hand type?

          when I hand type - I'm forced to question everything. Why is Keane writing this line? also I'm forced to explicitly declare all the variables. so i rewrite "var origin" to Point3d origin and work from there.

          and it's like everything is in in slow motion.

          Take this bit for example: notice how the centroid is calculated by first calling a method on the plane object? the information in the plane object is already contained within the region area properties object 'a' itself. In other words, the plane object seems to be a redundancy. So then I now question: why can't the centroid be calculated with a method a.centroid without involving pl?

          1. We're simply using the Plane to convert a Point2d (the Centroid) into a Point3d.

            Maybe there's another (better?) way to do this that I've missed.

            Kean

  3. Is it possible that AutoCAD 2025 do not support AreaProperties anymore?

Leave a Reply to Kean Walmsley Cancel reply

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