void SBezier::ClosestPointTo(Vector p, double *t, bool converge) { int i; double minDist = VERY_POSITIVE; *t = 0; double res = (deg <= 2) ? 7.0 : 20.0; for(i = 0; i < (int)res; i++) { double tryt = (i/res); Vector tryp = PointAt(tryt); double d = (tryp.Minus(p)).Magnitude(); if(d < minDist) { *t = tryt; minDist = d; } } Vector p0; for(i = 0; i < (converge ? 15 : 5); i++) { p0 = PointAt(*t); if(p0.Equals(p, RATPOLY_EPS)) { return; } Vector dp = TangentAt(*t); Vector pc = p.ClosestPointOnLine(p0, dp); *t += (pc.Minus(p0)).DivPivoting(dp); } if(converge) { dbp("didn't converge (closest point on bezier curve)"); } }
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_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; }