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