Files
EgtGeomKernel/CurveAux.cpp
Daniele Bariletti 9d18e1a9ba EgtGeomKernel :
- mantenuta l'approssimazione della spirale con bezier cubiche razionali.
2024-05-07 17:34:42 +02:00

1452 lines
52 KiB
C++

//----------------------------------------------------------------------------
// EgalTech 2013-2013
//----------------------------------------------------------------------------
// File : CurveAux.cpp Data : 22.11.13 Versione : 1.3a1
// Contenuto : Implementazione di alcune funzioni di utilità per le curve.
//
//
//
// Modifiche : 22.11.13 DS Creazione modulo.
//
//
//----------------------------------------------------------------------------
//--------------------------- Include ----------------------------------------
#include "stdafx.h"
#include "CurveAux.h"
#include "GeoConst.h"
#include "CurveLine.h"
#include "CurveArc.h"
#include "CurveBezier.h"
#include "CurveComposite.h"
#include "Voronoi.h"
#include "/EgtDev/Include/EGkDistPointCurve.h"
#include "/EgtDev/Include/EGkStringUtils3d.h"
#include "/EgtDev/Include/EGkUiUnits.h"
#include "/EgtDev/Include/EgtPointerOwner.h"
using namespace std ;
//----------------------------------------------------------------------------
bool
IsClosed( const ICurve& crvC)
{
Point3d ptStart, ptEnd ;
return ( crvC.GetStartPoint( ptStart) && crvC.GetEndPoint( ptEnd) && AreSamePointApprox( ptStart, ptEnd)) ;
}
//----------------------------------------------------------------------------
bool
IsValidParam( const ICurve& crvC, double dPar, ICurve::Side nSide)
{
// recupero l'intervallo di validità del parametro
double dStart, dEnd ;
if ( ! crvC.GetDomain( dStart, dEnd))
return false ;
// il parametro deve stare nei limiti
if ( dPar < dStart - EPS_PARAM || dPar > dEnd + EPS_PARAM)
return false ;
// se curva chiusa non sono necessari altri controlli
if ( crvC.IsClosed())
return true ;
// per curve aperte non posso studiare la curva prima dell'inizio e dopo la fine
if ( ( dPar < dStart + EPS_PARAM && nSide == ICurve::FROM_MINUS) ||
( dPar > dEnd - EPS_PARAM && nSide == ICurve::FROM_PLUS))
return false ;
return true ;
}
//----------------------------------------------------------------------------
bool
IsStartParam( const ICurve& crvC, double dPar)
{
// recupero l'intervallo di validità del parametro
double dStart, dEnd ;
if ( ! crvC.GetDomain( dStart, dEnd))
return false ;
// se il parametro non è nell'intorno dell'inizio
if ( abs( dPar - dStart) > EPS_PARAM)
return false ;
return true ;
}
//----------------------------------------------------------------------------
bool
IsEndParam( const ICurve& crvC, double dPar)
{
// recupero l'intervallo di validità del parametro
double dStart, dEnd ;
if ( ! crvC.GetDomain( dStart, dEnd))
return false ;
// se il parametro non è nell'intorno della fine
if ( abs( dPar - dEnd) > EPS_PARAM)
return false ;
return true ;
}
//----------------------------------------------------------------------------
bool
GetNearestExtremityToPoint( const Point3d& ptP, const ICurve& Curve, bool& bStart)
{
// recupero punto iniziale
Point3d ptStart ;
if ( ! Curve.GetStartPoint( ptStart))
return false ;
// recupero punto finale
Point3d ptEnd ;
if ( ! Curve.GetEndPoint( ptEnd))
return false ;
// se curva aperta
if ( ! AreSamePointApprox( ptStart, ptEnd)) {
// è il più vicino tra inizio e fine
bStart = ( SqDist( ptP, ptStart) <= SqDist( ptP, ptEnd)) ;
return true ;
}
// altrimenti curva chiusa, devo proiettare il punto sulla curva e misurare la distanza su di essa
DistPointCurve distPC( ptP, Curve) ;
int nFlag ;
double dLen, dU, dULen ;
if ( Curve.GetLength( dLen) &&
distPC.GetParamAtMinDistPoint( 0, dU, nFlag) && Curve.GetLengthAtParam( dU, dULen)) {
bStart = ( dULen <= ( dLen - dULen)) ;
return true ;
}
else
return false ;
}
//----------------------------------------------------------------------------
bool
MoveParamToAvoidTg( double& dU, ICurve::Side nSide, const ICurve& Curve)
{
// verifico che il parametro sia accettabile
if ( ! Curve.IsValidParam( dU, nSide))
return false ;
// determino un incremento piccolo ma valido del parametro U
double dDeltaU = 1000 * EPS_PARAM ;
Point3d ptPos ;
Vector3d vtDer1 ;
if ( Curve.GetPointD1D2( dU, nSide, ptPos, &vtDer1)) {
double dDer1 = vtDer1.LenXY() ;
if ( dDer1 > EPS_ZERO)
dDeltaU = 2 * EPS_SMALL / dDer1 ;
}
// cerco di spostarmi per evitare eventuali problemi di tangenza
if ( nSide == ICurve::FROM_MINUS) {
double dUm = dU - dDeltaU ;
if ( ! Curve.IsValidParam( dUm, nSide)) {
if ( Curve.IsClosed()) {
double dStart, dEnd ;
Curve.GetDomain( dStart, dEnd) ;
dUm = ( dEnd - dStart) - dDeltaU ;
}
else
dUm = dU ;
}
dU = dUm ;
return true ;
}
else { // nSide == ICurve::FROM_PLUS
double dUm = dU + dDeltaU ;
if ( ! Curve.IsValidParam( dUm, nSide)) {
if ( Curve.IsClosed()) {
double dStart, dEnd ;
Curve.GetDomain( dStart, dEnd) ;
dUm = dStart + dDeltaU ;
}
else
dUm = dU ;
}
dU = dUm ;
return true ;
}
}
//----------------------------------------------------------------------------
bool
GetTang( const ICurve& crvC, double dU, ICurve::Side nS, Vector3d& vtTang)
{
Point3d ptPos ;
return GetPointTang( crvC, dU, nS, ptPos, vtTang) ;
}
//----------------------------------------------------------------------------
bool
GetPointTang( const ICurve& crvC, double dU, ICurve::Side nS, Point3d& ptPos, Vector3d& vtTang)
{
if ( ! crvC.GetPointD1D2( dU, nS, ptPos, &vtTang))
return false ;
if ( vtTang.Normalize( EPS_ZERO))
return true ;
// nel caso la derivata prima sia nulla, utilizziamo la seconda
Vector3d vtDummy ;
if ( ! crvC.GetPointD1D2( dU, nS, ptPos, &vtDummy, &vtTang))
return false ;
double dUmod = dU + ( nS == ICurve::FROM_MINUS ? -1 : +1) * 100 * EPS_PARAM ;
Point3d ptDummy ;
// se anche la derivata seconda è nulla, provo a spostarmi di poco
if ( ! vtTang.Normalize( EPS_ZERO)) {
if ( ! crvC.GetPointD1D2( dUmod, nS, ptDummy, &vtDummy, &vtTang))
return false ;
if ( ! vtTang.Normalize( EPS_ZERO))
return false ;
}
// per il verso, lo confrontiamo con quello della derivata prima un poco discosta
Vector3d vtD1near ;
if ( ! crvC.GetPointD1D2( dUmod, nS, ptDummy, &vtD1near))
return false ;
if ( ( vtTang * vtD1near) < 0)
vtTang.Invert() ;
return true ;
}
//----------------------------------------------------------------------------
bool
GetPointDiffGeom( const ICurve& crvC, double dU, ICurve::Side nS, CrvPointDiffGeom& oDiffG)
{
// calcolo punto e derivate
Point3d ptPos ;
Vector3d vtDer1, vtDer2 ;
if ( ! crvC.GetPointD1D2( dU, nS, ptPos, &vtDer1, &vtDer2))
return false ;
// assegno parametro e posizione
oDiffG.dU = dU ;
oDiffG.ptP = ptPos ;
// se esiste la derivata prima non nulla
double dSqLenD1 = vtDer1.SqLen() ;
if ( vtDer1.Normalize()) {
oDiffG.vtT = vtDer1 ;
// del vettore deriv2^ tengo la sola componente perpendicolare al vettore tangente
oDiffG.vtN = vtDer2 - ( vtDer2 * vtDer1) * vtDer1 ;
if ( oDiffG.vtN.Normalize()) {
oDiffG.dCurv = ( vtDer1 ^ vtDer2).Len() / dSqLenD1 ;
oDiffG.nStatus = CrvPointDiffGeom::NCRV ;
}
else {
oDiffG.dCurv = 0 ;
oDiffG.nStatus = CrvPointDiffGeom::TANG ;
}
}
// se esiste almeno derivata seconda non nulla, definisce la tangente
else if ( vtDer2.Normalize()) {
oDiffG.vtT = vtDer2 ;
oDiffG.vtN.Set( 0, 0, 0) ;
oDiffG.dCurv = 0 ;
// per il verso, lo confrontiamo con quello della derivata prima un poco discosta
Point3d ptDummy ;
Vector3d vtD1near ;
dU += ( nS == ICurve::FROM_MINUS ? -1 : +1) * 100 * EPS_PARAM ;
if ( crvC.GetPointD1D2( dU, nS, ptDummy, &vtD1near)) {
if ( ( oDiffG.vtT * vtD1near) < 0)
oDiffG.vtT.Invert() ;
oDiffG.nStatus = CrvPointDiffGeom::TANG ;
}
else {
oDiffG.vtT.Set( 0, 0, 0) ;
oDiffG.nStatus = CrvPointDiffGeom::POS ;
}
}
// altrimenti non sono definite la tangente e la normale
else {
oDiffG.vtT.Set( 0, 0, 0) ;
oDiffG.vtN.Set( 0, 0, 0) ;
oDiffG.dCurv = 0 ;
oDiffG.nStatus = CrvPointDiffGeom::POS ;
}
return true ;
}
//----------------------------------------------------------------------------
bool
ImproveCurveParamAtPoint( double& dU, const Point3d& ptP, const ICurve* pCrv)
{
// Da usare quando il parametro è già molto vicino a quello esatto
// calcolo il punto della curva in corrispondenza al parametro
Point3d ptQ ;
Vector3d vtD ;
if ( ! pCrv->GetPointD1D2( dU, ICurve::FROM_MINUS, ptQ, &vtD))
return false ;
// se sono uguali, è già tutto ok
if ( AreSamePointExact( ptP, ptQ))
return true ;
// se derivata nulla, non posso migliorare
if ( vtD.IsZero())
return false ;
// incremento parametro su linea tangente (uso la derivata)
double dDeltaU = ( ptP - ptQ) * vtD / vtD.SqLen() ;
// se migliorato, tengo nuovo valore
Point3d ptR ;
if ( pCrv->GetPointD1D2( dU + dDeltaU, ICurve::FROM_MINUS, ptR) &&
( ptP - ptR).SqLen() < ( ptP - ptQ).SqLen())
dU += dDeltaU ;
return true ;
}
//----------------------------------------------------------------------------
bool
CurveGetAreaXY( const ICurve& crvC, double& dArea)
{
// verifico sia chiusa
if ( ! crvC.IsClosed())
return false ;
// approssimo la curva con una polilinea
PolyLine PL ;
crvC.ApproxWithLines( LIN_TOL_STD, ANG_TOL_STD_DEG, ICurve::APL_SPECIAL_INT, PL) ;
// calcolo l'area
double dAreaXY = 0 ;
PL.GetAreaXY( dAreaXY) ;
// restituisco il valore
if ( &dArea != nullptr)
dArea = dAreaXY ;
return true ;
}
//----------------------------------------------------------------------------
bool
CurveGetArea( const ICurve& crvC, Plane3d& plPlane, double& dArea)
{
// verifico sia chiusa
if ( ! crvC.IsClosed())
return false ;
// approssimo la curva con una polilinea
PolyLine PL ;
crvC.ApproxWithLines( LIN_TOL_STD, ANG_TOL_STD_DEG, ICurve::APL_SPECIAL_INT, PL) ;
// calcolo l'area
Plane3d plMyPlane ;
double dMyArea = 0 ;
if ( ! PL.IsClosedAndFlat( plMyPlane, dMyArea, 50 * EPS_SMALL))
return false ;
// restituisco i valori
if ( &plPlane != nullptr)
plPlane = plMyPlane ;
if ( &dArea != nullptr)
dArea = dMyArea ;
return true ;
}
//----------------------------------------------------------------------------
bool
CurveDump( const ICurve& crvC, string& sOut, bool bMM, const char* szNewLine)
{
// verifico validità curva
if ( ! crvC.IsValid())
return false ;
// punti iniziale e finale
Point3d ptPS ;
crvC.GetStartPoint( ptPS) ;
sOut += "PS(" + ToString( GetInUiUnits( ptPS, bMM), 3) + ")" + szNewLine ;
Point3d ptPE ;
crvC.GetEndPoint( ptPE) ;
sOut += "PE(" + ToString( GetInUiUnits( ptPE, bMM), 3) + ")" + szNewLine ;
// direzioni iniziale e finale
Vector3d vtDirS ;
crvC.GetStartDir( vtDirS) ;
sOut += "VS(" + ToString( vtDirS, 3) + ")" + szNewLine ;
Vector3d vtDirE ;
crvC.GetEndDir( vtDirE) ;
sOut += "VE(" + ToString( vtDirE, 3) + ")" + szNewLine ;
// eventuali direzione di estrusione e spessore
Vector3d vtExtr ;
crvC.GetExtrusion( vtExtr) ;
if ( ! vtExtr.IsSmall()) {
double dThick ;
crvC.GetThickness( dThick) ;
sOut += "Extr(" + ToString( vtExtr, 3) + ") Th=" + ToString( GetInUiUnits( dThick, bMM), 3) + szNewLine ;
}
// lunghezza
double dLen = 0 ;
crvC.GetLength( dLen) ;
sOut += "Len=" + ToString( GetInUiUnits( dLen, bMM), 3) + szNewLine ;
// altri dati per curva chiusa
double dAreaXY ;
if ( CurveGetAreaXY( crvC, dAreaXY)) {
bool bCCW = ( dAreaXY > 0) ;
double dAreaUi = GetAreaInUiUnits( abs( dAreaXY), bMM) ;
int nDec = ( dAreaUi > 100 ? 1 : ( dAreaUi > 0.1 ? 3 : 6)) ;
sOut += string( "Closed") + ( bCCW ? " CCW" : " CW") + " AreaXY=" +
ToString( dAreaUi, nDec) + szNewLine ;
}
return true ;
}
//----------------------------------------------------------------------------
bool
CopyExtrusion( const ICurve* pSouCrv, ICurve* pDestCrv)
{
// verifico puntatori
if ( pSouCrv == nullptr || pDestCrv == nullptr)
return false ;
// eseguo copia
Vector3d vtExtr ;
pSouCrv->GetExtrusion( vtExtr) ;
pDestCrv->SetExtrusion( vtExtr) ;
return true ;
}
//----------------------------------------------------------------------------
bool
CopyThickness( const ICurve* pSouCrv, ICurve* pDestCrv)
{
// verifico puntatori
if ( pSouCrv == nullptr || pDestCrv == nullptr)
return false ;
// eseguo copia
double dThick = 0 ;
pSouCrv->GetThickness( dThick) ;
pDestCrv->SetThickness( dThick) ;
return true ;
}
//----------------------------------------------------------------------------
ICurveBezier*
LineToBezierCurve( const ICurveLine* pCrvLine)
{
PtrOwner<ICurveBezier> pCrvBezier( CreateCurveBezier()) ;
// rendo tutte le curve di grado 2 e razionali così posso convertire anche archi e avere tutte curve dello stesso grado e razionali
pCrvBezier->Init( 2, true) ;
pCrvBezier->FromLine( *pCrvLine) ;
return Release( pCrvBezier) ;
}
//----------------------------------------------------------------------------
ICurve*
ArcToBezierCurve( const ICurve* pCrv, bool bDeg3OrDeg2)
{
// verifico sia un arco
const CurveArc* pArc = GetBasicCurveArc( pCrv) ;
if ( pArc == nullptr)
return nullptr ;
// se angolo al centro sotto il limite, basta una curva
if ( ( abs( pArc->GetAngCenter()) < BEZARC_ANG_CEN_MAX + EPS_ANG_SMALL && abs( pArc->GetDeltaN()) < EPS_ZERO && ! bDeg3OrDeg2) ||
( abs( pArc->GetAngCenter()) < BEZARC_ANG_CEN_MAX + EPS_ANG_SMALL && bDeg3OrDeg2)) {
// creo la curva di Bezier equivalente all'arco
PtrOwner<CurveBezier> pCrvBez( CreateBasicCurveBezier()) ;
if ( IsNull( pCrvBez) || ! pCrvBez->FromArc( *pArc))
return nullptr ;
// restituisco la curva
return Release( pCrvBez) ;
}
// altrimenti curva composita di Bezier
else {
// creo la curva composita
PtrOwner<CurveComposite> pCrvCompo( CreateBasicCurveComposite()) ;
if ( IsNull( pCrvCompo))
return nullptr ;
// inserisco nella CC le curve di Bezier equivalenti alle parti dell'arco
int nParts = (int) ceil( abs( pArc->GetAngCenter()) / ( BEZARC_ANG_CEN_MAX + EPS_ANG_SMALL)) ;
if ( ! bDeg3OrDeg2 && abs( pArc->GetDeltaN()) > EPS_ZERO)
nParts *= 2 ;
nParts = max( nParts, 2) ;
for ( int i = 0 ; i < nParts ; ++ i) {
// copio l'arco originale
CurveArc cArc = *pArc ;
// lo limito alla parte di interesse
cArc.TrimStartEndAtParam( i / double( nParts), ( i + 1) / double( nParts)) ;
// creo la curva di Bezier equivalente
PtrOwner<CurveBezier> pCrvBez( CreateBasicCurveBezier()) ;
if ( IsNull( pCrvBez) || ! pCrvBez->FromArc( cArc))
return nullptr ;
// aggiungo la curva di Bezier a quella composita
if ( ! pCrvCompo->AddCurve( Release( pCrvBez)))
return nullptr ;
}
// copio estrusione e spessore
CopyExtrusion( pArc, pCrvCompo) ;
CopyThickness( pArc, pCrvCompo) ;
// restituisco la curva
return Release( pCrvCompo) ;
}
}
//----------------------------------------------------------------------------
ICurve*
CompositeToBezierCurve( const ICurveComposite* pCC)
{
// converto tutte le curve in bezier razionali di grado 2
PtrOwner<ICurveComposite> pCCBezier( CreateCurveComposite()) ;
for ( int i = 0 ; i < int( pCC->GetCurveCount()) ; ++i) {
PtrOwner<ICurve> pCrvNew ;
if ( pCC->GetCurve(i)->GetType() == CRV_ARC) {
const CurveArc* crArc = GetBasicCurveArc( pCC->GetCurve(i)) ;
ICurve* pCrvBezier = ArcToBezierCurve( crArc) ;
if ( pCrvBezier == nullptr)
return nullptr ;
// se la curva è di grado superiore al secondo allora devo ricondurla al secondo grado
pCrvNew.Set( pCrvBezier) ;
}
else if ( pCC->GetCurve(i)->GetType() == CRV_LINE) {
const CurveLine* crLine = GetBasicCurveLine( pCC->GetCurve(i)) ;
ICurve* pCrvBezier = LineToBezierCurve( crLine) ;
if ( pCrvBezier == nullptr)
return nullptr ;
pCrvNew.Set( pCrvBezier) ;
}
else if ( pCC->GetCurve(i)->GetType() == CRV_BEZIER ) {
const CurveBezier* crvBezier = GetBasicCurveBezier( pCC->GetCurve(i)) ;
ICurve* pCrvBezier = BezierToBasicBezierCurve( crvBezier) ;
if ( pCrvBezier == nullptr)
return nullptr ;
pCrvNew.Set( pCrvBezier) ;
}
pCCBezier->AddCurve( Release( pCrvNew)) ;
}
return Release( pCCBezier) ;
}
//----------------------------------------------------------------------------
ICurve*
BezierToBasicBezierCurve( const ICurveBezier* pCrvBezier)
{
// resta da calcolare un errore sull'approssimazione oppure usare la tecnica di spezzare la curva originale in sottocurve e approssimarle con bezier cubiche
// per ridurre molto l'errore
// dovrei restituire una bezier di grado 2, razionale per poter essere uniforme con le altre curve trasmorate in bezier
PtrOwner<ICurveBezier> pCrvNew( pCrvBezier->Clone()) ;
int nDeg = pCrvBezier->GetDegree() ;
bool bRat = pCrvBezier->IsRational() ;
// se la curva è già nella forma giusta la restituisco
if ( nDeg == 2 && bRat)
return Release( pCrvNew) ;
// sennò la riduco di grado fino al 2
PtrOwner<ICurveBezier> pBasicBezier ( CreateCurveBezier()) ;
pBasicBezier->Init( 2, true) ;
double dW = 1 ;
if ( nDeg == 1) {
Point3d ptStart = pCrvBezier->GetControlPoint( 0) ;
Point3d ptEnd = pCrvBezier->GetControlPoint( 1) ;
pBasicBezier->SetControlPoint( 0, ptStart, dW) ;
pBasicBezier->SetControlPoint( 1, 0.5 * (ptStart + ptEnd), dW) ;
pBasicBezier->SetControlPoint( 2, ptEnd, dW) ;
return Release( pBasicBezier) ;
}
while ( nDeg > 2 ) {
pCrvNew.Set( BezierDecreaseDegree( pCrvNew, 1)) ;
if ( IsNull( pCrvNew) || ! pCrvNew->IsValid())
return nullptr ;
-- nDeg ;
}
for ( int i = 0 ; i < 3 ; ++i) {
Point3d ptCopy = pCrvNew->GetControlPoint( i) ;
if ( bRat)
dW = pCrvNew->GetControlWeight( i) ;
pBasicBezier->SetControlPoint( i, ptCopy, dW) ;
}
return Release( pBasicBezier) ;
}
ICurveBezier*
BezierIncreaseDegree(const ICurveBezier* pCrvBezier)
{
if ( pCrvBezier == nullptr)
return nullptr ;
// creo la versione con grado aumentato
PtrOwner<ICurveBezier> pNewBezier( CreateCurveBezier()) ;
int nDeg = pCrvBezier->GetDegree() + 1;
bool bRat = pCrvBezier->IsRational();
pNewBezier->Init( nDeg , bRat) ;
// prev e curr sono riferiti alla curva di partenza
// salvo il primo punto
Point3d ptCtrlPrev = pCrvBezier->GetControlPoint( 0) ;
double dWprev = 1 ;
if ( bRat ) {
dWprev = pCrvBezier->GetControlWeight( 0) ;
pNewBezier->SetControlPoint( 0, ptCtrlPrev, dWprev) ;
}
else
pNewBezier->SetControlPoint( 0, ptCtrlPrev) ;
// ciclo sui punti di controllo intermedi per calcolare quelli nuovi
Point3d ptCtrlCurr ;
double dWcurr ;
for ( double i = 1 ; i < nDeg ; ++i) {
ptCtrlCurr = pCrvBezier->GetControlPoint( int( i)) ;
Point3d ptNew = ( i / nDeg) * ptCtrlPrev + ( 1 - i / ( nDeg)) * ptCtrlCurr ;
if ( bRat) {
dWcurr = pCrvBezier->GetControlWeight( int( i)) ;
double dWnew = ( i / nDeg) * dWprev + ( 1 - i / ( nDeg)) * dWcurr ;
pNewBezier->SetControlPoint( int( i), ptNew, dWnew) ;
dWprev = dWnew ;
}
else
pNewBezier->SetControlPoint( int( i), ptNew) ;
ptCtrlPrev = ptCtrlCurr ;
}
// salvo l'ultimo punto
ptCtrlCurr = pCrvBezier->GetControlPoint( nDeg - 1) ;
if ( bRat ) {
dWcurr = pCrvBezier->GetControlWeight( nDeg - 1) ;
pNewBezier->SetControlPoint( nDeg, ptCtrlCurr, dWcurr) ;
}
else
pNewBezier->SetControlPoint( nDeg, ptCtrlCurr) ;
return Release( pNewBezier) ;
}
ICurveBezier*
BezierDecreaseDegree(const ICurveBezier* pCrvBezier, double dTol)
{
if ( pCrvBezier == nullptr)
return nullptr ;
PtrOwner<ICurveBezier> pNewBezier( CreateCurveBezier()) ;
int nDeg = pCrvBezier->GetDegree() - 1 ;
if ( nDeg == 0)
return nullptr ;
bool bRat = pCrvBezier->IsRational() ;
pNewBezier->Init( nDeg, bRat) ;
// prev e curr sono riferiti alla nuova curva
//salvo il primo punto
Point3d ptCtrlPrev = pCrvBezier->GetControlPoint( 0) ;
double dWprev = 1 ;
if ( bRat ) {
dWprev = pCrvBezier->GetControlWeight( 0) ;
pNewBezier->SetControlPoint( 0, ptCtrlPrev, dWprev) ;
}
else
pNewBezier->SetControlPoint( 0, ptCtrlPrev) ;
// ciclo sui punti intermedi per trovare i nuovi punti di controllo // equazioni da NURBS book
Point3d ptCtrlCurr ;
double dWcurr ;
int r = int (nDeg / 2) ;
// prima metà
for ( double i = 1 ; i < r ; ++i) {
double dAlpha = i / ( nDeg + 1) ;
// old è riferito alla curva originale
Point3d ptOld = pCrvBezier->GetControlPoint( int( i)) ;
ptCtrlCurr = ( ptOld + (- dAlpha * ptCtrlPrev)) / ( 1 - dAlpha) ;
if ( bRat) {
double dWold = pCrvBezier->GetControlWeight( int( i)) ;
dWcurr = ( dWold + (- dAlpha * dWprev)) / ( 1 - dAlpha) ;
pNewBezier->SetControlPoint( int( i), ptCtrlCurr, dWcurr) ;
dWprev = dWcurr ;
}
else
pNewBezier->SetControlPoint( int( i), ptCtrlCurr) ;
ptCtrlPrev = ptCtrlCurr ;
}
// risolvo il punto di mezzo per il caso nDeg pari
if ( ( nDeg + 1) % 2 == 0) { // pari
double dAlpha = r / ( nDeg + 1.) ;
Point3d ptOld = pCrvBezier->GetControlPoint( r) ;
ptCtrlCurr = ( ptOld + (- dAlpha * ptCtrlPrev)) / ( 1 - dAlpha) ;
if ( bRat) {
double dWold = pCrvBezier->GetControlWeight( r) ;
dWcurr = ( dWold + (- dAlpha * dWprev)) / ( 1 - dAlpha) ;
pNewBezier->SetControlPoint( r, ptCtrlCurr, dWcurr) ;
dWprev = dWcurr ;
}
else
pNewBezier->SetControlPoint( r, ptCtrlCurr) ;
}
// salvo l'ultimo punto
ptCtrlCurr = pCrvBezier->GetControlPoint( nDeg + 1) ;
if ( bRat ) {
dWcurr = pCrvBezier->GetControlWeight( nDeg + 1) ;
pNewBezier->SetControlPoint( nDeg, ptCtrlCurr, dWcurr) ;
}
else
pNewBezier->SetControlPoint( nDeg, ptCtrlCurr) ;
ptCtrlPrev = ptCtrlCurr ;
if ( bRat)
dWprev = dWcurr ;
// seconda metà
for ( double i = nDeg - 1 ; i > r ; --i) {
double dAlpha = ( i + 1) / ( nDeg + 1) ;
Point3d ptOld = pCrvBezier->GetControlPoint( int( i) + 1) ;
ptCtrlCurr = ( ptOld + ( - ( 1 - dAlpha) * ptCtrlPrev)) / dAlpha ;
if ( bRat) {
double dWold = pCrvBezier->GetControlWeight( int( i) + 1) ;
dWcurr = ( dWold - ( ( 1 - dAlpha) * dWprev)) / dAlpha ;
pNewBezier->SetControlPoint( int( i), ptCtrlCurr, dWcurr) ;
dWprev = dWcurr ;
}
else
pNewBezier->SetControlPoint( int( i), ptCtrlCurr) ;
ptCtrlPrev = ptCtrlCurr ;
}
// risolvo il punto di mezzo per il caso nDeg dispari
// calcolo anche l'errore di approssimazione
double dErr = 0 ;
if ( (nDeg + 1) % 2 != 0) { // dispari
// puntdo di sinistra
double dAlpha = r / ( nDeg + 1.) ;
ptCtrlPrev = pNewBezier->GetControlPoint( r - 1) ;
Point3d ptOld = pCrvBezier->GetControlPoint( r) ;
Point3d ptL = ( ptOld + ( - dAlpha * ptCtrlPrev)) / ( 1 - dAlpha) ;
double dWL = 1 ;
if ( bRat) {
dWprev = pNewBezier->GetControlWeight( r - 1) ;
double dWold =pCrvBezier->GetControlWeight( r) ;
dWL = ( dWold - ( dAlpha * dWprev)) / ( 1 - dAlpha) ;
}
// punto di destra
dAlpha = ( r + 1.) / ( nDeg + 1.) ;
ptCtrlPrev = pNewBezier->GetControlPoint( r + 1) ;
ptOld = pCrvBezier->GetControlPoint( r + 1) ;
double dWR = 1 ;
if ( bRat) {
dWprev = pNewBezier->GetControlWeight( r + 1) ;
double dWold =pCrvBezier->GetControlWeight( r + 1) ;
dWR = ( dWold - ( ( 1 - dAlpha) * dWprev)) / dAlpha ;
}
Point3d ptR = ( ptOld + ( - ( 1 - dAlpha) * ptCtrlPrev)) / dAlpha ;
// calcolo il punto di mezzo e lo aggiungo
Point3d ptNew = ( ptL + ptR) / 2 ;
double dWnew = ( dWL + dWR) / 2 ;
if ( bRat)
pNewBezier->SetControlPoint( r, ptNew, dWnew) ;
else
pNewBezier->SetControlPoint( r, ptNew) ;
dErr = Dist( ptL, ptR) ;
}
else {
ptCtrlCurr = pNewBezier->GetControlPoint( r + 1) ;
ptCtrlPrev = pNewBezier->GetControlPoint( r) ;
Point3d ptOld = pCrvBezier->GetControlPoint( r + 1) ;
dErr = Dist( ptOld, 0.5 * ( ptCtrlPrev + ptCtrlCurr)) ;
}
//// se l'approssimazione dà un errore troppo alto allora annullo tutto // da controllare
//if ( dErr > dTol)
// return nullptr ;
return Release( pNewBezier) ;
}
//----------------------------------------------------------------------------
ICurve*
CurveToNoArcsCurve( const ICurve* pCrv)
{
// verifico validità curva
if ( pCrv == nullptr)
return nullptr ;
// se arco, devo trasformarlo in curva di Bezier (semplice o composta)
if ( pCrv->GetType() == CRV_ARC) {
return ArcToBezierCurve( pCrv) ;
}
// se curva composita, devo trasformarla in composita senza archi
else if ( pCrv->GetType() == CRV_COMPO) {
PtrOwner<CurveComposite> pCopyCC( GetBasicCurveComposite( pCrv->Clone())) ;
if ( IsNull( pCopyCC) || ! pCopyCC->ArcsToBezierCurves())
return nullptr ;
return Release( pCopyCC) ;
}
// altrimenti devo solo copiarla
else {
return pCrv->Clone() ;
}
}
//----------------------------------------------------------------------------
ICurve*
CurveToArcsPerpExtrCurve( const ICurve* pCrv, double dLinTol, double dAngTolDeg)
{
// verifico validità curva
if ( pCrv == nullptr)
return nullptr ;
// se arco in piano non perpendicolare ad estrusione, curva composita o curva di Bezier trasformo
if ( ( pCrv->GetType() == CRV_ARC && ! GetBasicCurveArc(pCrv)->IsInPlanePerpExtr()) ||
pCrv->GetType() == CRV_COMPO ||
pCrv->GetType() == CRV_BEZIER) {
// creo un poliarco
PolyArc PA ;
if ( ! pCrv->ApproxWithArcs( dLinTol, dAngTolDeg, PA))
return nullptr ;
// creo una nuova curva composita a partire dal poliarco
PtrOwner<CurveComposite> pCopyCC( CreateBasicCurveComposite()) ;
if ( IsNull( pCopyCC))
return nullptr ;
if ( ! pCopyCC->FromPolyArc( PA))
return nullptr ;
// copio estrusione e spessore
CopyExtrusion( pCrv, pCopyCC) ;
CopyThickness( pCrv, pCopyCC) ;
return Release( pCopyCC) ;
}
// altrimenti devo solo copiarla
else {
return pCrv->Clone() ;
}
}
//----------------------------------------------------------------------------
bool
NurbsCurveCanonicalize( CNurbsData& cnData)
{
// se con nodi extra
if ( cnData.bExtraKnotes) {
int nKnotesNbr = int( cnData.vU.size()) ;
if ( nKnotesNbr < 4)
return false ;
cnData.bExtraKnotes = false ;
for ( int i = 0 ; i < nKnotesNbr - 2 ; ++ i)
cnData.vU[i] = cnData.vU[i+1] ;
cnData.vU.resize( nKnotesNbr - 2) ;
}
// se periodica
if ( cnData.bPeriodic || ! cnData.bClamped) {
// se la curva è peridica verifco che effettivamente ci sia un numero di punti ripetituti uguale al grado della curva
// wrap della curva su se stessa
if ( cnData.bPeriodic ) {
bool bRepetead = true ;
for ( int i = 0 ; i < cnData.nDeg ; ++i) {
if ( ! AreSamePointApprox( cnData.vCP[i], cnData.vCP.end()[-cnData.nDeg + i]) ) {
bRepetead = false ;
break ;
}
}
if ( ! bRepetead){
// salvo il vettore dei nodi in caso mi accorga di avere tra le mani una curva unclamped
DBLVECTOR vU = cnData.vU ;
// se il primo e l'ultimo punto non coincidono allora aggiungo il primo punto in fondo al vettore dei punti di controllo
if ( ! AreSamePointApprox( cnData.vCP[0], cnData.vCP.back()) ) {
cnData.vCP.push_back( cnData.vCP[0]) ;
if ( cnData.bRat)
cnData.vW.push_back( cnData.vW[0]) ;
}
// se effettivamente ho dei nodi in più da togliere allora li tolgo
if ( int( cnData.vU.size()) != int(cnData.vCP.size()) + cnData.nDeg - 1) {
// devo poi anche togliere i nodi di troppo // presuppongo che la convenzione sia che i nodi di troppo sono alla fine del vettore dei nodi
cnData.vU = DBLVECTOR( cnData.vU.begin(), cnData.vU.end() - cnData.nDeg) ;
// controllo eventualmente anche i nodi extra
// se ne ho due in più ne tolgo uno in cima e uno in fondo
if ( cnData.vU.size() == int( cnData.vCP.size()) + cnData.nDeg + 1 ) // significa che ci sono due nodi extra, uno all'inizio e uno alla fine, da togliere
cnData.vU = vector<double>( cnData.vU.begin() + 1, cnData.vU.end() - 1) ;
// se ne ho solo uno in più lo tolgo in cima
else if ( cnData.vU.size() == int( cnData.vCP.size()) + cnData.nDeg)
cnData.vU = vector<double>( cnData.vU.begin() + 1, cnData.vU.end()) ;
}
// controllo se il vettore dei nodi ha la giusta molteplicità all'inizio e alla fine, sennò ha comunque bisogno di essere resa non periodica
double dU0 = cnData.vU[0] ;
double dULast = cnData.vU.back() ;
bool bSame = true ;
for ( int i = 1 ; i < cnData.nDeg ; ++i ) {
bSame = bSame && abs(cnData.vU[i] - dU0) < EPS_SMALL ;
bSame = bSame && abs(cnData.vU.end()[-( i+ 1)] - dULast) < EPS_SMALL ;
}
if ( bSame) {
cnData.bPeriodic = false ;
return true ;
}
else {
// aggiungo i punti ripetuti ( il primo l'ho già aggiunto)
for ( int i = 1 ; i < cnData.nDeg ; ++i ) {
cnData.vCP.push_back( cnData.vCP[i]) ;
if ( cnData.bRat)
cnData.vW.push_back( cnData.vW[i]) ;
}
// recupero il vettore dei nodi
cnData.vU = vU ;
// verifico se ho nodi extra
// se ne ho due in più ne tolgo uno in cima e uno in fondo
if ( cnData.vU.size() == int( cnData.vCP.size()) + cnData.nDeg + 1 ) // significa che ci sono due nodi extra, uno all'inizio e uno alla fine, da togliere
cnData.vU = vector<double>( cnData.vU.begin() + 1, cnData.vU.end() - 1) ;
// se ne ho solo uno in più lo tolgo in cima
else if ( cnData.vU.size() == int( cnData.vCP.size()) + cnData.nDeg)
cnData.vU = vector<double>( cnData.vU.begin() + 1, cnData.vU.end()) ;
}
}
}
// qui aggiungo un controllo se la curva è collassata in un punto ( ho un polo), lascio stare
bool bCollapsed = true ;
Point3d ptFirst = cnData.vCP.front() ;
for( int i = 1 ; i < int( cnData.vCP.size()) ; ++i) {
if ( ! AreSamePointApprox( ptFirst, cnData.vCP[i])) {
bCollapsed = false ;
break ;
}
}
if ( bCollapsed)
return false ;
// va trasformata in non-periodica (clamped)
// bisogna aumentare la molteplicità dei nodi u_p-1 e u_(m-p+1) fino ad arrivare al grado della nurbs
// e poi scartare nodi e punti fuori dalla regione clamped ( al di fuori della regione u_p-1 -> u_(m-p+1))
// l'agoritmo per l'inserimento dei nodi l' A5.1 del libro delle Nurbs ( Piegl e Tiller), con qualche modifica
// agli indici perché uso u_p-1 e u_(m-p+1), anziché u_p e u_m-p
// comincio ad aumentare la molteplictià del nodo u_m-p+1
int nCP = int( cnData.vCP.size()) ;
int nU = nCP + cnData.nDeg - 1 ;
int nDeg = cnData.nDeg ;
PNTVECTOR vBC ;
vBC.resize( nDeg + 1) ;
DBLVECTOR vBW ;
vBW.resize( nDeg + 1) ;
// trovo il nodo di cui aumentare la molteplicità e ne calcolo la molteplicità
int b = nU - nDeg - 1 + 1 ;
int i = b ;
while ( b > 0 && abs( cnData.vU[b] - cnData.vU[b - 1]) < EPS_ZERO)
-- b ;
int mult = min( i - b + 1, nDeg) ; // mi aspetto che sia 1, ma comunque sarà < nDeg
// recupero i punti da modificare
if ( ! cnData.bRat) {
for ( int i = 0 ; i <= nDeg - mult ; ++ i)
vBC[i] = cnData.vCP[b - nDeg + 1 + i] ;
}
else {
for ( int i = 0 ; i <= nDeg - mult ; ++ i) {
vBC[i] = cnData.vCP[b - nDeg + 1 + i] * cnData.vW[b - nDeg + 1 + i] ;
vBW[i] = cnData.vW[b - nDeg + 1 + i] ;
}
}
// salvo i punti inalterati
int r = nDeg - mult ; // numero di volte che dovrò inserire il nodo
cnData.vCP.resize( nCP + r) ;
for ( int p = nCP - 1 ; p > b - mult ; --p) {
cnData.vCP[r + p] = cnData.vCP[p] ;
}
if ( cnData.bRat ) {
cnData.vW.resize( nCP + r) ;
for ( int p = nCP - 1 ; p > b - mult ; --p) {
cnData.vW[r + p] = cnData.vW[p] ;
}
}
// procedo all'inserimento
int L = 0 ;
double alpha ;
if ( mult < nDeg) {
// inserisco il nodo r volte
for ( int j = 1 ; j <= r ; ++ j) {
L = b - nDeg + j ;
for ( int i = 0; i <= r - j ; ++i) {
alpha = (cnData.vU[b] - cnData.vU[L + i])/ ( cnData.vU[i + b + 1] - cnData.vU[L + i]) ;
vBC[i] = alpha * vBC[i +1 ] + ( 1 - alpha) * vBC[i] ;
if ( cnData.bRat) {
vBW[i] = alpha * vBW[i + 1] + ( 1 - alpha) * vBW[i] ;
}
}
cnData.vCP[L + 1] = vBC[0] ;
cnData.vCP[b + nDeg - j - mult] = vBC[r - j] ;
if ( cnData.bRat ) {
cnData.vW[L + 1] = vBW[0] ;
cnData.vW[b + nDeg - j - mult] = vBW[r-j] ;
}
}
}
// allungo il vettore dei nodi e sposto gli ultimi nodi
cnData.vU.resize( nU + r) ;
for ( int p = nU - 1 ; p > b ; --p)
cnData.vU[p + r] = cnData.vU[p] ;
// aggiungo i nodi nuovi
for ( int p = 0 ; p < r ; ++p)
cnData.vU[b + 1 + p] = cnData.vU[b] ;
nU = nU + r ;
nCP = nCP + r ;
// aumento la molteplicità del punto u_p-1
b = nDeg - 1 ;
i = b ;
while ( b > 0 && abs( cnData.vU[b] - cnData.vU[b - 1]) < EPS_ZERO)
-- b ;
mult = min( i - b + 1, nDeg) ; // mi aspetto che sia 1, ma comunque sarà < cnData.nDeg
// recupero i punti da modificare
if ( ! cnData.bRat) {
for ( int i = 0 ; i <= nDeg - mult ; ++ i)
vBC[i] = cnData.vCP[i] ;
}
else {
for ( int i = 0 ; i <= nDeg - mult ; ++ i) {
vBC[i] = cnData.vCP[i] * cnData.vW[i] ;
vBW[i] = cnData.vW[i] ;
}
}
r = nDeg - mult ;
// salvo i punti inalterati
cnData.vCP.resize( nCP + r) ;
for ( int p = nCP - 1 ; p > b - mult ; --p) {
cnData.vCP[r + p] = cnData.vCP[p] ;
}
if ( cnData.bRat ) {
cnData.vW.resize( nCP + r) ;
for ( int p = nCP - 1 ; p > b - mult ; --p) {
cnData.vW[r + p] = cnData.vW[p] ;
}
}
// procedo all'inserimento
L = 0 ;
if ( mult < nDeg) {
// inserisco il nodo r volte
for ( int j = 1 ; j <= r ; ++ j) {
L = b - nDeg + j ;
for ( int i = 0; i <= r - j ; ++i) {
alpha = (cnData.vU[b] - cnData.vU[L + i])/ ( cnData.vU[i + b + 1] - cnData.vU[L + i]) ;
vBC[i] = alpha * vBC[i + 1] + ( 1 - alpha) * vBC[i] ;
if ( cnData.bRat) {
vBW[i] = alpha * vBW[i + 1] + ( 1 - alpha) * vBW[i] ;
}
}
cnData.vCP[L + 1] = vBC[0] ;
cnData.vCP[b + nDeg - j - mult] = vBC[r - j] ;
if ( cnData.bRat ) {
cnData.vW[L + 1] = vBW[0] ;
cnData.vW[b + nDeg - j - mult] = vBW[r - j] ;
}
}
}
// allungo il vettore dei nodi e sposto gli ultimi nodi
cnData.vU.resize(nU + r) ;
for ( int p = nU - 1 ; p > b ; --p)
cnData.vU[p+r] = cnData.vU[p] ;
// aggiungo i nodi nuovi
for ( int p = 0 ; p < r ; ++p)
cnData.vU[b + 1 + p] = cnData.vU[b] ;
nU = nU + r ;
nCP = nCP + r ;
// rendo la curva chiusa e non periodica eliminando i primi e gli ultimi nDeg punti e nodi
cnData.bPeriodic = false ;
nCP = nCP - 2 * ( nDeg - 1);
nU = nU - 2 * ( nDeg - 1);
PNTVECTOR vCP_clamped ;
vCP_clamped.resize( nCP) ;
DBLVECTOR vU_clamped ;
vU_clamped.resize( nU) ;
for ( int i = 0 ; i < nCP ; ++i) {
if ( ! cnData.bRat)
vCP_clamped[i] = cnData.vCP[i + nDeg - 1] ;
else
vCP_clamped[i] = cnData.vCP[i + nDeg - 1] / cnData.vW[i + nDeg - 1] ;
}
cnData.vCP = vCP_clamped ;
for ( int i = 0 ; i < nU ; ++i) {
vU_clamped[i] = cnData.vU[i + nDeg - 1] ;
}
cnData.vU = vU_clamped ;
}
return true ;
}
//----------------------------------------------------------------------------
ICurve*
NurbsToBezierCurve( const CNurbsData& cnData)
{
// la curva Nurbs deve essere in forma canonica
if ( cnData.bPeriodic || cnData.bExtraKnotes)
return nullptr ;
// numero dei nodi
int nU = int( cnData.vCP.size()) + cnData.nDeg - 1 ;
// controllo relazione nodi - punti di controllo
if ( nU != int( cnData.vU.size()))
return nullptr ;
// numero degli intervalli
int nInt = nU - 2 * cnData.nDeg + 1 ;
// verifico le condizioni agli estremi sui nodi (i primi nDeg nodi e gli ultimi nDeg nodi devono essere uguali tra loro)
bool bOk = true ;
for ( int i = 1 ; i < cnData.nDeg ; ++ i) {
if ( abs( cnData.vU[i] - cnData.vU[0]) >= EPS_ZERO)
bOk = false ;
}
for ( int i = 1 ; i < cnData.nDeg ; ++ i) {
if ( abs( cnData.vU[nU - 1 - i] - cnData.vU[nU - 1]) >= EPS_ZERO)
bOk = false ;
}
if ( ! bOk)
return nullptr ;
// se 1 solo intervallo, la Nurbs è già una curva di Bezier
if ( nInt == 1) {
// creo la curva di Bezier
PtrOwner<CurveBezier> pCrvBez( CreateBasicCurveBezier()) ;
if ( IsNull( pCrvBez))
return nullptr ;
// la inizializzo
if ( ! pCrvBez->Init( cnData.nDeg, cnData.bRat))
return nullptr ;
for ( int i = 0 ; i <= cnData.nDeg ; ++ i) {
if ( ! cnData.bRat) {
if ( ! pCrvBez->SetControlPoint( i, cnData.vCP[i]))
return nullptr ;
}
else {
if ( ! pCrvBez->SetControlPoint( i, cnData.vCP[i], cnData.vW[i]))
return nullptr ;
}
}
// se non è una curva ma un punto, la invalido
if ( pCrvBez->IsAPoint())
pCrvBez->Init( cnData.nDeg, cnData.bRat) ;
// restituisco la curva
return Release( pCrvBez) ;
}
// altrimenti è equivalente ad una curva composita, la creo
PtrOwner<CurveComposite> pCrvCompo( CreateBasicCurveComposite()) ;
if ( IsNull( pCrvCompo))
return nullptr ;
// vettore dei punti di controllo della curva di Bezier
PNTVECTOR vBC ;
vBC.resize( cnData.nDeg + 1) ;
DBLVECTOR vBW ;
vBW.resize( cnData.nDeg + 1) ;
if ( ! cnData.bRat) {
for ( int i = 0 ; i <= cnData.nDeg ; ++ i)
vBC[i] = cnData.vCP[i] ;
}
else {
for ( int i = 0 ; i <= cnData.nDeg ; ++ i) {
vBC[i] = cnData.vCP[i] * cnData.vW[i] ;
vBW[i] = cnData.vW[i] ;
}
}
// primi coefficienti della successiva
PNTVECTOR vNextBC ;
vNextBC.resize( cnData.nDeg - 1) ;
DBLVECTOR vNextBW ;
vNextBW.resize( cnData.nDeg - 1) ;
// ...
DBLVECTOR vAlfa ;
vAlfa.resize( cnData.nDeg - 1) ;
int a = cnData.nDeg - 1 ;
int b = cnData.nDeg ;
bool bPrevRejected = false ;
// ciclo
while ( b < nU - 1) {
int i = b ;
while ( b < nU - 1 && abs( cnData.vU[b+1] - cnData.vU[b]) < EPS_ZERO)
++ b ;
int mult = min( b - i + 1, cnData.nDeg) ;
if ( mult < cnData.nDeg) {
// numeratore di alfa
double numer = cnData.vU[b] - cnData.vU[a] ;
// calcola e salva gli alfa
for ( int j = cnData.nDeg ; j > mult ; -- j)
vAlfa[j-mult-1] = numer / ( cnData.vU[a+j] - cnData.vU[a]) ;
// inserisco il nodo r volte
int r = cnData.nDeg - mult ;
for ( int j = 1 ; j <= r ; ++ j) {
int save = r - j ;
int s = mult + j ;
for ( int k = cnData.nDeg ; k >= s ; -- k)
vBC[k] = vAlfa[k-s] * vBC[k] + ( 1 - vAlfa[k-s]) * vBC[k-1] ;
if ( cnData.bRat) {
for ( int k = cnData.nDeg ; k >= s ; -- k)
vBW[k] = vAlfa[k-s] * vBW[k] + ( 1 - vAlfa[k-s]) * vBW[k-1] ;
}
if ( b < nU - 1) {
vNextBC[save] = vBC[cnData.nDeg] ;
if ( cnData.bRat)
vNextBW[save] = vBW[cnData.nDeg] ;
}
}
}
// costruisco la curva di Bezier e la inserisco nella curva composita
PtrOwner<CurveBezier> pCrvBez( CreateBasicCurveBezier()) ;
if ( IsNull( pCrvBez))
return nullptr ;
// se precedente saltata
if ( bPrevRejected) {
// prendo l'ultimo punto della curva composita per garantire la continuità
Point3d ptEnd ;
if ( pCrvCompo->GetEndPoint( ptEnd))
vBC[0] = ptEnd ;
}
// la inizializzo
if ( ! pCrvBez->Init( cnData.nDeg, cnData.bRat))
return nullptr ;
if ( ! cnData.bRat) {
for ( int i = 0 ; i <= cnData.nDeg ; ++ i) {
if ( ! pCrvBez->SetControlPoint( i, vBC[i]))
return nullptr ;
}
}
else {
for ( int i = 0 ; i <= cnData.nDeg ; ++ i) {
if ( ! pCrvBez->SetControlPoint( i, vBC[i] / vBW[i], vBW[i]))
return nullptr ;
}
}
// se è una vera curva, la aggiungo alla curva composita
if ( ! pCrvBez->IsAPoint()) {
if ( ! pCrvCompo->AddCurve( Release( pCrvBez)))
return nullptr ;
bPrevRejected = false ;
}
// altrimenti è un punto, la cancello
else {
pCrvBez.Reset() ;
bPrevRejected = true ;
}
// inizializzazioni per la prossima curva di Bezier
if ( b < nU - 1) {
if ( ! cnData.bRat) {
for ( int i = 0 ; i < cnData.nDeg - 1 ; ++ i)
vBC[i] = vNextBC[i] ;
for ( int i = cnData.nDeg - mult ; i <= cnData.nDeg ; ++ i)
vBC[i] = cnData.vCP[b-cnData.nDeg+i+1] ;
}
else {
for ( int i = 0 ; i < cnData.nDeg - 1 ; ++ i) {
vBC[i] = vNextBC[i] ;
vBW[i] = vNextBW[i] ;
}
for ( int i = cnData.nDeg - mult ; i <= cnData.nDeg ; ++ i) {
vBC[i] = cnData.vCP[b-cnData.nDeg+i+1] * cnData.vW[b-cnData.nDeg+i+1] ;
vBW[i] = cnData.vW[b-cnData.nDeg+i+1] ;
}
}
a = b ;
++ b ;
}
}
// se la curva ha grado 1, manca da aggiungere l'ultimo tratto
if ( cnData.nDeg == 1 ) {
// costruisco la curva di Bezier e la inserisco nella curva composita
PtrOwner<CurveBezier> pCrvBez( CreateBasicCurveBezier()) ;
if ( ! pCrvBez->Init( cnData.nDeg, cnData.bRat))
return nullptr ;
if ( ! cnData.bRat) {
for ( int i = 0 ; i <= cnData.nDeg ; ++ i) {
if ( ! pCrvBez->SetControlPoint( i, vBC[i]))
return nullptr ;
}
}
else {
for ( int i = 0 ; i <= cnData.nDeg ; ++ i) {
if ( ! pCrvBez->SetControlPoint( i, vBC[i] / vBW[i], vBW[i]))
return nullptr ;
}
}
if ( ! pCrvBez->IsAPoint()) {
if ( ! pCrvCompo->AddCurve( Release( pCrvBez)))
return nullptr ;
}
}
// restituisco la curva composita
return Release( pCrvCompo) ;
}
//----------------------------------------------------------------------------
ICurve*
FlattenCurve( const ICurve& crCrv, double dToler, double dAngToler, int nFlag)
{
// Determino il piano medio della curva e verifico se scostamento accettabile
Plane3d plMid ;
if ( ! crCrv.IsFlat( plMid, ( nFlag == FLTCRV_USE_EXTR), dToler))
return nullptr ;
// Recupero estrusione rispetto alla normale al piano medio
Vector3d vtExtr ; crCrv.GetExtrusion( vtExtr) ;
if ( nFlag == FLTCRV_USE_EXTR)
plMid.Set( plMid.GetPoint(), vtExtr) ;
// Determino il suo centro geometrico
Point3d ptCen ;
if ( ! crCrv.GetCentroid( ptCen))
return nullptr ;
// Verifico se curva già piatta
PolyLine PL ;
if ( ! crCrv.ApproxWithLines( LIN_TOL_FINE, ANG_TOL_STD_DEG, ICurve::APL_SPECIAL_INT, PL))
return nullptr ;
bool bFlat = true ;
Plane3d plFlat ; plFlat.Set( ptCen, plMid.GetVersN()) ;
Point3d ptP ;
bool bPoint = PL.GetFirstPoint( ptP) ;
while ( bPoint && bFlat) {
if ( DistPointPlane( ptP, plFlat) > 0.1 * EPS_SMALL)
bFlat = false ;
bPoint = PL.GetNextPoint( ptP) ;
}
// Se curva già piatta, la copio ed esco
if ( bFlat) {
PtrOwner<ICurve> pCrv( crCrv.Clone()) ;
if ( IsNull( pCrv))
return nullptr ;
// assegno estrusione
if ( nFlag != FLTCRV_SET_EXTR)
pCrv->SetExtrusion( vtExtr) ;
else {
if ( vtExtr * plMid.GetVersN() >= 0)
pCrv->SetExtrusion( plMid.GetVersN()) ;
else
pCrv->SetExtrusion( - plMid.GetVersN()) ;
}
// restituisco la copia della curva
return Release( pCrv) ;
}
// altrimenti la appiattisco
else {
// mi assicuro che la curva non contenga archi
PtrOwner<ICurve> pCrv( CurveToNoArcsCurve( &crCrv)) ;
if ( IsNull( pCrv))
return nullptr ;
// calcolo un riferimento con piano XY coincidente con il piano di proiezione
Frame3d frRef ;
if ( ! frRef.Set( ptCen, plMid.GetVersN()))
return nullptr ;
// eseguo scalatura con fattori X e Y unitari e fattore Z nullo
if ( ! pCrv->Scale( frRef, 1, 1, 0))
return nullptr ;
// assegno estrusione
if ( nFlag != FLTCRV_SET_EXTR)
pCrv->SetExtrusion( vtExtr) ;
else {
if ( vtExtr * plMid.GetVersN() >= 0)
pCrv->SetExtrusion( plMid.GetVersN()) ;
else
pCrv->SetExtrusion( - plMid.GetVersN()) ;
}
// restituisco la nuova curva
return Release( pCrv) ;
}
}
//----------------------------------------------------------------------------
ICurve*
ProjectCurveOnPlane( const ICurve& crCrv, const Plane3d& plPlane)
{
// determino se curva piana e suo eventuale piano
Plane3d plCrv ;
if ( crCrv.IsFlat( plCrv, false, EPS_SMALL / 2)) {
// se il piano della curva è parallelo a quello di proiezione
if ( AreSameOrOppositeVectorExact( plCrv.GetVersN(), plPlane.GetVersN())) {
// copio la curva
PtrOwner<ICurve> pCrv( crCrv.Clone()) ;
if ( IsNull( pCrv))
return nullptr ;
// se non coincidenti, basta eseguire una traslazione
Point3d ptOC = ORIG + plCrv.GetDist() * plCrv.GetVersN() ;
Point3d ptOP = ORIG + plPlane.GetDist() * plPlane.GetVersN() ;
if ( ! AreSamePointApprox( ptOC, ptOP))
pCrv->Translate( ptOP - ptOC) ;
// restituisco la nuova curva
return Release( pCrv) ;
}
}
// mi assicuro che la curva non contenga archi
PtrOwner<ICurve> pCrv( CurveToNoArcsCurve( &crCrv)) ;
if ( IsNull( pCrv))
return nullptr ;
// calcolo un riferimento con piano XY coincidente con il piano di proiezione
Frame3d frRef ;
if ( ! frRef.Set( ORIG + plPlane.GetDist() * plPlane.GetVersN(), plPlane.GetVersN()))
return nullptr ;
// eseguo scalatura con fattori X e Y unitari e fattore Z nullo
if ( ! pCrv->Scale( frRef, 1, 1, 0))
return nullptr ;
// restituisco la nuova curva
return Release( pCrv) ;
}
//----------------------------------------------------------------------------
bool
AdjustCurveSlope( ICurveComposite* pCrv, double dNini, double dNfin)
{
// verifico curva
if ( pCrv == nullptr)
return false ;
// determino versore estrusione
Vector3d vtN ;
if ( ! pCrv->GetExtrusion( vtN) || vtN.IsSmall())
vtN = Z_AX ;
// assegno la corretta pendenza
int i = 0 ;
double dCurrLen = 0 ;
double dLen ; pCrv->GetLength( dLen) ;
const ICurve* pSCrv = pCrv->GetFirstCurve() ;
while ( pSCrv != nullptr) {
double dCrvLen ;
pSCrv->GetLength( dCrvLen) ;
double dCoeff = dCurrLen / dLen ;
double dCurrN = dNini * ( 1.0 - dCoeff) + dNfin * dCoeff ;
Point3d ptJoin ;
pSCrv->GetStartPoint( ptJoin) ;
pCrv->ModifyJoint( i, ptJoin + vtN * ( dCurrN - ( ptJoin - ORIG) * vtN)) ;
// passo al successivo
dCurrLen += dCrvLen ;
pSCrv = pCrv->GetNextCurve() ;
++ i ;
}
Point3d ptFin ; pCrv->GetEndPoint( ptFin) ;
ptFin += vtN * ( dNfin - ( ptFin - ORIG) * vtN) ;
pCrv->ModifyEnd( ptFin) ;
return true ;
}
//----------------------------------------------------------------------------
Voronoi*
GetCurveVoronoi( const ICurve& crvC)
{
switch ( crvC.GetType()) {
case CRV_LINE : return GetBasicCurveLine( &crvC)->GetVoronoiObject() ;
case CRV_ARC : return GetBasicCurveArc( &crvC)->GetVoronoiObject() ;
case CRV_BEZIER : return GetBasicCurveBezier( &crvC)->GetVoronoiObject() ;
case CRV_COMPO : return GetBasicCurveComposite( &crvC)->GetVoronoiObject() ;
}
return nullptr ;
}
//----------------------------------------------------------------------------
bool
CalcCurveVoronoiDiagram( const ICurve& crvC, ICURVEPOVECTOR& vCrvs, int nBound)
{
Voronoi* pVoronoiObj = GetCurveVoronoi( crvC) ;
if ( pVoronoiObj == nullptr)
return false ;
return pVoronoiObj->CalcVoronoiDiagram( vCrvs, nBound) ;
}
//----------------------------------------------------------------------------
bool
CalcCurveMedialAxis( const ICurve& crvC, ICURVEPOVECTOR& vCrvs, int nSide)
{
Voronoi* pVoronoiObj = GetCurveVoronoi( crvC) ;
if ( pVoronoiObj == nullptr)
return false ;
return pVoronoiObj->CalcMedialAxis( vCrvs, nSide) ;
}
//----------------------------------------------------------------------------
bool
CalcCurveFatCurve( const ICurve& crvC, ICURVEPOVECTOR& vCrvs, double dRadius, bool bSquareEnds, bool bSquareMids)
{
Voronoi* pVoronoiObj = GetCurveVoronoi( crvC) ;
if ( pVoronoiObj == nullptr)
return false ;
return pVoronoiObj->CalcFatCurve( vCrvs, dRadius, bSquareEnds, bSquareMids) ;
}
//----------------------------------------------------------------------------
void
ResetCurveVoronoi( const ICurve& crvC)
{
switch ( crvC.GetType()) {
case CRV_LINE :
GetBasicCurveLine( &crvC)->ResetVoronoiObject() ;
break ;
case CRV_ARC :
GetBasicCurveArc( &crvC)->ResetVoronoiObject() ;
break ;
case CRV_BEZIER :
GetBasicCurveBezier( &crvC)->ResetVoronoiObject() ;
break ;
case CRV_COMPO :
GetBasicCurveComposite( &crvC)->ResetVoronoiObject() ;
break ;
}
}