/********************************************************************/ /* 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/Position.h" #include "mg/Position_list.h" #include "mg/Transf.h" #include "mg/LBRepEndC.h" #include "mg/Straight.h" #include "mg/LBRep.h" #include "mg/CParam_list.h" #include "mg/CCisect_list.h" #include "mg/CSisect_list.h" #include "mg/SSisect_list.h" #include "mg/Surface.h" #include "mg/Plane.h" #include "mg/SBRep.h" #include "mg/RSBRep.h" #include "mg/Tolerance.h" #include "topo/Face.h" #include "cskernel/Bkdnp.h" #include "cskernel/blg4sq.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. //*******Intersection.************ //Compute intersection points of outer boundary curves of this face //with face2 to compute intersections. //Function's return value is the number of ip's obtained(appended) //into uvuv_list, may not be equal to the enlarged size of uvuv_list. int MGSurface::isect_outcurves( const MGFSurface& face2, MGPosition_list& uvuv_list, //intersection points will be appended. //One member in the list is of sdim 7, //and the last three elements are the ip direction vector. int id1 //id of uvuv(a member of uvuv_list). //uvuv(id1) for this face parameter uvuv(id2) for srf or face2 parameter. //id2=0 if id1=2, and id2=2 if id1=0. )const{ int pnum=perimeter_num(); if(!pnum) return 0; int id2=2; if(id1==2) id2=0; const MGSurface* sf2=face2.get_surface_pointer(); double t1[4]={param_s_u(),param_s_v(),param_e_u(),param_e_v()}; int i; int im1; double t; MGCurve* boundary; int numi=0; for(i=0; iparam_s_u(); t1=surf1->param_e_u();} else{ t0=surf1->param_s_v(); t1=surf1->param_e_v();} double tau=(t0+t1)*.5; double delta=(t1-t0)/double(nspan); int sign=-1; MGCSisect_list csiList; for(int i=0; it0 && tauparameter_curve(isU,tau); csiList=surf2->isect(*crv); delete crv; if(csiList.size()) break; } if(csiList.size()) break; sign*=-1; } if(!csiList.size()) return uvuv_list; double u1,v1; u1=tau; v1=csiList.front().param_curve(); if(!isU){ u1=v1; v1=tau;} const MGPosition& uv2=csiList.front().param_surface(); MGPosition uvuv(4); if(surf1==this){ uvuv(0)=u1; uvuv(1)=v1; uvuv.store_at(2,uv2,0,2); }else{ uvuv(2)=u1; uvuv(3)=v1; uvuv.store_at(0,uv2,0,2); } uvuv_list.append(uvuv); return uvuv_list; } //Compute the intersection lines of this surface and srf2(both are not planes). MGSSisect_list MGFSurface::isect_with_surf( MGPosition_list& uvuv_list, //Let a member of uvuv_list be uvuv. Then, uvuv's space dimension is //at least 4, and the first 2 is (u,v) of this and the next 2 is (u,v) of srf2. //When uvuv's space dimension is more than 4, it indicates that the uvuv //is used to input approximate tangent of the intersection. const MGFSurface& face2 //2nd surface for the intersection. )const{ MGSSisect_list lst(this,&face2); MGSSisect ssi; MGPosition_list::iterator uvuv_id; int obtained; int n; int loop_number=0, max_loop=uvuv_list.entries(); while(n=uvuv_list.entries()){ if(n>=2){ const MGPosition& Ps=uvuv_list.front(); MGVector N1=unit_normal(Ps[0],Ps[1]), N2=face2.unit_normal(Ps[2],Ps[3]); double saS=N1.sangle(N2); if(saS<.02){ const MGPosition& Pe=uvuv_list.back(); N1=unit_normal(Pe[0],Pe[1]); N2=face2.unit_normal(Pe[2],Pe[3]); double saE=N1.sangle(N2); if(saSuvuvE_is_a_midpoint(*f2,m1,ssi,uvuvE)){ uvuvS=MGPosition(7,uvuvS); uvuvS.store_at(4,iline.eval(iline.param_s(),1)); goto again; //goto again means uvuvE is a mid point and will be neglected. } //Check to the starting direction by reversing the ssi. ssi.negate(); if(uvuvE_is_a_midpoint(*f2,m1,ssi,uvuvS)){ uvuvS=MGPosition(7,uvuvE); uvuvS.store_at(4,iline.eval(iline.param_s(),1)); goto again; } } } if(obtained==7){ if(!ssi.is_null()){ MGSSisect_list::SSiterator i=lst.find_common(ssi); if(i==lst.end()) lst.append(ssi); else{ MGCurve& is1=(*i).line(); int nk1=is1.bdim(); MGCurve& is2=ssi.line(); int nk2=is1.bdim(); if(nk1<=nk2){ if(nk1=max_loop) break; } return lst; } //Default intersection program of MGSurface. //It is assumed that both this and srf2 are not a plane. MGSSisect_list MGSurface::intersect(const MGSurface& srf2) const{ MGSSisect_list lst(this,&srf2); if(!has_common(srf2)) return lst; MGPosition_list uvuv_list; intersect12Boundary(srf2,uvuv_list); if(!uvuv_list.size()) uvuv_list=intersectInner(srf2); //Compute intersection line using isect_with_surf. lst=isect_with_surf(uvuv_list,srf2); return lst; } //Default intersection program of MGSurface with a plane. MGSSisect_list MGSurface::intersectPl(const MGPlane& srf2)const{ MGSSisect_list lst(this,&srf2); MGPosition_list uvuv_list; if(!box().cutting(srf2)) return lst; intersect12Boundary(srf2,uvuv_list); if(!uvuv_list.size()) uvuv_list=intersectInner(srf2); //Compute intersection lines using isect_with_plane. lst=isect_with_plane(uvuv_list,srf2,srf2); return lst; } //isect_dt computes incremental values du and dv for the intersection //computation at parameter position (u,v). void MGFSurface::isect_dt( double u, double v, double& du, double& dv, double acuRatio //acuracy ratio. ) const{ const MGKnotVector& tu=knot_vector_u(); const MGKnotVector& tv=knot_vector_v(); int id,k, k_half1,k_half2; double alfa=isect_dt_coef(0); id=tu.locate(u); k=tu.order(); k_half1=k/2; k_half2=k-k_half1-1; du=(tu(id+k_half1)-tu(id-k_half2)); // if(k<4) k=4;//k=4; du=du/double(k)*alfa; du*=acuRatio; id=tv.locate(v); k=tv.order(); k_half1=k/2; k_half2=k-k_half1-1; dv=(tv(id+k_half1)-tv(id-k_half2)); if(k<4) k=4;//k=4; dv=dv/double(k)*alfa; dv*=acuRatio; } #define DELTA .05; //isect_direction() is used by isect_startPt() to define which constant //parameter line should be used to compute intersection, and what //incremental value be used for the parameter. //Function's return value is direction to get next intersection(with dt). //When =1: u=const direction, =0: v=const, =-1: cannot get intersection. int MGFSurface::isect_direction( const MGFSurface& sf2, //Second surface for the intersection. int m1, //id of uvuvS that indicates this surface's parameter //position in uvuvS. (uvuvS(m1), uvuvS(m1+1))=(u,v) of this surface. MGPosition& uvuvS,//start parameter (u,v) pair of this surface and sf2. double& du, //Incremental value of the parameter kind of kdt will be output. double& dv, //Right dt will be output according to the function's output =0,1. double acuRatio //acuracy ratio. )const{ int m1p1=m1+1; MGPosition uvuv(4),uv; double& u0=uvuvS(m1); double& u1=uvuv(m1); double& v0=uvuvS(m1p1); double& v1=uvuv(m1p1); double lzero=MGTolerance::line_zero(); MGTolerance::set_line_zero(lzero*acuRatio); int kdt=-1; //Normalize the starting point direction. int obtained, kdt2; int pnum; double du10, dv10; MGVector direction; if(uvuvS.sdim()>4){ //When direction specified. direction.resize(3); direction.store_at(0,uvuvS,4,3); } if(!direction.is_null() && !direction.is_zero_vector()){ kdt=isect_direction_with_direction(u0,v0,direction,du,dv); du10=du*DELTA; dv10=dv*DELTA; double u=u0, v=v0; if(kdt) u+=du10; else v+=dv10; if(!in_range(u,v)){ kdt=!kdt; u=u0, v=v0; if(kdt) u+=du10; else v+=dv10; if(!in_range(u,v)) {kdt=-1; goto ret10;} } int m2=2; if(m1) m2=0; double u20=uvuvS(m2); double v20=uvuvS(m2+1); int pnum; if(sf2.on_a_perimeter(u20,v20,pnum)){ int degu=(pnum+1)%2, degv=pnum%2; MGVector T=sf2.eval(u20,v20,degu,degv);if(pnum>=2) T*=-1.; if(!T.parallel(direction)){ MGVector N=sf2.normal(u20,v20); if(N%(T*direction)<0.) {kdt=-1; goto ret10;} } } MGVector P00=eval(u0,v0); if(obtained=isect_start_incr(sf2,uvuvS,kdt,du10,dv10,m1,uvuv)){ MGVector dir1(eval(u1,v1)-P00); double angle1=dir1.cangle(direction), angle2; int kdt3=!kdt; MGPosition uvuv2(4); double u=u0, v=v0; if(kdt3) u+=du10; else v+=dv10; if(in_range(u,v)){ if(isect_start_incr(sf2,uvuvS,kdt3,du10,dv10,m1,uvuv2)){ MGVector dir2(eval(uvuv2[m1],uvuv2[m1p1])-P00); angle2=dir2.cangle(direction); if(angle1eval(t,1); double du,dv; int kdt=isect_direction_with_direction(u0,v0,direction,du,dv); if(duv[0]<0.) du=-fabs(du); if(duv[1]<0.) dv=-fabs(dv); double du10=du*DELTA; double dv10=dv*DELTA; double u=u0, v=v0; if(kdt) u+=du10; else v+=dv10; if(!sf1->in_range(u,v)){ kdt=!kdt; u=u0, v=v0; if(kdt) u+=du10; else v+=dv10; if(!sf1->in_range(u,v)) return false; } double u20=uvuvE(m2); double v20=uvuvE(m2+1); int pnum; if(sf2->on_a_perimeter(u20,v20,pnum)){ int degu=(pnum+1)%2, degv=pnum%2; MGVector T=sf2->eval(u20,v20,degu,degv);if(pnum>=2) T*=-1.; if(!T.parallel(direction)){ MGVector N=sf2->normal(u20,v20); if(N%(T*direction)<0.) return false; } } if(face1){ if(!face1->in_range(u,v)){ kdt=!kdt; u=u0, v=v0; if(kdt) u+=du10; else v+=dv10; if(!face1->in_range(u,v)) return false; } } if(!isect_start_incr(f2,uvuvE,kdt,du10,dv10,m1,uvuv)){ kdt=!kdt; u=u0, v=v0; if(kdt) u+=du10; else v+=dv10; if(!sf1->in_range(u,v)) return false; if(!isect_start_incr(f2,uvuvE,kdt,du10,dv10,m1,uvuv)) return false;; } //Check if intersection is not going to opposite direction as uvS'direction. MGVector dir(eval(u1,v1)-eval(u0,v0)); if(dir.is_zero_vector()) return false; double cang=direction.cangle(dir); if(cang<=MGTolerance::angle_zero()) return false; if(face1) if(!face1->in_range(u1,v1)) return false; if(face2) if(!face2->in_range(uvuv[m2], uvuv[m2+1])) return false; return true; } //isect_direction_with_direction() is used by isect_direction() to temporalily define //which constant parameter line should be used to compute intersection, //and what incremental value be used for the parameter. //Function's return value isect_direction_with_direction() is 1 for u=const parameter //line, and 0 for v=const parameter line. int MGFSurface::isect_direction_with_direction( double u, double v, //start parameter (u,v) of this surface. const MGVector& tangent,//To indicate which direction isect line //should march toward. double& du, //Incremental value sign of the parameter kind of double& dv)const //isect_direction_with_direction will be output. { MGUnit_vector Su=eval(u,v,1,0), Sv=eval(u,v,0,1); int is_u=0; double err=MGTolerance::wc_zero(); err*=err*.1; double Sut=Su%tangent, Svt=Sv%tangent; if(Su%Susect_div_id_max) div_id=sect_div_id_max; return sect_div[div_id]; } //isect_div_id_max is maximum id of array of sect_div defined in //isect_dt_coef. That is, isect_div_id_max+1 is the length of the array //sect_div. int MGFSurface::isect_div_id_max()const{ return sect_div_id_max; } //"isect_inner_dt" is a dedicated function of isect_startPt, // comutes adequate incremental parameter value(du,dv) and parameter line kind //kdt(u=const or v=const). void MGFSurface::isect_inner_dt( int n, //num of i.p. obtained so far(not include uvnow). const MGPosition& uvnow,//intersection point obtained last(of this). double& du, double& dv, //incremental length from previous to uvnow is input. //New du or dv will be output according to kdt's return value. int& kdt, //Parameter kind used so far is input, will be output as: //=1:parameter line kind(u=const), =0: v=const, //=-1:should halt computation since incremental value is zero. double acuRatio //Accurate ratio. ) const{ double uerr=param_error_u()*acuRatio; double verr=param_error_v()*acuRatio; double abdu=fabs(du), abdv=fabs(dv); double ratio=acuRatio; if(abdu<=uerr){ if(abdv<=verr){kdt=-1; return;} else kdt=0; }else if(abdv<=verr) kdt=1; else{ MGVector dfdu=eval(uvnow,1,0), dfdv=eval(uvnow,0,1); double fuu=dfdu%dfdu, fuv=dfdu%dfdv, fvv=dfdv%dfdv; double dffu=fuu*du+fuv*dv, dffv=fuv*du+fvv*dv; double dffubyfvv=dffu*dffu*fvv, dffvbyfuu=dffv*dffv*fuu; if(kdt==-1){ if(dffubyfvv>=dffvbyfuu) kdt=1; else kdt=0; }else if(dffubyfvv>=dffvbyfuu*1.8){ //u=const and v-varying parameter line. if(abdu>uerr*4.) kdt=1; }else if(dffvbyfuu>=dffubyfvv*1.8){ //v=const and u-varying parameter line. if(abdv>verr*4.) kdt=0; } if(kdt) ratio*=dffubyfvv; else ratio*=dffvbyfuu; ratio/=(dffubyfvv+dffvbyfuu); } //Define new dt, kdt. const MGKnotVector* t; double dtold; if(kdt){ t=&(knot_vector_u()); dtold=du; }else{ t=&(knot_vector_v()); dtold=dv; } int k=t->order(); int k_half1=k/2; int k_half2=k-k_half1-1; int id=(*t).locate(uvnow[(kdt+1)%2]); double dt=(*t)(id+k_half1)-(*t)(id-k_half2); if(dtold<0.) dt=-dt; if(k<4) k=4; dt/=double(k); dt*=isect_dt_coef(n)*ratio; if(n){ //When this is not the 1st call of isect_inner_dt, //dt must not exceed twice or half of the old dt. double dtr1=dt*dt, dtr2=dtold*dtold; if(dtr1 > 2.*dtr2) dt=dtold*2.; else if(dtr1 < .5*dtr2) dt=dtold*.5; } if(kdt) du=dt; else dv=dt; return; } //Check if the intersection line lineb's start and end tangent vectors are accurate //enough. If they do not have enough accuracy, isect_start_tan returns //which end did not have the accuracy. // 1:start, 2:end, 3:start and end. If both ends had enough accuracy, returns 0. int isect_start_tan( const MGFSurface& sf1, const MGFSurface& sf2, const MGLBRep& lineb, MGVector* Tse[2] //If an end had not the accuracy, accurate tangent //will be output. Tse[0]:start, Tse[1]:end. ){ double t[2]={lineb.param_s(), lineb.param_e()}; double azero=MGTolerance::angle_zero(); double mzero=MGTolerance::mach_zero()*1.5; int ng=0; for(int j=0; j<2; j++){//Loop for start and end point. MGVector EndP=lineb.eval(t[j]); MGVector N1=sf1.unit_normal(EndP[0],EndP[1]), N2=sf2.unit_normal(EndP[2],EndP[3]); //double sa2=N1.sangle(N2); MGVector& tan=*(Tse[j]); tan=lineb.eval(t[j],1); //std::cout<=0.) N=N1+N2; else N=N1-N2; T=N*(Tline*N); }else{ if(N3%Tline>0.) T=N3; else T=-N3; } double sa=T.sangle(Tline); if(sa>=azero){ ng+=j+1; T.set_unit(); T*=Tline.len(); tan.store_at(4,T,0,3); //std::cout<=2){ endc=MGENDC_1D; tau(n-2)=tau(n-1); point.store_at(n-2,*(tan[1])); } MGBPointSeq& bp=lineb.line_bcoef(); bp.resize(n,point.sdim()); double* ktv=lineb.knot_data(); int irc=bp.capacity(); double* work=new double[n*9];//std::cout<n) k=n; MGKnotVector knotv(tau,k); // std::cout<error && loop++<32){ if(kdt) obtained=isect_start_incr(sf2,uvuv_pre,kdt,middle,dv,m1,uvuv_now); else obtained=isect_start_incr(sf2,uvuv_pre,kdt,du,middle,m1,uvuv_now); if(obtained){ obtained=2; left=middle; }else right=middle; middle=(left+right)/2.; } return obtained; } //isect_start_incr compute one intersection point of two surfaces, //this surface's parameter line at uv1+dt(according to kdt) and sf2, //given previous intersetion point(uv1,uv2) and incremental value dt. //Here uv1 is uvuv_pre(m1, m1+1), and uv2 is other two values of uvuvpre. //isect_start_incr is a dedicated function of isect_start //and isect_start_boundary. //Function's return value is true: if ip found, // false: if ip not found. int MGFSurface::isect_start_incr( const MGFSurface& sf2, //2nd surface b-rep. const MGPosition& uvuv_pre, //Starting parameter values of ip. int kdt, //kdt=true: u=const parameter line, // else: v=const parameter line of this surface. double du, double dv,//Incremental parameter length. int m1, //id of parameter of this surface in uvuv_pre or uvuv_now. MGPosition& uvuv_now//New parameter values of ip will be output. ) const{ double* t; double u,v; double dt; if(kdt){ t=&v; dt=dv*1.5; }else{ t=&u; dt=du*1.5; } int m2=2; if(m1==2) m2=0; MGPosition uv1i(2,uvuv_pre,0,m1); MGCurve* pline=isect_incr_pline(uv1i,kdt,du,dv,u,v); //std::cout<param_s(), te=pline->param_e(), tt; if((*t-ts)eval(*t)<<","<3 && uv_passed>>uvuv_start){ //std::cout<<" uv_passed="<isect_inner_dt(nm1,uv,du,dv,kdt2,acuRatio); if(kdt2==-1){ obtained=7; break;}//kdt will be used even if kdt2=-1. if(kdt!=kdt2){ kdt=kdt2; kdt_changed=true;} } if(n<=1) return 0; if(obtained==7){ uvuv_now=uvuv_pre; if(kdt_changed) kdt=!kdt; } // obtained=1: ip found as a point on a perimeter of one of the surfaces. // This case occurs at on_the_perimeter() since isect_start_incr's // return value is always 1. // =2: ip found by isect_start_boundary as a boundary point. // =3: ip passed one of boundary points in uvuv_list. // =4: ip returned to the starting point. // =7: ip was lost during the computation(may be tangent). //std::cout<<"isect_startPt, endpoint="<maxdivt) divt=maxdivt; double dt=dtsave*divt;//dtsave is always plus. if(kdt){if(du<0.) du=-dt;else du=dt;} else{if(dv<0.) dv=-dt;else dv=dt;} surf1->isect_start_incr(*surf2,uvuv_pre,kdt,du,dv,m1,uvuv_now); point.store_at(i,uvuv_now,0,0,4); //std::cout<<"n="<isect_start_incr(*surf2,uvuv_end,kdt,0.,0.,m1,uvuv_end); point.store_at(nm1,uvuv_end,0,0,4); point.set_length(n); //std::cout<<"n="<