/********************************************************************/ /* Copyright (c) 2017 System fugen G.K. and Yuzi Mizuno */ /* All rights reserved. */ /********************************************************************/ #include "MGCLStdAfx.h" #include "mg/nlbit.h" #include "mg/CurveParameter.h" #include "mg/Ellipse.h" #include "mg/Straight.h" #include "mg/CCisect_list.h" #include "mg/CCisect.h" #include "mg/CParam_list.h" #include "mg/Tolerance.h" //#if defined(_DEBUG) //#define new DEBUG_NEW //#undef THIS_FILE //static char THIS_FILE[] = __FILE__; //#endif using namespace std; // Implementation of MGEllipse. //Utility class for MGEllipse constructor of curve1, 2, and tangent point on crv1. class MGEllipseTTR: public MGCurveParameter{ const MGCurve& m_curve1; //curve1. const MGCurve& m_curve2; //curve2. double m_r; //radius of the target arc. double m_rabs;//absolute value of m_r. double m_t2guess;// initial guess parameter of m_curve2. const MGUnit_vector& m_normal;//The normal of the plane where the arc lies on. mutable double m_t2obtained; //When solution found, m_t2obtained will be the tangent point parameter of curve2. mutable MGPosition m_center; //When solution found, m_center will be the center of the arc. double m_r2; //square of m_r. public: MGEllipseTTR( const MGCurve& crv1,//curve1. double t2guess, //guess parameter value of crv2. const MGCurve& crv2,//curve2. double r, //radius of the target arc. const MGUnit_vector& normal//The normal of the plane where the arc lies on. ):MGCurveParameter(crv1.param_range(),MGTolerance::wc_zero_sqr()) ,m_curve1(crv1),m_t2guess(t2guess),m_curve2(crv2),m_r(r),m_rabs(r),m_normal(normal){ if(m_rabs<0.) m_rabs*=-1.; m_r2=r*r; }; //return the difference of the two radii. That is, //let m_center be the point normally away by r from curve's point P1(of parameter t), //and P2 is the foot point of perpendicular line from m_center to curve2. //Then this operator returns a*a-b*b, where a=the distance from m_center to //P1, and b=distance from P2 to m_center. double operator()( double t //is a parameter of m_curv2; )const{ MGVector tan1=m_curve1.eval(t,1); MGPosition P1=m_curve1.eval(t); MGUnit_vector N1=m_normal*tan1; if((N1%(m_curve2.eval(m_t2guess)-P1))<0.){ N1.negate(); } m_center=P1+N1*m_rabs; if(!m_curve2.perp_guess(1.,0.,m_center,m_t2guess,m_t2obtained)){ m_t2obtained=m_curve2.closest(m_center); } MGVector diff=m_curve2.eval(m_t2obtained)-m_center; return diff%diff-m_r2; }; MGPosition& center(){return m_center;}; double curve2_param(){return m_t2obtained;}; }; //目的: // 基本線2本と半径からコーナーRを作成する // 初期パラメータはコーナーRを作成したい側に設定し、基本線は同一平面上にあること //戻り値: // 0: 正常終了 // -1: 入力値が不正 // -2: 基本線の曲率半径より半径Rが大きい // -3: 基本線同士の交点が求まらない //Start point of the generated ellipse is either t1 side or t2, //which depends on normal direction. The ellipse direction is defined from normal, //If the arc length from t1 to t2(arond normal) is smaller than from t2 to t1(around normal), //the start point is on t1 side. If the arc length from t1 to t2 is longer than from t2 to t1, //the start point is on t2 side. MGEllipse::MGEllipse( const MGCurve& crv1, //I:基本線1 const MGCurve& crv2, //I:基本線2 const MGUnit_vector& normal, //I:基本線のノーマルベクトル double r,//I:コーナーRの半径 double& t1, //I:基本線1の初期パラメータ(コーナーRを作成する側を指定) //O:コーナーRと接する基本線1のパラメータ値 double& t2, //I:基本線2の初期パラメータ(コーナーRを作成する側を指定) //O:コーナーRと接する基本線2のパラメータ値 int& rc) //O:リターンコード :MGCurve(crv1),m_gprange(0),m_knotV(0){ MGEllipseTTR ttr(crv1,t2,crv2,r,normal); ttr.set_delta(crv1.param_span()/crv1.divide_number()); rc=ttr.getCurveParameter(t1); if(rc) return; //Construct arc data. MGPosition P1=crv1.eval(t1); t2=ttr.curve2_param(); MGPosition P2=crv2.eval(t2); MGPosition& Center=ttr.center(); MGVector vec1(P1 - Center), vec2(P2 - Center); double dAng = vec1.angle(vec2); MGVector V12(vec2 - vec1), B1(normal*vec1); if((V12%B1)>0.){ copy_ellipse_data(MGEllipse(Center, P1, dAng, normal));//start is P1. }else{ copy_ellipse_data(MGEllipse(Center, P2, dAng, normal));//start is P2. } rc=0;//Normal return; } //Utility class for MGEllipse constructor of curve1, 2, and tangent point on crv1. class MGEllipseTTP: public MGCurveParameter{ const MGUnit_vector& m_normal;//The normal of the plane where the arc lies on. const MGUnit_vector& m_N1; //curve1's normal at the point P1. const MGPosition& m_P1; //curve1's point which is the end of the arc. const MGCurve& m_crv2; //curve2. public: MGEllipseTTP( const MGUnit_vector& normal,//The normal of the plane where the arc lies on. const MGUnit_vector& N1, //curve1's normal at the point P1. const MGPosition& P1, //curve1's point which is the end of the arc. const MGCurve& crv2 //curve2. ):MGCurveParameter(crv2.param_range(),MGTolerance::wc_zero_sqr()) ,m_normal(normal), m_N1(N1), m_P1(P1), m_crv2(crv2){;}; //return the difference of the two radii. That is, //let Q be the intersection of the straight from m_P1 to m_N1 and from P2 to normal at P2 //of the curve2. Then this operator returns a*a-b*b, where a=signed distance from //P1 to Q, and b=signed distance from P2 to Q. double operator()( double t //is a parameter of m_curv2; )const{ MGUnit_vector N2=normal2(t); MGVector p12=getP12(t); double p12n1=p12%m_N1, p12n2=p12%N2, n12=m_N1%N2; return (p12n1*p12n1-p12n2*p12n2)/(1.-n12*n12); }; MGUnit_vector normal2(double t2)const{ return m_normal*m_crv2.eval(t2,1); }; MGVector getP12(double t2)const{ return m_crv2.eval(t2)-m_P1; }; double radius(double t2)const{ MGUnit_vector N2=normal2(t2); double n12=m_N1%N2; MGVector p12=getP12(t2); return (p12%m_N1-(p12%N2)*n12)/(1.-n12*n12); }; }; //Construct an arc from 2 curves and a tangent point on the curve 1. //The 2 curves must be planar and must lie on the sampe plane. //return code rc: // =0: normally constructed. // =1:no arcs found that are tangent to crv1 and 2. // =-2: error detected in mgNlbit // (This case does not occur normally, maybe some bugs are included). //Start point of the generated ellipse is either t1 side or t2, //which depends on normal direction. The ellipse direction is defined from normal, //If the arc length from t1 to t2(arond normal) is smaller than from t2 to t1(around normal), //the start point is on t1 side. If the arc length from t1 to t2 is longer than from t2 to t1, //the start point is on t2 side. MGEllipse::MGEllipse( const MGCurve& crv1, //curve1 const MGCurve& crv2, //curve2 const MGUnit_vector& normal, //normal of the plane 2 curvs lie on double t1, //tangent point's parameter of crv1. double& t2, //guess parameter value of crv2 is input initially, //correct tangent point's parameter will be output when rc=0. int& rc //return code. ):MGCurve(crv1),m_gprange(0), m_knotV(0){ MGVector tan1=crv1.eval(t1,1); MGPosition P1=crv1.eval(t1); MGUnit_vector N1=normal*tan1; MGEllipseTTP radius_diff(normal,N1,P1,crv2); double delta=crv2.param_span()/crv2.divide_number(); radius_diff.set_delta(delta); rc=radius_diff.getCurveParameter(t2); if(rc) return; //Construct arc data. MGPosition P2=crv2.eval(t2); MGPosition Center=P1+N1*radius_diff.radius(t2); MGVector vec1(P1 - Center), vec2(P2 - Center); double dAng = vec1.angle(vec2); MGVector V12(vec2 - vec1), B1(normal*vec1); if((V12%B1)>0.){ copy_ellipse_data(MGEllipse(Center, P1, dAng, normal)); }else{ copy_ellipse_data(MGEllipse(Center, P2, dAng, normal)); } rc=0;//Normal return; } //目的: // 基本線3本からコーナーRを作成する // crv1, crv2, crv3がそれぞれ始点、通過点、終点となるようにする。 // 初期パラメータはコーナーRが接する近辺に設定し、基本線は同一平面上にあること //戻り値: // 0: 正常終了 // -1: 連立方程式が解けなかった // -3: 収束しなかった(無限ループにおちいった) MGEllipse::MGEllipse( const MGCurve& crv1, //I:基本線1(始点) const MGCurve& crv2, //I:基本線2(通過点) const MGCurve& crv3, //I:基本線3(終点) const MGUnit_vector& normal,//I:基本線のノーマルベクトル double& t1, //I:基本線1の初期パラメータ(コーナーRの始点近辺) //O:コーナーRの始点における基本線1のパラメータ値 double& t2, //I:基本線2の初期パラメータ(コーナーRの通過点近辺) //O:コーナーRの通過点における基本線2のパラメータ値 double& t3, //I:基本線3の初期パラメータ(コーナーRの終点近辺) //O:コーナーRの終点における基本線2のパラメータ値 int& rc //O:リターンコード ):MGCurve(crv1),m_gprange(0), m_knotV(0){ int siNit=20; double d1, d2, d3, r; double error=MGTolerance::wc_zero_sqr(); MGPosition P1,P2,P3; rc=-3; while(siNit-->0){ P1=crv1.eval(t1), P2=crv2.eval(t2), P3=crv3.eval(t3); MGPosition posM((P1+P2+P3)/3.); //重心 MGVector tan1=crv1.eval(t1,1), tan2=crv2.eval(t2,1), tan3=crv3.eval(t3,1); MGUnit_vector T1(tan1), T2(tan2), T3(tan3); MGUnit_vector N1(normal*T1), N2(normal*T2), N3(normal*T3); //垂直ベクトルは、3点の重心方向になるようにする if(((posM-P1)%N1) < 0.0) N1.negate(); if(((posM-P2)%N2) < 0.0) N2.negate(); if(((posM-P3)%N3) < 0.0) N3.negate(); //以下の式のd1,d2,d3が0となるrを求める //P2 + d2*T2 + r*N2 = P1 + d1*T1 + r*N1 = P3 + d3*T3 + r*N3 double a00 = T1%N2, a01 = (N1-N2)%N2, a10 = T1%N3, a11 = (N1-N3)%N3, b0 = (P2-P1)%N2, b1 = (P3-P1)%N3; double det = a00*a11-a10*a01; if(!MGMZero(det)){ d1 = (b0*a11-b1*a01)/det; r = (a00*b1-a10*b0)/det; }else{ rc = -1; break; } MGPosition center(P1+r*N1); d2 = (center-P2)%T2; d3 = (center-P3)%T3; if((d1*d1+d2*d2+d3*d3)0.){ copy_ellipse_data(MGEllipse(Center, P1, dAng, normal)); }else{ copy_ellipse_data(MGEllipse(Center, P2, dAng, normal)); } rc=0;//Normal return; } //目的: //基本線と基本線上R止まりと円弧端点からコーナーRを作成する //初期パラメータはコーナーRを作成したい側に設定し、基本線と円弧端点は同一平面上にあること MGEllipse::MGEllipse ( const MGCurve& crv, //I:基本線 const MGPosition& P2, //I:円弧端点 const MGUnit_vector& normal, //I:基本線のノーマルベクトル double t //I:R止まり点のパラメータ ):MGCurve(crv),m_gprange(0), m_knotV(0){ MGPosition P1=crv.eval(t); MGPosition Q=(P1+P2)*.5; MGVector V=Q-P1; MGVector T=crv.eval(t,1); MGUnit_vector N=normal*T;//Normal at t. if(N%V<0.) N.negate();//The arc is always on the side of the vector V. if(T%V<0.) T.negate(); double vlen=V.len(); double r=vlen*vlen/(N%V);//radius of the arc. MGPosition C=P1+N*r;//center. double angle=acos(vlen/r);//angle of the arc. copy_ellipse_data(MGEllipse(C, P1, mgPAI-2.*angle, T*N)); }