/********************************************************************/ /* Copyright (c) 2017 System fugen G.K. and Yuzi Mizuno */ /* All rights reserved. */ /********************************************************************/ #include "MGCLStdAfx.h" #include "mg/Box.h" #include "mg/Position.h" #include "mg/Unit_vector.h" #include "mg/Position_list.h" #include "mg/Transf.h" #include "mg/Straight.h" #include "mg/RLBRep.h" #include "mg/CompositeCurve.h" #include "mg/SPointSeq.h" #include "mg/SurfCurve.h" #include "mg/RSBRep.h" #include "mg/Plane.h" #include "mg/CSisect_list.h" #include "mg/Tolerance.h" #include "Tl2/TLInputParam.h" #if defined(_DEBUG) #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif using namespace std; // Implementation of MGSurface. // MGSurface is an abstract class of 3D surface. // Surface is represented using two parameter u and v. // ///////////// Constructor /////////// // // 初期化なしでオブジェクトを作成する。 MGSurface::MGSurface():MGGeometry(){;} // ///////////デストラクタ ///////////// // MGSurface::~MGSurface(){;} ///////////Operator overload.///////////// //Generate arrow data of the tangent along u and v and the normal //at the parameter value (u,v) of the surface. //data[0] is the origin of the u-tangent arrow, data[1] is the top of the u-tangent arrow, //data[2], [3] are two bottoms of u-tangent arrowhead. //data[0], [4], [5], [6] are the points of v-tangent arrow. //data[0], [7], [8], [9] are the points of v-tangent arrow. void MGSurface::arrow(double u,double v, MGPosition data[10])const{ arrow(box(),u,v,data); } //Generate arrow data, given box. The length of the arrows are defined from //box.len() void MGSurface::arrow(const MGBox& box, double u,double v, MGPosition data[10])const{ const double arrow_length=.06//of total length of the curve , head_length=.3;//of arrow_length. data[0]=eval(u,v); MGVector du=eval(u,v,1,0),dv=eval(u,v,0,1); double len=box.len()*arrow_length; MGUnit_vector ndu(du), ndv(dv); du=ndu*len; dv=ndv*len; MGPosition& P=data[0]; MGCL::one_arrow(P,du,dv,data[1],data[2],data[3]); MGCL::one_arrow(P,dv,du,data[4],data[5],data[6]); MGUnit_vector N(du*dv); MGCL::one_arrow(P,N*len,du,data[7],data[8],data[9]); } //Return box of the parameter space of the surface. MGBox MGSurface::box_param() const{return parameter_range();} //Obtain ceter coordinate of the geometry. MGPosition MGSurface::center() const{ double u=(param_s_u()+param_e_u())*0.5; double v=(param_s_v()+param_e_v())*0.5; return eval(u,v); } //Obtain ceter parameter value of the geometry. MGPosition MGSurface::center_param() const{ double u=(param_s_u()+param_e_u())*0.5; double v=(param_s_v()+param_e_v())*0.5; return MGPosition(u,v); } bool MGBox2PositionCompare::operator()(const MGBox* b1, const MGBox* b2)const{ MGVector v1=b1->mid()-m_position; MGVector v2=b2->mid()-m_position; return v1%v1mid()-m_position; MGVector v2=b2->mid()-m_position; return v1%v1closest(point))); delete perimtr; } MGPosition uv1, uv; double dist1,dist; int n=list.entries(); if(n){ uv=list.removeFirst(); dist=(point-eval(uv)).len(); for(int i=1; i perims(pnum); std::vector boxes(pnum); for(int i=0; ibox()); } MGBox2PositionCompare comp(point); std::sort(boxes.begin(), boxes.end(), comp); MGPosition uv1, uv; double dist1,dist=-1.; for(int i=0; i=dist) continue; uv1=perimeter_uv(i,pi.closest(point)); dist1=(point-eval(uv1)).len(); if(dist1 perims(pnum); std::vector boxes(pnum); for(int i=0; i=dist) continue; double t=pi.closest(ORIGIN2); uv1=perimeter_uv(i,t); dist1=pi.eval(t).len(); if(dist10.){value[3]=(mb+r)/EGmF2h; value[2]=(mb-r)/EGmF2h;} else {value[2]=(mb+r)/EGmF2h; value[3]=(mb-r)/EGmF2h;} } //Compute direction unit vector of the geometry. MGUnit_vector MGSurface::direction(const MGPosition& param)const{ return normal(param); } //Test if pline has the same direction to world_curve, assuming that //pline=MGSurfCurve(MGSurface srf, parameter_curve of the srf) and //world_curve are the same curve. //Function's return value is: //1: same direction, -1:oppositie direction. int MGCL::SurfCurve_equal_direction( const MGCurve& pline, //MGSurfCurve(MGSurface srf, parameter_curve of the srf) const MGCurve& world_curve //world representation curve. ){ double ts=pline.param_s(), tsb=world_curve.param_s(); MGVector dir1=pline.eval(ts,1), dir2; MGVector Ps=pline.eval(ts), PBs=world_curve.eval(tsb); if(Ps==PBs){ dir2=world_curve.eval(tsb,1); }else{ double teb=world_curve.param_e(); MGVector PBe=world_curve.eval(teb); MGVector dif1(Ps,PBs), dif2(Ps,PBe); if(dif1%dif10.) return 1; else return -1; } //Compute if MGSurfCurve scurve(*this, param_curve) has the same direction //to world_curve, assuming that scurve and world_curve are the same curve. //Function's return value is: //1: same direction, -1:oppositie direction. int MGSurface::equal_direction( const MGCurve& param_curve, //(u,v) parameter representation curve of this. const MGCurve& world_curve //world representation curve. )const{ MGSurfCurve pline(*this, param_curve); return MGCL::SurfCurve_equal_direction(pline,world_curve); } //Evaluate all the points (ui, vj) into spoint(i,j,.), //where ui=utau(i) for 0<=i=num_peri || iperi<0) return 0.; uv.resize(2); MGCurve* peri = perimeter_curve(iperi); int id = -1; switch(iperi){ case 0: uv(1) = param_s_v(); id = 0;break; case 1: uv(0) = param_e_u(); id = 1;break; case 2: uv(1) = param_e_v(); id = 0;break; case 3: uv(0) = param_s_u(); id = 1;break; }; MGNDDArray tau; tau.update_from_knot(curve.knot_vector()); double t0 = peri->param_s(); double t1 = peri->param_e(); const double ratio = (t1-t0)/(curve.param_e()-tau[0]); MGPosition P = curve.start_point(); double t = peri->closest(P); double lenmax = (peri->eval(t) - P).len(); int ntaum1=tau.length()-1; for(int i = 0; i < ntaum1; i++){ double tmp = (tau[i] + tau[i+1]) * .5; P = curve.eval(tmp); double tg = ratio * (tmp - tau[0]) + t0; if(!peri->perp_guess(t0, t1, P, tg, t)){ //std::cerr << "** perp_guess (gap) error\n"; t = peri->closest(P); } double len = (P - peri->eval(t)).len(); if(len > lenmax){ lenmax = len; uv(id) = t; } P = curve.eval(tau[i+1]); tg = ratio * (tau[i+1] - tau[0]) + t0; if(!peri->perp_guess(t0, t1, P, tg, t)){ //std::cerr << "** perp_guess (gap) error\n"; t = peri->closest(P); } len = (P - peri->eval(t)).len(); if(len > lenmax){ lenmax = len; uv(id) = t; } } P = curve.end_point(); if(!peri->perp_guess(t0, t1, P, t1, t)){ //std::cerr << "** perp_guess (gap) error\n"; t = peri->closest(P); } double len = (P - peri->eval(t)).len(); if(len > lenmax){ lenmax = len; uv(id) = t; } delete peri; return lenmax; } //evaluate gap between this surface's perimeters and the given curve curve. //evaluation is performed for the perimeter i and curve[i] for 0<=i<=4. //function's return value is the maximum gap. double MGSurface::eval_gap( const MGCurve* curve[4], // (I/ ) curves to evaluate. MGPosition& uv // ( /O) the parameter of this that had the largest gap. )const{ int num_peri=perimeter_num(); double gap=-1.; MGPosition uv2; for(int i=0; igap){ gap=gap2; uv=uv2; } } return gap; } // Evaluate n'th derivative data. n=0 means positional data evaluation. MGVector MGSurface::evaluate( const MGPosition& t, // Parameter value. //t's space dimension is geometry's manifold dimension. const int* nderiv //Order of derivative of i-th parameter //in nderiv[i]. //When nderiv=null, nderiv[i]=0 is assumed for all i. )const{ if(nderiv) return eval(t,nderiv[0],nderiv[1]); else return eval(t); } //Compute 1st and 2nd fundamental quantities of the surface. //In Q, 1st and 2nd fundamental quantities are returned as: //Q[0]=E, Q[1]=F, Q[2]=G, //Q[3]=L, Q[4]=M, Q[5]=N. void MGSurface::fundamentals( const MGPosition&uv, //Surface parameter value (u,v) double Q[6], MGUnit_vector& N) //Normal vector at uv will be returned. const{ fundamentals(uv[0], uv[1], Q, N); } void MGSurface::fundamentals( double u, double v, //Surface parameter value (u,v) double Q[6], MGUnit_vector& N) //Normal vector at (u,v) will be returned. const{ MGPosition f; // Positional data. MGVector fu; // df(u,v)/du MGVector fv; // df/dv MGVector fuv; // d**2f/(du*dv) MGVector fuu; // d**2f/(du**2) MGVector fvv; // d**2f/(dv**2) eval_all(u,v,f,fu,fv,fuv,fuu,fvv); Q[0]=fu%fu; Q[1]=fu%fv; Q[2]=fv%fv; N=unit_normal(u,v); Q[3]=fuu%N; Q[4]=fuv%N; Q[5]=fvv%N; } //Compare two parameter values. If p1 is less than p2, return true. //Comparison is done after prjected to i-th perimeter of the surface. bool MGSurface::less_than( int i, //perimeter number. const MGPosition& p1, const MGPosition& p2) const{return true;} //Transform the coordinates of boundary of this geometry so that //new coordinate of boundary is the same coordinate as the new one of //this geometry after negate() of this geometry is done. //That is, boundary coordinates are of parameter world of this geometry. void MGSurface::negate_transform(MGGeometry& boundary) const{ MGCurve* pline=dynamic_cast(&boundary); if(pline) pline->coordinate_exchange(0,1); } double mgDistance_flat( const MGVector& P0, const MGVector& Pm, const MGVector& P1 ){ MGVector Qm=(P0+P1)*.5; MGVector dif=Pm-Qm; return dif%dif; } //Compute the difference of min and max of the three doubles a1, a2, and a3. double MGCL::Max3(double a1, double a2, double a3){ double min, max; if(a1>=a2){ if(a1>=a3){ max=a1; if(a2>=a3) min=a3; else min=a2;} else{ max=a3; min=a2;} }else if(a2>=a3){ max=a2; if(a1>=a3) min=a3; else min=a1;} else{ min=a1; max=a3; } return max-min; } //compute sample point of the surface to get the approximate plane //of the surface within the parameter range (u0,v0) to (u1, v1). void MGSurface::compute_sample_point( double u0, double u1, double v0, double v1, MGPosition Pn[9], //9 sample points will be output. MGPosition& center, //center of the sample points will be output. MGUnit_vector& N, //average normal of Nn[] will be output. MGVector* Nn_in //9 normals of the surface will be output. )const{ MGVector NnLocal[9]; double um=(u0+u1)*0.5, vm=(v0+v1)*0.5; MGVector* Nn; if(Nn_in) Nn=Nn_in; else Nn=NnLocal; int i; //id of Pn[]. Pn[0]=eval(u0,v0); Nn[0]=normal(u0,v0); Pn[1]=eval(um,v0); Nn[1]=normal(um,v0); Pn[2]=eval(u1,v0); Nn[2]=normal(u1,v0); Pn[3]=eval(u0,vm); Nn[3]=normal(u0,vm); Pn[4]=eval(um,vm); Nn[4]=normal(um,vm); Pn[5]=eval(u1,vm); Nn[5]=normal(u1,vm); Pn[6]=eval(u0,v1); Nn[6]=normal(u0,v1); Pn[7]=eval(um,v1); Nn[7]=normal(um,v1); Pn[8]=eval(u1,v1); Nn[8]=normal(u1,v1); center=Pn[0]; MGVector VN=Nn[0]; for(i=1; i<9; i++){center+=Pn[i]; VN+=Nn[i];} center/=9.; N=VN; } //Test if the surface is flat or not within the parameter value rectangle of uvbox. //Function's return value is: // true: if the surface is flat // false: if the surface is not falt. //When this is not falt, the direction that indicates which direction the surface //should be divided will be output. //***** the flatness is tested only approximately. This is for exclusive use of //planar(). bool MGSurface::flat( const MGBox& uvbox, double tol, //Tolerance allowed to regart flat //(Allowed distance from a plane). int& direction, // 1: u-direction is more non flat. // 0: v-direction is more non flat. MGPosition& P, //Position of the flat plane will be output. MGUnit_vector& N//Normal of the flat plane will be output. )const{ const MGInterval& urng=uvbox[0]; double u0=urng[0].value(), u1=urng[1].value(); const MGInterval& vrng=uvbox[1]; double v0=vrng[0].value(), v1=vrng[1].value(); MGPosition Pn[9]; compute_sample_point(u0,u1,v0,v1,Pn,P,N); double x, d=P%N; double dist[9]; bool is_flat=true; for(int i=0; i<9; i++){ x=dist[i]=d-Pn[i]%N; if(x<0.) x=-x; if(x>tol) is_flat=false;; } double um=(u0+u1)*0.5, vm=(v0+v1)*0.5; MGVector dfdu=eval(um,vm,1,0), dfdv=eval(um,vm,0,1); double ulen=dfdu.len()*(u1-u0), vlen=dfdv.len()*(v1-v0); if(ulen*5.vlen*5.) direction=1; else{ double udif=MGCL::Max3(dist[0],dist[1],dist[2]) +MGCL::Max3(dist[3],dist[4],dist[5]) +MGCL::Max3(dist[6],dist[7],dist[8]); double vdif=MGCL::Max3(dist[0],dist[3],dist[6]) +MGCL::Max3(dist[1],dist[4],dist[7]) +MGCL::Max3(dist[2],dist[5],dist[8]); double error=MGTolerance::wc_zero_sqr()*15.; double difmax; if(udif>=vdif){ difmax=udif; direction=1; }else{ difmax=vdif; direction=0; } if(difmax<=error){ if(ulen>vlen) direction=1; else direction=0; } } return is_flat; } //Given world curve wcrv on this face, get the parameter space representation pcrv. //Function's return value is pcrv, which is newed one. Must be deleted. MGCurve* MGSurface::get_parameterCurve(const MGCurve& wcrv)const{ MGPvector uvs, worlds; int n=project(wcrv,uvs,worlds);//n=number of curves projected. if(n<=0){ MGPosition uv0, uv1; on(wcrv.start_point(), uv0); on(wcrv.end_point(), uv1); return new MGStraight(uv1,uv0); }else if(n==1){ return uvs.release(0); } MGCompositeCurve* pcrv=new MGCompositeCurve(uvs.release(0)); for(int i=1; iend_point(), uv1=crvi.start_point(); MGPosition Pe=eval(uv0); MGPosition Ps=eval(uv1); if((Pe-Ps).len()>MGTolerance::wc_zero()) pcrv->connect_to_end(new MGStraight(uv1,uv0)); pcrv->connect_to_end(uvs.release(i)); } return pcrv; } //Compute the approximate plane in the parameter range from (u0, v0) to (u1,v1) //The plane's origin is center point of the plane when the surface is mapped //onto the plane. The uderiv of the plane is the direction from the point(u0, v0) to (u1,v0). void MGSurface::get_approximate_plane( double u0,double u1,//u range from u0 to u1. double v0,double v1,//v range from v0 to v1. MGPlane& plane, //The plane will be output. double* width, //The width and the height of the plane that include all the data double* height //for the surface point to map onto the plane will be output )const{ MGPosition Pn[9]; MGPosition center; MGUnit_vector N; compute_sample_point(u0,u1,v0,v1,Pn,center,N); int i; //id of Pn[]. MGPlane plane2(N,center); MGVector xaxis(plane2.eval(plane2.uv(Pn[2])),plane2.eval(plane2.uv(Pn[0]))); MGVector yaxis=N*xaxis; plane=MGPlane(xaxis,yaxis,center); if(!width) return; MGBox uvrange; for(i=0; i<9; i++){ uvrange.expand(plane.uv(Pn[i])); } double umin=fabs(uvrange[0].low_point()), umax=fabs(uvrange[0].high_point()); if(uminsurface_tol) return false; double sang=N.sangle(Nn[i]); if(sang>angle) return false; } MGPlane plane2(N,center); MGUnit_vector xaxis=MGVector(plane2.eval(plane2.uv(Pn[2])),plane2.eval(plane2.uv(Pn[0]))); MGUnit_vector yaxis=N*xaxis; plane=MGPlane(xaxis,yaxis,center); MGBox uvrange; for(i=0; i<9; i++){ uvrange.expand(plane.uv(Pn[i])); } double umin=fabs(uvrange[0].low_point()), umax=fabs(uvrange[0].high_point()); if(umin=10: (u,v) is on a perimeter, (10+perimeter number) will be returned. int MGSurface::in_range_with_on(const MGPosition& uv)const{ if(param_range()< MGSurface::inner_boundary(int i)const{ return MGPvector(); } //Obtain i-th inner_boundary curves(world coordinates representation) //of the FSurface. Let the output of inner_boundary(i) be wcurves and //of inner_boundary_param(i) be pcurves, then wcurves[j] corresponds //to pcurves[j] one to one. Number of inner_boundary can be obtained //by the function number_of_inner_boundary(). MGPvector MGSurface::inner_boundary_param(int i)const{ return MGPvector(); } //Compute normal vector(not unit) at uv. MGVector MGSurface::normal(const MGPosition& uv) const { return normal(uv.ref(0), uv.ref(1));} //Compute normal vector(not unit) at uv. MGVector MGSurface::normal(double u,double v) const { return eval(u,v,1,0)*eval(u,v,0,1);} //Compute unit normal vector at uv. MGUnit_vector MGSurface::unit_normal(const MGPosition& uv) const { return unit_normal(uv.ref(0), uv.ref(1));} #define MAX_LOOP_NUM 100 //Get nearest (u,v) that is not degenerated point. //That is, nearest point |fu*fv|>MGTolerance::mach_zero(). void MGSurface::nearest_non_degenerated(double& u,double& v) const{ double err_sqr=MGTolerance::mach_zero(); double rzero=MGTolerance::rc_zero(); int ndu=int(double(intersect_dnum_u())/rzero), ndv=int(double(intersect_dnum_v())/rzero); int n=ndu; if(n>ndv) n=ndv; double du=param_e_u()-u, du2=param_s_u()-u; if(du<(-du2)) du=du2; du/=double(ndu); double dv=param_e_v()-v, dv2=param_s_v()-v; if(dv<(-dv2)) dv=dv2; dv/=double(ndv); MGVector fu(eval(u,v,1,0)), fv(eval(u,v,0,1)); double fulen=fu%fu, fvlen=fv%fv; if(fulen<=err_sqr && fvlen>err_sqr)//case that fu is zero and fv is not. du=0.; else if(fulen>err_sqr && fvlen<=err_sqr)//case that fv is zero and fu is not. dv=0.; //case that fu is parallel to fv. n=MAX_LOOP_NUM; MGVector norml; for(int i=0;i=err_sqr) return; } } //Compute unit normal vector at uv. MGUnit_vector MGSurface::unit_normal(double u,double v)const{ MGVector fu(eval(u,v,1,0)), fv(eval(u,v,0,1)); MGVector norml=fu*fv; double err_sqr=MGTolerance::mach_zero(); if((norml%norml)>=err_sqr) return norml; nearest_non_degenerated(u,v); fu=eval(u,v,1,0); fv=eval(u,v,0,1); return fu*fv; } // 点がSpline上にあるか調べる。Spline上であれば,そのパラメータ値を, // そうでなくても最近傍点のパラメータ値を返す。 //Function's return value is true if input point is on the surface, // and false if the point is not on the surface. bool MGSurface::on( const MGPosition &P, // 指定点 MGPosition& uv // Parameter value will be returned. )const{ int i; uv=MGPosition(param_s_u(), param_s_v()); if(perp_one(P,uv)){if(MGAZero((P-eval(uv)).len())) return 1;} //Now if perp point exists, it is a candidate of the nearest point. //Compute nearest points with four perimeters. double t; bool onp=false; int pnum=perimeter_num(); MGPosition* uvp=new MGPosition[pnum+1]; uvp[0]=uv; MGCurve* perim; for(i=0;ion(P,t); uvp[i+1]=perimeter_uv(i,t); delete perim; if(onp){uv=uvp[i+1]; break;} } if(!onp){ //Compute the nearest point out of uvp[.]. double dist=(P-eval(uvp[0])).len(), dist2; int id=0; for(i=1;i<=pnum;i++){ dist2=(P-eval(uvp[i])).len(); if(dist20 if the point is on the surface, // and 0 if the point is not on the curve. bool MGPosition::on( const MGSurface& surf, // Surface pointer MGPosition& uv //Parameter value of the nearest point on the surface. )const{ return surf.on(*this, uv); } //Test if input (u,v) is parameter value on a perimeter of the surface. //If u or v is on a perimeter, it will be updated to the perimeter value. bool MGSurface::on_a_perimeter( double& u, double& v, //Surface parameter (u,v) int& perim_num//if function returns true,the perimete number will be output. //If function returns false, the nearest perimeter number will be output. )const{ bool on=1; double u0=param_s_u(), u1=param_e_u(); double v0=param_s_v(), v1=param_e_v(); double uspan=u1-u0, vspan=v1-v0; //uspan*=.1; vspan*=.1; double dist, distmin; perim_num=3; if(MGREqual_base(u,u0,uspan)){ u=u0; if(MGREqual_base(v,v0,vspan)){ perim_num=0; v=v0; } }else{ distmin=(u-u0)/uspan; if(distmin<0.) distmin=-distmin; if(MGREqual_base(u,u1,uspan)){ perim_num=1; u=u1; if(MGREqual_base(v,v1,vspan)){ perim_num=2; v=v1; } }else{ dist=(u1-u)/uspan; if(dist<0.) dist=-dist; if(dist MGSurface::outer_boundary()const{ MGPvector perims; int n=perimeter_num(); for(int i=0; i=2) peri->negate(); perims.push_back(peri); } return perims; } //Obtain boundary curves(parameter space representation) of the FSurface. //Let the output of boundary() be wcurves and of boundary_parameter() //be pcurves, then wcurves[i] corresponds to pcurves[i] one to one. MGPvector MGSurface::outer_boundary_param()const{ MGPvector crvs; int n=perimeter_num(); if(!n) return crvs; MGBox uvrange=param_range(); for(int i=0; i0.) sguess=s0+(tw-t0)*ratio;//if pcurve and wcurve are the same direction. else sguess=s0+(t1-tw)*ratio; } double tp=sguess; int pobtained=pline.perp_guess(s0,s1,P,sguess,tp); if(pobtained){ MGVector dif=P-pline.eval(tp); if(dif%dif<=(MGTolerance::wc_zero_sqr()*2.)) return tp; } pline.on(P,tp); return tp; } //Compute parameter value of given point. Same as param. // 自身の上の指定点を表すパラメータ値を返す。 // If input point is not on the geometry, return the nearest point on the // geometry. MGPosition MGSurface::parameter( const MGPosition& P //Point(指定点) )const{ return param(P);} //Obtain parameter curves. //In the case of surFSurface, parameter curve is only one. However, in the case //of FSurface, number of parameter curves are more than one. MGPvector MGSurface::parameter_curves( int is_u, //True(!=0) if x is u-value.(i.e. obtain u=const line) double x)const //parameter value. u or v-value accordint to is_u. { MGPvector crvs; crvs.push_back(parameter_curve(is_u,x)); return crvs; } /// パラメータ範囲を返す。 ///Return parameter range. MGBox MGSurface::param_range()const{ MGInterval urange(param_s_u(), param_e_u()); MGInterval vrange(param_s_v(), param_e_v()); return MGBox(urange,vrange); } //Return parameter range of the geometry(パラメータ範囲を返す) MGBox MGSurface::parameter_range() const { return param_range();} ///Retrieve perimeter i of this surface. /// i must be < perimeter_num(). ///When perimeter_num()==0, this function is undefined. ///Retured is newed object, must be deleted. MGCurve* MGSurface::perimeter_curve( int i// i is perimeter number: // =0: v=min line, =1: u=max line, =2: v=max line, =3: u=min line )const{ assert(i<4); int is_u; double x; switch(i){ case 0: is_u=0; x=param_s_v(); break; case 1: is_u=1; x=param_e_u(); break; case 2: is_u=0; x=param_e_v(); break; default: is_u=1; x=param_s_u(); break; } return parameter_curve(is_u, x); } // Construct perimeter (u,v) parameter position. MGPosition MGSurface::perimeter_uv(int i,double t) const // i is perimeter number: // =0: v=min line, =1: u=max line, =2: v=max line, =3: u=min line // t is perimeter parameter line's parameter value of u or v. { assert(i<4); MGPosition uv(2); switch(i){ case 0: uv(0)=t;uv(1)=param_s_v(); break; case 1: uv(1)=t;uv(0)=param_e_u(); break; case 2: uv(0)=t;uv(1)=param_e_v(); break; default: uv(1)=t;uv(0)=param_s_u(); break; } return uv; } //Return all foots of the perpendicular straight lines from P. MGPosition_list MGSurface::perps( const MGPosition& P // Point of a space(指定点) )const{ const MGKnotVector& tu=knot_vector_u(); const MGKnotVector& tv=knot_vector_v(); int ku=tu.order(), kv=tv.order(); int k=kv; if(k=uv1(0) or uv0(1)>=uv1(1), //no limit for this parameter range. const MGCompositeCurve& crv,//MGCompositeCurve. double t0, double t1,//parameter range of curve. //When t0>=t1, no limit for curve2 parameter range. const MGPosition& tuvg, //Guess parameter value of curve and this surface. MGPosition& tuv //perpendicular points' parameter values will be output. //tuv(0): curve's parameter, (tuv(1),tuv(2)):this surface's parameter. ) const{ if(!crv.number_of_curves()) return 0; double t=tuvg[0]; int i=crv.find(t); const MGCurve& curvei=crv.curve(i); if(t0=uv1(0) or uv0(1)>=uv1(1), //no limit for this parameter range. const MGPosition& P, //Point(指定点) const MGPosition& uvguess, // guess parameter value of surface MGPosition& uv // Parameter value will be returned. )const{ //Method: Let S(u,v) is the surface, then f=(S-P)**2 should be extremum. //If S(u,v) is the point where the shortest distance between P and S, the //following conditions are satisfied: // df/du=2*Su*(S-P)=0. df/dv=2*Sv*(S-P)=0. // Starting with guess parameter (u,v), du and dv are // obtained by solving the two equations // E+dE(u,v)=0 and F+dF(u,d)=0, where E(u,v)=Su*(S-P), and F(u,v)=Sv*(S-P). // Then (u+du, v+dv) is the next guess parameter value. // Here dE(u,v) and dF(u,v) are total differentials of E and F. // dE=(Suu*(S-P)+Su*Su)*du+(Suv*(S-P)+Su*Sv)*dv=-E // dF=(Suv*(S-P)+Su*Sv)*du+(Svv*(S-P)+Sv*Sv)*dv=-F // If we set A=Suu*(S-P)+Su*Su, B=Suv*(S-P)+Su*Sv, and C=Svv*(S-P)+Sv*Sv, // du=(B*F-C*E)/(A*C-B*B) dv=(B*E-A*F)/(A*C-B*B) . // uv.resize(2); MGPosition S; MGVector Su,Sv,Suv,Suu,Svv,SP,dS; double du,dv; double error_sqr=MGTolerance::wc_zero_sqr(); error_sqr *=.25; double error_rel=MGTolerance::rc_zero(); double u0=uv0(0), u1=uv1(0), v0=uv0(1), v1=uv1(1); if(u0>=u1) {u0=param_s_u(); u1=param_e_u();} if(v0>=v1) {v0=param_s_v(); v1=param_e_v();} double uspan=u1-u0, vspan=v1-v0; double A,B,C,E,F,ACmB2; int loop=0, ulow=0, uhigh=0, vlow=0, vhigh=0; double uold,usave, vold,vsave; double& u=uv(0); double& v=uv(1); uold=u=uvguess.ref(0), vold=v=uvguess.ref(1); while(loop++<16 && ulow<5 && uhigh<5 && vlow<5 && vhigh<5){ eval_all(u,v,S,Su,Sv,Suv,Suu,Svv); SP=S-P; E=Su%SP; F=Sv%SP; A=Suu%SP+Su%Su; B=Suv%SP+Su%Sv; C=Svv%SP+Sv%Sv; ACmB2=A*C-B*B; if(MGMZero(ACmB2)) return 0; du=(B*F-C*E)/ACmB2; dv=(B*E-A*F)/ACmB2; dS=Su*du+Sv*dv; u+=du; v+=dv; if(dS%dS<=error_sqr){ if(MGREqual_base(u0,u,uspan) || uu1) u=u1; if(MGREqual_base(v0,v,vspan) || vv1) v=v1; return true; } usave=u; vsave=v; if(uu1){ //if(uold>u1 && u>=uold) break; //If no convergence, return. ulow=0; uhigh+=1; u=u1; } else {ulow=0; uhigh=0;} if(vv1){ //if(vold>v1 && v>=vold) break; //If no convergence, return. vlow=0; vhigh+=1; v=v1; } else {vlow=0; vhigh=0;} uold=usave; vold=vsave; } return 0; } //Compute perpendicular points of a curve and a surface, //given guess starting paramter values. int MGSurface::perp_guess( const MGPosition& uv0, const MGPosition& uv1, //parameter range of this surface. //When uv0(0)>=uv1(0) or uv0(1)>=uv1(1), //no limit for this parameter range. const MGCurve& curve, //curve. double t0, double t1, //parameter range of curve. //When t0>=t1, no limit for curve2 parameter range. const MGPosition& tuvg, //Guess parameter value of curve and this surface. MGPosition& tuv //perpendicular points' parameter values //will be output. //tuv(0): curve's parameter, (tuv(1),tuv(2)):this surface's parameter. )const{ const MGCompositeCurve* ccrv=dynamic_cast(&curve); if(ccrv) return perp_guess(uv0,uv1,*ccrv,t0,t1,tuvg,tuv); return perp_guess_general(uv0,uv1,curve,t0,t1,tuvg,tuv); } //Return the foot of the perpendicular straight line from P. //Computation is done from the guess parameter value. //Function's return value is whether point is obtained(true) or not(false). bool MGSurface::perp_guess( const MGPosition& P, //Point const MGPosition& uvguess, // guess parameter value of the shell MGPosition& uv // Parameter value will be returned. )const{ MGPosition uv0(param_e_u(),param_e_v()), uv1(param_s_u(), param_s_v()); return perp_guess(uv0,uv1,P,uvguess,uv)!=0; } //Compute perpendicular points of a curve and the FSurface, //given guess starting paramter values. //Function's return value is: // perp_guess=true if perpendicular points obtained, // perp_guess=false if perpendicular points not obtained, bool MGSurface::perp_guess( const MGCurve& curve, //curve. const MGPosition& uvguess, //Guess parameter value of the FSurface. double tguess, //Guess parameter value of the curve. MGPosition& uv, //perpendicular point's parameter values of the shell double& t //will be output. )const{ MGPosition uv0(param_e_u(),param_e_v()), uv1(param_s_u(), param_s_v()); double t0=curve.param_e(), t1=curve.param_s(); MGPosition tuvg(tguess,uvguess[0],uvguess[1]); MGPosition tuv; int obtained=perp_guess_general(uv0,uv1,curve,t0,t1,tuvg,tuv); uv.resize(2); uv(0)=tuv[1]; uv(1)=tuv[2]; t=tuv[0]; return obtained!=0; } //Compute perpendicular points of a curve and a surface, //given guess starting paramter values. int MGSurface::perp_guess_general( const MGPosition& uv0, const MGPosition& uv1, //parameter range of this surface. //When uv0(0)>=uv1(0) or uv0(1)>=uv1(1), //no limit for this parameter range. const MGCurve& curve, //curve. double t0, double t1, //parameter range of curve. //When t0>=t1, no limit for curve2 parameter range. const MGPosition& tuvg, //Guess parameter value of curve and this surface. MGPosition& tuv //perpendicular points' parameter values will be output. //tuv(0): curve's parameter, (tuv(1),tuv(2)):this surface's parameter. )const{ //Function's return value is: // perp_guess=true if perpendicular points obtained, // perp_guess=false if perpendicular points not obtained, //****Method**** //Let f(t) and g(u,v) are curve and surface, then h=(f-g)**2 should be //minimized. //If f(t) and g(u,v) are the points where the shortest(or longest) //distance between f and g, then the following conditions are satisfied: // dh/dt=2*ft*(f-g)=0. dh/du=2*g1*(g-f)=0. dh/dv=2*g2*(g-f)=0. //Here ft=df/dt, g1=dg/du, g2=dg/dv. // Starting with guess parameter (t,(u,v)), dt and (du,dv) are // obtained by solving the three equations // E+dE(t,u,v)=0 , F+dF(t,u,v)=0, and G+dG(t,u,v)=0, // where E(t,u,v)=ft%(f-g) , F(t,u,v)=g1%(g-f), G(t,u,v)=g2%(g-f). // Then (t+dt,(u+du,v+dv)) is the next guess parameter value. // Here dE(t,u,v), dF(t,u,v), and dG(t,u,v) are total differentials of // E, F, and G. // dE= (ftt%(f-g)+ft%ft)*dt-ft%g1*du-ft%g2*dv=-E // dF=-ft%g1*dt+(g1%g1+(g-f)%g11)*du+(g1%g2+(g-f)%g12)*dv=-F // dG=-ft%g2*dt+(g1%g2+(g-f)%g12)*du+(g2%g2+(g-f)%g22)*dv=-G // (ft, g1, and g2 are once differential of f, g about u, ang g about v. // ftt, g11, g12, and g22 are twice differential of f and g.) // If we set // A=ftt%(f-g)+ft%ft, B=-ft%g1, C=-ft%g2, // X=g1%g1-(f-g)%g11, Y=g1%g2-(f-g)%g12, Z=g2%g2-(f-g)%g22 // L1=B*B-A*X, L2=B*C-A*Y, // L3=C*C-A*Z, // L4=C*X-B*Y, L5=C*Y-B*Z // M1=A*F-B*E, M2=A*G-C*E, M3=B*G-C*F, // du, dv, and dt are obtained as; // du=(M1*L3-L2*M2)/(L1*L3-L2*L2) dv=(L1*M2-M1*L2)/(L1*L3-L2*L2) // dt=(-E-B*du-C*dv)/A. // double error_sqr=(MGTolerance::wc_zero_sqr())*.25; //.25 is multiplied to enforce more strictness of line intersection. //Tolerance is made half(.25=.5*.5). double error_rel=MGTolerance::rc_zero(); double terror=(t1-t0)*error_rel; double t0e=t0-terror, t1e=t1+terror; double u0=uv0(0), u1=uv1(0), v0=uv0(1), v1=uv1(1); if(u0>=u1) {u0=param_s_u(); u1=param_e_u();} if(v0>=v1) {v0=param_s_v(); v1=param_e_v();} double uerror=(u1-u0)*error_rel; double u0e=u0-uerror, u1e=u1+uerror; double verror=(v1-v0)*error_rel; double v0e=v0-verror, v1e=v1+verror; MGPosition f,g; MGVector ft,ftt,g1,g2,g11,g12,g22,fmg, df,dg1,dg2; double A,B,C,E,F,G,X,Y,Z; double A2,B2,C2; double L1,L2,L3,L4,L5,L1322,L1524,L2534, M1,M2,M3; double AL1322,AL1524,AL2534; int loop=0, tlow=0,thigh=0, ulow=0,uhigh=0, vlow=0, vhigh=0; double dfdf,dgdg; double dt,told,tsave, du,uold,usave, dv,vold,vsave; double& t=tuv(0); double& u=tuv(1); double& v=tuv(2); told=t=tuvg(0), uold=u=tuvg(1), vold=v=tuvg(2); while(loop++<16 && tlow<5 && thigh<5 && ulow<5 && uhigh<5 && vlow<5 && vhigh<5){ eval_all(u,v,g,g1,g2,g12,g11,g22); curve.eval_all(t,f,ft,ftt); fmg=f-g; E=ft%fmg; F=-(g1%fmg); G=-(g2%fmg); A=ftt%fmg+ft%ft; B=-(ft%g1); C=-(ft%g2); A2=A*A; B2=B*B; C2=C*C; X=g1%g1-fmg%g11; Y=g1%g2-fmg%g12; Z=g2%g2-fmg%g22; L1=B2-A*X; L2=B*C-A*Y; L3=C2-A*Z; L4=C*X-B*Y; L5=C*Y-B*Z; M1=A*F-B*E; M2=A*G-C*E; M3=B*G-C*F; L1322=L1*L3-L2*L2; L1524=L1*L5-L2*L4; L2534=L2*L5-L3*L4; //std::cout<<"f="<