Site Tools


Calculating Tight Bounding Boxes for Breps

Developer: .NET
Summary: Discusses the calculation of tight bounding boxes and Brep objects.
NOTICE: The Rhino.NET SDK is deprecated in Rhino 5. This example adapted for the new RhinoCommon SDK is here

Question

I am having a problem with getting a tight bounding box of a trimmed Brep using C#. I have a trimmed surface which I then split into smaller pieces. I then want to get each face of this new Brep and get the bounding box of each. However, each of the resulting bounding boxes bounds the extent of the original Brep and not the individual faces. If I extract the edge loop, this bounds correctly, however.

Answer

Tight bounding boxes for surfaces and Breps are calculated with runtime information, such as display curves and render meshes. Thus, when you cook up your own Brep, which do not have display curves and render meshes, you only get the bounding boxes of the untrimmed surfaces.

One way round this is to call RhUtil.RhinoGetTightBoundingBox. This function requires Rhino objects that are in the document. This is probably not too convenient in your situation.

Another alternative is to created meshes, one for each Brep face, and then calculate the bounding box of each mesh. This should give you accurate results.

C#

public override IRhinoCommand.result RunCommand(IRhinoCommandContext context)
{
  MRhinoGetObject gs = new MRhinoGetObject();
  gs.SetCommandPrompt("Select surface to split");
  gs.SetGeometryFilter(IRhinoGetObject.GEOMETRY_TYPE_FILTER.surface_object);
  gs.GetObjects(1,1);
  if (gs.CommandResult() != IRhinoCommand.result.success)
    return gs.CommandResult();
 
  IOnBrep brep = gs.Object(0).Brep();
  if (null == brep)
    return IRhinoCommand.result.failure;
 
  MRhinoGetObject gc = new MRhinoGetObject();
  gc.SetCommandPrompt("Select cutting curve");
  gc.SetGeometryFilter(IRhinoGetObject.GEOMETRY_TYPE_FILTER.curve_object);
  gc.EnablePreSelect(false);
  gc.EnableDeselectAllBeforePostSelect(false);
  gc.GetObjects(1,1);
  if (gc.CommandResult() != IRhinoCommand.result.success)
    return gc.CommandResult();
 
  IOnCurve[] cutters = new IOnCurve[1];
  cutters[0] = gc.Object(0).Curve();
  if (null == cutters[0])
    return IRhinoCommand.result.failure;
 
  OnBrep split_brep = RhUtil.RhinoSplitBrepFace(brep, 0, cutters, context.m_doc.AbsoluteTolerance() );
  if (null == split_brep)
  {
    RhUtil.RhinoApp().Print("Unable to split surface.\n");
    return IRhinoCommand.result.nothing;
  }
 
  IOnMeshParameters mp = context.m_doc.Properties().RenderMeshSettings();
  OnMesh[] mesh_list = new OnMesh[0];
  int mesh_count = split_brep.CreateMesh(mp, ref mesh_list);
 
  int num_added = 0;
  for (int i = 0; i < mesh_list.Length; i++)
  {
    OnBoundingBox bbox = new OnBoundingBox();
    if (mesh_list[i].GetTightBoundingBox(ref bbox))
    {
      On3dPoint[] box_corners = new On3dPoint[8];
      box_corners[0] = bbox.Corner(0,0,0);
      box_corners[1] = bbox.Corner(1,0,0);
      box_corners[2] = bbox.Corner(1,1,0);
      box_corners[3] = bbox.Corner(0,1,0);
      box_corners[4] = bbox.Corner(0,0,1);
      box_corners[5] = bbox.Corner(1,0,1);
      box_corners[6] = bbox.Corner(1,1,1);
      box_corners[7] = bbox.Corner(0,1,1);
 
      On3dPoint[] rect = new On3dPoint[5];
      OnLine line = new OnLine(); ;
      int box_type = ClassifyBBox(box_corners, ref rect, ref line); // returns 0=box, 1=rectangle, 2=line, 3=point
 
      if (box_type == 3)
      {
        // BoundingBox failed. The bounding box is a point.
      }
      else if (box_type == 2)
      {
        // BoundingBox failed. The bounding box is a line.
      }
      else if (box_type == 1)
      {
        OnPolyline polyline = new OnPolyline();
        polyline.Append(rect[0]);
        polyline.Append(rect[1]);
        polyline.Append(rect[2]);
        polyline.Append(rect[3]);
        polyline.Append(rect[4]);
        context.m_doc.AddCurveObject(polyline);
      }
      else // box_type == 0
      {
        OnBrep brep_box = OnUtil.ON_BrepBox(box_corners);
        if (null != brep_box)
        {
          context.m_doc.AddBrepObject(brep_box);
          num_added++;
        }
      }
    }
  }
 
  if (num_added > 0)
    context.m_doc.Redraw();
 
  return IRhinoCommand.result.success;
}
 
int ClassifyBBox(On3dPoint[] box_corners, ref On3dPoint[] rect, ref OnLine line)
{
  const double FLT_EPSILON = 1.192092896e-07F;
  int numflat = 0;
  bool xflat = false, yflat = false, zflat = false;
 
  if (FLT_EPSILON > box_corners[0].DistanceTo(box_corners[1]))
  {
    numflat++;
    xflat = true;
  }
 
  if (FLT_EPSILON > box_corners[0].DistanceTo(box_corners[3]))
  {
    numflat++;
    yflat = true;
  }
 
  if (FLT_EPSILON > box_corners[0].DistanceTo(box_corners[4]))
  {
    numflat++;
    zflat = true;
  }
 
  if (numflat == 2)
  {
    line.from = box_corners[0];
    if (!xflat)
      line.to = box_corners[1];
    else if (!yflat)
      line.to = box_corners[3];
    else
      line.to = box_corners[4];
  }
  else if (numflat == 1)
  {
    rect[0] = rect[4] = box_corners[0];
    if (xflat)
    {
      rect[1] = box_corners[4];
      rect[2] = box_corners[7];
      rect[3] = box_corners[3];
    }
    else if (yflat)
    {
      rect[1] = box_corners[1];
      rect[2] = box_corners[5];
      rect[3] = box_corners[4];
    }
    else // zflat
    {
      rect[1] = box_corners[1];
      rect[2] = box_corners[2];
      rect[3] = box_corners[3];
    }
  }
 
  return numflat;
}
developer/sdksamples/tightboundingbox.txt ยท Last modified: 2015/09/14 (external edit)