/********************************************************************/ /* Copyright (c) 2017 System fugen G.K. and Yuzi Mizuno */ /* All rights reserved. */ /********************************************************************/ #include "MGCLStdAfx.h" #include "mg/Box.h" #include "mg/Unit_vector.h" #include "mg/Transf.h" #include "mg/Straight.h" #include "mg/Ellipse.h" #include "mg/RLBRep.h" #include "mg/Gausp.h" #include "mg/TrimmedCurve.h" #include "mg/CCisect_list.h" #include "mg/nlbit.h" #include "mg/Tolerance.h" #if defined(_DEBUG) #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif // MGCurve // Implementation of MGCurve. //The class for function object for mgGausp to compute curvilinear integral // of the line, compute sqrt(sum(1st deriv of axis i)). class MGCurve_curvilinear_integral{ const MGCurve* m_curve; public: MGCurve_curvilinear_integral(const MGCurve* curve):m_curve(curve){;}; double operator()(double t)const; }; double MGCurve_curvilinear_integral::operator()(double t)const{ MGVector pos=m_curve->eval(t,0); MGVector deri=m_curve->eval(t,1); return (pos.ref(0)*deri.ref(1)-deri.ref(0)*pos.ref(1)); } double MGCurveLenParamDrive::operator()(double t)const{ double len=m_curve->length(m_ts, t); return len-m_len; } //線積分を求める。 //Compute curvilinear integral of the 1st two coordinates. //This integral can be used to compute area sorrounded by the curve. double MGCurve::curvilinear_integral(double t1, double t2) const{ //Get interval. double s1=t1, s2=t2; if(s1>s2){ s1=t2; s2=t1;} s1=range(s1); s2=range(s2); //Get knot vector of the curve. const MGKnotVector& kv=knot_vector(); int i1=kv.locate(s1); int i2=kv.locate(s2); MGCurve_curvilinear_integral clFunc(this); double len; if(i1==i2) len=mgGausp(clFunc,s1,s2); else{ double len_span,u1,u2; u2=kv(i1+1); len=mgGausp(clFunc,s1,u2); for(int i=i1+1; it2) len=-len; return len; } //Compute mean length of 1st derivative vector. double MGCurve::deriv_length()const{ double t0=param_s(), t1=param_e(); double tmid=(t0+t1)*.5; double len=eval(t0,1).len(); len+=eval(t1,1).len(); len+=eval(tmid,1).len(); return len/3.; } /** * @brief eval_discrete_deviationの下請け関数 * @param curve1 face1側エッジのワールドカーブ(must be trimmed into evaluatin range). * @param curve2 face2側エッジのワールドカーブ(must be trimmed into evaluatin range). */ void deviation( const MGCurve& curve1,///< The target 1st curve. const MGCurve& curve2,///< The target 2nd curve. int npoint, ///& sts///& sts, int npoint, //indicates how many discrete points be obtained. double tolerance //tolerance to get two edge to compute deviation. )const{ if(tolerance < MGTolerance::line_zero()){ tolerance= MGTolerance::line_zero(); } MGPosition st=closest(curve2); MGPosition P1=eval(st[0]), P2=curve2.eval(st[1]); if(P1.distance(P2)>tolerance){ sts.push_back(st); return; } double tol_saved = MGTolerance::set_line_zero(tolerance); std::vector stspans; MGCCisect_list isects; int retcode = common(curve2, stspans,isects); MGTolerance::set_line_zero(tol_saved); if(retcode <= 0){//When common failed. sts.push_back(st); return; }else if(retcode==2){//When only intersections are obtained. MGCCisect_list::iterator i=isects.begin(), ie=isects.end(); for(;i!=ie;i++){ sts.push_back(MGPosition(i->param1(), i->param2())); } return; } size_t j, nspan=stspans.size()/4; double slen=stspans[1]-stspans[0]; for(j=1; js1){ double save=s0; s0=s1; s1=save; } if(t0>t1){ double save=t0; t0=t1; t1=save; }*/ // 共線部分曲線を取り出す MGTrimmedCurve crv1trmd(*this,s0, s1), crv2trmd(curve2,t0, t1); deviation(crv1trmd,crv2trmd,int(double(npoint)*(s1-s0)/slen),sts); } } MGKnotVector& MGCurve::knot_vector(){ const MGCurve* crv=const_cast(this); const MGKnotVector& t=crv->knot_vector(); return *(const_cast(&t)); } // 与えられたパラメータ値間の曲線の長さを返す。 // パラメータが昇順で与えられたときは正値、降順のときは負値を返す。 //Return line length from t1 to t2. double MGCurve::length(double t1, double t2) const{ double s1=t1, s2=t2; if(s1>s2){ s1=t2; s2=t1;} s1=range(s1); s2=range(s2); //Get knot vector of the curve. const MGKnotVector& kv=knot_vector(); int i1=kv.locate(s1); int i2=kv.locate(s2); double u1,u2; MGCurveLengthDrive lenFunc(this); double len; if(i1==i2) len=mgGausp(lenFunc,s1,s2); else{ double len_span; u2=knot(i1+1); len=mgGausp(lenFunc,s1,u2); for(int i=i1+1; it2) len=-len; return len; } // パラメータで示される点 t_in から指定距離 len はなれた点のパラメータ // を返す。 double MGCurve::length_param(double t_in, double len) const{ double ts=range(t_in); double tlimit1=param_s(); double tlimit2=param_e(); double tlimit = (len>=0.) ? tlimit2:tlimit1; MGVector deriv1=eval(ts,1); double dl1=deriv1.len(); if(MGMZero(dl1)) return tlimit; double d=range(ts+len/dl1); //d is approximate solution. // Check if we have solution. double len_limit=length(ts,tlimit); if(fabs(len)>=fabs(len_limit)) return tlimit; //Solution is outside range; double t1,t2,len1,lenm; if(ts<=tlimit){ t1=ts; t2=tlimit; len1=-len; }else{ t2=ts; t1=tlimit; len1=len_limit-len; } lenm=length(ts,d)-len; if(len1*lenm<=0.) t2=d; else t1=d; //Now there exists a solution between t1 and t2. int itr=30, ier; double eps=MGTolerance::wc_zero(); MGCurveLenParamDrive clen(this,len,ts); return mgNlbit(clen, t1,t2, eps, itr, ier); } //Compute curvatur in 3D space, ie, the value is not negative. double MG_Curvature(const MGVector& v1, const MGVector& v2){ double v1_len = v1.len(); if(MGMZero(v1_len)) return 0.; double v1_len3 = v1_len * v1_len * v1_len; return (v1*v2).len()/v1_len3; } //Compute torsion. double MG_Torsion( const MGVector& v1, //First derivative. const MGVector& v2, //Second derivative. const MGVector& v3) //Third derivative. { MGVector v12=v1*v2; double v12_len2=v12%v12; if(MGMZero(v12_len2)) return 0.; else return MGDeterminant(v1,v2,v3)/v12_len2; } ///Compute the length of the 1st derivative at the curve parameter t. double MGCurveLengthDrive::operator()(double t)const{ MGVector deriv=m_curve->eval_deriv(t); return sqrt(deriv%deriv); } //Generate arrow data of the tangent at the parameter value t of the curve. //data[0] is the origin of the arrow, data[1] is the top of the arrow, //data[2], [3] are two bottoms of arrowhead. void MGCurve::arrow(double t, MGPosition data[4])const{ const double arrow_length=.1//of total length of the curve) , head_length=.3;//of arrow_length. MGVector v1,v2; eval_all(t,data[0],v1,v2); v1*=param_span()*arrow_length; MGCL::one_arrow(data[0],v1,v1*v2*v1,data[1],data[2],data[3]); } //Generate arrow data from (root, vecx, vecy). void MGCL::one_arrow( const MGPosition& root, //root of the arrow const MGVector& vecx, //the vector from the root to the head of the arrrow const MGUnit_vector& vecy,//vecy that is normal to the vector from root to head MGPosition& head, //head of the arrow will be returned. MGPosition& headtail1, //two tail of arrowhead line segments will be returned. MGPosition& headtail2 ){ const double arrow_length=.1//of total length of the curve) , head_length=.3;//of arrow_length. head=root+vecx; MGVector head_rootx=vecx*head_length, head_rooty=vecy*(.5*head_length*vecx.len()); headtail1=head-head_rootx+head_rooty; headtail2=head-head_rootx-head_rooty; } //Round the parameter t into this parameter range. double MGCurve::param_round_into_range(double t)const{ if(tparam_e()) return param_e(); return t; } class MGtangent_guess_cangle{ const MGCurve& m_curve; const MGPosition& m_P; public: MGtangent_guess_cangle( const MGCurve& curve, const MGPosition& P ):m_curve(curve),m_P(P){;}; double operator()(double t)const{ MGPosition B; MGVector tan, deri2; m_curve.eval_all(t,B,tan,deri2); return (m_P-B).cangle(tan*deri2*tan); } }; #define MAX_ITR 10 //Return tangent point from a point P, //given guess starting paramter tg. // tangent_guess=true if perpendicular points obtained, // tangent_guess=false if perpendicular points not obtained, int MGCurve::tangent_guess( double t0, double t1, //parameter range of this. const MGPosition& P, //Point(指定点) double tg, //Guess parameter values of the two curves double& t //Output parameter )const{ //initial check. MGPosition BP; MGVector tan, deri2; eval_all(tg,BP,tan,deri2); double mzero=MGTolerance::mach_zero(); if(tan.len()<=mzero) return 0; if(deri2.len()<=mzero){ if(tan.is_collinear(P-BP)){ t=tg; return 1; } return 0; } if(t0>=t1){ t0=param_s(); t1=param_e(); } if(tgt1) tg=t1; double error=MGTolerance::rc_zero(); int ndiv=intersect_dnum(); double span=(param_e()-param_s())/double(ndiv); int no_converged; MGtangent_guess_cangle cang(*this,P); double cangg=cang(tg); double tn1=tg+span; if(tn1>t1) tn1=t1; double cangn1=cang(tn1); if(cangg*cangn1<=0.){ t=mgNlbit(cang,tg,tn1,error,MAX_ITR,no_converged); if(no_converged) return 0; return 1; } double tn2=tg-span; if(tn2fabs(cangn2)) span*=-1.; for(int loop=0; loopt1){ tn1=t1; loop=ndiv;} if(tn1(curve.copy_as_nurbs()); case MGELLIPSE_TID: return new MGRLBRep(dynamic_cast(curve)); case MGLBREP_TID: return new MGRLBRep(dynamic_cast(curve)); default: { std::unique_ptr r(new MGRLBRep(MGLBRep(curve))); if(r->order() < 4){ r->change_order(4); } return r.release(); } } } ///Compute polar coordinates of the point P, given previous point data. ///When previousPolar=0, P is the 1st point to compute. void getPolarCoordinates( const MGVector& P,///ref(1); double thetam2pai=theta-mgDBLPAI; double thetap2pai=theta+mgDBLPAI; double dif1=fabs(theta-thetaPre); double dif2=fabs(thetam2pai-thetaPre); double dif3=fabs(thetap2pai-thetaPre); if(dif1>dif2){ if(dif2>dif3) theta=thetap2pai; else theta=thetam2pai; }else{ if(dif1>dif3) theta=thetap2pai; } }else{ if(theta>mgPAI) theta-=mgDBLPAI; } } } ///Obtain polar coordinates system MGLBRep of this curve. ///This curve's (x,y) coordinates are changed polar coordinates system(r,theta) ///where r is the distance from origin and theta is the angel with x coordinate. ///The space dimension of this curve must be >=2; ///If this space dimension is lager than 2, the remaining coordinates are set unchanged ///to MGLBRep. std::unique_ptr MGCurve::PolarCoordinatesLBRep()const{ std::unique_ptr lbPolar; const MGLBRep* lb=dynamic_cast(this); if(lb){ lbPolar.reset(lb->clone()); }else{ lbPolar.reset(new MGLBRep); approximate_as_LBRep(*lbPolar); } int n=lbPolar->bdim(); assert(lbPolar->bdim()>=2); MGBPointSeq& bp=lbPolar->line_bcoef(); MGVector previousPolar; MGVector* previous=0; for(int i=0; iMGTolerance::angle_zero(). std::unique_ptr MGCurve::scalePolar( double angleBase, ///mgPAI) angleBm1-=mgDBLPAI; else if(angleBm1<-mgPAI) angleBm1+=mgDBLPAI; double angleBm2=angleBase-angle2; if(angleBm2>mgPAI) angleBm2-=mgDBLPAI; else if(angleBm2<-mgPAI) angleBm2+=mgDBLPAI; double angle2m1=angle2-angle1; if(angle2m1>mgPAI) angle2m1-=mgDBLPAI; else if(angle2m1<-mgPAI) angle2m1+=mgDBLPAI; double c1=angleBase*angle2m1/angleBm1; double c2=angleBm2/angleBm1; std::unique_ptr lbnew=PolarCoordinatesLBRep(); int n=lbnew->bdim(); MGBPointSeq& bp=lbnew->line_bcoef(); for(int i=0; imgPAI) thetaNew-=mgDBLPAI; else if(thetaNew<-mgPAI) thetaNew+=mgDBLPAI; thetaNew*=c2; thetaNew+=c1; theta=thetaNew; } lbnew->updatePolarCoordinates2Ordinary(); return lbnew; } ///Find C0 cotinuity parameter values o fhtis curve, ///and push back the parameter values to param. void MGCurve::getParamsC0Continuity(std::vector& param)const{ const MGKnotVector& t=knot_vector(); int k=t.order(); int index, n=t.bdim(), multi_found; int orderm1=k-1, start=k; do{ //折れの点を探し、そのYを求める multi_found=t.locate_multi(start,orderm1,index);//Locate C0 continuity point. if(index==n) break; //折れが最後までなかった double ti=t[index]; MGVector Tminor=eval(ti,1,1); MGVector Tplus=eval(ti,1); if(!Tminor.is_collinear(Tplus)||Tminor.is_zero_vector()||Tplus.is_zero_vector()){ param.push_back(ti); } start=index+multi_found; }while(index