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; }
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; }
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; }
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; }
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; }