Beispiel #1
0
bool ON_Plane::CreateFromFrame(
    const ON_3dPoint&  P, // point on the plane
    const ON_3dVector& X, // non-zero vector in plane
    const ON_3dVector& Y  // another non-zero vector in the plane
    )
{
  origin = P;

  xaxis = X;
  xaxis.Unitize();
  yaxis = Y - ON_DotProduct( Y, xaxis)*xaxis;
  yaxis.Unitize();
  zaxis = ON_CrossProduct( xaxis, yaxis );
  bool b = zaxis.Unitize();
  UpdateEquation();
  if ( b )
  {
    // 11 February 2004 Dale Lear
    //     Add more validation checks.
    b = IsValid();
    if ( b )
    {
      // make sure zaxis is perp to Y
      if ( fabs(Y*zaxis) > ON_SQRT_EPSILON*Y.Length() )
        b = false;
    }
  }
  return b;
}
CRhinoCommand::result CCommandSampleSelectVisibleMeshFaces::RunCommand( const CRhinoCommandContext& context )
{
  CRhinoGetObject go;
  go.SetCommandPrompt(L"Select mesh");
  go.SetGeometryFilter(ON::mesh_object);
  go.EnablePreSelect(false);
  go.EnableUnselectObjectsOnExit(false);
  go.GetObjects(1, 1);
  if (go.CommandResult() != CRhinoCommand::success)
    return go.CommandResult();

  CRhinoView* view = go.View();
  if (0 == view)
    return CRhinoCommand::failure;

  const CRhinoMeshObject* mesh_obj = CRhinoMeshObject::Cast(go.Object(0).Object());
  if (0 == mesh_obj)
    return CRhinoCommand::failure;

  ON_Mesh* mesh = const_cast<ON_Mesh*>(mesh_obj->Mesh());
  if (0 == mesh)
    return CRhinoCommand::failure;

  mesh_obj->Select(false);
  context.m_doc.Redraw();

  if (!mesh->HasFaceNormals())
    mesh->ComputeFaceNormals();

  ON_3fVector dir(view->ActiveViewport().VP().CameraZ());
  double min_angle = 0.0;
  double max_angle = 90.0 * (ON_PI/180);

  for (int fi = 0; fi < mesh->m_F.Count(); fi++)
  {
    const ON_3fVector& norm = mesh->m_FN[fi];
    double dot = ON_DotProduct(dir, norm) / (dir.Length() * norm.Length());
    double angle = acos(dot);
    if (min_angle <= angle && angle <= max_angle)
    {
      ON_COMPONENT_INDEX ci(ON_COMPONENT_INDEX::mesh_face, fi);
      mesh_obj->SelectSubObject(ci, true, true);
    }
  }
  context.m_doc.Redraw();

  CRhinoGetString gs;
  gs.SetCommandPrompt(L"Press <Enter> to continue");
  gs.AcceptNothing();
  gs.GetString();

  return CRhinoCommand::success;
}
Beispiel #3
0
bool ON_Plane::IsValid() const
{
  if ( !plane_equation.IsValid() )
    return false;


  double x = plane_equation.ValueAt(origin);
  if ( fabs(x) >  ON_ZERO_TOLERANCE )
  {
    double tol = fabs(origin.MaximumCoordinate()) + fabs(plane_equation.d);
    if ( tol > 1000.0 && origin.IsValid() )
    {
      // 8 September 2003 Chuck and Dale:
      //   Fixing discrepancy between optimized and debug behavior.
      //   In this case, the ON_ZERO_TOLERANCE test worked in release
      //   and failed in debug. The principal behind this fix is valid
      //   for release builds too.
      //   For large point coordinates or planes far from the origin,
      //   the best we can hope for is to kill the first 15 or so decimal
      //   places.
      tol *= (ON_EPSILON*10.0);
      if ( fabs(x) > tol )
        return false;
    }
    else
      return false;
  }

  if ( !ON_IsRightHandFrame( xaxis, yaxis, zaxis ) )
    return false;

  ON_3dVector N = plane_equation;
  N.Unitize();
  x = ON_DotProduct( N, zaxis );
  if ( fabs(x-1.0) >  ON_SQRT_EPSILON )
    return false;

  return true;
}
Beispiel #4
0
bool ON_Intersect( const ON_Line& lineA, const ON_Line& lineB, 
                double* lineA_parameter, 
                double* lineB_parameter
                )
{
  // If you are looking at this code because you don't like an
  // answer you are getting, then the first thing to try is
  // to read the header file comments and try calling 
  // ON_IntersectLineLine.
  bool rc = false;
  double M_zero_tol = 0.0;
  int i, rank;
  double pr_tolerance, pivot, X[2], Y[2];

  ON_3dVector A = lineA.Direction();
  ON_3dVector B = lineB.Direction();
  ON_3dVector C = lineB[0] - lineA[0];
  
  ON_Matrix M(2,2);
  M[0][0] =  ON_DotProduct( A, A );
  M[1][1] =  ON_DotProduct( B, B );
  M[0][1] = M[1][0] = -ON_DotProduct( A, B );

  // this swap done to get row+col pivot accuracy
  if ( M[0][0] < M[1][1] ) {
    M.SwapCols(0,1);
    i = 1;
  }
  else {
    i = 0;
  }
  pr_tolerance = fabs(M[1][1])*ON_SQRT_EPSILON;
  M_zero_tol = fabs(M[1][1])*ON_EPSILON;

  Y[0] =  ON_DotProduct( A, C );
  Y[1] = -ON_DotProduct( B, C );

  rank = M.RowReduce( M_zero_tol, Y, &pivot );
  if ( rank == 2 ) 
  {
    // 19 November 2003 Dale Lear and Chuck
    //   Added lineA.from/to == lineB.from/to tests
    //   so exact answer gets returned when people
    //   expect it.
    rc = true;
    if ( lineA.from == lineB.from )
    {
      if ( lineA_parameter )
        *lineA_parameter = 0.0;
      if ( lineB_parameter )
        *lineB_parameter = 0.0;
    }
    else if ( lineA.from == lineB.to )
    {
      if ( lineA_parameter )
        *lineA_parameter = 0.0;
      if ( lineB_parameter )
        *lineB_parameter = 1.0;
    }
    else if ( lineA.to == lineB.from )
    {
      if ( lineA_parameter )
        *lineA_parameter = 1.0;
      if ( lineB_parameter )
        *lineB_parameter = 0.0;
    }
    else if ( lineA.to == lineB.to )
    {
      if ( lineA_parameter )
        *lineA_parameter = 1.0;
      if ( lineB_parameter )
        *lineB_parameter = 1.0;
    }
    else
    {
      rc = M.BackSolve( 0.0, 2, Y, X );
      if ( rc ) 
      {
        if ( lineA_parameter )
          *lineA_parameter = X[i];
        if ( lineB_parameter )
          *lineB_parameter = X[1-i];
        if ( fabs(pivot) <= pr_tolerance ) 
        {
          // test answer because matrix was close to singular
          // (This test is slow but it is rarely used.)
          ON_3dPoint pA = lineA.PointAt(X[i]);
          ON_3dPoint pB = lineB.PointAt(X[1-i]);
          double d = pA.DistanceTo(pB);
          if ( d > pr_tolerance && d > ON_ZERO_TOLERANCE ) { 
            ON_3dPoint qA = lineA.ClosestPointTo(pB);
            ON_3dPoint qB = lineB.ClosestPointTo(pA);
            double dA = pA.DistanceTo(qB);
            double dB = pB.DistanceTo(qA);
            if ( 1.1*dA < d ) {
              rc = false;
            }
            else if ( 1.1*dB < d ) {
              rc = false;
            }
          }
        }
      }
    }
  }
  
  return rc;
}
Beispiel #5
0
bool ON_Arc::GetNurbFormParameterFromRadian(double RadianParameter, double* NurbParameter ) const
{
	if(!IsValid() || NurbParameter==NULL) 
		return false;

  ON_Interval ADomain = DomainRadians();

  double endtol = 10.0*ON_EPSILON*(fabs(ADomain[0]) + fabs(ADomain[1]));

  double del = RadianParameter - ADomain[0];
	if(del <= endtol && del >= -ON_SQRT_EPSILON)
  {
		*NurbParameter=ADomain[0];
		return true;
	} 
  else {
    del = ADomain[1] - RadianParameter;
    if(del <= endtol && del >= -ON_SQRT_EPSILON){
		  *NurbParameter=ADomain[1];
		  return true;
    }
	}

	if( !ADomain.Includes(RadianParameter ) )
		return false;


	ON_NurbsCurve crv;

	if( !GetNurbForm(crv))
		return false;

	//Isolate a bezier that contains the solution
	int cnt = crv.SpanCount();	
	int si =0;	//get span index
	int ki=0;		//knot index
	double ang = ADomain[0];
	ON_3dPoint cp;
	cp = crv.PointAt( crv.Knot(0) ) - Center();
	double x = ON_DotProduct(Plane().Xaxis(),cp);
	double y = ON_DotProduct(Plane().Yaxis(),cp);
	double at = atan2( y, x);	//todo make sure we dont go to far

	for( si=0, ki=0; si<cnt; si++, ki+=crv.KnotMultiplicity(ki) ){
		cp = crv.PointAt( crv.Knot(ki+2)) - Center();
		x = ON_DotProduct(Plane().Xaxis(),cp);
		y = ON_DotProduct(Plane().Yaxis(),cp);
		double at2 = atan2(y,x);
		if(at2>at)
			ang+=(at2-at);
		else
			ang += (2*ON_PI + at2 - at);
		at = at2;
		if( ang>RadianParameter)
			break;
	} 

	// Crash Protection trr#55679
	if( ki+2>= crv.KnotCount())
	{
		 *NurbParameter=ADomain[1];
		 return true;		
	}
	ON_Interval BezDomain(crv.Knot(ki), crv.Knot(ki+2));

	ON_BezierCurve bez;
	if(!crv.ConvertSpanToBezier(ki,bez))
		return false;

 	ON_Xform COC;
	COC.ChangeBasis( ON_Plane(),Plane());   

	
	bez.Transform(COC);	// change coordinates to circles local frame
	double a[3];							// Bez coefficients of a quadratic to solve
	for(int i=0; i<3; i++)
		a[i] = tan(RadianParameter)* bez.CV(i)[0] - bez.CV(i)[1];

	//Solve the Quadratic
	double descrim = (a[1]*a[1]) - a[0]*a[2];
	double squared = a[0]-2*a[1]+a[2];
	double tbez;
	if(fabs(squared)> ON_ZERO_TOLERANCE){
		ON_ASSERT(descrim>=0);
		descrim = sqrt(descrim);
		tbez = (a[0]-a[1] + descrim)/(a[0]-2*a[1]+a[2]);
		if( tbez<0 || tbez>1){
			double tbez2 = (a[0]-a[1]-descrim)/(a[0] - 2*a[1] + a[2]);
			if( fabs(tbez2 - .5)<fabs(tbez-.5) )
				tbez = tbez2;
		}

		ON_ASSERT(tbez>=-ON_ZERO_TOLERANCE && tbez<=1+ON_ZERO_TOLERANCE);
	}
	else{
		// Quadratic degenerates to linear
		tbez = 1.0;
		if(a[0]-a[2])
			tbez = a[0]/(a[0]-a[2]);
	}	
	if(tbez<0)
		tbez=0.0;
	else if(tbez>1.0)
		tbez=1.0;


		//Debug ONLY Code  - check the result
//		double aa = a[0]*(1-tbez)*(1-tbez)  + 2*a[1]*tbez*(1-tbez) + a[2]*tbez*tbez;
//		double tantheta= tan(RadianParameter);
//		ON_3dPoint bezp;
//		bez.Evaluate(tbez, 0, 3, bezp);
//		double yx = bezp.y/bezp.x;


	*NurbParameter = BezDomain.ParameterAt(tbez);
	return true;

}
Beispiel #6
0
bool ON_Arc::GetRadianFromNurbFormParameter(double NurbParameter, double* RadianParameter  ) const
{
	//  TRR#53994.
	// 16-Sept-09  Replaced this code so we dont use LocalClosestPoint.
	// In addition to being slower than neccessary the old method suffered from getting the
	// wrong answer at the seam of a full circle,  This probably only happened with large 
	// coordinates where many digits of precision get lost.

	ON_NurbsCurve crv;
	
	if( !IsValid()|| RadianParameter==NULL) 
		return false;

	ON_Interval dom= Domain();

	if( fabs(NurbParameter- dom[0])<=2.0*ON_EPSILON*fabs(dom[0]))
	{
		*RadianParameter=dom[0];
		return true;
	} 
	else if(  fabs(NurbParameter- dom[1])<=2.0*ON_EPSILON*fabs(dom[1]))
	{
		*RadianParameter=dom[1];
		return true;
	}

	if( !dom.Includes(NurbParameter) )
		return false;

	if( !GetNurbForm(crv) )
		return false;
		
	ON_3dPoint cp;
	cp = crv.PointAt(NurbParameter);
	cp -= Center();

	double x = ON_DotProduct(Plane().Xaxis(), cp);
	double y = ON_DotProduct(Plane().Yaxis(), cp);
	double theta = atan2(y,x);

	theta -= floor( (theta-dom[0])/(2*ON_PI)) * 2* ON_PI;
	if( theta<dom[0] || theta>dom[1])
	{
		// 24-May-2010 GBA 
		// We got outside of the domain because of a numerical error somewhere.
		// The only case that matters is because we are right near an endpoint.
		// So we need to decide which endpoint to return.  (Other possibilities 
		// are that the radius is way to small relative to the coordinates of the center.
		// In this case the circle is just numerical noise around the center anyway.)
		if( NurbParameter< (dom[0]+dom[1])/2.0)
			theta = dom[0];
		else 
			theta = dom[1];
	}


	// Carefully handle the potential discontinuity of this function
	//  when the domain is a full circle
	if(dom.Length()>.99999*2.0*ON_PI)
	{
		double np_theta = dom.NormalizedParameterAt(theta);
		double np_nurb = dom.NormalizedParameterAt(NurbParameter);
		if( np_nurb<.01 && np_theta>.99)
			theta = dom[0];
		else if( np_nurb>.99 && np_theta<.01)
			theta = dom[1];
	}

	*RadianParameter = theta;

//#if defined(ON_DEBUG)
//	double np2;
//	ON_3dPoint AP = PointAt(*RadianParameter);
//
//	GetNurbFormParameterFromRadian( *RadianParameter, &np2);
//	ON_ASSERT(fabs(np2-NurbParameter)<=100* ON_EPSILON*( fabs(NurbParameter) + AP.MaximumCoordinate()+1.0) ); 
//#endif

	return true;
  
}
void
subbrep_add_planar_face(struct subbrep_object_data *data, ON_Plane *pcyl,
			ON_SimpleArray<const ON_BrepVertex *> *vert_loop, int neg_surf)
{
    // We use the planar_obj's local_brep to store new faces.  The planar local
    // brep contains the relevant linear and planar components from its parent
    // - our job here is to add the new surface, identify missing edges to
    // create, find existing edges to re-use, and call NewFace with the
    // results.  At the end we should have just the faces needed
    // to define the planar volume of interest.
    struct subbrep_object_data *pdata = data->planar_obj;
    std::vector<int> edges;
    ON_SimpleArray<ON_Curve *> curves_2d;
    ON_SimpleArray<bool> reversed;
    std::map<int, int> vert_map;
    array_to_map(&vert_map, pdata->planar_obj_vert_map, pdata->planar_obj_vert_cnt);

    ON_3dPoint p1 = pdata->local_brep->m_V[vert_map[((*vert_loop)[0])->m_vertex_index]].Point();
    ON_3dPoint p2 = pdata->local_brep->m_V[vert_map[((*vert_loop)[1])->m_vertex_index]].Point();
    ON_3dPoint p3 = pdata->local_brep->m_V[vert_map[((*vert_loop)[2])->m_vertex_index]].Point();
    ON_Plane loop_plane(p1, p2, p3);
    ON_BoundingBox loop_pbox, cbox;

    // get 2d trim curves
    ON_Xform proj_to_plane;
    proj_to_plane[0][0] = loop_plane.xaxis.x;
    proj_to_plane[0][1] = loop_plane.xaxis.y;
    proj_to_plane[0][2] = loop_plane.xaxis.z;
    proj_to_plane[0][3] = -(loop_plane.xaxis*loop_plane.origin);
    proj_to_plane[1][0] = loop_plane.yaxis.x;
    proj_to_plane[1][1] = loop_plane.yaxis.y;
    proj_to_plane[1][2] = loop_plane.yaxis.z;
    proj_to_plane[1][3] = -(loop_plane.yaxis*loop_plane.origin);
    proj_to_plane[2][0] = loop_plane.zaxis.x;
    proj_to_plane[2][1] = loop_plane.zaxis.y;
    proj_to_plane[2][2] = loop_plane.zaxis.z;
    proj_to_plane[2][3] = -(loop_plane.zaxis*loop_plane.origin);
    proj_to_plane[3][0] = 0.0;
    proj_to_plane[3][1] = 0.0;
    proj_to_plane[3][2] = 0.0;
    proj_to_plane[3][3] = 1.0;

    ON_PlaneSurface *s = new ON_PlaneSurface(loop_plane);
    const int si = pdata->local_brep->AddSurface(s);

    double flip = ON_DotProduct(loop_plane.Normal(), pcyl->Normal()) * neg_surf;

    for (int i = 0; i < vert_loop->Count(); i++) {
	int vind1, vind2;
	const ON_BrepVertex *v1, *v2;
	v1 = (*vert_loop)[i];
	vind1 = vert_map[v1->m_vertex_index];
	if (i < vert_loop->Count() - 1) {
	    v2 = (*vert_loop)[i+1];
	} else {
	    v2 = (*vert_loop)[0];
	}
	vind2 = vert_map[v2->m_vertex_index];
	ON_BrepVertex &new_v1 = pdata->local_brep->m_V[vind1];
	ON_BrepVertex &new_v2 = pdata->local_brep->m_V[vind2];

	// Because we may have already created a needed edge only in the new
	// Brep with a previous face, we have to check all the edges in the new
	// structure for a vertex match.
	int edge_found = 0;
	for (int j = 0; j < pdata->local_brep->m_E.Count(); j++) {
	    int ev1 = pdata->local_brep->m_E[j].Vertex(0)->m_vertex_index;
	    int ev2 = pdata->local_brep->m_E[j].Vertex(1)->m_vertex_index;

	    ON_3dPoint pv1 = pdata->local_brep->m_E[j].Vertex(0)->Point();
	    ON_3dPoint pv2 = pdata->local_brep->m_E[j].Vertex(1)->Point();

	    if ((ev1 == vind1) && (ev2 == vind2)) {
		edges.push_back(pdata->local_brep->m_E[j].m_edge_index);
		edge_found = 1;

		reversed.Append(false);

		// Get 2D curve from this edge's 3D curve
		const ON_Curve *c3 = pdata->local_brep->m_E[j].EdgeCurveOf();
		ON_NurbsCurve *c2 = new ON_NurbsCurve();
		c3->GetNurbForm(*c2);
		c2->Transform(proj_to_plane);
		c2->GetBoundingBox(cbox);
		c2->ChangeDimension(2);
		c2->MakePiecewiseBezier(2);
		curves_2d.Append(c2);
		loop_pbox.Union(cbox);
		break;
	    }
	    if ((ev2 == vind1) && (ev1 == vind2)) {
		edges.push_back(pdata->local_brep->m_E[j].m_edge_index);
		edge_found = 1;
		reversed.Append(true);

		// Get 2D curve from this edge's points
		ON_Curve *c3 = new ON_LineCurve(pv2, pv1);
		ON_NurbsCurve *c2 = new ON_NurbsCurve();
		c3->GetNurbForm(*c2);
		c2->Transform(proj_to_plane);
		c2->GetBoundingBox(cbox);
		c2->ChangeDimension(2);
		c2->MakePiecewiseBezier(2);
		curves_2d.Append(c2);
		loop_pbox.Union(cbox);
		break;
	    }
	}
	if (!edge_found) {
	    int c3i = pdata->local_brep->AddEdgeCurve(new ON_LineCurve(new_v1.Point(), new_v2.Point()));
	    // Get 2D curve from this edge's 3D curve
	    const ON_Curve *c3 = pdata->local_brep->m_C3[c3i];
	    ON_NurbsCurve *c2 = new ON_NurbsCurve();
	    c3->GetNurbForm(*c2);
	    c2->Transform(proj_to_plane);
	    c2->GetBoundingBox(cbox);
	    c2->ChangeDimension(2);
	    c2->MakePiecewiseBezier(2);
	    curves_2d.Append(c2);
	    loop_pbox.Union(cbox);

	    ON_BrepEdge &new_edge = pdata->local_brep->NewEdge(pdata->local_brep->m_V[vind1], pdata->local_brep->m_V[vind2], c3i, NULL ,0);
	    edges.push_back(new_edge.m_edge_index);
	}
    }

    ON_BrepFace& face = pdata->local_brep->NewFace( si );
    ON_BrepLoop& loop = pdata->local_brep->NewLoop(ON_BrepLoop::outer, face);
    loop.m_pbox = loop_pbox;
    for (int i = 0; i < vert_loop->Count(); i++) {
	ON_NurbsCurve *c2 = (ON_NurbsCurve *)curves_2d[i];
	int c2i = pdata->local_brep->AddTrimCurve(c2);
	ON_BrepEdge &edge = pdata->local_brep->m_E[edges.at(i)];
	ON_BrepTrim &trim = pdata->local_brep->NewTrim(edge, reversed[i], loop, c2i);
	trim.m_type = ON_BrepTrim::mated;
	trim.m_tolerance[0] = 0.0;
	trim.m_tolerance[1] = 0.0;
    }

    // set face domain
    s->SetDomain(0, loop.m_pbox.m_min.x, loop.m_pbox.m_max.x );
    s->SetDomain(1, loop.m_pbox.m_min.y, loop.m_pbox.m_max.y );
    s->SetExtents(0,s->Domain(0));
    s->SetExtents(1,s->Domain(1));

    // need to update trim m_iso flags because we changed surface shape
    if (flip < 0) pdata->local_brep->FlipFace(face);
    pdata->local_brep->SetTrimIsoFlags(face);
    pdata->local_brep->SetTrimTypeFlags(true);
}
int
negative_polygon(struct subbrep_object_data *data)
{
    int io_state = 0;
    int all_faces_cnt = 0;
    std::vector<int> all_faces;
    int *final_faces = NULL;
    std::set<int> fol_faces;

    /* This will get reused for all faces, so make it once */
    point_t *all_verts = (point_t *)bu_calloc(data->brep->m_V.Count(), sizeof(point_t), "bot verts");
    for (int vi = 0; vi < data->brep->m_V.Count(); vi++) {
        VMOVE(all_verts[vi], data->brep->m_V[vi].Point());
    }

    array_to_set(&fol_faces, data->fol, data->fol_cnt);

    // Check each face to see if it is fil or fol - the first fol face, stash its
    // normal - don't even need the triangle face normal, we can just use the face's normal and
    // a point from the center of one of the fol triangles on that particular face.
    ON_3dPoint origin_pnt;
    ON_3dVector triangle_normal;
    int have_hit_pnt = 0;

    /* Get triangles from the faces */
    ON_BoundingBox vert_bbox;
    ON_MinMaxInit(&vert_bbox.m_min, &vert_bbox.m_max);
    for (int i = 0; i < data->loops_cnt; i++) {
	const ON_BrepLoop *b_loop = &(data->brep->m_L[data->loops[i]]);
	int *ffaces = NULL;
	int num_faces = subbrep_polygon_tri(data->brep, all_verts, (int *)&(b_loop->m_loop_index), 1, &ffaces);
	if (!num_faces) {
	    bu_log("Error - triangulation failed for loop %d!\n", b_loop->m_loop_index);
	    return 0;
	}
	if (!have_hit_pnt) {
	    const ON_BrepFace *b_face = b_loop->Face();
	    if (fol_faces.find(b_face->m_face_index) != fol_faces.end()) {
		ON_3dPoint p1 = data->brep->m_V[ffaces[0]].Point();
		ON_3dPoint p2 = data->brep->m_V[ffaces[1]].Point();
		ON_3dPoint p3 = data->brep->m_V[ffaces[2]].Point();
		ON_Plane fp;
		ON_Surface *ts = b_face->SurfaceOf()->Duplicate();
		(void)ts->IsPlanar(&fp, BREP_PLANAR_TOL);
		delete ts;
		triangle_normal = fp.Normal();
		if (b_face->m_bRev) triangle_normal = triangle_normal * -1;
		origin_pnt = (p1 + p2 + p3) / 3;
		have_hit_pnt = 1;
	    }
	}

	for (int f_ind = 0; f_ind < num_faces*3; f_ind++) {
	    all_faces.push_back(ffaces[f_ind]);
	    vert_bbox.Set(data->brep->m_V[ffaces[f_ind]].Point(), true);
	}
	if (ffaces) bu_free(ffaces, "free polygon face array");
	all_faces_cnt += num_faces;

    }

    /* Now we can build the final faces array */
    final_faces = (int *)bu_calloc(all_faces_cnt * 3, sizeof(int), "final bot verts");
    for (int i = 0; i < all_faces_cnt*3; i++) {
	final_faces[i] = all_faces[i];
    }

    // Scale bounding box to make sure corners are away from the volume
    vert_bbox.m_min = vert_bbox.m_min * 1.1;
    vert_bbox.m_max = vert_bbox.m_max * 1.1;

    // Pick a ray direction
    ON_3dVector rdir;
    ON_3dPoint box_corners[8];
    vert_bbox.GetCorners(box_corners);
    int have_dir = 0;
    int corner = 0;
    double dotp;
    while (!have_dir && corner < 8) {
	rdir = box_corners[corner] - origin_pnt;
	dotp = ON_DotProduct(triangle_normal, rdir);
	(NEAR_ZERO(dotp, 0.01)) ? corner++ : have_dir = 1;
    }
    if (!have_dir) {
	bu_log("Error: NONE of the corners worked??\n");
	return 0;
    }
    point_t origin, dir;
    VMOVE(origin, origin_pnt);
    VMOVE(dir, rdir);
#if 0
    std::cout << "working: " << bu_vls_addr(data->key) << "\n";
    bu_log("in origin.s sph %f %f %f 1\n", origin[0], origin[1], origin[2]);
    bu_log("in triangle_normal.s rcc %f %f %f %f %f %f 1 \n", origin_pnt.x, origin_pnt.y, origin_pnt.z, triangle_normal.x, triangle_normal.y, triangle_normal.z);
    bu_log("in ray.s rcc %f %f %f %f %f %f 1 \n", origin[0], origin[1], origin[2], dir[0], dir[1], dir[2]);
#endif
    // Test the ray against the triangle set
    int hit_cnt = 0;
    point_t p1, p2, p3, isect;
    ON_3dPointArray hit_pnts;
    for (int i = 0; i < all_faces_cnt; i++) {
	ON_3dPoint onp1, onp2, onp3, hit_pnt;
	VMOVE(p1, all_verts[all_faces[i*3+0]]);
	VMOVE(p2, all_verts[all_faces[i*3+1]]);
	VMOVE(p3, all_verts[all_faces[i*3+2]]);
	onp1.x = p1[0];
	onp1.y = p1[1];
	onp1.z = p1[2];
	onp2.x = p2[0];
	onp2.y = p2[1];
	onp2.z = p2[2];
	onp3.x = p3[0];
	onp3.y = p3[1];
	onp3.z = p3[2];
	ON_Plane fplane(onp1, onp2, onp3);
	int is_hit = bg_isect_tri_ray(origin, dir, p1, p2, p3, &isect);
	VMOVE(hit_pnt, isect);
	// Don't count the point on the ray origin
	if (hit_pnt.DistanceTo(origin_pnt) < 0.0001) is_hit = 0;
	if (is_hit) {
	    // No double-counting
	    for (int j = 0; j < hit_pnts.Count(); j++) {
		if (hit_pnts[j].DistanceTo(hit_pnt) < 0.001) is_hit = 0;
	    }
	    if (is_hit) {
		//bu_log("in hit_cnt%d.s sph %f %f %f 0.1\n", hit_pnts.Count()+1, isect[0], isect[1], isect[2]);
		hit_pnts.Append(hit_pnt);
	    }
	}
    }
    hit_cnt = hit_pnts.Count();
    //bu_log("hit count: %d\n", hit_cnt);
    //bu_log("dotp : %f\n", dotp);

    // Final inside/outside determination
    if (hit_cnt % 2) {
	io_state = (dotp > 0) ? -1 : 1;
    } else {
	io_state = (dotp < 0) ? -1 : 1;
    }

    //bu_log("inside out state: %d\n", io_state);

    bu_free(all_verts, "free top level vertex array");
    bu_free(final_faces, "free face array");
    return io_state;
}