Today’s post resulted from an internal discussion: Miroslav Schonauer – with the help of Jan Liska – put together some code for a recent consulting engagement that they felt was important to share with the community. They wanted to test point containment for a particular 3D solid, but also to test whether the selected point – if outside the solid – was above it.
They achieved this using AutoCAD’s Brep API. Here is the C# they put together (with some minor, cosmetic edits from my side):
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.BoundaryRepresentation;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.Runtime;
using System;
namespace BrepTest
{
public class Commands
{
[CommandMethod("PISO")]
public void PointInSolid()
{
var doc = Application.DocumentManager.MdiActiveDocument;
var db = doc.Database;
var ed = doc.Editor;
try
{
// Select Solid3d
var peo = new PromptEntityOptions("\nSelect a 3D solid");
peo.SetRejectMessage("\nMust be a 3D solid.");
peo.AddAllowedClass(typeof(Solid3d), true);
var per = ed.GetEntity(peo);
if (per.Status != PromptStatus.OK)
return;
using (var tr = db.TransactionManager.StartTransaction())
{
// Open the selected Solid3d
var sol = tr.GetObject(per.ObjectId, OpenMode.ForRead) as Solid3d;
if (sol != null) // Should always be true
{
// Create the sub-entity path to it
var ids = new ObjectId[] { per.ObjectId };
var path =
new FullSubentityPath(
ids, new SubentityId(SubentityType.Null, IntPtr.Zero)
);
// Create a Brep from the path
using (var brep = new Brep(path))
{
while (true)
{
// Select point
var ppr = ed.GetPoint("\nSelect a point");
if (ppr.Status != PromptStatus.OK)
break;
var pt = ppr.Value;
// 1) Check point containment w.r.t selected solid
var pc = new PointContainment();
using (var brepEnt = brep.GetPointContainment(pt, out pc))
{
ed.WriteMessage("\nPt {0}: {1}", pt, pc);
}
// 2) If Outside, check whether the solid is "below":
if (pc == PointContainment.Outside)
{
// Fire a -Z Ray from the point to find if it hits the Brep
var ray = new Ray3d(pt, -Vector3d.ZAxis);
var hits = brep.GetLineContainment(ray, 1);
if (hits == null || hits.Length == 0)
{
ed.WriteMessage("\nSolid is NOT below point.");
}
else
{
ed.WriteMessage(
"\nSolid IS below point, distance = {0}",
pt.DistanceTo(hits[0].Point)
);
}
}
}
}
}
tr.Commit();
}
}
catch (System.Exception ex)
{
ed.WriteMessage("Error: {0}\r\n at {1}", ex.Message, ex.StackTrace);
}
}
}
}
To give it a try, just run the PISO command (for PointInSolid), then select a Solid3d and start picking points. You’ll see on the application report via the command-line whether the selected point is contained within the solids and, if not, whether the point is above it (i.e. that the solid is below the point).
A big thanks to Miro & Jan for sharing this useful code! :-)