EgtGeomKernel :

- correzione di bug nel trim di sup di Bezier chiuse
- correzione di bug nella conversione di NURBS in sup di Bezier
This commit is contained in:
Daniele Bariletti
2023-09-12 14:49:43 +02:00
parent 69166fe585
commit 419d325409
5 changed files with 190 additions and 176 deletions
+163 -44
View File
@@ -29,7 +29,8 @@
//----------------------------------------------------------------------------
Cell::Cell( void)
: m_nId( -1),m_nTop ( -2), m_nBottom( -2), m_nLeft( -2), m_nRight ( -2), m_nParent( -2), m_nDepth( 0),
m_nChild1( -2), m_nChild2( -2), m_nFlag( -1), m_nFlag2( 0), m_nRightEdgeIn( -1), m_ptPbl( ORIG), m_ptPtr(), m_bProcessed( false), m_bSplitVert( true)
m_nChild1( -2), m_nChild2( -2), m_nFlag( -1), m_nFlag2( 0), m_nRightEdgeIn( -1), m_bOnLeftEdge( false),
m_ptPbl( ORIG), m_ptPtr(), m_bProcessed( false), m_bSplitVert( true)
{
Point3d ptTr ( 1 * SBZ_TREG_COEFF, 1 * SBZ_TREG_COEFF) ;
m_ptPtr = ptTr ;
@@ -39,7 +40,8 @@ Cell::Cell( void)
//----------------------------------------------------------------------------
Cell::Cell( const Point3d& ptBL, const Point3d& ptTR)
: m_nId( -1),m_nTop ( -2), m_nBottom( -2), m_nLeft( -2), m_nRight ( -2), m_nParent( -2), m_nDepth( 0),
m_nChild1( -2), m_nChild2( -2), m_nFlag( -1), m_nFlag2( 0), m_nRightEdgeIn( -1), m_ptPbl( ptBL), m_ptPtr( ptTR), m_bProcessed( false), m_bSplitVert( true)
m_nChild1( -2), m_nChild2( -2), m_nFlag( -1), m_nFlag2( 0), m_nRightEdgeIn( -1), m_bOnLeftEdge( false),
m_ptPbl( ptBL), m_ptPtr( ptTR), m_bProcessed( false), m_bSplitVert( true)
{}
@@ -200,8 +202,18 @@ Tree::SetSurf( const SurfBezier* pSrfBz, const bool bSplitPatches, const Point3d
m_mVert.insert( std::pair<int, PNTVECTOR>( -1, vVert)) ;
// se richiesto divido preliminarmente le patches
m_vnParents.clear() ;
if ( bSplitPatches && ( nSpanU > 1 || nSpanV > 1)) {
if ( m_bSplitPatches && ( nSpanU > 1 || nSpanV > 1)) {
int nId = -1 ;
// se la superficie è chiusa lungo il parametro U, sistemo le adiacenze al bordo
if ( AreSamePointApprox(ptP00, ptP10) && AreSamePointApprox(ptP01, ptP11) ) {
m_mTree[-1].m_nLeft = -1 ;
m_mTree[-1].m_nRight = -1 ;
}
// se la superficie è chiusa lungo il parametro V, sistemo le adiacenze al bordo
if ( ( AreSamePointApprox(ptP00, ptP01) && AreSamePointApprox(ptP10, ptP11) ) ) {
m_mTree[-1].m_nTop = -1 ;
m_mTree[-1].m_nBottom = -1 ;
}
for ( int i = 1 ; i < nSpanU ; ++i) {
if ( i * SBZ_TREG_COEFF > ptMin.x && i * SBZ_TREG_COEFF < ptTop.x){
m_mTree[nId].SetSplitDirVert( true) ;
@@ -225,17 +237,20 @@ Tree::SetSurf( const SurfBezier* pSrfBz, const bool bSplitPatches, const Point3d
GetHeightLeaves( -1, vLeaves) ;
m_vnParents = vLeaves ;
}
// controllo se la superficie è chiusa. In tal caso la splitto sul parametro su cui è chiusa
// verifico se la superficie è chiusa ed eventualmente sistemo le adiacenze
// controllo se la superficie è chiusa.
// se è chiusa e non ho già fatto split preliminare, splitto sul parametro su cui è chiusa
// e sistemo le adiacenze
if ( ( AreSamePointApprox( ptP00, ptP01) || AreSamePointApprox( ptP10, ptP11)) ||
( AreSamePointApprox( ptP00, ptP10) || AreSamePointApprox( ptP01, ptP11))) {
m_bClosed = true ;
if ( AreSamePointApprox( ptP00, ptP01)) {
m_mTree[-1].m_nTop = -1 ;
m_mTree[-1].m_nBottom = -1 ;
if ( ( AreSamePointApprox( ptP00, ptP01) || AreSamePointApprox( ptP10, ptP11)) && (int) m_mTree.size() == 1) {
if ( AreSamePointApprox( ptP00, ptP01) && AreSamePointApprox( ptP10, ptP11)) {
m_mTree[-1].m_nTop = -1 ;
m_mTree[-1].m_nBottom = -1 ;
}
m_mTree[-1].SetSplitDirVert( false) ;
Split( -1) ;
// qui devo fare il controllo capped
// qui devo fare il controllo capped ( chiusura a semisfera)
// devo controllare se i punti ai parametri U=0 e U=1 sono tutti coincidenti
// in caso devo fare uno split nell'altra direzione
bool bOk = false ;
@@ -255,10 +270,13 @@ Tree::SetSurf( const SurfBezier* pSrfBz, const bool bSplitPatches, const Point3d
Split( 1) ;
}
}
if ( AreSamePointApprox( ptP00, ptP10)) {
// nella condizione di questo if non controllo eventuali divisioni preliminari, perché ne tengo conto dopo
if ( AreSamePointApprox( ptP00, ptP10) || AreSamePointApprox( ptP01, ptP11)) {
if( (int) m_mTree.size() == 1) {
m_mTree[-1].m_nLeft = -1 ;
m_mTree[-1].m_nRight = -1 ;
if ( AreSamePointApprox( ptP00, ptP10) && AreSamePointApprox( ptP01, ptP11)) {
m_mTree[-1].m_nLeft = -1 ;
m_mTree[-1].m_nRight = -1 ;
}
m_mTree[-1].SetSplitDirVert( true) ;
Split( -1) ;
// devo controllare se i punti ai parametri V=0 e V=1 sono tutti coincidenti
@@ -280,7 +298,8 @@ Tree::SetSurf( const SurfBezier* pSrfBz, const bool bSplitPatches, const Point3d
Split( 1) ;
}
}
else if ( (int) m_mTree.size() > 1 && (int) m_mTree.size() < 4) {
// se ho fatto solo 1 split e ho due celle foglie nId = 0 e nId = 1
else if ( (int) m_mTree.size() > 1 && (int) m_mTree.size() < 4) { // si può mettere anche < 5
m_mTree[0].m_nLeft = -1 ;
m_mTree[0].m_nRight = -1 ;
m_mTree[1].m_nLeft = -1 ;
@@ -295,13 +314,29 @@ Tree::SetSurf( const SurfBezier* pSrfBz, const bool bSplitPatches, const Point3d
// calcolo e salvo la lunghezza reale delle curve di bezier di bordo
PtrOwner<CurveComposite> pCrvV0( m_pSrfBz->GetCurveOnU( 0)) ;
PtrOwner<CurveComposite> pCrvV1( m_pSrfBz->GetCurveOnU( 1)) ;
PtrOwner<CurveComposite> pCrvV1( m_pSrfBz->GetCurveOnU( double(nSpanV))) ;
PtrOwner<CurveComposite> pCrvU0( m_pSrfBz->GetCurveOnV( 0)) ;
PtrOwner<CurveComposite> pCrvU1( m_pSrfBz->GetCurveOnV( 1)) ;
PtrOwner<CurveComposite> pCrvU1( m_pSrfBz->GetCurveOnV( double(nSpanU))) ;
double dLen0 ; pCrvV0->GetApproxLength( dLen0) ;
double dLen1 ; pCrvU1->GetApproxLength( dLen1) ;
double dLen2 ; pCrvV1->GetApproxLength( dLen2) ;
double dLen3 ; pCrvU0->GetApproxLength( dLen3) ;
if ( dLen0 < EPS_ZERO && dLen2 < EPS_ZERO ) {
PtrOwner<CurveComposite> pCrvV( m_pSrfBz->GetCurveOnU( double(nSpanV) / 2)) ;
pCrvV->GetApproxLength( dLen0) ;
if ( dLen0 < EPS_ZERO ) {
pCrvV.Set( m_pSrfBz->GetCurveOnU( double(nSpanV) / 4)) ;
pCrvV->GetApproxLength( dLen0) ;
}
}
if ( dLen1 < EPS_ZERO && dLen3 < EPS_ZERO ) {
PtrOwner<CurveComposite> pCrvU( m_pSrfBz->GetCurveOnV( double(nSpanU) / 2)) ;
pCrvU->GetApproxLength( dLen1) ;
if ( dLen1 < EPS_ZERO ) {
pCrvU.Set( m_pSrfBz->GetCurveOnV( double(nSpanU) / 4)) ;
pCrvU->GetApproxLength( dLen1) ;
}
}
m_vDim.clear() ;
m_vDim.push_back( ( dLen0 > EPS_ZERO ? dLen0 : 1)) ;
m_vDim.push_back( ( dLen1 > EPS_ZERO ? dLen1 : 1)) ;
@@ -491,6 +526,11 @@ Tree::Split( const int& nId)
bool
Tree::BuildTree_test( const double& dLinTol, const double& dSideMin, const double& dSideMax)
{
// per poter usare questa funzione, anziché quella normale, bisogna:
// - commentare la parte di funzione di SetSurf dove si fanno gli split preliminare
// - se si usa anche la funzione GetLeaves, bisogna anche lì usare BuildTree_test al posto di BuildTree
//int nCToSplit = -1 ;
//celle 0,1
m_mTree[-1].SetSplitDirVert( true) ;
@@ -638,17 +678,33 @@ Tree::BuildTree( const double& dLinTol, const double& dSideMin, const double& dS
double dLen1 = Dist( ptP10, ptP11) ;
double dLen2 = Dist( ptP01, ptP11) ;
double dLen3 = Dist( ptP00, ptP01) ;
if ( dLen0 < EPS_ZERO && dLen2 < EPS_ZERO ) {
double dV = ( m_mTree[nCToSplit].GetBottomLeft().y + m_mTree[nCToSplit].GetTopRight().y) / 2 / SBZ_TREG_COEFF ;
PtrOwner<CurveComposite> pCrvV( m_pSrfBz->GetCurveOnU( dV)) ;
double dLenU0, dLenU1 ;
pCrvV->GetLengthAtParam( m_mTree[nCToSplit].GetBottomLeft().x / SBZ_TREG_COEFF, dLenU0) ;
pCrvV->GetLengthAtParam( m_mTree[nCToSplit].GetTopRight().x / SBZ_TREG_COEFF, dLenU1) ;
dLen0 = abs( dLenU1 - dLenU0) ;
}
if ( dLen1 < EPS_ZERO && dLen3 < EPS_ZERO ) {
double dU = ( m_mTree[nCToSplit].GetBottomLeft().x + m_mTree[nCToSplit].GetTopRight().x) / 2 / SBZ_TREG_COEFF ;
PtrOwner<CurveComposite> pCrvU( m_pSrfBz->GetCurveOnV( dU)) ;
double dLenV0, dLenV1 ;
pCrvU->GetLengthAtParam( m_mTree[nCToSplit].GetBottomLeft().y / SBZ_TREG_COEFF, dLenV0) ;
pCrvU->GetLengthAtParam( m_mTree[nCToSplit].GetTopRight().y / SBZ_TREG_COEFF, dLenV1) ;
dLen1 = abs( dLenV1 - dLenV0) ;
}
// verifico che la cella sia da splittare e che eventualmente sia abbastanza grande da poterlo fare
double dSideMinVal = 0, dSideMaxVal = 0 ;
if ( bVert) {
if ( dLen0 != 0 && dLen2 != 0)
dSideMinVal = std::max( dLen0, dLen2) ;
if ( dLen0 > EPS_ZERO && dLen2 > EPS_ZERO)
dSideMinVal = std::min( dLen0, dLen2) ;
else
dSideMinVal = std::max( dLen0, dLen2) ;
}
else {
if ( dLen1 != 0 && dLen3 != 0)
dSideMinVal = std::max( dLen1, dLen3) ;
if ( dLen1 > EPS_ZERO && dLen3 > EPS_ZERO)
dSideMinVal = std::min( dLen1, dLen3) ;
else
dSideMinVal = std::max( dLen1, dLen3) ;
}
@@ -657,11 +713,13 @@ Tree::BuildTree( const double& dLinTol, const double& dSideMin, const double& dS
// se la cella è abbastanza grande da poter essere divisa ancora, calcolo l'errore di approssimazione
bool bSplit = false ;
if ( dSideMinVal / 2 >= dSideMin && dSideMaxVal < dSideMax && ( dCurvV > dLinTol || dCurvU > dLinTol)) {
// dSideMinVal potrebbe essere zero se entrambi i lati che dovrei splittare sono collassati in un punto, ma questo non vuol
// dire che non dovrei eseguire lo split
if ( (dSideMinVal / 2 >= dSideMin || dSideMinVal < EPS_SMALL) && dSideMaxVal < dSideMax && ( dCurvV > dLinTol || dCurvU > dLinTol)) {
CurveLine cl0010, cl0001, cl1011, cl0111 ;
// U=0
// V=0
cl0010.Set( ptP00, ptP10) ;
// U=1
// V=1
cl0111.Set( ptP01, ptP11) ;
Point3d pt0010, pt0111, ptBz0, ptBz1, ptBzV ;
int nFlag ;
@@ -680,10 +738,22 @@ Tree::BuildTree( const double& dLinTol, const double& dSideMin, const double& dS
if ( ! m_pSrfBz->GetPointD1D2( dULoc, m_mTree[nCToSplit].GetBottomLeft().y / SBZ_TREG_COEFF, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptBz0) ||
! m_pSrfBz->GetPointD1D2( dULoc, m_mTree[nCToSplit].GetTopRight().y / SBZ_TREG_COEFF, ISurfBezier::FROM_MINUS, ISurfBezier::FROM_MINUS, ptBz1))
return false ;
DistPointCurve dpc0010( ptBz0, cl0010) ;
DistPointCurve dpc0111( ptBz1, cl0111) ;
dpc0010.GetMinDistPoint( 0, pt0010, nFlag) ;
dpc0111.GetMinDistPoint( 0, pt0111, nFlag) ;
// verifico che la cella non sia uno spicchio in verticale, cioè con ptP00 == ptP01 && ptP10 == ptP11
// ( vedi disegno sotto per uno spicchio verticale)
// sennò i punti che cerco sono semplicemente i vertici
if ( cl0010.IsValid()) {
DistPointCurve dpc0010( ptBz0, cl0010) ;
dpc0010.GetMinDistPoint( 0, pt0010, nFlag) ;
}
else
pt0010 = ptP00 ;
if ( cl0111.IsValid()) {
DistPointCurve dpc0111( ptBz1, cl0111) ;
dpc0111.GetMinDistPoint( 0, pt0111, nFlag) ;
}
else
pt0111 = ptP01 ;
// curva a parametro U fisso, con V che scorre
clV.Set( pt0010, pt0111) ;
for ( int v = 0 ; v < nStepsV ; ++ v) {
double dV = double ( v) / double ( nStepsV - 1) ;
@@ -694,6 +764,20 @@ Tree::BuildTree( const double& dLinTol, const double& dSideMin, const double& dS
// distanza di approssimazione locale
double dDist ;
dpc.GetDist( dDist) ;
// se la cella è uno spicchio, quindi con due lati collassati, devo calcolare in modo diverso dist
// ptP00 == ptP01
// / \
// / \
// / \
// ( )
// \ /
// \ /
// \ /
// ptP10 == ptP11
if ( ! clV.IsValid() && AreSamePointApprox( ptP00, ptP01) && AreSamePointApprox( ptP10, ptP11)) {
DistPointCurve dpcSlice( ptBzV, cl0010) ;
dpcSlice.GetDist( dDist) ;
}
if ( dDist > dLinTol) {
bSplit = true ;
break ;
@@ -1726,11 +1810,16 @@ Tree::TraceLoopLabelCell( void)
ResetTree() ;
INTVECTOR vNeigh, vFirst ;
GetRootNeigh( 1, vFirst) ;
for ( int k : vFirst) {
m_mTree[k].m_bOnLeftEdge = true ;
}
int nLastLeft = vFirst.back() ;
int nCell = vFirst[0] ;
GetRightNeigh( nCell, vNeigh) ;
// proseguo finché non sono sull'elemento più alto di vFirst e tutti i suoi vicini sono processati/categorizzati
bool bAllDone = false ;
// mentre scorro a destra mi basta trovare un vicino da cui non sono passato
// mentre torno indietro a sinistra, mi fermo quando trovo una cella non processata ( con vicini a destra da cui non sono passato)
while ( ! bAllDone) {
// categorizzo la cella
m_mTree[nCell].m_nFlag2 = 1 ;
@@ -1738,7 +1827,7 @@ Tree::TraceLoopLabelCell( void)
bool bDone = false ;
// fintanto che la cella ha tra i vicini a destra una cella non elaborata mi sposto a destra
// definisco una cella Processed se tutto il ramo a destra è categorizzato
while ( ( (int)vNeigh.size() > 0 && ! bDone) || ! m_mTree[nCell].IsProcessed()) {
while ( ! m_mTree[nCell].IsProcessed()) {
// verso la cella a destra più in basso da cui non sono ancora passato
bool bProceeded = false ;
for ( int i = 0 ; i < (int)vNeigh.size() ; ++ i) {
@@ -1762,20 +1851,31 @@ Tree::TraceLoopLabelCell( void)
vNeigh.clear() ;
GetRightNeigh( nCell, vNeigh) ;
bDone = true ;
// controllo che tra i vicini di destra ce ne sia almeno uno non processato
//controllo che tra i vicini di destra ce ne sia almeno uno da cui non sono passato
// ( e controllo che non sia una cella sul lato sinistro ( adiacenza in caso di superficie chiusa))
for ( int t: vNeigh) {
if ( ! m_mTree[t].IsProcessed()) {
bDone = false ;
break ;
if ( m_mTree[t].m_nFlag2 == 0 ) {
if ( ! m_bClosed) {
bDone = false ;
break ;
}
else {
// controllo che non sia sul lato sinistro
if ( ! m_mTree[t].m_bOnLeftEdge) {
bDone = false ;
break ;
}
}
}
}
m_mTree[nCell].SetProcessed( bDone) ;
}
}
vNeigh.clear() ;
GetRightNeigh( nCell, vNeigh) ;
// se non ho vicini a destra o se i vicini sono già tutti categorizzati
// torno indietro a sinistra alla cella già categorizzata più bassa
while ( (int) vNeigh.size() == 0 || m_mTree[nCell].IsProcessed()) {
while ( m_mTree[nCell].IsProcessed()) {
// trovo il vicino a sinistra, già categorizzato, più basso
vNeigh.clear() ;
GetLeftNeigh( nCell, vNeigh) ;
@@ -1785,21 +1885,36 @@ Tree::TraceLoopLabelCell( void)
break ;
}
}
// se non ho vicini a sinistra sono tornato sul lato sinistro e quindi devo procedere alla cella più bassa non processata sul lato sinistro
if ( vNeigh.empty()) {
for ( int p = 0 ; p < (int)vFirst.size() ; ++ p) {
if ( m_mTree[vFirst[p]].m_nFlag2 == 0) {
nCell = vFirst[p] ;
if ( ! m_bClosed) {
// se non ho vicini a sinistra sono tornato sul lato sinistro e quindi devo procedere alla cella più bassa non processata sul lato sinistro
if ( vNeigh.empty()) {
for ( int p = 0 ; p < (int)vFirst.size() ; ++ p) {
if ( m_mTree[vFirst[p]].m_nFlag2 == 0) {
nCell = vFirst[p] ;
break ;
}
}
if ( nCell == nLastLeft && m_mTree[nCell].IsProcessed()) {
bAllDone = true ;
break ;
}
}
if ( nCell == nLastLeft && m_mTree[nCell].IsProcessed()) {
// categorizzo
m_mTree[nCell].m_nFlag2 = 1 ;
CategorizeCell( nCell) ;
bAllDone = true ;
break ;
}
else {
// verifico se sono tornato sul lato sinistro, in questo caso procedo con la prima cella di vFirst non processata
if ( m_mTree[nCell].m_bOnLeftEdge) {
for ( int p = 0 ; p < (int) vFirst.size(); ++ p) {
if ( nCell == vFirst[p] && nCell != nLastLeft) {
nCell = vFirst[p + 1] ;
break ;
}
else if ( nCell == nLastLeft && m_mTree[nCell].IsProcessed()){
bAllDone = true ;
}
}
}
if ( bAllDone)
break ;
}
// controllo se tutti i vicini di destra sono categorizzati
vNeigh.clear() ;
@@ -2887,7 +3002,7 @@ Tree::AddVertex( const int& nId, const std::vector<PNTVECTOR>& vEdgeVertex, Poly
return true ;
}
//usando i poligoni
//usando i poligoni // deprecato e non funzionante
////----------------------------------------------------------------------------
//bool
//Tree::SetRightEdgeIn( int nId, std::vector<PNTVECTOR>& vEdgeVertex, PolyLine& plTrimmedPoly)
@@ -2959,6 +3074,10 @@ Tree::SetRightEdgeIn( const int& nId)
// categorizzo la cella in base a quanta parte del lato destro è conenuta all'interno delle curve di trim
// RightEdgeIn -> 0 non contenuto ; 1 contenuto ; 2 in parte contenuto
int nPass = (int) m_mTree[nId].m_vInters.size() ;
if ( nPass == 0 ) {
m_mTree[nId].m_nRightEdgeIn = 0 ;
return true ;
}
bool bDone = false ;
// se ho solo loop interni devo controllare se il più esterno è CCW ( lato destro esterno) o CW ( lato destro interno)
if ( m_mTree[nId].m_nFlag == 2) {