init
This commit is contained in:
635
lib/math/lc_quadratic.cpp
Normal file
635
lib/math/lc_quadratic.cpp
Normal file
@@ -0,0 +1,635 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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!
|
||||
**
|
||||
**********************************************************************/
|
||||
|
||||
#include <cfloat>
|
||||
#include <QDebug>
|
||||
#include "rs_math.h"
|
||||
#include "rs_information.h"
|
||||
#include "lc_quadratic.h"
|
||||
#include "rs_arc.h"
|
||||
#include "rs_circle.h"
|
||||
#include "rs_ellipse.h"
|
||||
#include "rs_line.h"
|
||||
#include "rs_debug.h"
|
||||
|
||||
#ifdef EMU_C99
|
||||
#include "emu_c99.h" /* C99 math */
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
|
||||
LC_Quadratic::LC_Quadratic():
|
||||
m_mQuad(2,2),
|
||||
m_vLinear(2),
|
||||
m_bValid(false)
|
||||
{}
|
||||
|
||||
LC_Quadratic::LC_Quadratic(const LC_Quadratic& lc0):
|
||||
m_bIsQuadratic(lc0.isQuadratic())
|
||||
,m_bValid(lc0)
|
||||
{
|
||||
if(m_bValid==false) return;
|
||||
if(m_bIsQuadratic) m_mQuad=lc0.getQuad();
|
||||
m_vLinear=lc0.getLinear();
|
||||
m_dConst=lc0.m_dConst;
|
||||
}
|
||||
|
||||
LC_Quadratic& LC_Quadratic::operator = (const LC_Quadratic& lc0)
|
||||
{
|
||||
if(lc0.isQuadratic()){
|
||||
m_mQuad.resize(2,2,false);
|
||||
m_mQuad=lc0.getQuad();
|
||||
}
|
||||
m_vLinear.resize(2);
|
||||
m_vLinear=lc0.getLinear();
|
||||
m_dConst=lc0.m_dConst;
|
||||
m_bIsQuadratic=lc0.isQuadratic();
|
||||
m_bValid=lc0.m_bValid;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
LC_Quadratic::LC_Quadratic(std::vector<double> ce):
|
||||
m_mQuad(2,2),
|
||||
m_vLinear(2)
|
||||
{
|
||||
if(ce.size()==6){
|
||||
//quadratic
|
||||
m_mQuad(0,0)=ce[0];
|
||||
m_mQuad(0,1)=0.5*ce[1];
|
||||
m_mQuad(1,0)=m_mQuad(0,1);
|
||||
m_mQuad(1,1)=ce[2];
|
||||
m_vLinear(0)=ce[3];
|
||||
m_vLinear(1)=ce[4];
|
||||
m_dConst=ce[5];
|
||||
m_bIsQuadratic=true;
|
||||
m_bValid=true;
|
||||
return;
|
||||
}
|
||||
if(ce.size()==3){
|
||||
m_vLinear(0)=ce[0];
|
||||
m_vLinear(1)=ce[1];
|
||||
m_dConst=ce[2];
|
||||
m_bIsQuadratic=false;
|
||||
m_bValid=true;
|
||||
return;
|
||||
}
|
||||
m_bValid=false;
|
||||
}
|
||||
|
||||
/** construct a parabola, ellipse or hyperbola as the path of center of tangent circles
|
||||
passing the point
|
||||
*@circle, an entity
|
||||
*@point, a point
|
||||
*@return, a path of center tangential circles which pass the point
|
||||
*/
|
||||
LC_Quadratic::LC_Quadratic(const RS_AtomicEntity* circle, const RS_Vector& point)
|
||||
: m_mQuad(2,2)
|
||||
,m_vLinear(2)
|
||||
,m_bIsQuadratic(true)
|
||||
,m_bValid(true)
|
||||
{
|
||||
if(circle==nullptr) {
|
||||
m_bValid=false;
|
||||
return;
|
||||
}
|
||||
switch(circle->rtti()){
|
||||
case RS2::EntityArc:
|
||||
case RS2::EntityCircle:
|
||||
{//arc/circle and a point
|
||||
RS_Vector center;
|
||||
double r;
|
||||
|
||||
center=circle->getCenter();
|
||||
r=circle->getRadius();
|
||||
if(center == false){
|
||||
m_bValid=false;
|
||||
return;
|
||||
}
|
||||
double c=0.5*(center.distanceTo(point));
|
||||
double d=0.5*r;
|
||||
if(fabs(c)<RS_TOLERANCE ||fabs(d)<RS_TOLERANCE || fabs(c-d)<RS_TOLERANCE){
|
||||
m_bValid=false;
|
||||
return;
|
||||
}
|
||||
m_mQuad(0,0)=1./(d*d);
|
||||
m_mQuad(0,1)=0.;
|
||||
m_mQuad(1,0)=0.;
|
||||
m_mQuad(1,1)=1./(d*d - c*c);
|
||||
m_vLinear(0)=0.;
|
||||
m_vLinear(1)=0.;
|
||||
m_dConst=-1.;
|
||||
center=(center + point)*0.5;
|
||||
rotate(center.angleTo(point));
|
||||
move(center);
|
||||
return;
|
||||
}
|
||||
case RS2::EntityLine:
|
||||
{//line and a point
|
||||
const RS_Line* line=static_cast<const RS_Line*>(circle);
|
||||
|
||||
RS_Vector direction=line->getEndpoint() - line->getStartpoint();
|
||||
double l2=direction.squared();
|
||||
if(l2<RS_TOLERANCE2) {
|
||||
m_bValid=false;
|
||||
return;
|
||||
}
|
||||
RS_Vector projection=line->getNearestPointOnEntity(point,false);
|
||||
// DEBUG_HEADER
|
||||
// std::cout<<"projection="<<projection<<std::endl;
|
||||
double p2=(projection-point).squared();
|
||||
if(p2<RS_TOLERANCE2) {
|
||||
//point on line, return a straight line
|
||||
m_bIsQuadratic=false;
|
||||
m_vLinear(0)=direction.y;
|
||||
m_vLinear(1)=-direction.x;
|
||||
m_dConst = direction.x*point.y-direction.y*point.x;
|
||||
return;
|
||||
}
|
||||
RS_Vector center= (projection+point)*0.5;
|
||||
// std::cout<<"point="<<point<<std::endl;
|
||||
// std::cout<<"center="<<center<<std::endl;
|
||||
double p=sqrt(p2);
|
||||
m_bIsQuadratic=true;
|
||||
m_bValid=true;
|
||||
m_mQuad(0,0)=0.;
|
||||
m_mQuad(0,1)=0.;
|
||||
m_mQuad(1,0)=0.;
|
||||
m_mQuad(1,1)=1.;
|
||||
m_vLinear(0)=-2.*p;
|
||||
m_vLinear(1)=0.;
|
||||
m_dConst=0.;
|
||||
// DEBUG_HEADER
|
||||
// std::cout<<*this<<std::endl;
|
||||
// std::cout<<"rotation by ";
|
||||
// std::cout<<"angle="<<center.angleTo(point)<<std::endl;
|
||||
rotate(center.angleTo(point));
|
||||
// std::cout<<"move by ";
|
||||
// std::cout<<"center="<<center<<std::endl;
|
||||
move(center);
|
||||
// std::cout<<*this<<std::endl;
|
||||
// std::cout<<"point="<<point<<std::endl;
|
||||
// std::cout<<"finished"<<std::endl;
|
||||
return;
|
||||
}
|
||||
default:
|
||||
m_bValid=false;
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
bool LC_Quadratic::isQuadratic() const {
|
||||
return m_bIsQuadratic;
|
||||
}
|
||||
|
||||
LC_Quadratic::operator bool() const
|
||||
{
|
||||
return m_bValid;
|
||||
}
|
||||
|
||||
bool LC_Quadratic::isValid() const
|
||||
{
|
||||
return m_bValid;
|
||||
}
|
||||
|
||||
void LC_Quadratic::setValid(bool value)
|
||||
{
|
||||
m_bValid=value;
|
||||
}
|
||||
|
||||
|
||||
bool LC_Quadratic::operator == (bool valid) const
|
||||
{
|
||||
return m_bValid == valid;
|
||||
}
|
||||
|
||||
bool LC_Quadratic::operator != (bool valid) const
|
||||
{
|
||||
return m_bValid != valid;
|
||||
}
|
||||
|
||||
boost::numeric::ublas::vector<double>& LC_Quadratic::getLinear()
|
||||
{
|
||||
return m_vLinear;
|
||||
}
|
||||
|
||||
const boost::numeric::ublas::vector<double>& LC_Quadratic::getLinear() const
|
||||
{
|
||||
return m_vLinear;
|
||||
}
|
||||
|
||||
boost::numeric::ublas::matrix<double>& LC_Quadratic::getQuad()
|
||||
{
|
||||
return m_mQuad;
|
||||
}
|
||||
|
||||
const boost::numeric::ublas::matrix<double>& LC_Quadratic::getQuad() const
|
||||
{
|
||||
return m_mQuad;
|
||||
}
|
||||
|
||||
double const& LC_Quadratic::constTerm()const
|
||||
{
|
||||
return m_dConst;
|
||||
}
|
||||
|
||||
double& LC_Quadratic::constTerm()
|
||||
{
|
||||
return m_dConst;
|
||||
}
|
||||
|
||||
/** construct a ellipse or hyperbola as the path of center of common tangent circles
|
||||
of this two given entities*/
|
||||
LC_Quadratic::LC_Quadratic(const RS_AtomicEntity* circle0,
|
||||
const RS_AtomicEntity* circle1,
|
||||
bool mirror):
|
||||
m_mQuad(2,2)
|
||||
,m_vLinear(2)
|
||||
,m_bValid(false)
|
||||
{
|
||||
// DEBUG_HEADER
|
||||
|
||||
if(!( circle0->isArcCircleLine() && circle1->isArcCircleLine())) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(circle1->rtti() != RS2::EntityLine)
|
||||
std::swap(circle0, circle1);
|
||||
if(circle0->rtti() == RS2::EntityLine) {
|
||||
//two lines
|
||||
RS_Line* line0=(RS_Line*) circle0;
|
||||
RS_Line* line1=(RS_Line*) circle1;
|
||||
|
||||
auto centers=RS_Information::getIntersection(line0,line1);
|
||||
// DEBUG_HEADER
|
||||
if(centers.size()!=1) return;
|
||||
double angle=0.5*(line0->getAngle1()+line1->getAngle1());
|
||||
m_bValid=true;
|
||||
m_bIsQuadratic=true;
|
||||
m_mQuad(0,0)=0.;
|
||||
m_mQuad(0,1)=0.5;
|
||||
m_mQuad(1,0)=0.5;
|
||||
m_mQuad(1,1)=0.;
|
||||
m_vLinear(0)=0.;
|
||||
m_vLinear(1)=0.;
|
||||
m_dConst=0.;
|
||||
rotate(angle);
|
||||
move(centers.get(0));
|
||||
// DEBUG_HEADER
|
||||
// std::cout<<*this<<std::endl;
|
||||
return;
|
||||
}
|
||||
if(circle1->rtti() == RS2::EntityLine) {
|
||||
// DEBUG_HEADER
|
||||
//one line, one circle
|
||||
const RS_Line* line1=static_cast<const RS_Line*>(circle1);
|
||||
RS_Vector normal=line1->getNormalVector()*circle0->getRadius();
|
||||
RS_Vector disp=line1->getNearestPointOnEntity(circle0->getCenter(),
|
||||
false)-circle0->getCenter();
|
||||
if(normal.dotP(disp)>0.) normal *= -1.;
|
||||
if(mirror) normal *= -1.;
|
||||
|
||||
RS_Line directrix{line1->getStartpoint()+normal,
|
||||
line1->getEndpoint()+normal};
|
||||
LC_Quadratic lc0(&directrix,circle0->getCenter());
|
||||
*this = lc0;
|
||||
return;
|
||||
|
||||
m_mQuad=lc0.getQuad();
|
||||
m_vLinear=lc0.getLinear();
|
||||
m_bIsQuadratic=true;
|
||||
m_bValid=true;
|
||||
m_dConst=lc0.m_dConst;
|
||||
|
||||
return;
|
||||
}
|
||||
//two circles
|
||||
|
||||
double const f=(circle0->getCenter()-circle1->getCenter()).magnitude()*0.5;
|
||||
double const a=fabs(circle0->getRadius()+circle1->getRadius())*0.5;
|
||||
double const c=fabs(circle0->getRadius()-circle1->getRadius())*0.5;
|
||||
// DEBUG_HEADER
|
||||
// qDebug()<<"circle center to center distance="<<2.*f<<"\ttotal radius="<<2.*a;
|
||||
if(a<RS_TOLERANCE) return;
|
||||
RS_Vector center=(circle0->getCenter()+circle1->getCenter())*0.5;
|
||||
double angle=center.angleTo(circle0->getCenter());
|
||||
if( f<a){
|
||||
//ellipse
|
||||
double const ratio=sqrt(a*a - f*f)/a;
|
||||
RS_Vector const& majorP=RS_Vector{angle}*a;
|
||||
RS_Ellipse const ellipse{nullptr, {center,majorP,ratio,0.,0.,false}};
|
||||
auto const& lc0=ellipse.getQuadratic();
|
||||
|
||||
m_mQuad=lc0.getQuad();
|
||||
m_vLinear=lc0.getLinear();
|
||||
m_bIsQuadratic=lc0.isQuadratic();
|
||||
m_bValid=lc0.isValid();
|
||||
m_dConst=lc0.m_dConst;
|
||||
// DEBUG_HEADER
|
||||
// std::cout<<"ellipse: "<<*this;
|
||||
return;
|
||||
}
|
||||
|
||||
// DEBUG_HEADER
|
||||
if(c<RS_TOLERANCE){
|
||||
//two circles are the same radius
|
||||
//degenerate hypberbola: straight lines
|
||||
//equation xy = 0
|
||||
m_bValid=true;
|
||||
m_bIsQuadratic=true;
|
||||
m_mQuad(0,0)=0.;
|
||||
m_mQuad(0,1)=0.5;
|
||||
m_mQuad(1,0)=0.5;
|
||||
m_mQuad(1,1)=0.;
|
||||
m_vLinear(0)=0.;
|
||||
m_vLinear(1)=0.;
|
||||
m_dConst=0.;
|
||||
rotate(angle);
|
||||
move(center);
|
||||
return;
|
||||
}
|
||||
//hyperbola
|
||||
// equation: x^2/c^2 - y^2/(f^2 -c ^2) = 1
|
||||
// f: from hyperbola center to one circle center
|
||||
// c: half of difference of two circles
|
||||
|
||||
double b2= f*f - c*c;
|
||||
m_bValid=true;
|
||||
m_bIsQuadratic=true;
|
||||
m_mQuad(0,0)=1./(c*c);
|
||||
m_mQuad(0,1)=0.;
|
||||
m_mQuad(1,0)=0.;
|
||||
m_mQuad(1,1)=-1./b2;
|
||||
m_vLinear(0)=0.;
|
||||
m_vLinear(1)=0.;
|
||||
m_dConst=-1.;
|
||||
rotate(angle);
|
||||
move(center);
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief LC_Quadratic, construct a Perpendicular bisector line, which is the path of circles passing point0 and point1
|
||||
* @param point0
|
||||
* @param point1
|
||||
*/
|
||||
LC_Quadratic::LC_Quadratic(const RS_Vector& point0, const RS_Vector& point1)
|
||||
{
|
||||
RS_Vector vStart=(point0+point1)*0.5;
|
||||
RS_Vector vEnd=vStart + (point0-vStart).rotate(0.5*M_PI);
|
||||
*this=RS_Line(vStart, vEnd).getQuadratic();
|
||||
}
|
||||
|
||||
std::vector<double> LC_Quadratic::getCoefficients() const
|
||||
{
|
||||
std::vector<double> ret(0,0.);
|
||||
if(isValid()==false) return ret;
|
||||
if(m_bIsQuadratic){
|
||||
ret.push_back(m_mQuad(0,0));
|
||||
ret.push_back(m_mQuad(0,1)+m_mQuad(1,0));
|
||||
ret.push_back(m_mQuad(1,1));
|
||||
}
|
||||
ret.push_back(m_vLinear(0));
|
||||
ret.push_back(m_vLinear(1));
|
||||
ret.push_back(m_dConst);
|
||||
return ret;
|
||||
}
|
||||
|
||||
LC_Quadratic LC_Quadratic::move(const RS_Vector& v)
|
||||
{
|
||||
if(m_bValid==false || v.valid == false) return *this;
|
||||
|
||||
m_dConst -= m_vLinear(0) * v.x + m_vLinear(1)*v.y;
|
||||
|
||||
if(m_bIsQuadratic){
|
||||
m_vLinear(0) -= 2.*m_mQuad(0,0)*v.x + (m_mQuad(0,1)+m_mQuad(1,0))*v.y;
|
||||
m_vLinear(1) -= 2.*m_mQuad(1,1)*v.y + (m_mQuad(0,1)+m_mQuad(1,0))*v.x;
|
||||
m_dConst += m_mQuad(0,0)*v.x*v.x + (m_mQuad(0,1)+m_mQuad(1,0))*v.x*v.y+ m_mQuad(1,1)*v.y*v.y ;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
LC_Quadratic LC_Quadratic::rotate(const double& angle)
|
||||
{
|
||||
using namespace boost::numeric::ublas;
|
||||
auto m=rotationMatrix(angle);
|
||||
auto t=trans(m);
|
||||
m_vLinear = prod(t, m_vLinear);
|
||||
if(m_bIsQuadratic){
|
||||
m_mQuad=prod(m_mQuad,m);
|
||||
m_mQuad=prod(t, m_mQuad);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
LC_Quadratic LC_Quadratic::rotate(const RS_Vector& center, const double& angle)
|
||||
{
|
||||
move(-center);
|
||||
rotate(angle);
|
||||
move(center);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** switch x,y coordinates */
|
||||
LC_Quadratic LC_Quadratic::flipXY(void) const
|
||||
{
|
||||
LC_Quadratic qf(*this);
|
||||
if(isQuadratic()){
|
||||
std::swap(qf.m_mQuad(0,0),qf.m_mQuad(1,1));
|
||||
std::swap(qf.m_mQuad(0,1),qf.m_mQuad(1,0));
|
||||
}
|
||||
std::swap(qf.m_vLinear(0),qf.m_vLinear(1));
|
||||
return qf;
|
||||
}
|
||||
|
||||
RS_VectorSolutions LC_Quadratic::getIntersection(const LC_Quadratic& l1, const LC_Quadratic& l2)
|
||||
{
|
||||
RS_VectorSolutions ret;
|
||||
if( l1 == false || l2 == false ) {
|
||||
// DEBUG_HEADER
|
||||
// std::cout<<l1<<std::endl;
|
||||
// std::cout<<l2<<std::endl;
|
||||
return ret;
|
||||
}
|
||||
auto p1=&l1;
|
||||
auto p2=&l2;
|
||||
if(p1->isQuadratic()==false){
|
||||
std::swap(p1,p2);
|
||||
}
|
||||
if(RS_DEBUG->getLevel()>=RS_Debug::D_INFORMATIONAL){
|
||||
DEBUG_HEADER
|
||||
std::cout<<*p1<<std::endl;
|
||||
std::cout<<*p2<<std::endl;
|
||||
}
|
||||
if(p1->isQuadratic()==false){
|
||||
//two lines
|
||||
std::vector<std::vector<double> > ce(2,std::vector<double>(3,0.));
|
||||
ce[0][0]=p1->m_vLinear(0);
|
||||
ce[0][1]=p1->m_vLinear(1);
|
||||
ce[0][2]=-p1->m_dConst;
|
||||
ce[1][0]=p2->m_vLinear(0);
|
||||
ce[1][1]=p2->m_vLinear(1);
|
||||
ce[1][2]=-p2->m_dConst;
|
||||
std::vector<double> sn(2,0.);
|
||||
if(RS_Math::linearSolver(ce,sn)){
|
||||
ret.push_back(RS_Vector(sn[0],sn[1]));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
if(p2->isQuadratic()==false){
|
||||
//one line, one quadratic
|
||||
//avoid division by zero
|
||||
if(fabs(p2->m_vLinear(0))+DBL_EPSILON<fabs(p2->m_vLinear(1))){
|
||||
ret=getIntersection(p1->flipXY(),p2->flipXY()).flipXY();
|
||||
// for(size_t j=0;j<ret.size();j++){
|
||||
// DEBUG_HEADER
|
||||
// std::cout<<j<<": ("<<ret[j].x<<", "<< ret[j].y<<")"<<std::endl;
|
||||
// }
|
||||
return ret;
|
||||
}
|
||||
std::vector<std::vector<double> > ce(0);
|
||||
if(fabs(p2->m_vLinear(1))<RS_TOLERANCE){
|
||||
const double angle=0.25*M_PI;
|
||||
LC_Quadratic p11(*p1);
|
||||
LC_Quadratic p22(*p2);
|
||||
ce.push_back(p11.rotate(angle).getCoefficients());
|
||||
ce.push_back(p22.rotate(angle).getCoefficients());
|
||||
ret=RS_Math::simultaneousQuadraticSolverMixed(ce);
|
||||
ret.rotate(-angle);
|
||||
// for(size_t j=0;j<ret.size();j++){
|
||||
// DEBUG_HEADER
|
||||
// std::cout<<j<<": ("<<ret[j].x<<", "<< ret[j].y<<")"<<std::endl;
|
||||
// }
|
||||
return ret;
|
||||
}
|
||||
ce.push_back(p1->getCoefficients());
|
||||
ce.push_back(p2->getCoefficients());
|
||||
ret=RS_Math::simultaneousQuadraticSolverMixed(ce);
|
||||
// for(size_t j=0;j<ret.size();j++){
|
||||
// DEBUG_HEADER
|
||||
// std::cout<<j<<": ("<<ret[j].x<<", "<< ret[j].y<<")"<<std::endl;
|
||||
// }
|
||||
return ret;
|
||||
}
|
||||
if( fabs(p1->m_mQuad(0,0))<RS_TOLERANCE && fabs(p1->m_mQuad(0,1))<RS_TOLERANCE
|
||||
&&
|
||||
fabs(p2->m_mQuad(0,0))<RS_TOLERANCE && fabs(p2->m_mQuad(0,1))<RS_TOLERANCE
|
||||
){
|
||||
if(fabs(p1->m_mQuad(1,1))<RS_TOLERANCE && fabs(p2->m_mQuad(1,1))<RS_TOLERANCE){
|
||||
//linear
|
||||
std::vector<double> ce(0);
|
||||
ce.push_back(p1->m_vLinear(0));
|
||||
ce.push_back(p1->m_vLinear(1));
|
||||
ce.push_back(p1->m_dConst);
|
||||
LC_Quadratic lc10(ce);
|
||||
ce.clear();
|
||||
ce.push_back(p2->m_vLinear(0));
|
||||
ce.push_back(p2->m_vLinear(1));
|
||||
ce.push_back(p2->m_dConst);
|
||||
LC_Quadratic lc11(ce);
|
||||
return getIntersection(lc10,lc11);
|
||||
}
|
||||
return getIntersection(p1->flipXY(),p2->flipXY()).flipXY();
|
||||
}
|
||||
std::vector<std::vector<double> > ce(0);
|
||||
ce.push_back(p1->getCoefficients());
|
||||
ce.push_back(p2->getCoefficients());
|
||||
if(RS_DEBUG->getLevel()>=RS_Debug::D_INFORMATIONAL){
|
||||
DEBUG_HEADER
|
||||
std::cout<<*p1<<std::endl;
|
||||
std::cout<<*p2<<std::endl;
|
||||
}
|
||||
auto sol= RS_Math::simultaneousQuadraticSolverFull(ce);
|
||||
bool valid= sol.size()>0;
|
||||
for(auto & v: sol){
|
||||
if(v.magnitude()>=RS_MAXDOUBLE){
|
||||
valid=false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(valid) return sol;
|
||||
ce.clear();
|
||||
ce.push_back(p1->getCoefficients());
|
||||
ce.push_back(p2->getCoefficients());
|
||||
sol=RS_Math::simultaneousQuadraticSolverFull(ce);
|
||||
ret.clear();
|
||||
for(auto const& v: sol){
|
||||
if(v.magnitude()<=RS_MAXDOUBLE){
|
||||
ret.push_back(v);
|
||||
if(RS_DEBUG->getLevel()>=RS_Debug::D_INFORMATIONAL){
|
||||
DEBUG_HEADER
|
||||
std::cout<<v<<std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
rotation matrix:
|
||||
|
||||
cos x, sin x
|
||||
-sin x, cos x
|
||||
*/
|
||||
boost::numeric::ublas::matrix<double> LC_Quadratic::rotationMatrix(const double& angle)
|
||||
{
|
||||
boost::numeric::ublas::matrix<double> ret(2,2);
|
||||
ret(0,0)=cos(angle);
|
||||
ret(0,1)=sin(angle);
|
||||
ret(1,0)=-ret(0,1);
|
||||
ret(1,1)=ret(0,0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Dumps the point's data to stdout.
|
||||
*/
|
||||
std::ostream& operator << (std::ostream& os, const LC_Quadratic& q) {
|
||||
|
||||
os << " quadratic form: ";
|
||||
if(q == false) {
|
||||
os<<" invalid quadratic form"<<std::endl;
|
||||
return os;
|
||||
}
|
||||
os<<std::endl;
|
||||
auto ce=q.getCoefficients();
|
||||
unsigned short i=0;
|
||||
if(ce.size()==6){
|
||||
os<<ce[0]<<"*x^2 "<<( (ce[1]>=0.)?"+":" ")<<ce[1]<<"*x*y "<< ((ce[2]>=0.)?"+":" ")<<ce[2]<<"*y^2 ";
|
||||
i=3;
|
||||
}
|
||||
if(q.isQuadratic() && ce[i]>=0.) os<<"+";
|
||||
os<<ce[i]<<"*x "<<((ce[i+1]>=0.)?"+":" ")<<ce[i+1]<<"*y "<< ((ce[i+2]>=0.)?"+":" ")<<ce[i+2]<<" == 0"
|
||||
<<std::endl;
|
||||
return os;
|
||||
}
|
||||
//EOF
|
||||
117
lib/math/lc_quadratic.h
Normal file
117
lib/math/lc_quadratic.h
Normal file
@@ -0,0 +1,117 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** This file is part of the LibreCAD project, a 2D CAD program
|
||||
**
|
||||
|
||||
Copyright (C) 2012-2015 Dongxu Li (dongxuli2011@gmail.com)
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
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.
|
||||
|
||||
**********************************************************************/
|
||||
|
||||
|
||||
#ifndef LC_QUADRATIC_H
|
||||
#define LC_QUADRATIC_H
|
||||
|
||||
|
||||
#include "rs_vector.h"
|
||||
#include <boost/numeric/ublas/matrix.hpp>
|
||||
#include <boost/numeric/ublas/io.hpp>
|
||||
|
||||
class RS_VectorSolutions;
|
||||
class RS_AtomicEntity;
|
||||
|
||||
/**
|
||||
* Class for generic linear and quadratic equation
|
||||
* supports translation and rotation of an equation
|
||||
*
|
||||
* @author Dongxu Li
|
||||
*/
|
||||
class LC_Quadratic {
|
||||
public:
|
||||
explicit LC_Quadratic();
|
||||
LC_Quadratic(const LC_Quadratic& lc0);
|
||||
LC_Quadratic& operator = (const LC_Quadratic& lc0);
|
||||
/** \brief construct a ellipse or hyperbola as the path of center of tangent circles
|
||||
passing the point */
|
||||
LC_Quadratic(const RS_AtomicEntity* circle, const RS_Vector& point);
|
||||
/** \brief construct a ellipse or hyperbola as the path of center of common tangent circles
|
||||
of this two given entities,
|
||||
mirror option allows to specify the mirror quadratic around the line
|
||||
*/
|
||||
LC_Quadratic(const RS_AtomicEntity* circle0,const RS_AtomicEntity* circle1,
|
||||
bool mirror = false);
|
||||
/**
|
||||
* @brief LC_Quadratic, construct a Perpendicular bisector line, which is the path of circles passing point0 and point1
|
||||
* @param point0
|
||||
* @param point1
|
||||
*/
|
||||
LC_Quadratic(const RS_Vector& point0, const RS_Vector& point1);
|
||||
|
||||
LC_Quadratic(std::vector<double> ce);
|
||||
std::vector<double> getCoefficients() const;
|
||||
LC_Quadratic move(const RS_Vector& v);
|
||||
LC_Quadratic rotate(const double& a);
|
||||
LC_Quadratic rotate(const RS_Vector& center, const double& a);
|
||||
/** \brief whether it's quadratic or linear
|
||||
@return true, if quadratic;
|
||||
return false, if linear
|
||||
*/
|
||||
bool isQuadratic() const;
|
||||
|
||||
//!
|
||||
//! \brief operator bool explicit and implicit conversion to bool
|
||||
//!
|
||||
explicit operator bool() const;
|
||||
bool isValid() const;
|
||||
void setValid(bool value);
|
||||
//!
|
||||
//! \brief operator == comparison of validity with bool
|
||||
//! \param valid boolean parameter
|
||||
//! \return true is the parameter valid is the same as validity
|
||||
//!
|
||||
bool operator == (bool valid) const;
|
||||
bool operator != (bool valid) const;
|
||||
|
||||
boost::numeric::ublas::vector<double>& getLinear();
|
||||
const boost::numeric::ublas::vector<double>& getLinear() const;
|
||||
boost::numeric::ublas::matrix<double>& getQuad();
|
||||
const boost::numeric::ublas::matrix<double>& getQuad() const;
|
||||
double const& constTerm()const;
|
||||
double& constTerm();
|
||||
|
||||
/** switch x,y coordinates */
|
||||
LC_Quadratic flipXY(void) const;
|
||||
/** the matrix of rotation by angle **/
|
||||
static boost::numeric::ublas::matrix<double> rotationMatrix(const double& angle);
|
||||
|
||||
static RS_VectorSolutions getIntersection(const LC_Quadratic& l1, const LC_Quadratic& l2);
|
||||
|
||||
friend std::ostream& operator << (std::ostream& os, const LC_Quadratic& l);
|
||||
|
||||
private:
|
||||
// the equation form: {x, y}.m_mQuad.{{x},{y}} + m_vLinear.{{x},{y}}+m_dConst=0
|
||||
boost::numeric::ublas::matrix<double> m_mQuad;
|
||||
boost::numeric::ublas::vector<double> m_vLinear;
|
||||
double m_dConst;
|
||||
bool m_bIsQuadratic;
|
||||
/** whether this quadratic form is valid */
|
||||
bool m_bValid;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
//EOF
|
||||
1237
lib/math/rs_math.cpp
Normal file
1237
lib/math/rs_math.cpp
Normal file
File diff suppressed because it is too large
Load Diff
153
lib/math/rs_math.h
Normal file
153
lib/math/rs_math.h
Normal file
@@ -0,0 +1,153 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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!
|
||||
**
|
||||
**********************************************************************/
|
||||
|
||||
#ifndef RS_MATH_H
|
||||
#define RS_MATH_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
class RS_Vector;
|
||||
class RS_VectorSolutions;
|
||||
class QString;
|
||||
|
||||
/**
|
||||
* Math functions.
|
||||
*/
|
||||
class RS_Math {
|
||||
private:
|
||||
RS_Math() = delete;
|
||||
public:
|
||||
static int round(double v);
|
||||
static double round(const double v, const double precision);
|
||||
static double pow(double x, double y);
|
||||
static RS_Vector pow(RS_Vector x, double y);
|
||||
static bool equal(const double d1, const double d2);
|
||||
|
||||
static double rad2deg(double a);
|
||||
static double deg2rad(double a);
|
||||
static double rad2gra(double a);
|
||||
static double gra2rad(double a);
|
||||
static unsigned findGCD(unsigned a, unsigned b);
|
||||
static bool isAngleBetween(double a,
|
||||
double a1, double a2,
|
||||
bool reversed = false);
|
||||
//! \brief correct angle to be within [0, +PI*2.0)
|
||||
static double correctAngle(double a);
|
||||
//! \brief correct angle to be within [-PI, +PI)
|
||||
static double correctAngle2(double a);
|
||||
//! \brief correct angle to be unsigned [0, +PI)
|
||||
static double correctAngleU(double a);
|
||||
|
||||
//! \brief angular difference
|
||||
static double getAngleDifference(double a1, double a2, bool reversed = false);
|
||||
/**
|
||||
* @brief getAngleDifferenceU abs of minimum angular difference, unsigned version of angular difference
|
||||
* @param a1,a2 angles
|
||||
* @return the minimum of angular difference a1-a2 and a2-a1
|
||||
*/
|
||||
static double getAngleDifferenceU(double a1, double a2);
|
||||
static double makeAngleReadable(double angle, bool readable=true,
|
||||
bool* corrected=nullptr);
|
||||
static bool isAngleReadable(double angle);
|
||||
static bool isSameDirection(double dir1, double dir2, double tol);
|
||||
|
||||
//! \{ \brief evaluate a math string
|
||||
static double eval(const QString& expr, double def=0.0);
|
||||
static double eval(const QString& expr, bool* ok);
|
||||
//! \}
|
||||
|
||||
static std::vector<double> quadraticSolver(const std::vector<double>& ce);
|
||||
static std::vector<double> cubicSolver(const std::vector<double>& ce);
|
||||
/** quartic solver
|
||||
* x^4 + ce[0] x^3 + ce[1] x^2 + ce[2] x + ce[3] = 0
|
||||
@ce, a vector of size 4 contains the coefficient in order
|
||||
@return, a vector contains real roots
|
||||
**/
|
||||
static std::vector<double> quarticSolver(const std::vector<double>& ce);
|
||||
/** quartic solver
|
||||
* ce[4] x^4 + ce[3] x^3 + ce[2] x^2 + ce[1] x + ce[0] = 0
|
||||
@ce, a vector of size 5 contains the coefficient in order
|
||||
@return, a vector contains real roots
|
||||
**/
|
||||
static std::vector<double> quarticSolverFull(const std::vector<double>& ce);
|
||||
//solver for linear equation set
|
||||
/**
|
||||
* Solve linear equation set
|
||||
*@param mt holds the augmented matrix
|
||||
*@param sn holds the solution
|
||||
*@param return true, if the equation set has a unique solution, return false otherwise
|
||||
*
|
||||
*@author: Dongxu Li
|
||||
*/
|
||||
static bool linearSolver(const std::vector<std::vector<double> >& m, std::vector<double>& sn);
|
||||
|
||||
/** solver quadratic simultaneous equations of a set of two **/
|
||||
/* solve the following quadratic simultaneous equations,
|
||||
* ma000 x^2 + ma011 y^2 - 1 =0
|
||||
* ma100 x^2 + 2 ma101 xy + ma111 y^2 + mb10 x + mb11 y +mc1 =0
|
||||
*
|
||||
*@m, a vector of size 8 contains coefficients in the strict order of:
|
||||
ma000 ma011 ma100 ma101 ma111 mb10 mb11 mc1
|
||||
*@return a RS_VectorSolutions contains real roots (x,y)
|
||||
*/
|
||||
static RS_VectorSolutions simultaneousQuadraticSolver(const std::vector<double>& m);
|
||||
|
||||
/** solver quadratic simultaneous equations of a set of two **/
|
||||
/** solve the following quadratic simultaneous equations,
|
||||
* ma000 x^2 + ma001 xy + ma011 y^2 + mb00 x + mb01 y + mc0 =0
|
||||
* ma100 x^2 + ma101 xy + ma111 y^2 + mb10 x + mb11 y + mc1 =0
|
||||
*
|
||||
*@param m a vector of size 2 each contains a vector of size 6 coefficients in the strict order of:
|
||||
ma000 ma001 ma011 mb00 mb01 mc0
|
||||
ma100 ma101 ma111 mb10 mb11 mc1
|
||||
*@return a RS_VectorSolutions contains real roots (x,y)
|
||||
*/
|
||||
static RS_VectorSolutions simultaneousQuadraticSolverFull(const std::vector<std::vector<double> >& m);
|
||||
static RS_VectorSolutions simultaneousQuadraticSolverMixed(const std::vector<std::vector<double> >& m);
|
||||
|
||||
/** \brief verify simultaneousQuadraticVerify a solution for simultaneousQuadratic
|
||||
*@param m the coefficient matrix
|
||||
*@param v a candidate to verify
|
||||
*@return true, for a valid solution
|
||||
**/
|
||||
static bool simultaneousQuadraticVerify(const std::vector<std::vector<double> >& m, RS_Vector& v);
|
||||
/** wrapper for elliptic integral **/
|
||||
/**
|
||||
* wrapper of elliptic integral of the second type, Legendre form
|
||||
*@k the elliptic modulus or eccentricity
|
||||
*@phi elliptic angle, must be within range of [0, M_PI]
|
||||
*
|
||||
*@\author: Dongxu Li
|
||||
*/
|
||||
static double ellipticIntegral_2(const double& k, const double& phi);
|
||||
|
||||
static QString doubleToString(double value, double prec);
|
||||
static QString doubleToString(double value, int prec);
|
||||
|
||||
static void test();
|
||||
};
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user