f790736fd7
- modificata conversione da linea a bezier.
2474 lines
77 KiB
C++
2474 lines
77 KiB
C++
//----------------------------------------------------------------------------
|
|
// EgalTech 2013-2014
|
|
//----------------------------------------------------------------------------
|
|
// File : CurveBezier.cpp Data : 26.03.14 Versione : 1.5c9
|
|
// Contenuto : Implementazione della classe Curva di Bezier.
|
|
//
|
|
//
|
|
//
|
|
// Modifiche : 28.12.12 DS Creazione modulo.
|
|
// 26.03.14 DS Modif. Save/Load, ora ogni punto su una linea.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
//--------------------------- Include ----------------------------------------
|
|
#include "stdafx.h"
|
|
#include "CurveAux.h"
|
|
#include "CurveBezier.h"
|
|
#include "CurveComposite.h"
|
|
#include "DistPointCrvBezier.h"
|
|
#include "BiArcs.h"
|
|
#include "GeoConst.h"
|
|
#include "PolygonPlane.h"
|
|
#include "GeoObjFactory.h"
|
|
#include "NgeWriter.h"
|
|
#include "NgeReader.h"
|
|
#include "PolynomialPoint3d.h"
|
|
#include "Bernstein.h"
|
|
#include "deCasteljau.h"
|
|
#include "Voronoi.h"
|
|
#include "IntersLineLine.h"
|
|
#include "/EgtDev/Include/EGkCurveArc.h"
|
|
#include "/EgtDev/Include/EGkDistPointLine.h"
|
|
#include "/EgtDev/Include/EGkStringUtils3d.h"
|
|
#include "/EgtDev/Include/EGkUiUnits.h"
|
|
#include "/EgtDev/Include/ENkPolynomial.h"
|
|
#include "/EgtDev/Include/EgtNumUtils.h"
|
|
#include "/EgtDev/Include/EgtPointerOwner.h"
|
|
#include "/EgtDev/Include/EGkGeoPoint3d.h"
|
|
#include "/EgtDev/Include/EGkCurveLine.h"
|
|
#include <array>
|
|
|
|
using namespace std ;
|
|
|
|
//----------------------------------------------------------------------------
|
|
static const double MIN_EXT_WEIGHT = 0.25 ;
|
|
|
|
//----------------------------------------------------------------------------
|
|
GEOOBJ_REGISTER( CRV_BEZIER, NGE_C_BEZ, CurveBezier) ;
|
|
|
|
//----------------------------------------------------------------------------
|
|
CurveBezier::CurveBezier( void)
|
|
: m_nStatus( TO_VERIFY), m_nDeg(), m_bRat( false), m_dParSing( -2),
|
|
m_VtExtr(), m_dThick(), m_nTempProp{0,0}, m_dTempParam{0.0,0.0}, m_pVoronoiObj( nullptr)
|
|
{
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
CurveBezier::~CurveBezier( void)
|
|
{
|
|
ResetVoronoiObject() ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
CurveBezier::Init( int nDeg, bool bIsRational)
|
|
{
|
|
// verifico validità grado
|
|
if ( nDeg < 1 || nDeg > MAXDEG)
|
|
return false ;
|
|
|
|
// imposto grado, flag di razionale e singolarità non studiata
|
|
m_nDeg = nDeg ;
|
|
m_bRat = bIsRational ;
|
|
m_dParSing = - 2 ;
|
|
|
|
// dimensiono i vettori dei punti e dei pesi
|
|
m_vPtCtrl.assign( nDeg + 1, ORIG) ;
|
|
if ( bIsRational)
|
|
m_vWeCtrl.assign( nDeg + 1, 1) ;
|
|
else
|
|
m_vWeCtrl.clear() ;
|
|
m_nStatus = TO_VERIFY ;
|
|
|
|
// imposto ricalcolo Voronoi
|
|
ResetVoronoiObject() ;
|
|
// imposto ricalcolo della grafica
|
|
m_OGrMgr.Reset() ;
|
|
|
|
return Validate() ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
CurveBezier::SetControlPoint( int nInd, const Point3d& ptCtrl)
|
|
{
|
|
// verifico validità indice e punto
|
|
if ( m_nStatus != OK || m_bRat || nInd < 0 || nInd > m_nDeg || ! ptCtrl.IsValid())
|
|
return false ;
|
|
|
|
if ( abs( ptCtrl.x) > INFINITO || abs( ptCtrl.y) > INFINITO || abs( ptCtrl.z) > INFINITO)
|
|
return false ;
|
|
|
|
// assegno il valore
|
|
m_vPtCtrl[nInd] = ptCtrl ;
|
|
|
|
// se razionale, metto il peso a 1
|
|
if ( m_bRat)
|
|
m_vWeCtrl[nInd] = 1 ;
|
|
|
|
// annullo analisi presenza singolarità
|
|
m_dParSing = - 2 ;
|
|
|
|
// imposto ricalcolo Voronoi
|
|
ResetVoronoiObject() ;
|
|
// imposto ricalcolo della grafica
|
|
m_OGrMgr.Reset() ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
CurveBezier::SetControlPoint( int nInd, const Point3d& ptCtrl, double dW)
|
|
{
|
|
// verifico validità, razionalità e indice
|
|
if ( m_nStatus != OK || ! m_bRat || nInd < 0 || nInd > m_nDeg)
|
|
return false ;
|
|
|
|
// verifico che il punto sia valido e il peso non sia nullo o negativo
|
|
if ( ! ptCtrl.IsValid() || dW < EPS_SMALL || ! isfinite( dW) || dW > INFINITO)
|
|
return false ;
|
|
|
|
if ( abs( ptCtrl.x) > INFINITO || abs( ptCtrl.y) > INFINITO || abs( ptCtrl.z) > INFINITO)
|
|
return false ;
|
|
|
|
// assegno il valore e il peso
|
|
m_vPtCtrl[nInd] = ptCtrl ;
|
|
m_vWeCtrl[nInd] = dW ;
|
|
|
|
// annullo analisi presenza singolarità
|
|
m_dParSing = - 2 ;
|
|
|
|
// imposto ricalcolo Voronoi
|
|
ResetVoronoiObject() ;
|
|
// imposto ricalcolo della grafica
|
|
m_OGrMgr.Reset() ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
CurveBezier::SetControlWeight( int nInd, double dW)
|
|
{
|
|
// verifico validità, razionalità e indice
|
|
if ( m_nStatus != OK || ! m_bRat || nInd < 0 || nInd > m_nDeg)
|
|
return false ;
|
|
|
|
// verifico che il peso non sia nullo o negativo
|
|
if ( dW < EPS_SMALL || ! isfinite( dW) || dW > INFINITO)
|
|
return false ;
|
|
|
|
// assegno il valore e il peso
|
|
m_vWeCtrl[nInd] = dW ;
|
|
|
|
// annullo analisi presenza singolarità
|
|
m_dParSing = - 2 ;
|
|
|
|
// imposto ricalcolo Voronoi
|
|
ResetVoronoiObject() ;
|
|
// imposto ricalcolo della grafica
|
|
m_OGrMgr.Reset() ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
CurveBezier::FromArc( const ICurveArc& crArc)
|
|
{
|
|
const double MAX_ANG_CEN_DEG = 120 ;
|
|
|
|
if ( ! crArc.IsValid())
|
|
return false ;
|
|
double dAngCen = crArc.GetAngCenter() ;
|
|
if ( abs( dAngCen) > MAX_ANG_CEN_DEG)
|
|
return false ;
|
|
double dCosAhalf = cos( 0.5 * dAngCen * DEGTORAD) ;
|
|
|
|
// se arco piatto, basta quadrica razionale
|
|
// algoritmo di Sederberg (BYU) CAGD cap. 2 pag. 33
|
|
if ( abs( crArc.GetDeltaN()) < EPS_ZERO) {
|
|
// peso del punto di controllo intermedio
|
|
double dW = dCosAhalf ;
|
|
// calcolo dei punti di controllo
|
|
Point3d ptStart ;
|
|
crArc.GetStartPoint( ptStart) ;
|
|
Point3d ptEnd ;
|
|
crArc.GetEndPoint( ptEnd) ;
|
|
Point3d ptMed = Media( ptStart, ptEnd, 0.5) ;
|
|
Vector3d vtDir = ptMed - crArc.GetCenter() ;
|
|
Point3d ptNew = crArc.GetCenter() + vtDir / ( dCosAhalf * dCosAhalf) ;
|
|
// inserimento nella curva dei punti di controllo con i pesi
|
|
Init( 2, true) ;
|
|
SetControlPoint( 0, ptStart, 1) ;
|
|
SetControlPoint( 1, ptNew, dW) ;
|
|
SetControlPoint( 2, ptEnd, 1) ;
|
|
}
|
|
// altrimenti per elica serve cubica razionale
|
|
// algoritmo di Juhasz HelixApproxByCubicBezier CAD 1995
|
|
else {
|
|
// peso dei punti di controllo intermedi
|
|
double dW = ( 1 + 2 * dCosAhalf) / 3 ;
|
|
// calcolo dei punti di controllo
|
|
Point3d ptStart ;
|
|
crArc.GetStartPoint( ptStart) ;
|
|
Point3d ptEnd ;
|
|
crArc.GetEndPoint( ptEnd) ;
|
|
Vector3d vtDeltaN = crArc.GetDeltaN() * crArc.GetNormVersor() ;
|
|
Point3d ptEndNoDN = ptEnd - vtDeltaN ;
|
|
Point3d ptMed = Media( ptStart, ptEndNoDN, 0.5) ;
|
|
Vector3d vtDir = ptMed - crArc.GetCenter() ;
|
|
Point3d ptNew = crArc.GetCenter() + vtDir / ( dCosAhalf * dCosAhalf) ;
|
|
Point3d ptNew1 = ( ptStart + 2 * dCosAhalf * ptNew) / ( 1 + 2 * dCosAhalf) +
|
|
0.5 * ( 1 - dCosAhalf / ( 2 + dCosAhalf)) * vtDeltaN ;
|
|
Point3d ptNew2 = ( ptEndNoDN + 2 * dCosAhalf * ptNew) / ( 1 + 2 * dCosAhalf) +
|
|
0.5 * ( 1 + ( dCosAhalf / ( 2 + dCosAhalf))) * vtDeltaN ;
|
|
// inserimento nella curva dei punti di controllo con i pesi
|
|
Init( 3, true) ;
|
|
SetControlPoint( 0, ptStart, 1) ;
|
|
SetControlPoint( 1, ptNew1, dW) ;
|
|
SetControlPoint( 2, ptNew2, dW) ;
|
|
SetControlPoint( 3, ptEnd, 1) ;
|
|
|
|
//// anziché usare una bezier cubica approssimo le eliche con più bezier quadratiche razionali.
|
|
//// più è grande l'angolo al centro dell'arco e maggiore sarà l'errore di approssimazione
|
|
//
|
|
// // quadratica razionale
|
|
// // peso del punto di controllo intermedio
|
|
// double dW = dCosAhalf ;
|
|
// // calcolo dei punti di controllo
|
|
// Point3d ptStart ;
|
|
// crArc.GetStartPoint( ptStart) ;
|
|
// Point3d ptEnd ;
|
|
// crArc.GetEndPoint( ptEnd) ;
|
|
// // Point3d ptMed = Media( ptStart, ptEnd, 0.5) ;
|
|
// //Vector3d vtDir = ptMed - crArc.GetCenter() ;
|
|
// // Point3d ptNew = crArc.GetCenter() + vtDir / ( dCosAhalf * dCosAhalf) ;
|
|
// Vector3d vtStart ; crArc.GetStartDir( vtStart) ;
|
|
// Vector3d vtEnd ; crArc.GetEndDir( vtEnd) ;
|
|
// PtrOwner<CurveLine> pCrvLine1( CreateBasicCurveLine()) ;
|
|
// pCrvLine1->SetPVL( ptStart, vtStart, 10) ;
|
|
// PtrOwner<CurveLine> pCrvLine2( CreateBasicCurveLine()) ;
|
|
// pCrvLine2->SetPVL( ptEnd, vtEnd, 10) ;
|
|
// IntersLineLine ill( *pCrvLine1, *pCrvLine2, false) ;
|
|
// IntCrvCrvInfo iccInfo ; ill.GetIntCrvCrvInfo( iccInfo) ;
|
|
// Point3d ptNew = 0.5 * (iccInfo.IciA->ptI + iccInfo.IciB->ptI) ;
|
|
// // inserimento nella curva dei punti di controllo con i pesi
|
|
// Init( 2, true) ;
|
|
// SetControlPoint( 0, ptStart, 1) ;
|
|
// SetControlPoint( 1, ptNew, dW) ;
|
|
// SetControlPoint( 2, ptEnd, 1) ;
|
|
}
|
|
|
|
// copio estrusione e spessore
|
|
crArc.GetExtrusion( m_VtExtr) ;
|
|
crArc.GetThickness( m_dThick) ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
CurveBezier::FromLine( const ICurveLine& crLine)
|
|
{
|
|
if ( m_nStatus != OK || ! crLine.IsValid())
|
|
return false ;
|
|
int nCount = 0 ;
|
|
Point3d ptStart ; crLine.GetStartPoint( ptStart) ;
|
|
SetControlPoint( nCount, ptStart) ;
|
|
++nCount ;
|
|
double dPart = 1. / m_nDeg ;
|
|
for ( int i = 1 ; i < m_nDeg ; ++i) {
|
|
double dU = i * dPart ;
|
|
Point3d ptMid ; crLine.GetPointD1D2( dU, ICurve::FROM_MINUS, ptMid) ;
|
|
SetControlPoint( nCount, ptMid) ;
|
|
++nCount ;
|
|
}
|
|
Point3d ptEnd ; crLine.GetEndPoint( ptEnd) ;
|
|
SetControlPoint( nCount, ptEnd) ;
|
|
++nCount ;
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
CurveBezier::IsAPoint( void) const
|
|
{
|
|
// verifico lo stato
|
|
if ( m_nStatus != OK)
|
|
return false ;
|
|
// ciclo sui punti
|
|
for ( int i = 1 ; i <= m_nDeg ; ++ i) {
|
|
if ( ! AreSamePointApprox( m_vPtCtrl[0], m_vPtCtrl[i]))
|
|
return false ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
const Point3d&
|
|
CurveBezier::GetControlPoint( int nInd, bool* pbOk) const
|
|
{
|
|
// verifico validità e indice
|
|
if ( m_nStatus != OK || nInd < 0 || nInd > m_nDeg) {
|
|
if ( pbOk != NULL)
|
|
*pbOk = false ;
|
|
return ORIG ;
|
|
}
|
|
// ritorno i dati
|
|
if ( pbOk != NULL)
|
|
*pbOk = true ;
|
|
return m_vPtCtrl[nInd] ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
double
|
|
CurveBezier::GetControlWeight( int nInd, bool* pbOk) const
|
|
{
|
|
// verifico validità, razionalità e indice
|
|
if ( m_nStatus != OK || ! m_bRat || nInd < 0 || nInd > m_nDeg) {
|
|
if ( pbOk != NULL)
|
|
*pbOk = false ;
|
|
return 0 ;
|
|
}
|
|
// ritorno i dati
|
|
if ( pbOk != NULL)
|
|
*pbOk = true ;
|
|
return m_vWeCtrl[nInd] ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
CurveBezier*
|
|
CurveBezier::Clone( void) const
|
|
{
|
|
// alloco oggetto
|
|
CurveBezier* pCrv = new( nothrow) CurveBezier ;
|
|
if ( pCrv != nullptr) {
|
|
if ( ! pCrv->CopyFrom( *this)) {
|
|
delete pCrv ;
|
|
return nullptr ;
|
|
}
|
|
}
|
|
|
|
return pCrv ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
CurveBezier::CopyFrom( const IGeoObj* pGObjSrc)
|
|
{
|
|
const CurveBezier* pCB = GetBasicCurveBezier( pGObjSrc) ;
|
|
if ( pCB == nullptr)
|
|
return false ;
|
|
return CopyFrom( *pCB) ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
CurveBezier::CopyFrom( const CurveBezier& cbSrc)
|
|
{
|
|
if ( &cbSrc == this)
|
|
return true ;
|
|
if ( ! cbSrc.IsValid())
|
|
return false ;
|
|
if ( ! Init( cbSrc.m_nDeg, cbSrc.m_bRat))
|
|
return false ;
|
|
m_dParSing = cbSrc.m_dParSing ;
|
|
m_vPtCtrl = cbSrc.m_vPtCtrl ;
|
|
if ( cbSrc.m_bRat)
|
|
m_vWeCtrl = cbSrc.m_vWeCtrl ;
|
|
m_VtExtr = cbSrc.m_VtExtr ;
|
|
m_dThick = cbSrc.m_dThick ;
|
|
m_nTempProp[0] = cbSrc.m_nTempProp[0] ;
|
|
m_nTempProp[1] = cbSrc.m_nTempProp[1] ;
|
|
m_dTempParam[0] = cbSrc.m_dTempParam[0] ;
|
|
m_dTempParam[1] = cbSrc.m_dTempParam[1] ;
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
GeoObjType
|
|
CurveBezier::GetType( void) const
|
|
{
|
|
return static_cast<GeoObjType>( GEOOBJ_GETTYPE( CurveBezier)) ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
const string&
|
|
CurveBezier::GetTitle( void) const
|
|
{
|
|
static const string sTitle = "CurveBezier" ;
|
|
return sTitle ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
CurveBezier::Dump( string& sOut, bool bMM, const char* szNewLine) const
|
|
{
|
|
// dati generali di una curva
|
|
if ( ! CurveDump( *this, sOut, bMM, szNewLine))
|
|
return false ;
|
|
// parametri : flag razionale
|
|
sOut += ( m_bRat ? "Rat " : "Int ") ;
|
|
// grado
|
|
sOut += "Deg=" + ToString( m_nDeg) + szNewLine ;
|
|
// ciclo sui punti di controllo ( con pesi se razionale)
|
|
for ( int i = 0 ; i <= m_nDeg ; ++ i) {
|
|
sOut += "PC(" + ToString( GetInUiUnits( m_vPtCtrl[i], bMM), 3) ;
|
|
if ( m_bRat)
|
|
sOut += "," + ToString( m_vWeCtrl[i], 3) ;
|
|
sOut += string( ")") + szNewLine ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
int
|
|
CurveBezier::GetNgeId( void) const
|
|
{
|
|
return GEOOBJ_GETNGEID( CurveBezier) ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
CurveBezier::Save( NgeWriter& ngeOut) const
|
|
{
|
|
// flag razionale
|
|
if ( ! ngeOut.WriteBool( m_bRat, ";"))
|
|
return false ;
|
|
// grado
|
|
if ( ! ngeOut.WriteInt( m_nDeg, ";", true))
|
|
return false ;
|
|
// ciclo sui punti di controllo ( con pesi se razionale)
|
|
for ( int i = 0 ; i <= m_nDeg ; ++ i) {
|
|
if ( ! m_bRat) {
|
|
if ( ! ngeOut.WritePoint( m_vPtCtrl[i], ";", true))
|
|
return false ;
|
|
}
|
|
else {
|
|
if ( ! ngeOut.WritePointW( m_vPtCtrl[i], m_vWeCtrl[i], ";", true))
|
|
return false ;
|
|
}
|
|
}
|
|
// da versione 1008 : linea con VtEstrusione e Spessore
|
|
if ( ! ngeOut.WriteVector( m_VtExtr, ";"))
|
|
return false ;
|
|
if ( ! ngeOut.WriteDouble( m_dThick, ";", true))
|
|
return false ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
CurveBezier::Load( NgeReader& ngeIn)
|
|
{
|
|
// imposto ricalcolo della grafica
|
|
m_OGrMgr.Reset() ;
|
|
// leggo la prossima linea ( 2 parametri)
|
|
// recupero il flag razionale
|
|
bool bIsRat ;
|
|
if ( ! ngeIn.ReadBool( bIsRat, ";"))
|
|
return false ;
|
|
// recupero il grado
|
|
int nDeg ;
|
|
if ( ! ngeIn.ReadInt( nDeg, ";", true))
|
|
return false ;
|
|
// inizializzo la curva di Bezier
|
|
if ( ! Init( nDeg, bIsRat))
|
|
return false ;
|
|
// se integrale
|
|
if ( ! bIsRat) {
|
|
// recupero e setto punti di controllo
|
|
Point3d ptP ;
|
|
for ( int i = 0 ; i <= nDeg ; ++ i) {
|
|
// leggo la prossima linea ( un punto)
|
|
if ( ! ngeIn.ReadPoint( ptP, ";", true))
|
|
return false ;
|
|
// lo assegno
|
|
if ( ! SetControlPoint( i, ptP))
|
|
return false ;
|
|
}
|
|
}
|
|
// altrimenti razionale
|
|
else {
|
|
// recupero e setto punti di controllo
|
|
Point3d ptP ;
|
|
double dW ;
|
|
for ( int i = 0 ; i <= nDeg ; ++ i) {
|
|
// leggo la prossima linea ( un punto con peso)
|
|
if ( ! ngeIn.ReadPointW( ptP, dW, ";", true))
|
|
return false ;
|
|
// lo assegno
|
|
if ( ! SetControlPoint( i, ptP, dW))
|
|
return false ;
|
|
}
|
|
}
|
|
// da versione 1008 : linea con VtEstrusione e Spessore
|
|
if ( ngeIn.GetFileVersion() >= NGE_VER_1008) {
|
|
if ( ! ngeIn.ReadVector( m_VtExtr, ";"))
|
|
return false ;
|
|
if ( ! ngeIn.ReadDouble( m_dThick, ";", true))
|
|
return false ;
|
|
}
|
|
// eseguo validazione
|
|
return Validate() ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
CurveBezier::Validate( void)
|
|
{
|
|
if ( m_nStatus == TO_VERIFY) {
|
|
for ( const auto& ptP : m_vPtCtrl) {
|
|
if ( ! ptP.IsValid()) {
|
|
m_nStatus = ERR ;
|
|
break ;
|
|
}
|
|
}
|
|
}
|
|
if ( m_nStatus == TO_VERIFY) {
|
|
for ( const auto& dWe : m_vWeCtrl) {
|
|
if ( ! isfinite( dWe)) {
|
|
m_nStatus = ERR ;
|
|
break ;
|
|
}
|
|
}
|
|
}
|
|
if ( m_nStatus == TO_VERIFY)
|
|
m_nStatus = ( ( m_nDeg >= 1 && m_vPtCtrl.size() >= 2) ? OK : ERR) ;
|
|
|
|
return ( m_nStatus == OK) ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
CurveBezier::GetLocalBBox( BBox3d& b3Loc, int nFlag) const
|
|
{
|
|
// verifico lo stato
|
|
if ( m_nStatus != OK)
|
|
return false ;
|
|
// assegno il box in locale
|
|
b3Loc.Reset() ;
|
|
// basta approssimato
|
|
if ( ( nFlag & BBF_EXACT) == 0) {
|
|
for ( int i = 0 ; i <= m_nDeg ; ++ i)
|
|
b3Loc.Add( m_vPtCtrl[i]) ;
|
|
}
|
|
// deve essere preciso
|
|
else {
|
|
// costruisco una approssimazione lineare
|
|
PolyLine PL ;
|
|
if ( ! ApproxWithLines( LIN_TOL_MIN, ANG_TOL_APPROX_DEG, APL_STD, PL))
|
|
return false ;
|
|
// ciclo sui punti della approssimazione
|
|
Point3d ptTemp ;
|
|
for ( bool bFound = PL.GetFirstPoint( ptTemp) ; bFound ; bFound = PL.GetNextPoint( ptTemp)) {
|
|
b3Loc.Add( ptTemp) ;
|
|
}
|
|
}
|
|
// se c'è estrusione, devo tenerne conto
|
|
if ( ! m_VtExtr.IsSmall() && abs( m_dThick) > EPS_SMALL) {
|
|
Point3d ptMinExtr = b3Loc.GetMin() + m_VtExtr * m_dThick ;
|
|
Point3d ptMaxExtr = b3Loc.GetMax() + m_VtExtr * m_dThick ;
|
|
b3Loc.Add( ptMinExtr) ;
|
|
b3Loc.Add( ptMaxExtr) ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
CurveBezier::GetBBox( const Frame3d& frRef, BBox3d& b3Ref, int nFlag) const
|
|
{
|
|
// verifico lo stato
|
|
if ( m_nStatus != OK)
|
|
return false ;
|
|
// verifico validità del frame
|
|
if ( frRef.GetType() == Frame3d::ERR)
|
|
return false ;
|
|
// assegno il box nel riferimento
|
|
b3Ref.Reset() ;
|
|
// basta approssimato
|
|
if ( ( nFlag & BBF_EXACT) == 0) {
|
|
for ( int i = 0 ; i <= m_nDeg ; ++ i) {
|
|
Point3d ptTemp = m_vPtCtrl[i] ;
|
|
ptTemp.ToGlob( frRef) ;
|
|
b3Ref.Add( ptTemp) ;
|
|
}
|
|
}
|
|
// deve essere preciso
|
|
else {
|
|
// costruisco una approssimazione lineare
|
|
PolyLine PL ;
|
|
if ( ! ApproxWithLines( LIN_TOL_MIN, ANG_TOL_APPROX_DEG, APL_STD, PL))
|
|
return false ;
|
|
// ciclo sui punti della approssimazione
|
|
Point3d ptTemp ;
|
|
for ( bool bFound = PL.GetFirstPoint( ptTemp) ; bFound ; bFound = PL.GetNextPoint( ptTemp)) {
|
|
ptTemp.ToGlob( frRef) ;
|
|
b3Ref.Add( ptTemp) ;
|
|
}
|
|
}
|
|
// se c'è estrusione, devo tenerne conto
|
|
if ( ! m_VtExtr.IsSmall() && abs( m_dThick) > EPS_SMALL) {
|
|
Vector3d vtFrExtr = m_VtExtr ;
|
|
vtFrExtr.ToGlob( frRef) ;
|
|
Point3d ptMinExtr = b3Ref.GetMin() + vtFrExtr * m_dThick ;
|
|
Point3d ptMaxExtr = b3Ref.GetMax() + vtFrExtr * m_dThick ;
|
|
b3Ref.Add( ptMinExtr) ;
|
|
b3Ref.Add( ptMaxExtr) ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
CurveBezier::IsFlat( Plane3d& plPlane, bool bUseExtrusion, double dToler) const
|
|
{
|
|
// verifico lo stato
|
|
if ( m_nStatus != OK)
|
|
return false ;
|
|
|
|
// polilinea dei punti rappresentativi
|
|
PolyLine shPL ;
|
|
int nLastPC = m_nDeg ;
|
|
for ( int i = 0 ; i <= nLastPC ; ++ i) {
|
|
double dU = i / double( nLastPC) ;
|
|
if ( ! shPL.AddUPoint( dU, GetControlPoint( i)))
|
|
return false ;
|
|
}
|
|
// recupero dati sulla planarità
|
|
int nRank ;
|
|
Point3d ptCen ;
|
|
Vector3d vtDir ;
|
|
bool bFlat = shPL.IsFlat( nRank, ptCen, vtDir, dToler) ;
|
|
// se punto
|
|
switch ( nRank) {
|
|
case 0 : // punto
|
|
if ( bFlat)
|
|
plPlane.Set( ptCen, ( m_VtExtr.IsSmall() ? Z_AX : m_VtExtr)) ;
|
|
else
|
|
plPlane.Set( 0, ( m_VtExtr.IsSmall() ? V_NULL : m_VtExtr)) ;
|
|
break ;
|
|
case 1 : // linea
|
|
if ( m_VtExtr.IsSmall())
|
|
plPlane.Set( ptCen, FromUprightOrtho( vtDir)) ;
|
|
else {
|
|
Vector3d vtN = OrthoCompo( m_VtExtr, vtDir) ;
|
|
if ( ! vtN.Normalize())
|
|
vtN = FromUprightOrtho( vtDir) ;
|
|
plPlane.Set( ptCen, vtN) ;
|
|
bFlat = bFlat && ( ! bUseExtrusion || AreSameOrOppositeVectorApprox( m_VtExtr, vtN)) ;
|
|
}
|
|
break ;
|
|
default : // piana o 3d
|
|
if ( m_VtExtr.IsSmall())
|
|
plPlane.Set( ptCen, vtDir) ;
|
|
else {
|
|
plPlane.Set( ptCen, (( m_VtExtr * vtDir) > 0 ? vtDir : - vtDir)) ;
|
|
bFlat = bFlat && ( ! bUseExtrusion || AreSameOrOppositeVectorApprox( m_VtExtr, vtDir)) ;
|
|
}
|
|
break ;
|
|
}
|
|
|
|
return bFlat ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
CurveBezier::GetStartPoint( Point3d& ptStart) const
|
|
{
|
|
// verifico lo stato
|
|
if ( m_nStatus != OK)
|
|
return false ;
|
|
|
|
// assegno il punto
|
|
ptStart = m_vPtCtrl[0] ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
CurveBezier::GetEndPoint( Point3d& ptEnd) const
|
|
{
|
|
// verifico lo stato
|
|
if ( m_nStatus != OK)
|
|
return false ;
|
|
|
|
// assegno il punto
|
|
ptEnd = m_vPtCtrl[m_nDeg] ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
CurveBezier::GetMidPoint( Point3d& ptMid) const
|
|
{
|
|
// verifico lo stato
|
|
if ( m_nStatus != OK)
|
|
return false ;
|
|
// determino il valore del parametro a metà lunghezza
|
|
double dLen, dMid ;
|
|
if ( ! GetLengthAtParam( 1, dLen) || ! GetParamAtLength( 0.5 * dLen, dMid))
|
|
return false ;
|
|
// calcolo il punto
|
|
return GetPointD1D2( dMid, ptMid) ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
CurveBezier::GetCentroid( Point3d& ptCen) const
|
|
{
|
|
// verifico lo stato
|
|
if ( m_nStatus != OK)
|
|
return false ;
|
|
// approssimo la curva con una polilinea
|
|
PolyLine PL ;
|
|
if ( ! ApproxWithLines( LIN_TOL_STD, ANG_TOL_STD_DEG, APL_STD, PL))
|
|
return false ;
|
|
// calcolo il centro mediante PolygonPlane
|
|
Point3d ptP ;
|
|
PolygonPlane PolyPlane ;
|
|
for ( bool bFound = PL.GetFirstPoint( ptP) ; bFound ; bFound = PL.GetNextPoint( ptP))
|
|
PolyPlane.AddPoint( ptP) ;
|
|
if ( PolyPlane.GetCentroid( ptCen))
|
|
return true ;
|
|
// se non riuscito, uso il punto medio
|
|
return GetMidPoint( ptCen) ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
CurveBezier::GetMidDir( Vector3d& vtDir) const
|
|
{
|
|
// verifico lo stato
|
|
if ( m_nStatus != OK)
|
|
return false ;
|
|
// determino il valore del parametro a metà lunghezza
|
|
double dLen, dMid ;
|
|
if ( ! GetLengthAtParam( 1, dLen) || ! GetParamAtLength( 0.5 * dLen, dMid))
|
|
return false ;
|
|
// calcolo la direzione
|
|
return ::GetTang( *this, dMid, FROM_MINUS, vtDir) ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
CurveBezier::GetPointD1D2( double dU, Side nS, Point3d& ptPos, Vector3d* pvtDer1, Vector3d* pvtDer2) const
|
|
{
|
|
// la curva deve essere validata
|
|
if ( m_nStatus != OK)
|
|
return false ;
|
|
|
|
// il parametro U deve essere compreso tra 0 e 1
|
|
dU = Clamp( dU, 0., 1.) ;
|
|
|
|
// verifico se punto singolare
|
|
double dSingU ;
|
|
if ( GetSingularParam( dSingU) > 0 && abs( dU - dSingU) < EPS_PARAM) {
|
|
if ( nS == ICurve::FROM_MINUS)
|
|
dU -= 100 * EPS_PARAM ;
|
|
else
|
|
dU += 100 * EPS_PARAM ;
|
|
}
|
|
|
|
return GetPointD1D2( dU, ptPos, pvtDer1, pvtDer2) ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// Calcolo i polinomi di Bernstein e da questi il punto ( vedi Piegl-Tiller)
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
CurveBezier::GetPointD1D2( double dU, Point3d& ptPos, Vector3d* pvtDer1, Vector3d* pvtDer2) const
|
|
{
|
|
// la curva deve essere validata
|
|
if ( m_nStatus != OK)
|
|
return false ;
|
|
|
|
// il parametro U deve essere compreso tra 0 e 1
|
|
dU = Clamp( dU, 0., 1.) ;
|
|
|
|
// se forma polinomiale (o integrale)
|
|
if ( ! m_bRat) {
|
|
|
|
// calcolo dei polinomi di Bernstein di grado opportuno
|
|
DBLVECTOR vBern( m_nDeg + 1) ;
|
|
GetAllBernstein( dU, m_nDeg - 2, vBern) ;
|
|
|
|
// se richiesto, calcolo della derivata seconda
|
|
if ( pvtDer2 != nullptr && pvtDer1 != nullptr) {
|
|
*pvtDer2 = V_NULL ;
|
|
for ( int i = 0 ; i <= m_nDeg - 2 ; ++ i) {
|
|
*pvtDer2 += vBern[i] * ( m_vPtCtrl[i+2] + m_vPtCtrl[i] - 2 * m_vPtCtrl[i+1]) ;
|
|
}
|
|
*pvtDer2 *= m_nDeg * ( m_nDeg - 1) ;
|
|
}
|
|
|
|
// aumento il grado
|
|
IncreaseAllBernsteinOneDegree( dU, m_nDeg - 1, vBern) ;
|
|
|
|
// se richiesto, calcolo della derivata prima
|
|
if ( pvtDer1 != nullptr) {
|
|
*pvtDer1 = V_NULL ;
|
|
for ( int i = 0 ; i <= m_nDeg - 1 ; ++ i) {
|
|
*pvtDer1 += vBern[i] * ( m_vPtCtrl[i+1] - m_vPtCtrl[i]) ;
|
|
}
|
|
*pvtDer1 *= m_nDeg ;
|
|
}
|
|
|
|
// aumento il grado
|
|
IncreaseAllBernsteinOneDegree( dU, m_nDeg, vBern) ;
|
|
|
|
// calcolo del punto
|
|
ptPos = ORIG ;
|
|
for ( int i = 0 ; i <= m_nDeg ; ++ i)
|
|
ptPos += vBern[i] * m_vPtCtrl[i] ;
|
|
}
|
|
|
|
// altrimenti forma razionale
|
|
else {
|
|
|
|
// porto i punti in forma omogenea moltiplicandoli per i pesi
|
|
PNTVECTOR vPtWCtrl( m_nDeg + 1) ;
|
|
for ( int i = 0 ; i <= m_nDeg ; ++ i)
|
|
vPtWCtrl[i] = m_vWeCtrl[i] * m_vPtCtrl[i] ;
|
|
|
|
// calcolo dei polinomi di Bernstein di grado opportuno
|
|
DBLVECTOR vBern( m_nDeg + 1) ;
|
|
GetAllBernstein( dU, m_nDeg - 2, vBern) ;
|
|
|
|
// se richiesto, calcolo della derivata seconda
|
|
double dW2 = 0 ;
|
|
if ( pvtDer2 != nullptr && pvtDer1 != nullptr) {
|
|
*pvtDer2 = V_NULL ;
|
|
for ( int i = 0 ; i <= m_nDeg - 2 ; ++ i) {
|
|
*pvtDer2 += vBern[i] * ( vPtWCtrl[i+2] + vPtWCtrl[i] - 2 * vPtWCtrl[i+1]) ;
|
|
dW2 += vBern[i] * ( m_vWeCtrl[i+2] + m_vWeCtrl[i] - 2 * m_vWeCtrl[i+1]) ;
|
|
}
|
|
*pvtDer2 *= m_nDeg * ( m_nDeg - 1) ;
|
|
dW2 *= m_nDeg * ( m_nDeg - 1) ;
|
|
}
|
|
|
|
// aumento il grado
|
|
IncreaseAllBernsteinOneDegree( dU, m_nDeg - 1, vBern) ;
|
|
|
|
// se richiesto, calcolo della derivata prima
|
|
double dW1 = 0 ;
|
|
if ( pvtDer1 != nullptr) {
|
|
*pvtDer1 = V_NULL ;
|
|
for ( int i = 0 ; i <= m_nDeg - 1 ; ++ i) {
|
|
*pvtDer1 += vBern[i] * ( vPtWCtrl[i+1] - vPtWCtrl[i]) ;
|
|
dW1 += vBern[i] * ( m_vWeCtrl[i+1] - m_vWeCtrl[i]) ;
|
|
}
|
|
*pvtDer1 *= m_nDeg ;
|
|
dW1 *= m_nDeg ;
|
|
}
|
|
|
|
// aumento il grado
|
|
IncreaseAllBernsteinOneDegree( dU, m_nDeg, vBern) ;
|
|
|
|
// calcolo del punto
|
|
double dW = 0 ;
|
|
ptPos = ORIG ;
|
|
for ( int i = 0 ; i <= m_nDeg ; ++ i) {
|
|
ptPos += vBern[i] * vPtWCtrl[i] ;
|
|
dW += vBern[i] * m_vWeCtrl[i] ;
|
|
}
|
|
|
|
// ritrasformo da forma omogenea a forma standard
|
|
double dInvW = 1 / ( ( dW > EPS_ZERO) ? dW : EPS_ZERO) ;
|
|
ptPos *= dInvW ;
|
|
if ( pvtDer1 != nullptr) {
|
|
Vector3d vtPos( ptPos.x, ptPos.y, ptPos.z) ;
|
|
*pvtDer1 = ( *pvtDer1 - dW1 * vtPos) * dInvW ;
|
|
if ( pvtDer2 != nullptr)
|
|
*pvtDer2 = ( *pvtDer2 - 2 * dW1 * *pvtDer1 - dW2 * vtPos) * dInvW ;
|
|
}
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
int
|
|
CurveBezier::GetSingularParam( double& dPar) const
|
|
{
|
|
// se da calcolare, lo faccio
|
|
if ( m_dParSing < -1.5)
|
|
CalcSingularParam() ;
|
|
|
|
// se esiste singolarità
|
|
if ( m_dParSing > 0 && m_dParSing < 1) {
|
|
dPar = m_dParSing ;
|
|
return 1 ;
|
|
}
|
|
// altrimenti, non esiste
|
|
else
|
|
return 0 ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
CurveBezier::CalcSingularParam( void) const
|
|
{
|
|
// non si considerano singolarità agli estremi ( non creano problemi)
|
|
|
|
// le curve di grado 1 non possono avere singolarità
|
|
if ( m_nDeg <= 1) {
|
|
m_dParSing = -1 ;
|
|
return true ;
|
|
}
|
|
|
|
// le curve di grado 2 possono avere singolarità solo se i punti sono allineati con ordine P0 P2 P1
|
|
if ( m_nDeg == 2) {
|
|
Vector3d vtV1 = m_vPtCtrl[1] - m_vPtCtrl[0] ;
|
|
Vector3d vtV2 = m_vPtCtrl[2] - m_vPtCtrl[1] ;
|
|
if ( ( vtV1 * vtV2) > - EPS_ZERO) {
|
|
m_dParSing = -1 ;
|
|
return true ;
|
|
}
|
|
}
|
|
|
|
// calcolo della derivata o del suo numeratore se curva razionale
|
|
PolynomialPoint3d pol3P ;
|
|
|
|
// se polinomiale
|
|
if ( ! m_bRat) {
|
|
// derivata come curva di Bezier delle differenze tra i punti di controllo
|
|
CurveBezier cbDeriv ;
|
|
cbDeriv.Init( m_nDeg - 1, false) ;
|
|
for ( int i = 0 ; i < m_nDeg ; ++ i)
|
|
cbDeriv.SetControlPoint( i, m_nDeg * ( m_vPtCtrl[i+1] + ( - m_vPtCtrl[i]))) ;
|
|
// trasformo nella forma monomia
|
|
cbDeriv.ToPowerBase( pol3P) ;
|
|
}
|
|
// altrimenti razionale
|
|
else {
|
|
// numeratore e denominatore in forma monomia
|
|
PolynomialPoint3d pol3Num ;
|
|
Polynomial polDen ;
|
|
ToPowerBase( pol3Num, polDen) ;
|
|
// derivata del numeratore
|
|
PolynomialPoint3d pol3NumD ;
|
|
pol3NumD.Derive( pol3Num) ;
|
|
// derivata del denominatore
|
|
Polynomial polDenD ;
|
|
polDenD.Derive( polDen) ;
|
|
// numeratore della derivata complessiva ( NumD * Den - Num * DenD)
|
|
pol3P = pol3NumD * polDen - pol3Num * polDenD ;
|
|
pol3P.AdjustDegree() ;
|
|
}
|
|
|
|
// calcolo gli zeri della componente più importante della derivata
|
|
DBLVECTOR vdRoot ;
|
|
int nZ = pol3P.FindMainComponentRoots( vdRoot) ;
|
|
// scarto gli zeri fuori dall'intervallo aperto 0-1 e quelli ripetuti
|
|
nZ = FilterMultipleAndOutOfRangeRoots( vdRoot, 0 + EPS_PARAM, 1 - EPS_PARAM, EPS_PARAM) ;
|
|
|
|
// verifico se in corrispondenza di qualche zero si annulla la derivata
|
|
for ( int i = 0 ; i < nZ ; ++ i) {
|
|
Point3d ptPos ;
|
|
Vector3d vtDer1 ;
|
|
GetPointD1D2( vdRoot[i], ptPos, &vtDer1) ;
|
|
if ( vtDer1.IsZero()) {
|
|
m_dParSing = vdRoot[i] ;
|
|
if ( GetEGkDebugLev() >= 5)
|
|
LOG_DBG_INFO( GetEGkLogger(), "INFO : Found Singularity in CurveBezier")
|
|
return true ;
|
|
}
|
|
}
|
|
|
|
// non ci sono singolarità
|
|
m_dParSing = -1 ;
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// i coefficienti sono ordinati dalla potenza più bassa alla più alta
|
|
// se razionale, ritorna la sola forma monomiale del numeratore
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
CurveBezier::ToPowerBase( PolynomialPoint3d& pol3P) const
|
|
{
|
|
// algoritmo di Sederberg (BYU) CAGD cap. 3
|
|
// copio i punti di controllo
|
|
PNTVECTOR vPow( m_vPtCtrl.begin(), m_vPtCtrl.end()) ;
|
|
// se razionale, li moltiplico per i relativi pesi
|
|
if ( m_bRat) {
|
|
for ( int i = 0 ; i <= m_nDeg ; ++ i)
|
|
vPow[i] *= m_vWeCtrl[i] ;
|
|
}
|
|
// applico differenze successive, lasciando sul posto il risultato
|
|
for ( int i = 1 ; i <= m_nDeg ; ++ i) {
|
|
for ( int j = m_nDeg ; j >= i ; -- j)
|
|
vPow[j] += - vPow[j-1] ;
|
|
}
|
|
// calcolo i coefficienti binomiali
|
|
DBLVECTOR dBinom( m_nDeg + 1, 0) ;
|
|
dBinom[0] = 1 ;
|
|
for ( int i = 1 ; i <= m_nDeg ; ++ i) {
|
|
for ( int j = i ; j > 0 ; -- j)
|
|
dBinom[j] += dBinom[j-1] ;
|
|
}
|
|
// ottengo i coefficienti delle potenze
|
|
for ( int i = 1 ; i <= m_nDeg ; ++ i)
|
|
vPow[i] *= dBinom[i] ;
|
|
|
|
// assegno il risultato al parametro di ritorno
|
|
pol3P.Set( m_nDeg, vPow) ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// i coefficienti sono ordinati dalla potenza più bassa alla più alta
|
|
// se polinomiale, la forma monomiale del denominatore è un 1
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
CurveBezier::ToPowerBase( PolynomialPoint3d& pol3Num, Polynomial& polDen) const
|
|
{
|
|
// algoritmo di Sederberg (BYU) CAGD cap. 3
|
|
// copio i punti di controllo
|
|
PNTVECTOR vPow( m_vPtCtrl.begin(), m_vPtCtrl.end()) ;
|
|
// sistemo i pesi
|
|
DBLVECTOR dPow( m_nDeg + 1, 1) ;
|
|
if ( m_bRat) {
|
|
// moltiplico i punti per i relativi pesi
|
|
for ( int i = 0 ; i <= m_nDeg ; ++ i)
|
|
vPow[i] *= m_vWeCtrl[i] ;
|
|
// copio i pesi
|
|
dPow = m_vWeCtrl ;
|
|
}
|
|
// applico differenze successive, lasciando sul posto il risultato
|
|
for ( int i = 1 ; i <= m_nDeg ; ++ i) {
|
|
for ( int j = m_nDeg ; j >= i ; -- j) {
|
|
vPow[j] += - vPow[j-1] ;
|
|
if ( m_bRat)
|
|
dPow[j] += - dPow[j-1] ;
|
|
}
|
|
}
|
|
// calcolo i coefficienti binomiali
|
|
DBLVECTOR dBinom( m_nDeg + 1, 0) ;
|
|
dBinom[0] = 1 ;
|
|
for ( int i = 1 ; i <= m_nDeg ; ++ i) {
|
|
for ( int j = i ; j > 0 ; -- j)
|
|
dBinom[j] += dBinom[j-1] ;
|
|
}
|
|
// ottengo i coefficienti delle potenze
|
|
for ( int i = 1 ; i <= m_nDeg ; ++ i) {
|
|
vPow[i] *= dBinom[i] ;
|
|
if ( m_bRat)
|
|
dPow[i] *= dBinom[i] ;
|
|
}
|
|
|
|
// assegno i risultati ai parametri di ritorno
|
|
pol3Num.Set( m_nDeg, vPow) ;
|
|
if ( m_bRat)
|
|
polDen.Set( m_nDeg, dPow) ;
|
|
else
|
|
polDen.SetToConstant( 1) ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
CurveBezier::GetControlPolygonLength( double& dLen) const
|
|
{
|
|
// la curva deve essere valida
|
|
if ( m_nStatus != OK)
|
|
return false ;
|
|
|
|
// ciclo sui punti di controllo
|
|
dLen = 0 ;
|
|
for ( int i = 1 ; i <= m_nDeg ; ++ i)
|
|
dLen += Dist( m_vPtCtrl[i], m_vPtCtrl[i-1]) ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
CurveBezier::GetApproxLength( double& dLen) const
|
|
{
|
|
// la curva deve essere valida
|
|
if ( m_nStatus != OK)
|
|
return false ;
|
|
// se ridotta ad un punto
|
|
if ( IsAPoint()) {
|
|
dLen = 0 ;
|
|
return true ;
|
|
}
|
|
// se grado 1
|
|
if ( m_nDeg == 1) {
|
|
dLen = Dist( m_vPtCtrl[0], m_vPtCtrl[m_nDeg]) ;
|
|
return true ;
|
|
}
|
|
// la divido in due metà
|
|
CurveBezier crvBez1 = *this ;
|
|
crvBez1.TrimEndAtParam( 0.5) ;
|
|
double dCordLen1 = Dist( crvBez1.m_vPtCtrl[0], crvBez1.m_vPtCtrl[m_nDeg]) ;
|
|
double dCtrlLen1 ; crvBez1.GetControlPolygonLength( dCtrlLen1) ;
|
|
CurveBezier crvBez2 = *this ;
|
|
crvBez2.TrimStartAtParam( 0.5) ;
|
|
double dCordLen2 = Dist( crvBez2.m_vPtCtrl[0], crvBez2.m_vPtCtrl[m_nDeg]) ;
|
|
double dCtrlLen2 ; crvBez2.GetControlPolygonLength( dCtrlLen2) ;
|
|
// applico la formula di Gravesen (Graphics Gems V)
|
|
dLen = ( 2 * ( dCordLen1 + dCordLen2) + ( m_nDeg - 1) * ( dCtrlLen1 + dCtrlLen2)) / ( m_nDeg + 1) ;
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
CurveBezier::GetLength( double& dLen) const
|
|
{
|
|
return ( GetLengthAtParam( 1, dLen) && dLen > EPS_SMALL) ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
double
|
|
CurveBezier::GetSegmentLength( int nLev, double dU0, double dU1, double dU2,
|
|
const Point3d& ptP0, const Point3d& ptP1, const Point3d& ptP2) const
|
|
{
|
|
// algoritmo dell'approssimazione con arco per tre punti di S. Vincent e D. Forsey
|
|
|
|
const int MAX_LEV = 10 ;
|
|
const double MAX_ARC = 1.05 ;
|
|
const double LEN_RATIO = 1.2 ;
|
|
|
|
// verifica superamento del massimo livello di recursione per debug
|
|
if ( nLev >= MAX_LEV && GetEGkDebugLev() >= 5)
|
|
LOG_DBG_ERR( GetEGkLogger(), "ERROR : Exceeded recursions")
|
|
|
|
// calcolo delle distanze
|
|
double dD1 = Dist( ptP0, ptP2) ;
|
|
double dDa = Dist( ptP0, ptP1) ;
|
|
double dDb = Dist( ptP1, ptP2) ;
|
|
double dD2 = dDa + dDb ;
|
|
|
|
// distanza piccola o massimo livello di recursione, approx arco ok
|
|
if ( dD2 < EPS_SMALL || nLev >= MAX_LEV)
|
|
return ( dD2 + ( dD2 - dD1) / 3) ;
|
|
|
|
// se ci sono anomalie si suddivide
|
|
if ( dD1 < EPS_SMALL || dD2 / dD1 > MAX_ARC ||
|
|
dDa < 0.1 * EPS_SMALL || dDb / dDa > LEN_RATIO ||
|
|
dDb < 0.1 * EPS_SMALL || dDa / dDb > LEN_RATIO) {
|
|
|
|
double dUMid ;
|
|
Point3d ptMid ;
|
|
|
|
// prima metà
|
|
dUMid = 0.5 * ( dU0 + dU1) ;
|
|
GetPointD1D2( dUMid, ptMid) ;
|
|
double dD3 = GetSegmentLength( nLev +1, dU0, dUMid, dU1,
|
|
ptP0, ptMid, ptP1) ;
|
|
// seconda metà
|
|
dUMid = 0.5 * ( dU1 + dU2) ;
|
|
GetPointD1D2( dUMid, ptMid) ;
|
|
double dD4 = GetSegmentLength( nLev + 1, dU1, dUMid, dU2,
|
|
ptP1, ptMid, ptP2) ;
|
|
|
|
// la lunghezza è la somma delle due
|
|
return ( dD3 + dD4) ;
|
|
}
|
|
|
|
// caso normale
|
|
return ( dD2 + ( dD2 - dD1) / 3) ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
CurveBezier::GetLengthAtParam( double dU, double& dLen) const
|
|
{
|
|
// la curva deve essere validata
|
|
if ( m_nStatus != OK)
|
|
return false ;
|
|
|
|
// fuori dominio del parametro -> errore
|
|
if ( dU < 0 - EPS_PARAM)
|
|
return false ;
|
|
if ( dU > ( 1 + EPS_PARAM))
|
|
return false ;
|
|
|
|
// inizio
|
|
if ( dU < 0 + EPS_PARAM) {
|
|
dLen = 0 ;
|
|
return true ;
|
|
}
|
|
|
|
// parto con un numero fisso di punti e poi affino dove serve
|
|
dLen = 0 ;
|
|
double dUIni = 0 ;
|
|
Point3d ptIni ;
|
|
GetPointD1D2( 0, ptIni) ;
|
|
double dUMid ;
|
|
Point3d ptMid ;
|
|
const int NUM_SEG = 16 ;
|
|
for ( int i = 1 ; i <= NUM_SEG ; ++ i) {
|
|
// ricavo il punto
|
|
double dUFin = i / (double) NUM_SEG * dU ;
|
|
Point3d ptFin ;
|
|
GetPointD1D2( dUFin, ptFin) ;
|
|
// se punto dispari
|
|
if ( ( i % 2) == 1) {
|
|
// assegno nuovo medio
|
|
dUMid = dUFin ;
|
|
ptMid = ptFin ;
|
|
}
|
|
// se punto pari, calcolo la lunghezza
|
|
else {
|
|
dLen += GetSegmentLength( 0, dUIni, dUMid, dUFin, ptIni, ptMid, ptFin) ;
|
|
// nuovo iniziale prende i valori del finale
|
|
dUIni = dUFin ;
|
|
ptIni = ptFin ;
|
|
}
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
CurveBezier::GetSegmentParam( double dLen, double& dCurrLen, double& dSegLen,
|
|
double& dUIni, double& dUFin) const
|
|
{
|
|
double dUStart = dUIni ;
|
|
double dUEnd = dUFin ;
|
|
Point3d ptIni ;
|
|
GetPointD1D2( dUIni, ptIni) ;
|
|
double dUMid ;
|
|
Point3d ptMid ;
|
|
const int NUM_SEG = 16 ;
|
|
for ( int i = 1 ; i <= NUM_SEG ; ++ i) {
|
|
// ricavo il punto
|
|
dUFin = dUStart + i / (double) NUM_SEG * ( dUEnd - dUStart) ;
|
|
Point3d ptFin ;
|
|
GetPointD1D2( dUFin, ptFin) ;
|
|
// se punto dispari
|
|
if ( ( i % 2) == 1) {
|
|
// assegno nuovo medio
|
|
dUMid = dUFin ;
|
|
ptMid = ptFin ;
|
|
}
|
|
// se punto pari, calcolo la lunghezza
|
|
else {
|
|
dSegLen = GetSegmentLength( 0, dUIni, dUMid, dUFin, ptIni, ptMid, ptFin) ;
|
|
if ( ( dCurrLen + dSegLen) >= dLen) {
|
|
return true ;
|
|
}
|
|
else
|
|
dCurrLen += dSegLen ;
|
|
// nuovo iniziale prende i valori del finale
|
|
dUIni = dUFin ;
|
|
ptIni = ptFin ;
|
|
}
|
|
}
|
|
|
|
return false ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
CurveBezier::GetParamAtLength( double dLen, double& dU) const
|
|
{
|
|
// la curva deve essere validata
|
|
if ( m_nStatus != OK)
|
|
return false ;
|
|
|
|
// se prima di inizio, errore
|
|
if ( dLen < - EPS_SMALL)
|
|
return false ;
|
|
|
|
// inizio
|
|
if ( dLen < EPS_SMALL) {
|
|
dU = 0 ;
|
|
return true ;
|
|
}
|
|
|
|
// approfondisco nell'intervallo di interesse
|
|
bool bOk ;
|
|
double dSegLen ;
|
|
double dCurrLen = 0 ;
|
|
double dUIni = 0 ;
|
|
double dUFin = 1 ;
|
|
const double MIN_SEG_LEN = 0.2 ;
|
|
while ( ( bOk = GetSegmentParam( dLen, dCurrLen, dSegLen, dUIni, dUFin)) && dSegLen > MIN_SEG_LEN) {
|
|
// allungo di un poco l'intervallo di ricerca per compensare l'approssimazione della lunghezza
|
|
dUFin = min( dUFin + ( dUFin - dUIni) * 0.05, 1.) ;
|
|
}
|
|
// aggiustamento finale parametro
|
|
if ( bOk)
|
|
dU = dUIni + ( dLen - dCurrLen) / dSegLen * ( dUFin - dUIni) ;
|
|
|
|
return bOk ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
CurveBezier::IsPointOn( const Point3d& ptP, double dTol) const
|
|
{
|
|
// verifico la distanza
|
|
return ( DistPointCrvBezier( ptP, *this).IsEpsilon( dTol)) ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
CurveBezier::GetParamAtPoint( const Point3d& ptP, double& dPar, double dTol) const
|
|
{
|
|
DistPointCrvBezier DPB( ptP, *this) ;
|
|
if ( ! DPB.IsEpsilon( dTol))
|
|
return false ;
|
|
int nFlag ;
|
|
return DPB.GetParamAtMinDistPoint( 0, dPar, nFlag) ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
CurveBezier::GetLengthAtPoint( const Point3d& ptP, double& dLen, double dTol) const
|
|
{
|
|
double dU ;
|
|
if ( ! GetParamAtPoint( ptP, dU, dTol))
|
|
return false ;
|
|
return GetLengthAtParam( dU, dLen) ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
CurveBezier::ApproxWithLines( int nStep, PolyLine& PL) const
|
|
{
|
|
// pulisco la polilinea
|
|
PL.Clear() ;
|
|
|
|
// la curva deve essere validata
|
|
if ( m_nStatus != OK)
|
|
return false ;
|
|
|
|
// calcolo i punti richiesti
|
|
double dStep = 1.0 / nStep ;
|
|
for ( int i = 0 ; i <= nStep ; ++ i) {
|
|
double dU = i * dStep ;
|
|
Point3d ptPos ;
|
|
if ( GetPointD1D2( dU, ptPos))
|
|
PL.AddUPoint( dU, ptPos) ;
|
|
else
|
|
return false ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
CurveBezier::ApproxWithLines( double dLinTol, double dAngTolDeg, int nType, PolyLine& PL) const
|
|
{
|
|
// pulisco la polilinea
|
|
PL.Clear() ;
|
|
|
|
// la curva deve essere validata
|
|
if ( m_nStatus != OK)
|
|
return false ;
|
|
|
|
// se di primo grado, basta inserire gli estremi
|
|
if ( m_nDeg == 1) {
|
|
return ( PL.AddUPoint( 0, m_vPtCtrl[0]) && PL.AddUPoint( 1, m_vPtCtrl[m_nDeg])) ;
|
|
}
|
|
|
|
// limiti minimi su tolleranza e deviazione angolare
|
|
dLinTol = max( dLinTol, LIN_TOL_MIN) ;
|
|
dAngTolDeg = max( dAngTolDeg, ANG_TOL_MIN_DEG) ;
|
|
|
|
// se lineare con lato obbligato, gestione speciale
|
|
if ( nType == APL_LEFT || nType == APL_LEFT_CONVEX ||
|
|
nType == APL_RIGHT || nType == APL_RIGHT_CONVEX) {
|
|
// prima approssimazione lineare a 10 * Epsilon
|
|
if ( ! ApproxWithLines( 10 * EPS_SMALL, dAngTolDeg, APL_STD, PL))
|
|
return false ;
|
|
// eliminazione dei punti in tolleranza andando solo dalla parte ammessa
|
|
Vector3d vtExtr = ( m_VtExtr.IsSmall() ? Z_AX : m_VtExtr) ;
|
|
if ( ! PL.ApproxOnSide( vtExtr, ( nType == APL_LEFT), dLinTol))
|
|
return false ;
|
|
// se necessario, sistemo per convessità dalla parte ammessa
|
|
if ( nType == APL_RIGHT_CONVEX || nType == APL_LEFT_CONVEX) {
|
|
if ( ! PL.MakeConvex( vtExtr, ( nType == APL_LEFT_CONVEX)))
|
|
return false ;
|
|
}
|
|
return true ;
|
|
}
|
|
|
|
// inserisco il punto iniziale
|
|
if ( ! PL.AddUPoint( 0, m_vPtCtrl[0]))
|
|
return false ;
|
|
|
|
// verifico se va divisa
|
|
if ( ! FlatOrSplit( 0, *this, 0, 1, dLinTol, dAngTolDeg, PL))
|
|
return false ;
|
|
|
|
// se è stato inserito un solo punto, aggiungo il finale
|
|
if ( PL.GetPointNbr() == 1)
|
|
return PL.AddUPoint( 1, m_vPtCtrl[m_nDeg]) ;
|
|
// altrimenti, se l'ultimo punto non coincide con il finale lo sostituisco (distano al max di dLinTol)
|
|
else {
|
|
Point3d ptLast ;
|
|
PL.GetLastPoint( ptLast) ;
|
|
if ( ! AreSamePointApprox( ptLast, m_vPtCtrl[m_nDeg])) {
|
|
PL.EraseLastUPoint() ;
|
|
return PL.AddUPoint( 1, m_vPtCtrl[m_nDeg]) ;
|
|
}
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
CurveBezier::FlatOrSplit( int nLev, const CurveBezier& crvBez, double dParStart, double dParEnd,
|
|
double dLinTol, double dAngTolDeg, PolyLine& PL) const
|
|
{
|
|
// se raggiunto il massimo livello di recursione ...
|
|
const int MAX_LEV = 10 ;
|
|
if ( nLev >= MAX_LEV) {
|
|
// segnalo situazione per debug
|
|
if ( GetEGkDebugLev() >= 5)
|
|
LOG_DBG_ERR( GetEGkLogger(), "ERROR : Exceeded recursions")
|
|
// considero la curva piatta (inserisco il punto se abbastanza lontano dal precedente) ed esco
|
|
Point3d ptLast ;
|
|
PL.GetLastPoint( ptLast) ;
|
|
if ( SqDist( ptLast, crvBez.m_vPtCtrl[m_nDeg]) > ( dLinTol * dLinTol))
|
|
PL.AddUPoint( dParEnd, crvBez.m_vPtCtrl[m_nDeg]) ;
|
|
return true ;
|
|
}
|
|
|
|
// massima distanza al quadrato dei punti di controllo intermedi dalla linea tra primo e ultimo
|
|
double dMaxSqDist = 0 ;
|
|
double dLen ;
|
|
Vector3d vtDir ;
|
|
DirDist( crvBez.m_vPtCtrl[0], crvBez.m_vPtCtrl[m_nDeg], vtDir, dLen) ;
|
|
for ( int i = 1 ; i < m_nDeg ; i ++) {
|
|
DistPointLine dstPL( crvBez.m_vPtCtrl[i], crvBez.m_vPtCtrl[0], vtDir, dLen) ;
|
|
double dSqDist ;
|
|
if ( dstPL.GetSqDist( dSqDist) && dSqDist > dMaxSqDist)
|
|
dMaxSqDist = dSqDist ;
|
|
}
|
|
|
|
// se distanza entro tolleranza
|
|
if ( dMaxSqDist <= ( dLinTol * dLinTol)) {
|
|
// deviazione angolare tra primo e ultimo tratto del poligono di controllo (grado >= 1)
|
|
Vector3d vtDirI = crvBez.m_vPtCtrl[1] - crvBez.m_vPtCtrl[0] ;
|
|
Vector3d vtDirF = crvBez.m_vPtCtrl[m_nDeg] - crvBez.m_vPtCtrl[m_nDeg-1] ;
|
|
double dAngDeg ;
|
|
vtDirI.GetAngle( vtDirF, dAngDeg) ;
|
|
// se deviazione angolare entro tolleranza oppure lunghezza poligono controllo entro tolleranza
|
|
double dPolLen ;
|
|
if ( abs( dAngDeg) <= dAngTolDeg ||
|
|
( crvBez.GetControlPolygonLength( dPolLen) && dPolLen <= dLinTol)) {
|
|
// considero la curva piatta (inserisco il punto se abbastanza lontano dal precedente) ed esco
|
|
Point3d ptLast ;
|
|
PL.GetLastPoint( ptLast) ;
|
|
if ( SqDist( ptLast, crvBez.m_vPtCtrl[m_nDeg]) > ( dLinTol * dLinTol))
|
|
PL.AddUPoint( dParEnd, crvBez.m_vPtCtrl[m_nDeg]) ;
|
|
return true ;
|
|
}
|
|
}
|
|
|
|
// curva da dividere
|
|
{
|
|
double dParDiv ;
|
|
double dParMid ;
|
|
CurveBezier crvBez1 ;
|
|
// se prima suddivisione e c'è singolarità, divido su questa
|
|
if ( nLev == 0 && GetSingularParam( dParDiv) > 0)
|
|
;
|
|
// altrimenti divido a metà
|
|
else
|
|
dParDiv = 0.5 ;
|
|
dParMid = dParDiv * ( dParStart + dParEnd) ;
|
|
// prima metà
|
|
crvBez1 = crvBez ;
|
|
crvBez1.TrimEndAtParam( dParDiv) ;
|
|
if ( ! FlatOrSplit( nLev + 1, crvBez1, dParStart, dParMid, dLinTol, dAngTolDeg, PL))
|
|
return false ;
|
|
// seconda metà
|
|
crvBez1 = crvBez ;
|
|
crvBez1.TrimStartAtParam( dParDiv) ;
|
|
if ( ! FlatOrSplit( nLev + 1, crvBez1, dParMid, dParEnd, dLinTol, dAngTolDeg, PL))
|
|
return false ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
CurveBezier::ApproxWithArcs( double dLinTol, double dAngTolDeg, PolyArc& PA) const
|
|
{
|
|
// se estrusione definita e diversa da Z+
|
|
if ( ! m_VtExtr.IsSmall() && ! m_VtExtr.IsZplus()) {
|
|
// devo portarmi in un piano perpendicolare all'estrusione per costruirvi gli archi
|
|
Frame3d frExtr ;
|
|
if ( ! frExtr.Set( ORIG, m_VtExtr))
|
|
return false ;
|
|
// copio la curva e la porto nel riferimento di estrusione
|
|
CurveBezier crvAux ;
|
|
if ( ! crvAux.CopyFrom( *this))
|
|
return false ;
|
|
crvAux.ToLoc( frExtr) ;
|
|
// approssimo
|
|
bool bOk = crvAux.ApproxWithArcsXY( dLinTol, dAngTolDeg, PA) ;
|
|
// porto il risultato nel riferimento originale
|
|
PA.ToGlob( frExtr) ;
|
|
return bOk ;
|
|
}
|
|
// l'estrusione è secondo Z+, pertanto sono già nel piano XY
|
|
else
|
|
return ApproxWithArcsXY( dLinTol, dAngTolDeg, PA) ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
CurveBezier::ApproxWithArcsXY( double dLinTol, double dAngTolDeg, PolyArc& PA) const
|
|
{
|
|
// si creano gli archi nel piano XY
|
|
|
|
// pulisco il poliarco
|
|
PA.Clear() ;
|
|
|
|
// costruisco una approssimazione lineare
|
|
PolyLine PL ;
|
|
if ( ! ApproxWithLines( dLinTol, dAngTolDeg, APL_STD, PL))
|
|
return false ;
|
|
|
|
// approssimo la curva per approssimazioni successive mediante bisezione
|
|
return BiArcOrSplit( 0, PL, dLinTol, dAngTolDeg, PA) ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
CurveBezier::BiArcOrSplit( int nLev, PolyLine& PL, double dLinTol, double dAngTolDeg, PolyArc& PA) const
|
|
{
|
|
const int MAX_LEV = 10 ;
|
|
|
|
// se non chiusa
|
|
if ( ! PL.IsClosed()) {
|
|
|
|
PtrOwner<ICurve> pCrv ;
|
|
double dMaxDist ;
|
|
|
|
// se la polilinea ha più di 2 punti
|
|
if ( PL.GetPointNbr() > 2) {
|
|
// calcolo punti e direzioni agli estremi della polilinea usando la curva di Bezier
|
|
double dU ;
|
|
Point3d ptP0, ptP1 ;
|
|
Vector3d vtDir ;
|
|
double dDir0Deg, dDir1Deg ;
|
|
if ( ! PL.GetFirstU( dU) || ! GetPointTang( dU, ICurve::FROM_PLUS, ptP0, vtDir))
|
|
return false ;
|
|
vtDir.ToSpherical( nullptr, nullptr, &dDir0Deg) ;
|
|
if ( ! PL.GetLastU( dU) || ! GetPointTang( dU, ICurve::FROM_MINUS, ptP1, vtDir))
|
|
return false ;
|
|
vtDir.ToSpherical( nullptr, nullptr, &dDir1Deg) ;
|
|
// costruisco un biarco sulla polilinea (secondo metodo di Z. Sir)
|
|
pCrv.Set( GetBiArc( ptP0, dDir0Deg, ptP1, dDir1Deg, PL, dMaxDist, dLinTol)) ;
|
|
if ( IsNull( pCrv))
|
|
return false ;
|
|
}
|
|
// se la polilinea è formata da 2 punti
|
|
else if ( PL.GetPointNbr() == 2) {
|
|
// se molto vicini, esco
|
|
double dLen ;
|
|
if ( PL.GetLength( dLen) && dLen < EPS_SMALL)
|
|
return true ;
|
|
// costruisco la retta che li unisce
|
|
PtrOwner<CurveComposite> pCC( CreateBasicCurveComposite()) ;
|
|
if ( IsNull( pCC))
|
|
return false ;
|
|
if ( ! pCC->FromPolyLine( PL))
|
|
return false ;
|
|
pCrv.Set( pCC) ;
|
|
dMaxDist = 0 ;
|
|
}
|
|
// se la polilinea ha un solo punto, esco
|
|
else
|
|
return true ;
|
|
|
|
// se raggiunto il massimo livello di recursione, forzo l'accettazione del biarco
|
|
if ( nLev >= MAX_LEV) {
|
|
dMaxDist = 0 ;
|
|
// segnalo situazione per debug
|
|
if ( GetEGkDebugLev() >= 5)
|
|
LOG_DBG_ERR( GetEGkLogger(), "ERROR : Exceeded recursions")
|
|
}
|
|
|
|
// se lunghezza abbastanza picccola, forzo l'accettazione della curva
|
|
double dLen ;
|
|
if ( PL.GetApproxLength( dLen) && dLen < 10 * EPS_SMALL)
|
|
dMaxDist = 0 ;
|
|
|
|
// se in tolleranza
|
|
if ( dMaxDist <= dLinTol) {
|
|
// creo un nuovo poliarco
|
|
PolyArc PA2 ;
|
|
if ( ! pCrv->ApproxWithArcs( dLinTol, dAngTolDeg, PA2))
|
|
return false ;
|
|
// aggiusto la parametrizzazione
|
|
double dUStart, dUEnd ;
|
|
PL.GetFirstU( dUStart) ;
|
|
PL.GetLastU( dUEnd) ;
|
|
PA2.ParamLinearTransform( dUStart, dUEnd) ;
|
|
// accodo all'esistente
|
|
if ( ! PA.Join( PA2))
|
|
return false ;
|
|
// esco
|
|
return true ;
|
|
}
|
|
}
|
|
|
|
// se raggiunto il massimo livello di recursione, errore
|
|
if ( nLev >= MAX_LEV) {
|
|
// segnalo situazione per debug
|
|
LOG_ERROR( GetEGkLogger(), "ERROR : Exceeded recursions")
|
|
return false ;
|
|
}
|
|
|
|
// spezzo l'intervallo in due parti
|
|
double dParMid ;
|
|
// se prima suddivisione e c'è singolarità, divido su questa
|
|
if ( nLev == 0 && GetSingularParam( dParMid) > 0)
|
|
;
|
|
// altrimenti divido a metà
|
|
else {
|
|
double dParStart, dParEnd ;
|
|
if ( ! PL.GetFirstU( dParStart) || ! PL.GetLastU( dParEnd))
|
|
return false ;
|
|
dParMid = 0.5 * ( dParStart + dParEnd) ;
|
|
}
|
|
PolyLine PL2 ;
|
|
if ( ! PL.Split( dParMid, PL2))
|
|
return false ;
|
|
// prima metà
|
|
if ( ! BiArcOrSplit( nLev + 1, PL, dLinTol, dAngTolDeg, PA))
|
|
return false ;
|
|
// seconda metà
|
|
return BiArcOrSplit( nLev + 1, PL2, dLinTol, dAngTolDeg, PA) ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
ICurve*
|
|
CurveBezier::CopyParamRange( double dUStart, double dUEnd) const
|
|
{
|
|
// la curva deve essere valida
|
|
if ( m_nStatus != OK)
|
|
return nullptr ;
|
|
|
|
// i parametri start ed end devono essere compresi nel dominio parametrico della curva
|
|
if ( dUStart < - EPS_PARAM || dUStart > 1 + EPS_PARAM ||
|
|
dUEnd < - EPS_PARAM || dUEnd > 1 + EPS_PARAM)
|
|
return nullptr ;
|
|
// se il parametro start supera quello di end
|
|
if ( dUStart > dUEnd - EPS_PARAM) {
|
|
// se curva aperta, il trim la cancella completamente quindi non resta alcunché
|
|
if ( ! IsClosed())
|
|
return nullptr ;
|
|
// se curva chiusa, il trim si avvolge attorno al punto di giunzione
|
|
else {
|
|
// eseguo la copia sulla curva composita equivalente
|
|
CurveComposite cCompo ;
|
|
cCompo.CopyFrom( this) ;
|
|
return cCompo.CopyParamRange( dUStart, dUEnd) ;
|
|
}
|
|
}
|
|
// creo la curva di Bezier copia
|
|
PtrOwner<CurveBezier> pCopy( Clone()) ;
|
|
if ( IsNull( pCopy))
|
|
return nullptr ;
|
|
// eseguo il trim
|
|
if ( ! pCopy->TrimStartEndAtParam( dUStart, dUEnd))
|
|
return nullptr ;
|
|
return ( ::Release( pCopy)) ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
CurveBezier::Invert( void)
|
|
{
|
|
// la curva deve essere valida
|
|
if ( m_nStatus != OK)
|
|
return false ;
|
|
|
|
// inverto i punti di controllo
|
|
int nMid = ( m_nDeg + 1) / 2 ;
|
|
for ( int i = 0 ; i < nMid ; ++ i)
|
|
swap( m_vPtCtrl[i], m_vPtCtrl[m_nDeg-i]) ;
|
|
|
|
// imposto ricalcolo Voronoi
|
|
ResetVoronoiObject() ;
|
|
// imposto ricalcolo della grafica
|
|
m_OGrMgr.Reset() ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
CurveBezier::ModifyStart( const Point3d& ptNewStart)
|
|
{
|
|
// la curva deve essere valida
|
|
if ( m_nStatus != OK)
|
|
return false ;
|
|
|
|
// modifico il primo punto di controllo
|
|
m_vPtCtrl[0] = ptNewStart ;
|
|
|
|
// imposto ricalcolo Voronoi
|
|
ResetVoronoiObject() ;
|
|
// imposto ricalcolo della grafica
|
|
m_OGrMgr.Reset() ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
CurveBezier::ModifyEnd( const Point3d& ptNewEnd)
|
|
{
|
|
// la curva deve essere valida
|
|
if ( m_nStatus != OK)
|
|
return false ;
|
|
|
|
// modifico l'ultimo punto di controllo
|
|
m_vPtCtrl[m_nDeg] = ptNewEnd ;
|
|
|
|
// imposto ricalcolo Voronoi
|
|
ResetVoronoiObject() ;
|
|
// imposto ricalcolo della grafica
|
|
m_OGrMgr.Reset() ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
CurveBezier::TrimStartAtParam( double dUTrim)
|
|
{
|
|
// la curva deve essere valida
|
|
if ( m_nStatus != OK)
|
|
return false ;
|
|
|
|
// eseguo il trim (algoritmo di de Casteljau)
|
|
if ( ! m_bRat) {
|
|
if ( ! deCastTrimStart( dUTrim, m_nDeg, m_vPtCtrl))
|
|
return false ;
|
|
}
|
|
else {
|
|
if ( ! deCastRatTrimStart( dUTrim, m_nDeg, m_vPtCtrl, m_vWeCtrl))
|
|
return false ;
|
|
}
|
|
|
|
// imposto ricalcolo Voronoi
|
|
ResetVoronoiObject() ;
|
|
// con i controlli sopra fatti rimane validata, ma la grafica va ricalcolata
|
|
m_OGrMgr.Reset() ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
CurveBezier::TrimEndAtParam( double dUTrim)
|
|
{
|
|
// la curva deve essere valida
|
|
if ( m_nStatus != OK)
|
|
return false ;
|
|
|
|
// eseguo il trim (algoritmo di de Casteljau)
|
|
if ( ! m_bRat) {
|
|
if ( ! deCastTrimEnd( dUTrim, m_nDeg, m_vPtCtrl))
|
|
return false ;
|
|
}
|
|
else {
|
|
if ( ! deCastRatTrimEnd( dUTrim, m_nDeg, m_vPtCtrl, m_vWeCtrl))
|
|
return false ;
|
|
}
|
|
|
|
// imposto ricalcolo Voronoi
|
|
ResetVoronoiObject() ;
|
|
// con i controlli sopra fatti rimane validata, ma la grafica va ricalcolata
|
|
m_OGrMgr.Reset() ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
CurveBezier::TrimStartEndAtParam( double dUStartTrim, double dUEndTrim)
|
|
{
|
|
// la curva deve essere valida
|
|
if ( m_nStatus != OK)
|
|
return false ;
|
|
|
|
// i parametri start ed end devono essere compresi nel dominio parametrico della curva
|
|
if ( dUStartTrim < - EPS_PARAM || dUStartTrim > 1 + EPS_PARAM ||
|
|
dUEndTrim < - EPS_PARAM || dUEndTrim > 1 + EPS_PARAM)
|
|
return false ;
|
|
// verifico che i trim non cancellino interamente la curva
|
|
if ( dUStartTrim > dUEndTrim - EPS_PARAM)
|
|
return false ;
|
|
// se razionale devo trovare il punto di trim iniziale per ricalcolare il parametro di trim
|
|
Point3d ptStart ;
|
|
if ( m_bRat)
|
|
GetPointD1D2( dUStartTrim, ptStart) ;
|
|
// trim finale
|
|
if ( ! TrimEndAtParam( dUEndTrim))
|
|
return false ;
|
|
// trim iniziale con il parametro opportunamente ricalcolato
|
|
double dNewUStartTrim ;
|
|
if ( m_bRat)
|
|
GetParamAtPoint( ptStart, dNewUStartTrim) ;
|
|
else
|
|
dNewUStartTrim = dUStartTrim / dUEndTrim ;
|
|
//trim iniziale
|
|
if ( ! TrimStartAtParam( dNewUStartTrim))
|
|
return false ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
CurveBezier::TrimStartAtLen( double dLenTrim)
|
|
{
|
|
// la curva deve essere valida
|
|
if ( m_nStatus != OK)
|
|
return false ;
|
|
|
|
// lunghezze negative vengono considerate nulle
|
|
dLenTrim = max( dLenTrim, 0.) ;
|
|
|
|
// converto le lunghezze in valori parametrici
|
|
double dUTrim ;
|
|
if ( ! GetParamAtLength( dLenTrim, dUTrim))
|
|
return false ;
|
|
|
|
// utilizzo il trim sui parametri
|
|
if ( ! TrimStartAtParam( dUTrim))
|
|
return false ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
CurveBezier::TrimEndAtLen( double dLenTrim)
|
|
{
|
|
// la curva deve essere valida
|
|
if ( m_nStatus != OK)
|
|
return false ;
|
|
|
|
// lunghezze negative vengono considerate nulle
|
|
dLenTrim = max( dLenTrim, 0.) ;
|
|
|
|
// converto le lunghezze in valori parametrici
|
|
double dUTrim ;
|
|
if ( ! GetParamAtLength( dLenTrim, dUTrim))
|
|
return false ;
|
|
|
|
// utilizzo il trim sui parametri
|
|
if ( ! TrimEndAtParam( dUTrim))
|
|
return false ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
CurveBezier::ExtendStartByLen( double dLenExt)
|
|
{
|
|
// la curva deve essere valida
|
|
if ( m_nStatus != OK)
|
|
return false ;
|
|
|
|
// la lunghezza deve essere positiva
|
|
if ( dLenExt < - EPS_ZERO)
|
|
return false ;
|
|
// determino la lunghezza originale della curva
|
|
double dLen ;
|
|
if ( ! GetLengthAtParam( 1, dLen))
|
|
return false ;
|
|
// stimo il valore del parametro al nuovo punto iniziale
|
|
double dUTrim = - dLenExt / dLen ;
|
|
// estrapolo sul parametro con de Casteljau
|
|
// se polinomiale, va sicuramente bene
|
|
if ( ! m_bRat) {
|
|
for ( int k = 1 ; k <= m_nDeg ; k ++) {
|
|
for ( int i = 0 ; i <= m_nDeg - k ; i ++)
|
|
m_vPtCtrl[i] = ( 1 - dUTrim) * m_vPtCtrl[i] + dUTrim * m_vPtCtrl[i+1] ;
|
|
}
|
|
}
|
|
// se razionale, devo verificare che i pesi non diventino negativi
|
|
else {
|
|
Point3d vPtCtrl[MAXDEG+1] ;
|
|
double vWeCtrl[MAXDEG+1] ;
|
|
for ( int i = 0 ; i <= m_nDeg ; i ++) {
|
|
vPtCtrl[i] = m_vPtCtrl[i] ;
|
|
vWeCtrl[i] = m_vWeCtrl[i] ;
|
|
}
|
|
for ( int k = 1 ; k <= m_nDeg ; k ++) {
|
|
for ( int i = 0 ; i <= m_nDeg - k ; i ++) {
|
|
vPtCtrl[i] = ( 1 - dUTrim) * vWeCtrl[i] * vPtCtrl[i] + dUTrim * vWeCtrl[i+1] * vPtCtrl[i+1] ;
|
|
vWeCtrl[i] = ( 1 - dUTrim) * vWeCtrl[i] + dUTrim * vWeCtrl[i+1] ;
|
|
if ( vWeCtrl[i] < MIN_EXT_WEIGHT)
|
|
return false ;
|
|
vPtCtrl[i] = vPtCtrl[i] * ( 1 / vWeCtrl[i]) ;
|
|
}
|
|
}
|
|
for ( int i = 0 ; i <= m_nDeg ; i ++) {
|
|
m_vPtCtrl[i] = vPtCtrl[i] ;
|
|
m_vWeCtrl[i] = vWeCtrl[i] ;
|
|
}
|
|
}
|
|
|
|
// imposto ricalcolo Voronoi
|
|
ResetVoronoiObject() ;
|
|
// con i controlli sopra fatti rimane validata, ma la grafica va ricalcolata
|
|
m_OGrMgr.Reset() ;
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
CurveBezier::ExtendEndByLen( double dLenExt)
|
|
{
|
|
// la curva deve essere valida
|
|
if ( m_nStatus != OK)
|
|
return false ;
|
|
|
|
// la lunghezza deve essere positiva
|
|
if ( dLenExt < - EPS_ZERO)
|
|
return false ;
|
|
// determino la lunghezza originale della curva
|
|
double dLen ;
|
|
if ( ! GetLengthAtParam( 1, dLen))
|
|
return false ;
|
|
// stimo il valore del parametro al nuovo punto finale
|
|
double dUTrim = 1 + dLenExt / dLen ;
|
|
// estrapolo sul parametro con de Casteljau
|
|
// se polinomiale, va sicuramente bene
|
|
if ( ! m_bRat) {
|
|
for ( int k = 1 ; k <= m_nDeg ; k ++) {
|
|
for ( int i = m_nDeg ; i >= k ; i --)
|
|
m_vPtCtrl[i] = ( 1 - dUTrim) * m_vPtCtrl[i-1] + dUTrim * m_vPtCtrl[ i] ;
|
|
}
|
|
}
|
|
// se razionale, devo verificare che i pesi non diventino negativi
|
|
else {
|
|
Point3d vPtCtrl[MAXDEG+1] ;
|
|
double vWeCtrl[MAXDEG+1] ;
|
|
for ( int i = 0 ; i <= m_nDeg ; i ++) {
|
|
vPtCtrl[i] = m_vPtCtrl[i] ;
|
|
vWeCtrl[i] = m_vWeCtrl[i] ;
|
|
}
|
|
for ( int k = 1 ; k <= m_nDeg ; k ++) {
|
|
for ( int i = m_nDeg ; i >= k ; i --) {
|
|
vPtCtrl[i] = ( 1 - dUTrim) * vWeCtrl[i-1] * vPtCtrl[i-1] + dUTrim * vWeCtrl[ i] * vPtCtrl[ i] ;
|
|
vWeCtrl[i] = ( 1 - dUTrim) * vWeCtrl[i-1] + dUTrim * vWeCtrl[ i] ;
|
|
if ( vWeCtrl[i] < MIN_EXT_WEIGHT)
|
|
return false ;
|
|
vPtCtrl[i] = vPtCtrl[i] * ( 1 / vWeCtrl[i]) ;
|
|
}
|
|
}
|
|
for ( int i = 0 ; i <= m_nDeg ; i ++) {
|
|
m_vPtCtrl[i] = vPtCtrl[i] ;
|
|
m_vWeCtrl[i] = vWeCtrl[i] ;
|
|
}
|
|
}
|
|
|
|
// imposto ricalcolo Voronoi
|
|
ResetVoronoiObject() ;
|
|
// con i controlli sopra fatti rimane validata, ma la grafica va ricalcolata
|
|
m_OGrMgr.Reset() ;
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
CurveBezier::Translate( const Vector3d& vtMove)
|
|
{
|
|
// la curva deve essere validata
|
|
if ( m_nStatus != OK)
|
|
return false ;
|
|
|
|
// traslo Voronoi
|
|
if ( m_pVoronoiObj != nullptr)
|
|
m_pVoronoiObj->Translate( vtMove) ;
|
|
|
|
// imposto ricalcolo della grafica
|
|
m_OGrMgr.Reset() ;
|
|
|
|
// traslo i punti di controllo
|
|
for ( int i = 0 ; i <= m_nDeg ; ++ i)
|
|
m_vPtCtrl[i].Translate( vtMove) ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
CurveBezier::Rotate( const Point3d& ptAx, const Vector3d& vtAx, double dCosAng, double dSinAng)
|
|
{
|
|
// la curva deve essere validata
|
|
if ( m_nStatus != OK)
|
|
return false ;
|
|
|
|
// verifico validità dell'asse di rotazione
|
|
if ( vtAx.IsSmall())
|
|
return false ;
|
|
|
|
// ruoto Voronoi
|
|
if ( m_pVoronoiObj != nullptr)
|
|
m_pVoronoiObj->Rotate( ptAx, vtAx, dCosAng, dSinAng) ;
|
|
|
|
// imposto ricalcolo della grafica
|
|
m_OGrMgr.Reset() ;
|
|
|
|
// ruoto i punti di controllo
|
|
for ( int i = 0 ; i <= m_nDeg ; ++ i)
|
|
m_vPtCtrl[i].Rotate( ptAx, vtAx, dCosAng, dSinAng) ;
|
|
// ruoto il vettore estrusione
|
|
m_VtExtr.Rotate( vtAx, dCosAng, dSinAng) ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
CurveBezier::Scale( const Frame3d& frRef, double dCoeffX, double dCoeffY, double dCoeffZ)
|
|
{
|
|
// la curva deve essere validata
|
|
if ( m_nStatus != OK)
|
|
return false ;
|
|
|
|
// verifico non sia nulla
|
|
if ( abs( dCoeffX) < EPS_ZERO && abs( dCoeffY) < EPS_ZERO && abs( dCoeffZ) < EPS_ZERO)
|
|
return false ;
|
|
|
|
// verifico che la scalatura dei punti non li porti tutti a coincidere
|
|
PNTVECTOR vPnt ;
|
|
vPnt.reserve( m_nDeg + 1) ;
|
|
for ( int i = 0 ; i <= m_nDeg ; ++ i) {
|
|
Point3d ptTmp = m_vPtCtrl[i] ;
|
|
ptTmp.Scale( frRef, dCoeffX, dCoeffY, dCoeffZ) ;
|
|
vPnt.push_back( ptTmp) ;
|
|
}
|
|
bool bOk = false ;
|
|
for ( int i = 1 ; i <= m_nDeg ; ++ i) {
|
|
if ( ! AreSamePointApprox( vPnt[0], vPnt[i])) {
|
|
bOk = true ;
|
|
break ;
|
|
}
|
|
}
|
|
if ( ! bOk)
|
|
return false ;
|
|
|
|
// imposto ricalcolo Voronoi
|
|
ResetVoronoiObject() ;
|
|
// imposto ricalcolo della grafica
|
|
m_OGrMgr.Reset() ;
|
|
|
|
// scalo i punti di controllo
|
|
for ( int i = 0 ; i <= m_nDeg ; ++ i)
|
|
m_vPtCtrl[i] = vPnt[i] ;
|
|
// scalo vettore estrusione, lo normalizzo e aggiusto spessore
|
|
m_VtExtr.Scale( frRef, dCoeffX, dCoeffY, dCoeffZ) ;
|
|
double dLen = m_VtExtr.Len() ;
|
|
if ( dLen > EPS_ZERO) {
|
|
m_VtExtr /= dLen ;
|
|
m_dThick *= dLen ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
CurveBezier::Mirror( const Point3d& ptOn, const Vector3d& vtNorm)
|
|
{
|
|
// la curva deve essere validata
|
|
if ( m_nStatus != OK)
|
|
return false ;
|
|
|
|
// verifico validità del piano di specchiatura
|
|
if ( vtNorm.IsSmall())
|
|
return false ;
|
|
|
|
// imposto ricalcolo Voronoi
|
|
ResetVoronoiObject() ;
|
|
// imposto ricalcolo della grafica
|
|
m_OGrMgr.Reset() ;
|
|
|
|
// specchio i punti di controllo
|
|
for ( int i = 0 ; i <= m_nDeg ; ++ i)
|
|
m_vPtCtrl[i].Mirror( ptOn, vtNorm) ;
|
|
// specchio il vettore estrusione
|
|
m_VtExtr.Mirror( vtNorm) ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
CurveBezier::Shear( const Point3d& ptOn, const Vector3d& vtNorm, const Vector3d& vtDir, double dCoeff)
|
|
{
|
|
// la curva deve essere validata
|
|
if ( m_nStatus != OK)
|
|
return false ;
|
|
|
|
// verifico validità dei parametri
|
|
if ( vtNorm.IsSmall() || vtDir.IsSmall())
|
|
return false ;
|
|
|
|
// imposto ricalcolo Voronoi
|
|
ResetVoronoiObject() ;
|
|
// imposto ricalcolo della grafica
|
|
m_OGrMgr.Reset() ;
|
|
|
|
// eseguo scorrimento dei punti di controllo
|
|
for ( int i = 0 ; i <= m_nDeg ; ++ i)
|
|
m_vPtCtrl[i].Shear( ptOn, vtNorm, vtDir, dCoeff) ;
|
|
// eseguo scorrimento del vettore estrusione, lo normalizzo e aggiusto spessore
|
|
m_VtExtr.Shear( vtNorm, vtDir, dCoeff) ;
|
|
double dLen = m_VtExtr.Len() ;
|
|
if ( dLen > EPS_ZERO) {
|
|
m_VtExtr /= dLen ;
|
|
m_dThick *= dLen ;
|
|
}
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
CurveBezier::ToGlob( const Frame3d& frRef)
|
|
{
|
|
// la curva deve essere validata
|
|
if ( m_nStatus != OK)
|
|
return false ;
|
|
// verifico validità del frame
|
|
if ( frRef.GetType() == Frame3d::ERR)
|
|
return false ;
|
|
|
|
// se frame identità, non devo fare alcunché
|
|
if ( IsGlobFrame( frRef))
|
|
return true ;
|
|
|
|
// trasformo Voronoi
|
|
if ( m_pVoronoiObj != nullptr)
|
|
m_pVoronoiObj->ToGlob( frRef) ;
|
|
|
|
// imposto ricalcolo della grafica
|
|
m_OGrMgr.Reset() ;
|
|
|
|
// trasformo i punti di controllo
|
|
for ( int i = 0 ; i <= m_nDeg ; ++ i)
|
|
m_vPtCtrl[i].ToGlob( frRef) ;
|
|
// trasformo il vettore estrusione
|
|
m_VtExtr.ToGlob( frRef) ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
CurveBezier::ToLoc( const Frame3d& frRef)
|
|
{
|
|
// la curva deve essere validata
|
|
if ( m_nStatus != OK)
|
|
return false ;
|
|
// verifico validità del frame
|
|
if ( frRef.GetType() == Frame3d::ERR)
|
|
return false ;
|
|
|
|
// se frame identità, non devo fare alcunché
|
|
if ( IsGlobFrame( frRef))
|
|
return true ;
|
|
|
|
// trasformo Voronoi
|
|
if ( m_pVoronoiObj != nullptr)
|
|
m_pVoronoiObj->ToLoc( frRef) ;
|
|
|
|
// imposto ricalcolo della grafica
|
|
m_OGrMgr.Reset() ;
|
|
|
|
// trasformo i punti di controllo
|
|
for ( int i = 0 ; i <= m_nDeg ; ++ i)
|
|
m_vPtCtrl[i].ToLoc( frRef) ;
|
|
// trasformo il vettore estrusione
|
|
m_VtExtr.ToLoc( frRef) ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
CurveBezier::LocToLoc( const Frame3d& frOri, const Frame3d& frDest)
|
|
{
|
|
// la curva deve essere validata
|
|
if ( m_nStatus != OK)
|
|
return false ;
|
|
// verifico validità dei frame
|
|
if ( frOri.GetType() == Frame3d::ERR || frDest.GetType() == Frame3d::ERR)
|
|
return false ;
|
|
|
|
// se i due riferimenti coincidono, non devo fare alcunché
|
|
if ( AreSameFrame( frOri, frDest))
|
|
return true ;
|
|
|
|
// trasformo Voronoi
|
|
if ( m_pVoronoiObj != nullptr)
|
|
m_pVoronoiObj->LocToLoc( frOri, frDest) ;
|
|
|
|
// imposto ricalcolo della grafica
|
|
m_OGrMgr.Reset() ;
|
|
|
|
// trasformo i punti di controllo
|
|
for ( int i = 0 ; i <= m_nDeg ; ++ i)
|
|
m_vPtCtrl[i].LocToLoc( frOri, frDest) ;
|
|
// trasformo il vettore estrusione
|
|
m_VtExtr.LocToLoc( frOri, frDest) ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
CurveBezier::CalcVoronoiObject() const
|
|
{
|
|
if ( m_nStatus != OK)
|
|
return false ;
|
|
|
|
// creo oggetto vroni con la curva
|
|
m_pVoronoiObj = new( std::nothrow) Voronoi( this, false) ;
|
|
if ( m_pVoronoiObj == nullptr)
|
|
return false ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
Voronoi*
|
|
CurveBezier::GetVoronoiObject() const
|
|
{
|
|
if ( m_nStatus != OK)
|
|
return nullptr ;
|
|
|
|
// se non è stato calcolato, lo calcolo
|
|
if ( m_pVoronoiObj == nullptr)
|
|
CalcVoronoiObject() ;
|
|
|
|
// restituisco Voronoi
|
|
return m_pVoronoiObj ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void
|
|
CurveBezier::ResetVoronoiObject() const
|
|
{
|
|
if ( m_pVoronoiObj != nullptr)
|
|
delete m_pVoronoiObj ;
|
|
m_pVoronoiObj = nullptr ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
CurveBezier::MakeRational( void)
|
|
{
|
|
// la curva deve essere valida
|
|
if ( m_nStatus != OK)
|
|
return false ;
|
|
|
|
if ( m_bRat)
|
|
return true ;
|
|
// creo il vettore dei pesi e li setto tutti a 1
|
|
m_vWeCtrl.assign( m_nDeg + 1, 1) ;
|
|
// aggiorno il flag rational
|
|
m_bRat = true ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
CurveBezier::MakeRationalStandardForm( void)
|
|
{
|
|
// la curva deve essere valida
|
|
if ( m_nStatus != OK)
|
|
return false ;
|
|
|
|
if ( ! m_bRat)
|
|
return false ;
|
|
double dW0 = m_vWeCtrl.front() ;
|
|
double dWn = m_vWeCtrl.back() ;
|
|
if ( dW0 > 1 - EPS_ZERO && dWn > 1 - EPS_ZERO)
|
|
return true ;
|
|
if ( dW0 < EPS_ZERO || dWn < EPS_ZERO)
|
|
return false ;
|
|
|
|
// formula del Farin
|
|
double dCoeff = pow( dW0 / dWn, 1. / m_nDeg) ;
|
|
for ( int i = 0 ; i < m_nDeg + 1 ; ++i)
|
|
m_vWeCtrl[i] *= Pow( dCoeff, i) / dW0 ;
|
|
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
CurveBezier::MakeNonRational( double dTol)
|
|
{
|
|
// la curva deve essere valida
|
|
if ( m_nStatus != OK)
|
|
return false ;
|
|
|
|
if ( ! m_bRat)
|
|
return true ;
|
|
|
|
// controllo se i pesi sono tutti == 1 allora è una finta razionale e mi basta fare una copia dei punti di controllo
|
|
bool bIsActualRat = false ;
|
|
for ( int i = 0 ; i < m_nDeg ; ++i) {
|
|
if ( abs( m_vWeCtrl[i] - 1) > EPS_SMALL) {
|
|
bIsActualRat = true ;
|
|
break ;
|
|
}
|
|
}
|
|
|
|
bool bOk = true ;
|
|
if ( ! bIsActualRat) {
|
|
// semplicemente tolgo il booleano della razionalità e i punti restano gli stessi
|
|
m_bRat = false ;
|
|
}
|
|
else {
|
|
// provo ad approssimare la curva di bezier con una controparte non razionale
|
|
int nDeg = m_nDeg ;
|
|
// se ho una curva razionale di grado 2 verifico se è un arco, in quel caso la converto in una curva di grado 3 non razionale con la funzione dedicata
|
|
if ( nDeg == 2 && m_bRat) {
|
|
// prendo due punti sulla curva e calcolo l'intersezione dei due assi dei segmenti formati da pt2-pt0 e pt3-pt1
|
|
Point3d pt0 ; GetStartPoint( pt0) ;
|
|
Point3d pt1 ; GetPointD1D2( 0.3, pt1) ;
|
|
Point3d pt2 ; GetPointD1D2( 0.6, pt2) ;
|
|
Point3d pt3 ; GetEndPoint( pt3) ;
|
|
|
|
Vector3d vtDir1 = pt2 - pt0 ;
|
|
Vector3d vtDir2 = pt3 - pt1 ;
|
|
Vector3d vtN = vtDir2 ^ vtDir1 ;
|
|
|
|
CurveLine cl1 ; cl1.Set( pt1, pt1 + (vtDir1 ^ vtN) * 5) ;
|
|
CurveLine cl2 ; cl1.Set( pt2, pt2 + (vtDir2 ^ vtN) * 5) ;
|
|
IntersLineLine ill( cl1, cl2, false) ;
|
|
IntCrvCrvInfo iccInfo ; ill.GetIntCrvCrvInfo( iccInfo) ;
|
|
Point3d ptCen = iccInfo.IciA[0].ptI ;
|
|
|
|
// se sia l'inizio che la fine della curva distano uguale dal punto di intersezione tra i due assi trovati allora la curva è un arco di circonferenza
|
|
if ( abs(Dist( pt0, ptCen) - Dist( pt3, ptCen)) < EPS_SMALL) {
|
|
PtrOwner<ICurveBezier> pNew ( ApproxArcCurveBezierWithSingleCubic( this, ptCen, vtN)) ;
|
|
|
|
if ( IsNull( pNew) || ! pNew->IsValid())
|
|
return false ;
|
|
Init( 3, false) ;
|
|
for ( int i = 0 ; i < 3 ; ++i)
|
|
SetControlPoint( i, pNew->GetControlPoint(i)) ;
|
|
|
|
return true ;
|
|
}
|
|
}
|
|
|
|
// punto di rientro in caso fallisca il primo tentativo
|
|
retry :
|
|
nDeg += 2 ;
|
|
PtrOwner<CurveBezier> pNewBez( CreateBasicCurveBezier()) ;
|
|
pNewBez->Init( nDeg, false) ;
|
|
PNTVECTOR vPntCtrl ;
|
|
PNTVECTOR vPntSampling ;
|
|
for ( int p = 0 ; p < nDeg + 1 ; ++p) {
|
|
Point3d pt ; GetPointD1D2( double( p) / nDeg, pt) ;
|
|
pNewBez->SetControlPoint( p, pt) ;
|
|
vPntCtrl.push_back( pt) ;
|
|
}
|
|
vPntSampling = vPntCtrl ;
|
|
int c = 0 ;
|
|
double dErr = INFINITO ;
|
|
while ( dErr > dTol && c < 100) {
|
|
double dErrMax = 0 ;
|
|
// calcolo le differenze tra i punti di sampling sulla nuova curva e quelli sulla curva originale
|
|
for ( int p = 0 ; p < nDeg + 1 ; ++p) {
|
|
Point3d pt ; pNewBez->GetPointD1D2( double( p) / nDeg, pt) ;
|
|
Vector3d vDiff = vPntSampling[p] - pt ;
|
|
double dErrLoc = vDiff.Len() ;
|
|
if ( dErrLoc > dErrMax)
|
|
dErrMax = dErrLoc ;
|
|
// aggiorno il vettore dei punti di controllo della nuova curva
|
|
vPntCtrl[p] += vDiff ;
|
|
}
|
|
dErr = dErrMax ;
|
|
// aggiorno i punti di controllo della nuova curva
|
|
for ( int i = 0 ; i < nDeg + 1 ; ++i)
|
|
pNewBez->SetControlPoint( i, vPntCtrl[i]) ;
|
|
++c ;
|
|
}
|
|
|
|
// calcolo l'errore di approssimazione sulla curva
|
|
CalcApproxError( this, pNewBez, dErr) ;
|
|
bOk = dErr < dTol ;
|
|
if ( bOk) {
|
|
// aggiorno la curva di bezier originale con quella approssimata
|
|
Init( nDeg, false) ;
|
|
for ( int i = 0 ; i < nDeg + 1 ; ++i) {
|
|
SetControlPoint( i, pNewBez->GetControlPoint( i)) ;
|
|
SetControlWeight( i, pNewBez->GetControlWeight( i)) ;
|
|
}
|
|
}
|
|
else if ( nDeg < m_nDeg + 4)
|
|
goto retry ;
|
|
}
|
|
|
|
return bOk ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool
|
|
CurveBezier::IsALine( void) const
|
|
{
|
|
// la curva deve essere valida
|
|
if ( m_nStatus != OK)
|
|
return false ;
|
|
|
|
Point3d ptStart ; GetStartPoint( ptStart) ;
|
|
Point3d ptEnd ; GetEndPoint( ptEnd) ;
|
|
for ( int i = 1 ; i < m_nDeg ; ++i) {
|
|
Point3d ptCtrl = GetControlPoint( i) ;
|
|
DistPointLine dpl( ptCtrl, ptStart, ptEnd) ;
|
|
double dDist = 0 ; dpl.GetDist( dDist) ;
|
|
if ( dDist > EPS_SMALL)
|
|
return false ;
|
|
}
|
|
return true ;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
PNTVECTOR
|
|
CurveBezier::GetAllControlPoints( void) const
|
|
{
|
|
PNTVECTOR vPntCtrl ;
|
|
// la curva deve essere valida
|
|
if ( m_nStatus != OK)
|
|
return vPntCtrl ;
|
|
|
|
return m_vPtCtrl ;
|
|
} |