RH_C_FUNCTION int ON_RayShooter_OneSurface(ON_3DPOINT_STRUCT _point, ON_3DVECTOR_STRUCT _direction, const ON_Surface* pConstSurface, ON_SimpleArray<ON_3dPoint>* pPoints, int maxReflections) { int rc = 0; ON_3dPoint point(_point.val[0], _point.val[1], _point.val[2]); ON_3dVector direction(_direction.val[0], _direction.val[1], _direction.val[2]); if( pConstSurface && pPoints && maxReflections>0 && point.IsValid() && direction.Unitize() ) { ON_RayShooter shooter; ON_X_EVENT hit; ON_3dPoint Q = point; ON_3dVector R = direction; ON_3dVector V[3]; for( int i=0; i<maxReflections; i++ ) { memset(&hit,0,sizeof(hit)); ON_3dVector T = R; if( !T.Unitize() ) break; if( !shooter.Shoot(Q,T,pConstSurface,hit) ) break; Q = hit.m_A[0]; pPoints->Append(Q); if( !hit.m_snodeB[0] ) break; hit.m_snodeB[0]->Evaluate(hit.m_b[0], hit.m_b[1], 1, 3, &V[0].x); ON_3dVector N = ON_CrossProduct(V[1],V[2]); if ( !N.Unitize() ) break; double d = N*T; R = T + (-2.0*d)*N; // R = reflection direction } rc = pPoints->Count(); } return rc; }
// Try to make up a 1st derivative if one is zero length static void CookDerivativesHelper( ON_3dVector& du, ON_3dVector& dv, ON_3dVector& duu, ON_3dVector& duv, ON_3dVector& dvv) { bool du_ok = du.LengthSquared() > ON_SQRT_EPSILON; bool dv_ok = dv.LengthSquared() > ON_SQRT_EPSILON; if( !du_ok || !dv_ok) { ON_3dVector normal; bool normal_ok = ON_EvNormal( 0, du, dv, duu, duv, dvv, normal ) ? true : false; if( normal_ok) normal_ok = normal.LengthSquared() > ON_SQRT_EPSILON; if( normal_ok) { if(( !du_ok) && ( dv_ok && normal_ok)) { du = ON_CrossProduct( dv, normal); du_ok = du.Unitize(); du *= (0.00390625*dv.Length()); } if( du_ok && ( !dv_ok) && normal_ok) { dv = ON_CrossProduct( normal, du); dv_ok = dv.Unitize(); dv *= (0.00390625*du.Length()); } } } }
ON_3dPoint ON_Torus::ClosestPointTo( ON_3dPoint test_point ) const { const ON_Circle major_circle(plane,major_radius); ON_3dPoint C = major_circle.ClosestPointTo( test_point ); ON_3dVector v = test_point - C; if ( !v.Unitize() ) { v = C - plane.origin; v.Unitize(); } return C + minor_radius*v; }
ON_3dPoint ON_Circle::ClosestPointTo( const ON_3dPoint& point ) const { ON_3dPoint P; ON_3dVector V = plane.ClosestPointTo( point ) - Center(); if ( V.Unitize() ) { V.Unitize(); P = Center() + Radius()*V; } else { P = PointAt(0.0); } return P; }
ON_3dVector ON_Light::PerpindicularDirection() const { // returns a consistent vector perpendicular to the // light's direction. This vector is useful for // user interface display. ON_3dVector dir = m_direction; if ( !dir.IsValid() || !dir.Unitize() ) return ON_UNSET_VECTOR; ON_3dVector xdir; if ( IsLinearLight() || IsRectangularLight() ) { xdir = m_length; if ( xdir.IsValid() && xdir.Unitize() && fabs(xdir*dir) <= ON_SQRT_EPSILON ) return xdir; } if( dir.IsParallelTo( ON_zaxis, ON_DEGREES_TO_RADIANS * 3.0)) xdir = ON_CrossProduct( dir, ON_xaxis); else xdir = ON_CrossProduct( dir, ON_zaxis); xdir.Unitize(); ON_3dVector ydir = ON_CrossProduct(dir,xdir); ydir.Unitize(); ON_3dVector right; switch(dir.MaximumCoordinateIndex()) { case 0: right = (fabs(xdir.y) > fabs(ydir.y)) ? xdir : ydir; if ( right.y < 0.0 ) right.Reverse(); break; case 1: case 2: right = (fabs(xdir.x) > fabs(ydir.x)) ? xdir : ydir; if ( right.x < 0.0 ) right.Reverse(); break; default: right = xdir; break; } if ( right[right.MaximumCoordinateIndex()] < 0.0 ) right.Reverse(); return right; }
ON_BOOL32 ON_Torus::ClosestPointTo( ON_3dPoint test_point, double* major__angle_radians, double* minor__angle_radians ) const { double major_angle_radians, minor_angle_radians; const ON_Circle major_circle(plane,major_radius); ON_BOOL32 rc = major_circle.ClosestPointTo( test_point, &major_angle_radians ); if ( rc && minor__angle_radians ) { EVAL_SETUP_MAJOR; ON_3dVector v = test_point - major_radius*raxis; rc = v.Unitize(); if ( rc ) { double sma = v*plane.zaxis; double cma = v*raxis; minor_angle_radians = atan2(sma,cma); if ( minor_angle_radians < 0.0 ) minor_angle_radians += 2.0*ON_PI; } else minor_angle_radians = 0.0; *minor__angle_radians = minor_angle_radians; } if ( major__angle_radians ) *major__angle_radians = major_angle_radians; return rc; }
bool ON_PolyEdgeCurve::EvSrfTangent( double t, bool bIsoDir, ON_3dPoint& srfpoint, ON_3dVector& srftangent, ON_3dVector& srfnormal ) const { ON_3dPoint srfpt; ON_3dVector du, dv, duu, duv, dvv; bool rc = EvSrfDerivatives(t,srfpoint,du,dv,duu,duv,dvv); if (rc ) rc = ON_EvNormal( 0, du, dv, duu, duv, dvv, srfnormal ) ? true : false; if (rc) { int segment_index = SegmentIndex(t); ON_PolyEdgeSegment* seg = SegmentCurve(segment_index); if ( seg ) { if ( bIsoDir && seg->IsoType() == ON_Surface::not_iso ) bIsoDir = false; ON_3dVector crvtangent = TangentAt(t); ON_3dVector binormal = ON_CrossProduct(crvtangent,srfnormal); binormal.Unitize(); if ( seg->ReversedTrimDir() ) binormal.Reverse(); // at this point, binormal points "out" and is tangent // to the surface. if ( bIsoDir ) { du.Unitize(); dv.Unitize(); double B_dot_du = binormal*du; double B_dot_dv = binormal*dv; if ( fabs(B_dot_dv) > fabs(B_dot_du) ) { if (B_dot_dv < 0.0) dv.Reverse(); srftangent = dv; } else { if (B_dot_du < 0.0) du.Reverse(); srftangent = du; } } else srftangent = binormal; if ( seg && seg->m_face && seg->m_face->m_bRev ) srfnormal.Reverse(); } else rc = false; } return rc; }
ON_3dVector ON_Ellipse::TangentAt( double t // parameter ) const { ON_3dVector T = DerivativeAt( 1, t ); T.Unitize(); return T; }
bool ON_Quaternion::GetRotation(double& angle, ON_3dVector& axis) const { const double s = Length(); angle = (s > ON_DBL_MIN) ? 2.0*acos(a/s) : 0.0; axis.x = b; axis.y = c; axis.z = d; return (axis.Unitize() && s > ON_DBL_MIN); }
void arbaxis(const ON_3dVector& givenaxis, ON_3dVector& newaxis) { if(fabs(givenaxis[0]) < ARBBOUND && fabs(givenaxis[1]) < ARBBOUND) // near world z newaxis = ON_CrossProduct(ON_yaxis, givenaxis); else newaxis = ON_CrossProduct(ON_zaxis, givenaxis); newaxis.Unitize(); }
static double Angle3d(const ON_3dVector& axis, ON_3dVector& from, const ON_3dVector& to) { ON_3dVector x = from, a = to; x.Unitize(); a.Unitize(); ON_3dVector y = ON_CrossProduct(axis, from); y.Unitize(); double cosa = x * a; if(cosa > 1.0 - ON_SQRT_EPSILON) return 0.0; if(cosa < ON_SQRT_EPSILON - 1.0) return ON_PI; double sina = a * y; return atan2(sina, cosa); }
bool ON_Cone::ClosestPointTo( ON_3dPoint point, double* radial_parameter, double* height_parameter ) const { // untested code bool rc = false; ON_3dVector v = (point-plane.origin); double x = v*plane.xaxis; double y = v*plane.yaxis; double z = v*plane.zaxis; if ( radial_parameter ) { double a = ( 0.0 == y && 0.0 == x ) ? 0.0 : atan2(y,x); if (a > 2.0*ON_PI ) { a -= 2.0*ON_PI; } if (a < 0.0 ) { a += 2.0*ON_PI; } *radial_parameter = a; } if (height_parameter) { point.x -= plane.origin.x; point.y -= plane.origin.y; point.z -= plane.origin.z; v.x = x; v.y = y; v.z = 0.0; v.Unitize(); v.x *= radius; v.y *= radius; ON_Line line(ON_origin, v.x*plane.xaxis + v.y*plane.yaxis + height*plane.zaxis ); rc = line.ClosestPointTo(point,&z); if (rc) { *height_parameter = z*height; } } return rc; }
ON_3dVector ON_Cone::NormalAt( double radial_parameter, double height_parameter ) const { double s = sin(radial_parameter); double c = cos(radial_parameter); if ( radius<0.) { c = -c; s = -s; } ON_3dVector ds = c*plane.yaxis - s*plane.xaxis; ON_3dVector N = ON_CrossProduct( ((radius<0.0)?-ds:ds), plane.PointAt(radius*c,radius*s,height) - plane.origin ); N.Unitize(); return N; }
RH_C_FUNCTION ON_AngularDimension2* ON_AngularDimension2_New(ON_Arc* arc, double offset) { ON_AngularDimension2* rc = NULL; if( arc ) { rc = new ON_AngularDimension2(); ON_3dVector v = arc->StartPoint()-arc->Center(); v.Unitize(); ON_3dPoint apex = arc->Center(); ON_3dPoint p0 = arc->StartPoint(); ON_3dPoint p1 = arc->EndPoint(); ON_3dPoint arc_pt = p0 + ( v * offset ); ON_3dVector normal = arc->Normal(); rc->CreateFromPoints(apex, p0, p1, arc_pt, normal); } return rc; }
int ON_Intersect( // returns 0 = no intersections, // 1 = one intersection, // 2 = 2 intersections // If 0 is returned, first point is point // on line closest to sphere and 2nd point is the point // on the sphere closest to the line. // If 1 is returned, first point is obtained by evaluating // the line and the second point is obtained by evaluating // the sphere. const ON_Line& line, const ON_Sphere& sphere, ON_3dPoint& A, ON_3dPoint& B // intersection point(s) returned here ) { int rc = 0; const ON_3dPoint sphere_center = sphere.plane.origin; const double sphere_radius = fabs(sphere.radius); double tol = sphere_radius*ON_SQRT_EPSILON; if ( tol < ON_ZERO_TOLERANCE ) tol = ON_ZERO_TOLERANCE; ON_3dPoint line_center = line.ClosestPointTo(sphere_center); double d = line_center.DistanceTo(sphere_center); if ( d >= sphere_radius-tol ) { rc = ( d <= sphere_radius-tol ) ? 1 : 0; A = line_center; B = sphere.ClosestPointTo(line_center); } else { d /= sphere_radius; double h = sphere_radius*sqrt(1.0 - d*d); ON_3dVector V = line.Direction(); V.Unitize(); A = sphere.ClosestPointTo(line_center - h*V); B = sphere.ClosestPointTo(line_center + h*V); d = A.DistanceTo(B); if ( d <= ON_ZERO_TOLERANCE ) { A = line_center; B = sphere.ClosestPointTo(line_center); rc = 1; } else rc = 2; } return rc; }
bool ON_Line::InPlane( ON_Plane& plane, double tolerance ) const { const ON_3dVector v = to-from; const bool bTinyX = fabs(v.x) <= tolerance; const bool bTinyY = fabs(v.y) <= tolerance; const bool bTinyZ = fabs(v.z) <= tolerance; bool rc = true; ON_3dVector X; ON_3dVector Y; if ( bTinyZ && ( !bTinyX || !bTinyY ) ) { X = ON_xaxis; Y = ON_yaxis; } else if ( bTinyX && ( !bTinyY || !bTinyZ ) ) { X = ON_yaxis; Y = ON_zaxis; } else if ( bTinyY && ( !bTinyZ || !bTinyX ) ) { X = ON_zaxis; Y = ON_xaxis; } else { X = v; X.Unitize(); Y.PerpendicularTo(X); if ( bTinyX && bTinyY && bTinyZ ) { rc = false; if ( X.IsZero() ) { X = ON_xaxis; Y = ON_yaxis; } } } plane.CreateFromFrame( from, X, Y ); return rc; }
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_Plane::GetDistanceToBoundingBox(const ON_BoundingBox& Box, double* min, double* max) const { //min and max signed distance. Returns false if plane normal has zero length. ON_3dVector UnitNormal = Normal(); if (!UnitNormal.Unitize()) return false; double mind, maxd; mind = maxd = (Box.Min() - Origin())*UnitNormal; int i0, i1, i2; for (i0=0;i0<2;i0++) { for(i1=0;i1<2;i1++) { for (i2=0;i2<2;i2++) { if (i0||i1||i2) { ON_3dPoint P; P[0]=(i0)?Box.Max()[0]:Box.Min()[0]; P[1]=(i1)?Box.Max()[1]:Box.Min()[1]; P[2]=(i2)?Box.Max()[2]:Box.Min()[2]; double d = (P - Origin())*UnitNormal; //double dd = P.DistanceTo(ClosestPointTo(P)); if (d < mind) mind=d; else if (d > maxd) maxd=d; } } } } *min = mind; *max = maxd; return true; }
// returns point on cylinder that is closest to given point ON_3dPoint ON_Cone::ClosestPointTo( ON_3dPoint point ) const { // untested code ON_3dVector v = (point-plane.origin); double x = v*plane.xaxis; double y = v*plane.yaxis; //double z = v*plane.zaxis; point.x -= plane.origin.x; point.y -= plane.origin.y; point.z -= plane.origin.z; v.x = x; v.y = y; v.z = 0.0; v.Unitize(); v.x *= radius; v.y *= radius; ON_Line line(ON_origin, v.x*plane.xaxis + v.y*plane.yaxis + height*plane.zaxis ); return line.ClosestPointTo(point); }
ON_3dVector ON_Polyline::SegmentTangent( int segment_index ) const { ON_3dVector v = SegmentDirection(segment_index); v.Unitize(); return v; }
int ON_Intersect( const ON_Sphere& sphere0, const ON_Sphere& sphere1, ON_Circle& circle ) { double r0 = sphere0.Radius(); double r1 = sphere1.Radius(); ON_3dPoint C0 = sphere0.Center(); ON_3dPoint C1 = sphere1.Center(); ON_3dVector D = C1-C0; double d = D.Length(); if (!D.Unitize()){ if (fabs(r1-r0) > ON_ZERO_TOLERANCE) return 0;//Same center, different radii return 3;//Same sphere. } //Spheres are appart. if (d > r0 + r1) return 0; //Spheres tangent and appart if (d == r0+r1){ ON_3dPoint P = C0 + r0*D; circle.Create(P, 0.0); return 1; } //Spheres tangent, one inside the other if (d == fabs(r0-r1)){ ON_3dPoint P = (r0 > r1) ? C0 + r0*D : C0 - r0*D; circle.Create(P, 0.0); return 1; } //Spheres don't intersect, one inside the other. if (d < fabs(r0-r1)) return 0; //Intersection is a circle double x = 0.5*(d*d + r0*r0 - r1*r1)/d; if (x >= r0){//Shouldn't happen ON_3dPoint P = C0 + r0*D; circle.Create(P, 0.0); return 1; } if (x <= -r0){//Shouldn't happen ON_3dPoint P = C0 - r0*D; circle.Create(P, 0.0); return 1; } double y = r0*r0 - x*x; if (y < 0.0)//Shouldn't happen return 0; y = sqrt(y); ON_3dPoint P = C0 + x*D; ON_Plane plane(P, D); circle.Create(plane, y); return 2; }
int ON_Intersect( // returns 0 = no intersections, // 1 = one intersection, // 2 = 2 intersections // 3 = line lies on cylinder // If 0 is returned, first point is point // on line closest to cylinder and 2nd point is the point // on the sphere closest to the line. // If 1 is returned, first point is obtained by evaluating // the line and the second point is obtained by evaluating // the cylinder. const ON_Line& line, const ON_Cylinder& cylinder, // if cylinder.height[0]==cylinder.height[1], // then infinite cyl is used. Otherwise // finite cyl is used. ON_3dPoint& A, ON_3dPoint& B // intersection point(s) returned here ) { ON_BOOL32 bFiniteCyl = true; int rc = 0; const double cylinder_radius = fabs(cylinder.circle.radius); double tol = cylinder_radius*ON_SQRT_EPSILON; if ( tol < ON_ZERO_TOLERANCE ) tol = ON_ZERO_TOLERANCE; ON_Line axis; axis.from = cylinder.circle.plane.origin + cylinder.height[0]*cylinder.circle.plane.zaxis; axis.to = cylinder.circle.plane.origin + cylinder.height[1]*cylinder.circle.plane.zaxis; if ( axis.Length() <= tol ) { axis.to = cylinder.circle.plane.origin + cylinder.circle.plane.zaxis; bFiniteCyl = false; } //ON_BOOL32 bIsParallel = false; double line_t, axis_t; if ( !ON_Intersect(line,axis,&line_t,&axis_t) ) { axis.ClosestPointTo( cylinder.circle.plane.origin, &axis_t ); line.ClosestPointTo( cylinder.circle.plane.origin, &line_t ); } ON_3dPoint line_point = line.PointAt(line_t); ON_3dPoint axis_point = axis.PointAt(axis_t); double d = line_point.DistanceTo(axis_point); if ( bFiniteCyl ) { if ( axis_t < 0.0 ) axis_t = 0.0; else if ( axis_t > 1.0 ) axis_t = 1.0; axis_point = axis.PointAt(axis_t); } if ( d >= cylinder_radius-tol) { rc = ( d <= cylinder_radius+tol ) ? 1 : 0; A = line_point; ON_3dVector V = line_point - axis_point; if ( bFiniteCyl ) { V = V - (V*cylinder.circle.plane.zaxis)*cylinder.circle.plane.zaxis; } V.Unitize(); B = axis_point + cylinder_radius*V; if ( rc == 1 ) { // check for overlap ON_3dPoint P = axis.ClosestPointTo(line.from); d = P.DistanceTo(line.from); if ( fabs(d-cylinder_radius) <= tol ) { P = axis.ClosestPointTo(line.to); d = P.DistanceTo(line.to); if ( fabs(d-cylinder_radius) <= tol ) { rc = 3; A = cylinder.ClosestPointTo(line.from); B = cylinder.ClosestPointTo(line.to); } } } } else { // transform to coordinate system where equation of cyl // is x^2 + y^2 = R^2 and solve for line parameter(s). ON_Xform xform; xform.Rotation( cylinder.circle.plane, ON_xy_plane ); ON_Line L = line; L.Transform(xform); const double x0 = L.from.x; const double x1 = L.to.x; const double x1mx0 = x1-x0; double ax = x1mx0*x1mx0; double bx = 2.0*x1mx0*x0; double cx = x0*x0; const double y0 = L.from.y; const double y1 = L.to.y; const double y1my0 = y1-y0; double ay = y1my0*y1my0; double by = 2.0*y1my0*y0; double cy = y0*y0; double t0, t1; int qerc = ON_SolveQuadraticEquation(ax+ay, bx+by, cx+cy-cylinder_radius*cylinder_radius, &t0,&t1); if ( qerc == 2 ) { // complex roots - ignore (tiny) imaginary part caused by computational noise. t1 = t0; } A = cylinder.ClosestPointTo(line.PointAt(t0)); B = cylinder.ClosestPointTo(line.PointAt(t1)); d = A.DistanceTo(B); if ( d <= ON_ZERO_TOLERANCE ) { A = line_point; ON_3dVector V = line_point - axis_point; if ( bFiniteCyl ) { V = V - (V*cylinder.circle.plane.zaxis)*cylinder.circle.plane.zaxis; } V.Unitize(); B = axis_point + cylinder_radius*V; rc = 1; } else rc = 2; } return rc; }
RH_C_FUNCTION int ON_RayShooter_ShootRay(ON_3DPOINT_STRUCT _point, ON_3DVECTOR_STRUCT _direction, const ON_SimpleArray<const ON_Geometry*>* pConstGeometryArray, ON_SimpleArray<ON_3dPoint>* pPoints, int maxReflections) { int rc = 0; ON_3dPoint point(_point.val[0], _point.val[1], _point.val[2]); ON_3dVector direction(_direction.val[0], _direction.val[1], _direction.val[2]); // Currently only supports surfaces and breps with untrimmed faces. // Add support for meshes and trimmed breps int count = pConstGeometryArray?pConstGeometryArray->Count():0; if( count<1 ) return 0; ON_SimpleArray<const ON_SurfaceTreeNode*> snode_list(count); for ( int i=0; i<count; i++ ) { const ON_Geometry* pGeometry = (*pConstGeometryArray)[i]; const ON_Surface* surface = ON_Surface::Cast(pGeometry); if ( surface ) { const ON_SurfaceTree* stree = surface->SurfaceTree(); if ( stree ) snode_list.Append(stree); continue; } const ON_Brep* brep = ON_Brep::Cast(pGeometry); if( brep ) { for( int fi=0; fi<brep->m_F.Count(); fi++ ) { const ON_SurfaceTree* stree = brep->m_F[fi].SurfaceTree(); if( stree ) snode_list.Append(stree); } continue; } } if( snode_list.Count()<1 ) return 0; if( pPoints && maxReflections>0 && point.IsValid() && direction.Unitize() ) { ON_RayShooter shooter; ON_X_EVENT hit; ON_3dPoint Q = point; ON_3dVector R = direction; ON_3dVector V[3]; for( int i=0; i<maxReflections; i++ ) { memset(&hit,0,sizeof(hit)); ON_3dVector T = R; if( !T.Unitize() ) break; if( !shooter.Shoot(Q,T,snode_list,hit) ) break; Q = hit.m_A[0]; pPoints->Append(Q); if( !hit.m_snodeB[0] ) break; ON_3dVector N = hit.m_B[1]; // surface normal double d = -2.0*(N.x*T.x + N.y*T.y + N.z*T.z); R.x = T.x + d*N.x; R.y = T.y + d*N.y; R.z = T.z + d*N.z; // Part of the fix for RR 22717. See opennurbs_plus_xray.cpp // for the rest of the fix. d = hit.m_A[0].DistanceTo( hit.m_B[0] ); shooter.m_min_travel_distance = d; if( shooter.m_min_travel_distance < 1.0e-8 ) shooter.m_min_travel_distance = 1.0e-8; } rc = pPoints->Count(); } return rc; }
// Copied from opennurbs_intersect.cpp but with a bug fix. // We can remove it once the bug is fixed in OpenNurbs and once // Grasshopper has dropped Rhino4 support. int PS_Intersect( const ON_Plane& plane, const ON_Sphere& sphere, ON_Circle& circle ) { // 16 April 2011 Dale Lear // Prior to this date, this function did not return the correct answer. int rc = 0; const double sphere_radius = fabs(sphere.radius); double tol = sphere_radius*ON_SQRT_EPSILON; if ( !(tol >= ON_ZERO_TOLERANCE) ) tol = ON_ZERO_TOLERANCE; const ON_3dPoint sphere_center = sphere.Center(); ON_3dPoint circle_center = plane.ClosestPointTo(sphere_center); double d = circle_center.DistanceTo(sphere_center); circle.radius = 0.0; if ( ON_IsValid(sphere_radius) && ON_IsValid(d) && d <= sphere_radius + tol ) { if ( sphere_radius > 0.0 ) { d /= sphere_radius; d = 1.0 - d*d; // The d > 4.0*ON_EPSILON was picked by testing spheres with // radius = 1 and center = (0,0,0). Do not make 4.0*ON_EPSILON // any smaller and please discuss changes with Dale Lear. circle.radius = (d > 4.0*ON_EPSILON) ? sphere_radius*sqrt(d) : 0.0; } else circle.radius = 0.0; if ( circle.radius <= ON_ZERO_TOLERANCE ) { // return a single point rc = 1; circle.radius = 0.0; // When tolerance is in play, put the point on the sphere. // If the caller prefers the plane, then they can adjust the // returned answer to get the plane. ON_3dVector R = circle_center - sphere_center; double r0 = R.Length(); if ( r0 > 0.0 ) { R.Unitize(); ON_3dPoint C1 = sphere_center + sphere_radius*R; double r1 = C1.DistanceTo(sphere_center); if ( fabs(sphere.radius-r1) < fabs(sphere.radius-r0) ) circle_center = C1; } } else { // return a circle rc = 2; } } // Update circle's plane here in case the input plane // is the circle's plane member. circle.plane = plane; circle.plane.origin = circle_center; circle.plane.UpdateEquation(); return rc; }
ON_BOOL32 ON_Ellipse::ClosestPointTo( const ON_3dPoint& point, double* t ) const { ON_BOOL32 rc = true; if ( t ) { ON_2dPoint uv; rc = plane.ClosestPointTo( point, &uv.x, &uv.y ); if ( uv.x == 0.0 ) { if ( uv.y == 0.0 ) { *t = (radius[0] <= radius[1]) ? 0.0 : 0.5*ON_PI; return true; } if ( uv.y >= radius[1] ) { *t = 0.5*ON_PI; return true; } if ( uv.y <= -radius[1] ) { *t = 1.5*ON_PI; return true; } } else if ( uv.y == 0.0 ) { if ( uv.x >= radius[0] ) { *t = 0.0; return true; } if ( uv.x <= -radius[0] ) { *t = ON_PI; return true; } } { // use circluar approximation to get a seed value double t0, t1; *t = atan2( uv.y, uv.x ); if ( *t < 0.0 ) { *t += 2.0*ON_PI; if ( 2.0*ON_PI <= *t) { // == happens when atan2() <= 0.5*ON_EPSILON*2.0*PI *t = 0.0; } } if ( radius[0] != radius[1] ) { // set limits for search if ( uv.x >= 0.0 ) { if ( uv.y >= 0.0 ) { // search quadrant I t0 = 0.0; t1 = 0.5*ON_PI; } else { // search quadrant IV t0 = 1.5*ON_PI; t1 = 2.0*ON_PI; } } else { if ( uv.y >= 0.0 ) { // search quadrant II t0 = 0.5*ON_PI; t1 = ON_PI; } else { // search quadrant III t0 = ON_PI; t1 = 1.5*ON_PI; } } // solve for closest point using Brent's algorithm { // 6 October 2003 Dale Lear: // Fixed several serious bugs here. // get seed value appropriate for Brent double p[4], et, d0, d1, dt; int i; p[0] = radius[0]; p[1] = radius[1]; p[2] = uv.x; p[3] = uv.y; et = *t; if ( et <= t0 ) et = 0.9*t0 + 0.1*t1; else if ( et >= t1 ) et = 0.9*t1 + 0.1*t0; distSqToEllipse( p, t0, &d0, NULL ); distSqToEllipse( p, t1, &d1, NULL ); if ( d0 == 0.0 ) { *t = (t0 == 2.0*ON_PI) ? 0.0 : t0; return true; } if ( d1 == 0.0 ) { *t = (t1 == 2.0*ON_PI) ? 0.0 : t1; return true; } if ( d0 > d1 ) { dt = t0; t0 = t1; t1 = dt; dt = d0; d0 = d1; d1 = dt; } *t = (t0 == 2.0*ON_PI) ? 0.0 : t0; for ( i = 0; true; i++ ) { distSqToEllipse( p, et, &dt, NULL ); if ( dt < d0 ) { *t = (et >= 2.0*ON_PI) ? 0.0 : et; break; } if ( i >= 100 ) { ON_3dPoint E0 = PointAt(t0); if ( sqrt(d0) <= ON_ZERO_TOLERANCE || sqrt(d0) <= ON_SQRT_EPSILON*E0.DistanceTo(Center()) ) { // Could not find a seed value for dbrent, // but t0 is pretty close. return true; } ON_3dVector T = TangentAt(t0); ON_3dVector V = E0 - point; if ( V.Unitize() ) { // Could not find a seed value for dbrent, // but V and T are orthoganal, so t0 is // pretty close. if ( fabs(V*T) <= 0.087155742747658173558064270837474 ) return true; } return false; // can't get valid seed - bail out } et = (i) ? (0.5*(t0+et)) : 0.5*(t0+t1); if ( et == t0 ) { return true; } } rc = ON_FindLocalMinimum( distSqToEllipse, p, t0, et, t1, ON_EPSILON, ON_SQRT_EPSILON, 100, &et ); rc = (rc > 0) ? true : false; if ( rc ) *t = (et >= 2.0*ON_PI) ? 0.0 : et; } } } } return rc; }
bool ON_Mesh::CollapseEdge( int topei ) { ON_Mesh& mesh = *this; ON__MESHEDGE me; memset(&me,0,sizeof(me)); const ON_MeshTopology& top = mesh.Topology(); const int F_count = mesh.m_F.Count(); const int V_count = mesh.m_V.Count(); const int topv_count = top.m_topv.Count(); //const int tope_count = top.m_tope.Count(); if ( topei < 0 || topei >= top.m_tope.Count() ) { return false; } const ON_MeshTopologyEdge& tope = top.m_tope[topei]; if ( tope.m_topf_count < 1 || tope.m_topvi[0] == tope.m_topvi[1] || tope.m_topvi[0] < 0 || tope.m_topvi[1] < 0 || tope.m_topvi[0] >= topv_count || tope.m_topvi[1] >= topv_count ) { return false; } const ON_MeshTopologyVertex& topv0 = top.m_topv[tope.m_topvi[0]]; const ON_MeshTopologyVertex& topv1 = top.m_topv[tope.m_topvi[1]]; if ( topv0.m_v_count < 1 || topv1.m_v_count < 1 ) { return false; } if ( topv0.m_vi[0] < 0 || topv0.m_vi[0] >= V_count ) { return false; } if ( topv1.m_vi[0] < 0 || topv1.m_vi[0] >= V_count ) { return false; } // create a ON__MESHEDGE for each face (usually one or two) that uses the edge ON__MESHEDGE* me_list = (ON__MESHEDGE*)alloca(tope.m_topf_count*sizeof(me_list[0])); int me_list_count = 0; int efi; for ( efi = 0; efi < tope.m_topf_count; efi++ ) { int fi = tope.m_topfi[efi]; if ( fi < 0 || fi >= F_count ) continue; const ON_MeshFace& f = mesh.m_F[fi]; if ( !f.IsValid(V_count) ) continue; me.vi1 = f.vi[3]; me.topvi1 = top.m_topv_map[me.vi1]; int fvi; for ( fvi = 0; fvi < 4; fvi++ ) { me.vi0 = me.vi1; me.topvi0 = me.topvi1; me.vi1 = f.vi[fvi]; me.topvi1 = top.m_topv_map[me.vi1]; if ( me.vi0 != me.vi1 ) { if ( (me.topvi0 == tope.m_topvi[0] && me.topvi1 == tope.m_topvi[1]) || (me.topvi0 == tope.m_topvi[1] && me.topvi1 == tope.m_topvi[0]) ) { if ( me.vi0 > me.vi1 ) { int i = me.vi0; me.vi0 = me.vi1; me.vi1 = i; i = me.topvi0; me.topvi0 = me.topvi1; me.topvi1 = i; } me_list[me_list_count++] = me; break; } } } } if (me_list_count<1) { return false; } // Sort me_list[] so edges using same vertices are adjacent // to each other in the list. This is needed so that non-manifold // crease edges will be properly collapsed. ON_qsort(me_list,me_list_count,sizeof(me_list[0]),(QSORTCMPFUNC)CompareMESHEDGE); // create new vertex or vertices that edge will be // collapsed to. mesh.m_C.Destroy(); mesh.m_K.Destroy(); int mei; bool bHasVertexNormals = mesh.HasVertexNormals(); bool bHasTextureCoordinates = mesh.HasTextureCoordinates(); bool bHasFaceNormals = mesh.HasFaceNormals(); if ( topv0.m_v_count == 1 || topv1.m_v_count == 1 ) { // a single new vertex ON_Line Vline(ON_origin,ON_origin); ON_Line Tline(ON_origin,ON_origin); ON_3dVector N0(0,0,0); ON_3dVector N1(0,0,0); ON_3dPoint P; int vi, tvi, cnt; int newvi = topv0.m_vi[0]; cnt = 0; for ( tvi = 0; tvi < topv0.m_v_count; tvi++ ) { vi = topv0.m_vi[tvi]; if ( vi < 0 || vi > V_count ) continue; if ( vi < newvi ) newvi = vi; cnt++; P = mesh.m_V[vi]; Vline.from += P; if ( bHasVertexNormals ) { N0 += ON_3dVector(mesh.m_N[vi]); } if ( bHasTextureCoordinates ) { P = mesh.m_T[vi]; Tline.from += P; } } if (cnt > 1) { double s = 1.0/((double)cnt); Vline.from.x *= s; Vline.from.y *= s; Vline.from.z *= s; Tline.from.x *= s; Tline.from.y *= s; Tline.from.z *= s; N0 = s*N0; } cnt = 0; for ( tvi = 0; tvi < topv1.m_v_count; tvi++ ) { vi = topv1.m_vi[tvi]; if ( vi < 0 || vi > V_count ) continue; if ( vi < newvi ) newvi = vi; cnt++; P = mesh.m_V[vi]; Vline.to += P; if ( bHasVertexNormals ) { N1 += ON_3dVector(mesh.m_N[vi]); } if ( bHasTextureCoordinates ) { P = mesh.m_T[vi]; Tline.to += P; } } if (cnt > 1) { double s = 1.0/((double)cnt); Vline.to.x *= s; Vline.to.y *= s; Vline.to.z *= s; Tline.to.x *= s; Tline.to.y *= s; Tline.to.z *= s; N1 = s*N1; } ON_3fPoint newV(Vline.PointAt(0.5)); ON_3fVector newN; ON_2fPoint newT; if ( bHasVertexNormals ) { N0.Unitize(); N1.Unitize(); ON_3dVector N = N0+N1; if ( !N.Unitize() ) { N = (topv0.m_v_count == 1) ? mesh.m_N[topv0.m_vi[0]] :mesh.m_N[topv1.m_vi[0]]; } newN = N; } if ( bHasTextureCoordinates ) { newT = Tline.PointAt(0.5); } for ( mei = 0; mei < me_list_count; mei++ ) { me_list[mei].newvi = newvi; me_list[mei].newV = newV; me_list[mei].newN = newN; me_list[mei].newT = newT; } } else { // collapsing a "crease" edge - attempt to preserve // the crease. memset(&me,0,sizeof(me)); me.vi0 = -1; me.vi1 = -1; for ( mei = 0; mei < me_list_count; mei++ ) { if ( 0 == mei && CompareMESHEDGE(&me,me_list+mei) ) { // cook up new vertex me_list[mei].newvi = mesh.m_V.Count(); me = me_list[mei]; ON_Line line; line.from = mesh.m_V[me.vi0]; line.to = mesh.m_V[me.vi1]; me.newV = line.PointAt(0.5); if ( bHasVertexNormals ) { ON_3dVector N0(mesh.m_N[me.vi0]); ON_3dVector N1(mesh.m_N[me.vi1]); ON_3dVector N = N0 + N1; if ( !N.Unitize() ) N = N0; me.newN = N; } if ( bHasTextureCoordinates ) { line.from = mesh.m_T[me.vi0]; line.to = mesh.m_T[me.vi1]; me.newT = line.PointAt(0.5); } me.newvi = (me.vi0 < me.vi1) ? me.vi0 : me.vi1; } else { me_list[mei].newvi = me.newvi; me_list[mei].newV = me.newV; me_list[mei].newN = me.newN; me_list[mei].newT = me.newT; } } } // We are done averaging old mesh values. // Change values in mesh m_V[], m_N[] and m_T[] arrays. for ( mei = 0; mei < me_list_count; mei++ ) { mesh.m_V[me_list[mei].vi0] = me_list[mei].newV; mesh.m_V[me_list[mei].vi1] = me_list[mei].newV; if ( bHasVertexNormals ) { mesh.m_N[me_list[mei].vi0] = me_list[mei].newN; mesh.m_N[me_list[mei].vi1] = me_list[mei].newN; } if ( bHasTextureCoordinates ) { mesh.m_T[me_list[mei].vi0] = me_list[mei].newT; mesh.m_T[me_list[mei].vi1] = me_list[mei].newT; } } // make a map of old to new int old2new_map_count = 0; ON__NEWVI* old2new_map = (ON__NEWVI*)alloca(2*me_list_count*sizeof(old2new_map[0])); for ( mei = 0; mei < me_list_count; mei++ ) { old2new_map[old2new_map_count].oldvi = me_list[mei].vi0; old2new_map[old2new_map_count].newvi = me_list[mei].newvi; old2new_map_count++; old2new_map[old2new_map_count].oldvi = me_list[mei].vi1; old2new_map[old2new_map_count].newvi = me_list[mei].newvi; old2new_map_count++; } // sort old2new_map[] so we can use a fast bsearch() call // to update faces. ON_qsort(old2new_map,old2new_map_count,sizeof(old2new_map[0]),(QSORTCMPFUNC)CompareNEWVI); // count faces that use the vertices that are being changed int bad_fi_count = 0; int topv_end, vei, fi, fvi23, fvi; ON__NEWVI nvi; for ( topv_end = 0; topv_end < 2; topv_end++ ) { const ON_MeshTopologyVertex& topv = (topv_end) ? topv1 : topv0; for ( vei = 0; vei < topv.m_tope_count; vei++ ) { topei = topv.m_topei[vei]; if ( topei < 0 && topei >= top.m_tope.Count() ) continue; bad_fi_count += top.m_tope[topei].m_topf_count; } } int* bad_fi = (int*)alloca(bad_fi_count*sizeof(*bad_fi)); bad_fi_count = 0; // Go through all the faces that use the vertices at the // ends of the edge and update the vi[] values to use the // new vertices. for ( topv_end = 0; topv_end < 2; topv_end++ ) { const ON_MeshTopologyVertex& topv = (topv_end) ? topv1 : topv0; for ( vei = 0; vei < topv.m_tope_count; vei++ ) { topei = topv.m_topei[vei]; if ( topei < 0 && topei >= top.m_tope.Count() ) continue; const ON_MeshTopologyEdge& e = top.m_tope[topei]; for ( efi = 0; efi < e.m_topf_count; efi++ ) { fi = e.m_topfi[efi]; if ( fi < 0 || fi >= F_count ) continue; bool bChangedFace = false; ON_MeshFace& f = mesh.m_F[fi]; for ( fvi = 0; fvi < 4; fvi++ ) { nvi.oldvi = f.vi[fvi]; ON__NEWVI* p = (ON__NEWVI*)bsearch(&nvi,old2new_map,old2new_map_count,sizeof(old2new_map[0]),(QSORTCMPFUNC)CompareNEWVI); if ( 0 != p && p->oldvi != p->newvi) { f.vi[fvi] = p->newvi; bChangedFace = true; } } if ( bChangedFace ) { if ( !f.IsValid(V_count) ) { if ( f.vi[3] == f.vi[0] ) { f.vi[0] = f.vi[1]; f.vi[1] = f.vi[2]; f.vi[2] = f.vi[3]; } else if ( f.vi[0] == f.vi[1] ) { fvi23 = f.vi[0]; f.vi[0] = f.vi[2]; f.vi[1] = f.vi[3]; f.vi[2] = fvi23; f.vi[3] = fvi23; } else if ( f.vi[1] == f.vi[2] ) { fvi23 = f.vi[1]; f.vi[1] = f.vi[0]; f.vi[0] = f.vi[3]; f.vi[2] = fvi23; f.vi[3] = fvi23; } if ( f.vi[0] == f.vi[1] || f.vi[1] == f.vi[2] || f.vi[2] == f.vi[0] || f.vi[2] != f.vi[3] ) { bad_fi[bad_fi_count++] = fi; } } if ( bHasFaceNormals ) { // invalid faces are removed below ON_3fVector a, b, n; a = mesh.m_V[f.vi[2]] - mesh.m_V[f.vi[0]]; b = mesh.m_V[f.vi[3]] - mesh.m_V[f.vi[1]]; n = ON_CrossProduct( a, b ); n.Unitize(); mesh.m_FN[fi] = n; } } } } } if ( bad_fi_count > 0 ) { // remove collapsed faces ON_qsort(bad_fi,bad_fi_count,sizeof(bad_fi[0]),CompareInt); int bfi = 1; int dest_fi = bad_fi[0]; for ( fi = dest_fi+1; fi < F_count && bfi < bad_fi_count; fi++ ) { if ( fi == bad_fi[bfi] ) { bfi++; } else { mesh.m_F[dest_fi++] = mesh.m_F[fi]; } } while (fi<F_count) { mesh.m_F[dest_fi++] = mesh.m_F[fi++]; } mesh.m_F.SetCount(dest_fi); if ( bHasFaceNormals ) { bfi = 1; dest_fi = bad_fi[0]; for ( fi = dest_fi+1; fi < F_count && bfi < bad_fi_count; fi++ ) { if ( fi == bad_fi[bfi] ) { bfi++; } else { mesh.m_FN[dest_fi++] = mesh.m_FN[fi]; } } while (fi<F_count) { mesh.m_FN[dest_fi++] = mesh.m_FN[fi++]; } mesh.m_FN.SetCount(dest_fi); } } mesh.Compact(); mesh.DestroyTopology(); mesh.DestroyPartition(); return true; }
// returns point on cylinder that is closest to given point ON_3dPoint ON_Sphere::ClosestPointTo( ON_3dPoint point ) const { ON_3dVector v = point - plane.origin; v.Unitize(); return plane.origin + radius*v; }
ON_3dVector ON_Polyline::TangentAt( double t ) const { ON_3dVector v = DerivativeAt(t); v.Unitize(); return v; }
RH_C_FUNCTION double ON_Intersect_MeshRay1(const ON_Mesh* pMesh, ON_3dRay* ray, ON_SimpleArray<int>* face_indices) { double rc = -1.0; // it is ok if face_indices is null if( pMesh && ray ) { #if defined(RHINO_V5SR) // only available in V5 const ON_MeshTree* mt = pMesh->MeshTree(true); #else const ON_MeshTree* mt = pMesh->MeshTree(); #endif ON_3dVector rayVec = ray->m_V; if( mt && rayVec.Unitize() ) { // increase the range by a factor of 2 so we are confident the // line passes entirely through the mesh double rayRange = RhCmnMaxDistance_Helper( mt->m_bbox, ray->m_P ) * 2.0; ON_Line line(ray->m_P, ray->m_P + rayRange * rayVec ); ON_SimpleArray<ON_CMX_EVENT> hits; mt->IntersectLine( line, hits ); int hitCount = hits.Count(); if( hitCount > 0 ) { ON_SimpleArray<double> tvals; ON_SimpleArray<int> indices; // tMin should be between 0 and 1 for the line double tMin = 100.0; for( int i=0; i<hitCount; i++ ) { const ON_CMX_EVENT& e = hits[i]; if( e.m_C[0].m_t <= tMin ) { tMin = e.m_C[0].m_t; if( face_indices ) { tvals.Append(tMin); indices.Append(e.m_M[0].m_face_index); } } if( e.m_type == ON_CMX_EVENT::cmx_overlap && e.m_C[1].m_t <= tMin ) { tMin = e.m_C[1].m_t; if( face_indices ) { tvals.Append(tMin); indices.Append( e.m_M[1].m_face_index); } } } if( tMin >=0 && tMin <= 1.0 ) { if( face_indices ) { for( int i=0; i<tvals.Count(); i++ ) { if( tvals[i]==tMin ) face_indices->Append(indices[i]); } } double lineLength = line.Length(); double rayLength = ray->m_V.Length(); if( rayLength > ON_SQRT_EPSILON ) { rc = tMin * lineLength / rayLength; } } } } } return rc; }
ON_BOOL32 ON_Surface::EvNormal( // returns false if unable to evaluate double s, double t, // evaluation parameters (s,t) ON_3dPoint& point, // returns value of surface ON_3dVector& ds, // first partial derivatives (Ds) ON_3dVector& dt, // (Dt) ON_3dVector& normal, // unit normal int side, // optional - determines which side to evaluate from // 0 = default // 1 from NE quadrant // 2 from NW quadrant // 3 from SW quadrant // 4 from SE quadrant int* hint // optional - evaluation hint (int[2]) used to speed // repeated evaluations ) const { // simple cross product normal - override to support singular surfaces ON_BOOL32 rc = Ev1Der( s, t, point, ds, dt, side, hint ); if ( rc ) { const double len_ds = ds.Length(); const double len_dt = dt.Length(); // do not reduce the tolerance used here - there is a retry in the code // below. if ( len_ds > ON_SQRT_EPSILON*len_dt && len_dt > ON_SQRT_EPSILON*len_ds ) { ON_3dVector a = ds/len_ds; ON_3dVector b = dt/len_dt; normal = ON_CrossProduct( a, b ); rc = normal.Unitize(); } else { // see if we have a singular point double v[6][3]; int normal_side = side; ON_BOOL32 bOnSide = false; ON_Interval sdom = Domain(0); ON_Interval tdom = Domain(1); if (s == sdom.Min()) { normal_side = (normal_side >= 3) ? 4 : 1; bOnSide = true; } else if (s == sdom.Max()) { normal_side = (normal_side >= 3) ? 3 : 2; bOnSide = true; } if (t == tdom.Min()) { normal_side = (normal_side == 2 || normal_side == 3) ? 2 : 1; bOnSide = true; } else if (t == tdom.Max()) { normal_side = (normal_side == 2 || normal_side == 3) ? 3 : 4; bOnSide = true; } if ( !bOnSide ) { // 2004 November 11 Dale Lear // Added a retry again with a more generous tolerance if ( len_ds > ON_EPSILON*len_dt && len_dt > ON_EPSILON*len_ds ) { ON_3dVector a = ds/len_ds; ON_3dVector b = dt/len_dt; normal = ON_CrossProduct( a, b ); rc = normal.Unitize(); } else { rc = false; } } else { rc = Evaluate( s, t, 2, 3, &v[0][0], normal_side, hint ); if ( rc ) { rc = ON_EvNormal( normal_side, v[1], v[2], v[3], v[4], v[5], normal); } } } } if ( !rc ) { normal.Zero(); } return rc; }