Files
EgtGeomKernel/CurveAux.cpp
T
Dario Sassi 3e8e7e2e2a EgtGeomKernel 1.5l1 :
- aggiornamento a VS2013
- migliorato SimpleOffset e implementato anche per CurveComposite
- il lato di offset ora viene dal segno dello spostamento ( + a destra, - a sinistra)
- il vettore estrusione ora è la normale al piano di offset (se non c'è uso Z+)
- aggiunto a tutte le entità geometriche membro m_nTempProp intero temporaneo
- migliorata DistPointCrvBezier e DistPointArc
- corretta IntersLineArc con linee che non giacciono nel piano XY
- corretta ModifyStart di CurveArc
- a PolyArc aggiunto metodo ParamLinearTransform
- aggiunta gestione riferimento di griglia (CPlane).
2014-12-17 15:03:29 +00:00

599 lines
20 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 "/EgtDev/Include/EGkStringUtils3d.h"
#include "/EgtDev/Include/EgtPointerOwner.h"
using namespace std ;
//----------------------------------------------------------------------------
bool
IsClosed( const ICurve& crvC)
{
Point3d ptStart ;
Point3d 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 ( fabs( 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 ( fabs( dPar - dEnd) > EPS_PARAM)
return false ;
return true ;
}
//----------------------------------------------------------------------------
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 = 10 * 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
CurveDump( const ICurve& crvC, string& sOut, const char* szNewLine)
{
// verifico validità curva
if ( ! crvC.IsValid())
return false ;
// punti iniziale e finale
Point3d ptPS ;
crvC.GetStartPoint( ptPS) ;
sOut += "PS(" + ToString( ptPS, 3) + ")" + szNewLine ;
Point3d ptPE ;
crvC.GetEndPoint( ptPE) ;
sOut += "PE(" + ToString( ptPE, 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( dThick) + szNewLine ;
}
// lunghezza
double dLen = 0 ;
crvC.GetLength( dLen) ;
sOut += "Len=" + ToString( dLen,3) + szNewLine ;
// altri dati per curva chiusa
if ( crvC.IsClosed()) {
PolyLine PL ;
crvC.ApproxWithLines( LIN_TOL_STD, ANG_TOL_STD_DEG, PL) ;
double dAreaXY = 0 ;
PL.GetAreaXY( dAreaXY) ;
bool bCCW = ( dAreaXY > 0) ;
sOut += string( "Closed") + ( bCCW ? " CCW" : " CW") + " AreaXY=" + ToString( fabs( dAreaXY),1) + 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 ;
}
//----------------------------------------------------------------------------
ICurve*
ArcToBezierCurve( const ICurve* pCrv)
{
// 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 ( fabs( pArc->GetAngCenter()) < BEZARC_ANG_CEN_MAX + EPS_ANG_SMALL) {
// 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( fabs( pArc->GetAngCenter()) / BEZARC_ANG_CEN_MAX) ;
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 false ;
}
// copio estrusione e spessore
CopyExtrusion( pArc, Get( pCrvCompo)) ;
CopyThickness( pArc, Get( pCrvCompo)) ;
// restituisco la curva
return Release( pCrvCompo) ;
}
}
//----------------------------------------------------------------------------
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 o curva di Bezier trasformo
if ( ( pCrv->GetType() == CRV_ARC && ! GetBasicCurveArc(pCrv)->IsInPlanePerpExtr()) ||
pCrv->GetType() == CRV_BEZ) {
// 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, Get( pCopyCC)) ;
CopyThickness( pCrv, Get( pCopyCC)) ;
return Release( pCopyCC) ;
}
// altrimenti devo solo copiarla
else {
return pCrv->Clone() ;
}
}
//----------------------------------------------------------------------------
bool
NurbsCurveCanonicalize( CNurbsData& cnData)
{
// se periodica
if ( cnData.bPeriodic) {
// va trasformata in non-periodica (clamped)
// vedere The NurbsBook di Les Piegl e Tiller
// mancano esempi per testare
return false ;
}
// 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) ;
return true ;
}
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 ;
// se 1 solo intervallo, la Nurbs è già una curva di Bezier
if ( nInt == 1) {
// creo la curva di Bezier
PtrOwner<ICurveBezier> pCrvBez( CreateCurveBezier()) ;
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<ICurveComposite> pCrvCompo( CreateCurveComposite()) ;
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 && fabs( 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<ICurveBezier> pCrvBez( CreateCurveBezier()) ;
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 ;
}
}
// restituisco la curva composita
return Release( pCrvCompo) ;
}