November 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            










« Using the Microsoft Kinect SDK to sweep simple solids inside AutoCAD | Main | Using the Microsoft Kinect SDK to sweep segmented solids inside AutoCAD »

October 21, 2011

Identifying holes in an AutoCAD solid using .NET

A big thanks to Ishwar Nagwani – an old friend, colleague and member of Autodesk Consulting working in our Bangalore office – for kindly providing this code.

Ishwar tells me that he has come across many developers struggling to identify holes in 3D solid using its boundary representation (Brep). The code he has provided works on the basis that a hole’s normal is typically facing inwards and will therefore intersect the hole’s axis of symmetry, providing we extend the line representing the normal by the hole’s radius and the line representing the axis of symmetry by the cylinder’s height (just to make sure).

Here is the C# code:

using Autodesk.AutoCAD.ApplicationServices;

using Autodesk.AutoCAD.BoundaryRepresentation;

using AcBr = Autodesk.AutoCAD.BoundaryRepresentation;

using Autodesk.AutoCAD.Colors;

using Autodesk.AutoCAD.DatabaseServices;

using Autodesk.AutoCAD.EditorInput;

using Autodesk.AutoCAD.Geometry;

using AcGe = Autodesk.AutoCAD.Geometry;

using Autodesk.AutoCAD.Runtime;

using System.Collections.Generic;

using System;

 

// Not mandatory, but improves loading performance

 

[assembly: CommandClass(typeof(HoleFeature.MyCommands))]

 

namespace HoleFeature

{

  public class MyCommands

  {

    [CommandMethod("GETHOLES")]

    public void GetHoles()

    {

      Document doc =

          Application.DocumentManager.MdiActiveDocument;

      Database db = doc.Database;

      Editor ed = doc.Editor;

 

      PromptEntityOptions peo =

          new PromptEntityOptions("\nSelect a 3D solid: ");

      peo.SetRejectMessage("\nMust be a 3D solid.");

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

 

      PromptEntityResult per = ed.GetEntity(peo);

 

      if (per.Status != PromptStatus.OK)

        return;

 

      Transaction tr = db.TransactionManager.StartTransaction();

      using (tr)

      {

        Solid3d solid =

          tr.GetObject(per.ObjectId, OpenMode.ForWrite) as Solid3d;

 

        ObjectId[] ids = new ObjectId[] { solid.ObjectId };

 

        FullSubentityPath path =

          new FullSubentityPath(

            ids,

            new SubentityId(SubentityType.Null, IntPtr.Zero)

          );

 

        // For storing SubentityIds of cylinderical faces

 

        List<SubentityId> subentIds = new List<SubentityId>();

 

        using (Brep brep = new Brep(path))

        {

          foreach (AcBr.Face face in brep.Faces)

          {

            AcGe.Surface surf = face.Surface;

 

            ExternalBoundedSurface ebSurf =

              surf as ExternalBoundedSurface;

 

            // We are only looking only cylinders

 

            if (ebSurf != null && ebSurf.IsCylinder)

            {

              Cylinder cyl = ebSurf.BaseSurface as Cylinder;

 

              // And fully closed cylinders

 

              if (cyl != null && cyl.IsClosed())

              {

                // Get normal and point on surface

 

                Vector3d normal = new Vector3d();

                Point3d pt = new Point3d();

 

                GetNormalAndPoint(surf, ref normal, ref pt);

 

                if (IsHole(face, normal, pt, cyl))

                {

                  subentIds.Add(face.SubentityPath.SubentId);

                }

              }

            }

          }

        }

 

        // Assign red color to hole features

 

        if (subentIds.Count > 0)

        {

          short colorIdx = 1;

          AssignColor(solid, subentIds, colorIdx);

        }

 

        tr.Commit();

      }

    }

 

    // Get normal and point at mid U and V parameters

 

    void GetNormalAndPoint(

      AcGe.Surface surf, ref Vector3d normal, ref Point3d pt

    )

    {

      Interval[] box = surf.GetEnvelope();

      double p1 = box[0].LowerBound + box[0].Length / 2.0;

      double p2 = box[1].LowerBound + box[1].Length / 2.0;

      Point2d ptParams = new Point2d(p1, p2);

      PointOnSurface pos = new PointOnSurface(surf, ptParams);

      normal = pos.GetNormal(ptParams);

      pt = pos.GetPoint(ptParams);

    }

 

    // A cylinder is a hole if the normal points inwards

    // and the normal after extending by radius intersects

    // with axis of symmetry, the axis of symmetry is also

    // extended by height of cylinder

 

    Boolean IsHole(

      AcBr.Face face, Vector3d normal, Point3d pt, Cylinder cyl

    )

    {

      if (!face.IsOrientToSurface)

      {

        // Correct the normal and save back

 

        normal = normal.Negate();

      }

 

      // Calculate another point on normal by extending the normal

      // by radius of cylinder

 

      Point3d opt = new Point3d(

        pt.X + normal.X * cyl.Radius,

        pt.Y + normal.Y * cyl.Radius,

        pt.Z + normal.Z * cyl.Radius

      );

 

      // Get the cylinder's axis

 

      Vector3d v1 = cyl.AxisOfSymmetry;

 

      double dist = cyl.Height.Length;

 

      // Calculate another point on axis by extending v1 by dist

 

      Point3d pt2 = new Point3d(

        cyl.Origin.X + v1.X * dist,

        cyl.Origin.Y + v1.Y * dist,

        cyl.Origin.Z + v1.Z * dist

      );

 

      // Create line segment representing the cylinder's normal

 

      LineSegment3d ls1 = new LineSegment3d(pt, opt);

 

      // Create line segment representing the cylinder's axis

 

      LineSegment3d ls2 = new LineSegment3d(cyl.Origin, pt2);

 

      // Get intersection of normal and cylinder axis

 

      Point3d[] intpt;

      intpt = ls1.IntersectWith(ls2);

 

      return (intpt != null);

    }

 

    // Assign color to cylinderical surfaces which are holes   

 

    void AssignColor(

      Solid3d solid, List<SubentityId> subentIds, short idx

    )

    {

      foreach (SubentityId subentId in subentIds)

      {

        Color col = Color.FromColorIndex(ColorMethod.ByColor, idx);

        solid.SetSubentityColor(subentId, col);

      }

    }

  }

}

 

 

Here’s a 3D solid for which we would like to identify holes:

Holey solid

When we run the GETHOLES command and select our solid, its cylindrical holes are turned red:

Solid with holes identified

Thanks again, Ishwar! :-)

Update:

See this post for an updated version of the above code (as well as a description of why the changes are needed).

blog comments powered by Disqus

Feed/Share

10 Random Posts