/**************************************************************************** ** ** This file is part of the LibreCAD project, a 2D CAD program ** ** Copyright (C) 2010 R. van Twisk (librecad@rvt.dds.nl) ** Copyright (C) 2001-2003 RibbonSoft. All rights reserved. ** ** ** This file may be distributed and/or modified under the terms of the ** GNU General Public License version 2 as published by the Free Software ** Foundation and appearing in the file gpl-2.0.txt included in the ** packaging of this file. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ** ** This copyright notice MUST APPEAR in all copies of the script! ** **********************************************************************/ #ifdef HAS_BOOST #include #include #include #endif #include "rs_math.h" #include "rs_debug.h" /** * Rounds the given double to the closest int. */ int RS_Math::round(double v) { return (int) lrint(v); //return (v-floor(v)<0.5 ? (int)floor(v) : (int)ceil(v)); } /** * Save pow function */ double RS_Math::pow(double x, double y) { errno = 0; double ret = ::pow(x, y); if (errno==EDOM) { RS_DEBUG->print(RS_Debug::D_ERROR, "RS_Math::pow: EDOM in pow"); ret = 0.0; } else if (errno==ERANGE) { RS_DEBUG->print(RS_Debug::D_WARNING, "RS_Math::pow: ERANGE in pow"); ret = 0.0; } return ret; } /* pow of vector components */ RS_Vector RS_Math::pow(RS_Vector vp, double y) { return RS_Vector(pow(vp.x,y),pow(vp.y,y)); } /** * Converts radians to degrees. */ double RS_Math::rad2deg(double a) { return (a/(2.0*M_PI)*360.0); } /** * Converts degrees to radians. */ double RS_Math::deg2rad(double a) { return ((a/360.0)*(2.0*M_PI)); } /** * Converts radians to gradians. */ double RS_Math::rad2gra(double a) { return (a/(2.0*M_PI)*400.0); } /** * Finds greatest common divider using Euclid's algorithm. */ int RS_Math::findGCD(int a, int b) { int rem; while (b!=0) { rem = a % b; a = b; b = rem; } return a; } /** * Tests if angle a is between a1 and a2. a, a1 and a2 must be in the * range between 0 and 2*PI. * All angles in rad. * * @param reversed true for clockwise testing. false for ccw testing. * @return true if the angle a is between a1 and a2. */ bool RS_Math::isAngleBetween(double a, double a1, double a2, bool reversed) { // bool ret = false; if (reversed) swap(a1,a2); if ( ( correctAngle(a2 -a1) >= correctAngle(a - a1) + RS_TOLERANCE_ANGLE && correctAngle(a - a1) >= RS_TOLERANCE_ANGLE ) || fabs( remainder(correctAngle(a2 - a1 ) , 2.*M_PI)) < RS_TOLERANCE_ANGLE) { // the |a2-a1| % (2 pi)=0 means the whole angular range return true; } else { return false; } } // if(a1>=a2-RS_TOLERENCE) { // if(a>=a1-RS_TOLERENCE || a<=a2+RS_TOLERENCE) { // ret = true; // } // } else { // if(a>=a1-RS_TOLERENCE && a<=a2+RS_TOLERENCE) { // ret = true; // } // } //RS_DEBUG->print("angle %f is %sbetween %f and %f", // a, ret ? "" : "not ", a1, a2); // return ret; //} /** * Corrects the given angle to the range of 0-2*Pi. */ double RS_Math::correctAngle(double a) { return M_PI + remainder(a - M_PI, 2*M_PI); } // while (a>2*M_PI) // a-=2*M_PI; // while (a<0) // a+=2*M_PI; // return a; //} /** * @return The angle that needs to be added to a1 to reach a2. * Always positive and less than 2*pi. */ double RS_Math::getAngleDifference(double a1, double a2) { double ret; ret=M_PI + remainder(a2 -a1 -M_PI, 2*M_PI); // if (a1>=a2) { // a2+=2*M_PI; // } // ret = a2-a1; if (ret>=2*M_PI) { ret=0.0; } return ret; } /** * Makes a text constructed with the given angle readable. Used * for dimension texts and for mirroring texts. * * @param readable true: make angle readable, false: unreadable * @param corrected Will point to true if the given angle was * corrected, false otherwise. * * @return The given angle or the given angle+PI, depending which on * is readable from the bottom or right. */ double RS_Math::makeAngleReadable(double angle, bool readable, bool* corrected) { double ret; bool cor = isAngleReadable(angle) ^ readable; // quadrant 1 & 4 if (!cor) { ret = angle; } // quadrant 2 & 3 else { ret = angle+M_PI; } if (corrected!=NULL) { *corrected = cor; } return ret; } /** * @return true: if the given angle is in a range that is readable * for texts created with that angle. */ bool RS_Math::isAngleReadable(double angle) { if (angle>M_PI/2.0*3.0+0.001 || angle2*M_PI-tol) { //std::cout << "RS_Math::isSameDirection: " << dir1 << " and " << dir2 // << " point in the same direction" << "\n"; return true; } else { //std::cout << "RS_Math::isSameDirection: " << dir1 << " and " << dir2 // << " don't point in the same direction" << "\n"; return false; } } /** * Compares two double values with a tolerance. */ bool RS_Math::cmpDouble(double v1, double v2, double tol) { return (fabs(v2-v1) unsigned int RS_Math::quadraticSolver(double * ce, double * roots) //quadratic solver for // x^2 + ce[0] x + ce[2] =0 { double discriminant=0.25*ce[0]*ce[0]-ce[1]; if (discriminant < 0. ) return 0; roots[0]= -0.5*ce[0] + sqrt(discriminant); roots[1]= -ce[0] - roots[0]; return 2; } unsigned int RS_Math::cubicSolver(double * ce, double *roots) //cubic equation solver // x^3 + ce[0] x^2 + ce[1] x + ce[2] = 0 { // depressed cubic, Tschirnhaus transformation, x= t - b/(3a) // t^3 + p t +q =0 unsigned int ret=0; double shift=(1./3)*ce[0]; double p=ce[1] -shift*ce[0]; double q=ce[0]*( (2./27)*ce[0]*ce[0]-(1./3)*ce[1])+ce[2]; //Cardano's method, // t=u+v // u^3 + v^3 + ( 3 uv + p ) (u+v) + q =0 // select 3uv + p =0, then, // u^3 + v^3 = -q // u^3 v^3 = - p^3/27 // so, u^3 and v^3 are roots of equation, // z^2 + q z - p^3/27 = 0 // and u^3,v^3 are, // -q/2 \pm sqrt(q^2/4 + p^3/27) // discriminant= q^2/4 + p^3/27 //std::cout<<"p="<0)?-pow(q,(1./3)):pow(-q,(1./3)); *roots -= shift; return ret; } //std::cout<<"discriminant="<0) { double ce2[2]= {q, -1./27*p*p*p},u3[2]; ret=quadraticSolver(ce2,u3); if (! ret ) { //should not happen std::cerr<<"cubicSolver()::Error cubicSolver("< 0. && croots[1] > 0. ) { double sqrtz0=sqrt(croots[0]); double ce2[2]; ce2[0]= -sqrtz0; ce2[1]=0.5*(p+croots[0])+0.5*q/sqrtz0; ret=quadraticSolver(ce2,roots); ce2[0]= sqrtz0; ce2[1]=0.5*(p+croots[0])-0.5*q/sqrtz0; ret=quadraticSolver(ce2,roots+2); ret=4; for(unsigned int i=0; i >& mt, QVector& sn){ //verify the matrix size int mSize(mt.size()); //rows int aSize(mSize+1); //columns of augmented matrix for(int i=0;i bm (mSize, mSize); boost::numeric::ublas::vector bs(mSize); for(int i=0;i >(bm) ) { RS_DEBUG->print(RS_Debug::D_WARNING, "Those 4 points do not define an ellipse"); return false; } boost::numeric::ublas:: triangular_matrix lm = boost::numeric::ublas::triangular_adaptor< boost::numeric::ublas::matrix, boost::numeric::ublas::unit_lower>(bm); boost::numeric::ublas:: triangular_matrix um = boost::numeric::ublas::triangular_adaptor< boost::numeric::ublas::matrix, boost::numeric::ublas::upper>(bm); ; boost::numeric::ublas::inplace_solve(lm,bs, boost::numeric::ublas::lower_tag()); boost::numeric::ublas::inplace_solve(um,bs, boost::numeric::ublas::upper_tag()); for(int i=0;i