EgtGeomKernel :

- aggiornamento versione di RuledSmooth.
- aggiunta controlli.
- prima versione della regolarizzazione di curve bezier composte.
This commit is contained in:
Daniele Bariletti
2026-04-16 09:41:22 +02:00
parent 4bc8590ce9
commit 02cb8a0d3c
3 changed files with 448 additions and 192 deletions
+396 -139
View File
@@ -57,6 +57,7 @@
|| SAVEFAILEDTREE || SAVELIMITSURF || SAVEPACEDISO
#include "/EgtDev/Include/EGkGeoObjSave.h"
std::vector<IGeoObj*> vGeo ;
std::vector<Color> vCol ;
#endif
using namespace std ;
@@ -5896,15 +5897,31 @@ SurfBezier::CreateByTwoCurves( const ICurve* pCurve0, const ICurve* pCurve1, int
return true ;
}
// Struttura per Spigolo
struct SharpEdge {
double dParU ;
Point3d ptEdge ;
Vector3d vtTanPrev, vtTanAft ;
Vector3d vtTanPrev, vtTanNext ;
SharpEdge( double dU, const Point3d ptE, const Vector3d& vtTP, const Vector3d& vtTA)
: dParU( dU), ptEdge( ptE), vtTanPrev( vtTP), vtTanAft( vtTA) {}
: dParU( dU), ptEdge( ptE), vtTanPrev( vtTP), vtTanNext( vtTA) {}
} ;
typedef vector<SharpEdge> SHARPEDGEVECTOR ;
// Struttura per Punto a Curvatura Massima
struct CurvaturePoint {
double dParU ;
Point3d ptCurvature ;
Vector3d vtTanPrev, vtTanNext ;
CurvaturePoint( double dU, const Point3d ptE, const Vector3d& vtTP, const Vector3d& vtTA)
: dParU( dU), ptCurvature( ptE), vtTanPrev( vtTP), vtTanNext( vtTA) {}
} ;
typedef vector<SharpEdge> SHARPEDGEVECTOR ;
// ---------------------------------------------------------------------------
// Calcolo delle Curve di Sync tra gli spigoli di una curva e l'altra curva nella sua integrità
// [Dato un vettore di SharpEdge di una Curva di Bordo si calcola la Linea di Sincronizzazione tra ognuno di essi
// e l'altra Curva di Bordo]
// -> Restituisce un vettore di Linee di Sincronizzazione
static bool
CalcSyncPointFromEdge( const SHARPEDGEVECTOR& vSharpEdge, const ICurveComposite* pCompoSubEdge, const SHARPEDGEVECTOR& vSharpSubEdge,
BIPNTVECTOR& vSyncLines)
@@ -5919,13 +5936,20 @@ CalcSyncPointFromEdge( const SHARPEDGEVECTOR& vSharpEdge, const ICurveComposite*
if ( vSharpEdge.empty())
return true ;
// Se il numero di Spigoli coincide, allora li unisco sequenzialmente tra loro ( considerazione buona ???)
if ( ssize( vSharpEdge) == ssize( vSharpSubEdge)) {
for ( int i = 0 ; i < ssize( vSharpEdge) ; ++ i)
vSyncLines.emplace_back( vSharpEdge[i].ptEdge, vSharpSubEdge[i].ptEdge) ;
return false ;
}
// Scorro gli SharpEdges
double dUPrevSubEdge = 0., dUCurrSubEdge = 0. ;
for ( const SharpEdge& mySharpEdge : vSharpEdge) {
// Recupero il parametro sulla curva di sincronizzazione
// --- Piano di taglio per punto a distanza minima
IntersCurvePlane ICP( *pCompoSubEdge, mySharpEdge.ptEdge, Media( mySharpEdge.vtTanPrev, mySharpEdge.vtTanAft)) ;
IntersCurvePlane ICP( *pCompoSubEdge, mySharpEdge.ptEdge, Media( mySharpEdge.vtTanPrev, mySharpEdge.vtTanNext)) ;
int nIndParCloser = -1, nIndPointCloser = -1 ;
double dSqMinDist = INFINITO ;
for ( int nInfo = 0 ; nInfo < ICP.GetIntersCount() ; ++ nInfo) {
@@ -5940,7 +5964,7 @@ CalcSyncPointFromEdge( const SHARPEDGEVECTOR& vSharpEdge, const ICurveComposite*
}
}
}
bool bOkPlane = ( nIndParCloser != -1 && nIndPointCloser != -1) ;
bool bOkPlane = ( nIndParCloser != -1 && nIndPointCloser != -1) ;
if ( bOkPlane) {
// Se gli indici sono tra loro coerenti allora ho individuato il punto
if ( nIndParCloser == nIndPointCloser) {
@@ -5948,30 +5972,31 @@ CalcSyncPointFromEdge( const SHARPEDGEVECTOR& vSharpEdge, const ICurveComposite*
ICP.GetIntCrvPlnInfo( nIndParCloser, aInfo) ;
dUCurrSubEdge = aInfo.Ici[0].dU ;
}
// Se gli indici sono discordi, devo scegliere quale dei due punti tenere
else {
// scelgo il punto più vicino al corrente
IntCrvPlnInfo aInfoPt, aInfoPar ;
ICP.GetIntCrvPlnInfo( nIndPointCloser, aInfoPt) ;
ICP.GetIntCrvPlnInfo( nIndParCloser, aInfoPar) ;
dUCurrSubEdge = ( SqDist( mySharpEdge.ptEdge, aInfoPt.Ici[0].ptI) < SqDist( mySharpEdge.ptEdge, aInfoPar.Ici[0].ptI) ?
aInfoPt.Ici[0].dU : aInfoPar.Ici[0].dU) ;
}
}
// Se gli indici sono discordi, devo scegliere quale dei due punti tenere
else {
// scelgo il punto più vicino al corrente
IntCrvPlnInfo aInfoPt, aInfoPar ;
ICP.GetIntCrvPlnInfo( nIndPointCloser, aInfoPt) ;
ICP.GetIntCrvPlnInfo( nIndParCloser, aInfoPar) ;
dUCurrSubEdge = ( SqDist( mySharpEdge.ptEdge, aInfoPt.Ici[0].ptI) < SqDist( mySharpEdge.ptEdge, aInfoPar.Ici[0].ptI) ?
aInfoPt.Ici[0].dU : aInfoPar.Ici[0].dU) ;
if ( ! bOkPlane) {
// --- Altrimenti, cerco il punto a minima distanza
DistPointCurve DPC( mySharpEdge.ptEdge, *pCompoSubEdge) ;
int nFlag ;
bool bOkMinDist = ( DPC.GetParamAtMinDistPoint( dUPrevSubEdge, dUCurrSubEdge, nFlag) && dUCurrSubEdge > dUPrevSubEdge) ;
if ( ! bOkMinDist) {
// --- Eh.... bho ( se arrivo qui non so che fare...)
double dUStart, dUEnd ;
pCompoSubEdge->GetDomain( dUStart, dUEnd) ;
dUCurrSubEdge = ( dUEnd - dUCurrSubEdge) / 2. ;
}
if ( ! bOkPlane) {
// --- Altrimenti, cerco il punto a minima distanza
DistPointCurve DPC( mySharpEdge.ptEdge, *pCompoSubEdge) ;
int nFlag ;
bool bOkMinDist = ( DPC.GetParamAtMinDistPoint( dUPrevSubEdge, dUCurrSubEdge, nFlag) && dUCurrSubEdge > dUPrevSubEdge) ;
if ( ! bOkMinDist) {
// --- Alla peggio mi posiziono a metà tra la posizione corrente e quella finale
double dPrevLen ; pCompoSubEdge->GetLengthAtParam( dUPrevSubEdge, dPrevLen) ;
double dLen ; pCompoSubEdge->GetLength( dLen) ;
pCompoSubEdge->GetParamAtLength( ( dPrevLen + dLen) / 2., dUCurrSubEdge) ;
}
}
// Verifico se tale parametro può essere avvicinato ad uno Spigolo presente sulla curva
// NB. Come descitto sopra gli spigoli in generale è meglio sincronizzarli tra loro
if ( ! vSharpSubEdge.empty()) {
const double EDGE_LEN_TOL = 5. ;
for ( const SharpEdge& mySharpSubEdge : vSharpSubEdge) {
@@ -6001,7 +6026,104 @@ CalcSyncPointFromEdge( const SHARPEDGEVECTOR& vSharpEdge, const ICurveComposite*
}
// ---------------------------------------------------------------------------
// Debug [Gestione Edge in Quadrangolazioni]
// Calcolo della curva di Sync tra un punto di una curva di Bordo (calcolato come il punto a curvatura massima) e il suo
// associato sull'altra Curva di Bordo
// -> Restituisce la singola Linea di Sincronizzazione
static bool
CalcSyncPointFromCurvature( const CurvaturePoint& PointK, const ICurveComposite* pCompoSubEdge, BIPOINT& SyncLine)
{
// Verifico la validità del SubEdge su cui trovare il punto di Sincronizzazione
if ( pCompoSubEdge == nullptr || ! pCompoSubEdge->IsValid())
return false ;
// --- Piano di taglio per punto a distanza minima
double dUCurrSubEdge = 0. ;
IntersCurvePlane ICP( *pCompoSubEdge, PointK.ptCurvature, Media( PointK.vtTanPrev, PointK.vtTanNext)) ;
int nIndParCloser = -1, nIndPointCloser = -1 ;
double dSqMinDist = INFINITO ;
for ( int nInfo = 0 ; nInfo < ICP.GetIntersCount() ; ++ nInfo) {
IntCrvPlnInfo aInfo ;
if ( ICP.GetIntCrvPlnInfo( nInfo, aInfo)) {
if ( nIndParCloser == -1)
nIndParCloser = nInfo ;
double dSqDist = SqDist( PointK.ptCurvature, aInfo.Ici[0].ptI) ;
if ( dSqDist < dSqMinDist) {
dSqMinDist = dSqDist ;
nIndPointCloser = nInfo ;
}
}
}
bool bOkPlane = ( nIndParCloser != -1 && nIndPointCloser != -1) ;
if ( bOkPlane) {
// Se gli indici sono tra loro coerenti allora ho individuato il punto
if ( nIndParCloser == nIndPointCloser) {
IntCrvPlnInfo aInfo ;
ICP.GetIntCrvPlnInfo( nIndParCloser, aInfo) ;
dUCurrSubEdge = aInfo.Ici[0].dU ;
}
// Se gli indici sono discordi, devo scegliere quale dei due punti tenere
else {
// scelgo il punto più vicino al corrente
IntCrvPlnInfo aInfoPt, aInfoPar ;
ICP.GetIntCrvPlnInfo( nIndPointCloser, aInfoPt) ;
ICP.GetIntCrvPlnInfo( nIndParCloser, aInfoPar) ;
dUCurrSubEdge = ( SqDist( PointK.ptCurvature, aInfoPt.Ici[0].ptI) < SqDist( PointK.ptCurvature, aInfoPar.Ici[0].ptI) ?
aInfoPt.Ici[0].dU : aInfoPar.Ici[0].dU) ;
}
}
if ( ! bOkPlane) {
// --- Altrimenti, cerco il punto a minima distanza
DistPointCurve DPC( PointK.ptCurvature, *pCompoSubEdge) ;
int nFlag ;
bool bOkMinDist = ( DPC.GetParamAtMinDistPoint( 0., dUCurrSubEdge, nFlag)) ;
if ( ! bOkMinDist) {
// --- Alla peggio mi posiziono a metà tra la posizione corrente e quella finale
double dPrevLen ; pCompoSubEdge->GetLengthAtParam( 0., dPrevLen) ;
double dLen ; pCompoSubEdge->GetLength( dLen) ;
pCompoSubEdge->GetParamAtLength( ( dPrevLen + dLen) / 2., dUCurrSubEdge) ;
}
}
// Definisco il primo estremo della linea di Sincronizzazione corrente
SyncLine.first = PointK.ptCurvature ;
// Verifico se posso muovermi attorno al punto trovato per allinearmi meglio con le normali alla curva
// NB. Si cerca di privilgiare i punti più vicini ( in parametro) al punto calcolato sopra, in modo da cercare di
// non discostare troppo la Linea di sincronizzazione dal punto trovato.
const double MAX_DIST = 2.5 ;
const int NUM_SAMPLE_PNT = 20 ;
double dLen ; pCompoSubEdge->GetLength( dLen) ;
double dSyncLen ; pCompoSubEdge->GetLengthAtParam( dUCurrSubEdge, dSyncLen) ;
Vector3d vtSyncTanPrev, vtSyncTanNext ;
pCompoSubEdge->GetPointD1D2( dUCurrSubEdge, ICurve::FROM_MINUS, SyncLine.second, &vtSyncTanPrev) ; vtSyncTanPrev.Normalize() ;
pCompoSubEdge->GetPointD1D2( dUCurrSubEdge, ICurve::FROM_PLUS, SyncLine.second, &vtSyncTanNext) ; vtSyncTanNext.Normalize() ;
double dLimInfLen = Clamp( dSyncLen - MAX_DIST, 0., dLen) ;
double dLimSupLen = Clamp( dSyncLen + MAX_DIST, 0., dLen) ;
int nSamplePnt = int( ceil( NUM_SAMPLE_PNT / 4.)) ;
double dMaxCos = ( Media( PointK.vtTanPrev, PointK.vtTanNext) * Media( vtSyncTanPrev, vtSyncTanNext)) ;
double dShiftR = ( dLimSupLen - dSyncLen) / nSamplePnt ;
double dShiftL = ( dSyncLen - dLimInfLen) / nSamplePnt ;
array<double, 2> vdLens = { dSyncLen, dSyncLen} ;
for ( int i = 1 ; i <= nSamplePnt ; ++ i) {
vdLens[0] += dShiftR ;
vdLens[1] -= dShiftL ;
for ( int j = 0 ; j < 2 ; ++ j) {
double dU ; pCompoSubEdge->GetParamAtLength( vdLens[j], dU) ;
Point3d ptCurr ; Vector3d vtTan ;
pCompoSubEdge->GetPointD1D2( dU, ICurve::FROM_MINUS, ptCurr, &vtTan) ; vtTan.Normalize() ;
double dCos = ( vtTan * Media( PointK.vtTanPrev, PointK.vtTanNext)) ;
if ( dCos > dMaxCos) {
dMaxCos = dCos ;
SyncLine.second = ptCurr ;
}
}
}
return true ;
}
// ---------------------------------------------------------------------------
// Gestione degli spigoli all'interno della Quadrangolazione corrente
static bool
ManageEdgesInQuadrangulation( const ICurveComposite* pSubEdge1, const ICurveComposite* pSubEdge2, BIPNTVECTOR& vSyncLines)
{
@@ -6020,7 +6142,7 @@ ManageEdgesInQuadrangulation( const ICurveComposite* pSubEdge1, const ICurveComp
SHARPEDGEVECTOR vSharpEdges2 ; vSharpEdges2.reserve( max( 0, nCrv2 - 1)) ;
// --- Cerco gli Spigoli sul SottoTratto del primo bordo
// Se la CurvaA ha solo una sottocurva, non faccio nulla
// Se la CurvaA ha più di una sottocurva, verifico l'esistenza di Spigoli (Sharp Edges)
if ( nCrv1 > 1) {
Point3d ptCurr1 ;
// Scorro le sottocurve
@@ -6029,17 +6151,17 @@ ManageEdgesInQuadrangulation( const ICurveComposite* pSubEdge1, const ICurveComp
double dU = double( nCrv + 1) ;
// Recupero le tangenti prima e dopo tale parametro
Vector3d vtTanPrev ; pSubEdge1->GetPointD1D2( dU, ICurve::FROM_MINUS, ptCurr1, &vtTanPrev) ;
Vector3d vtTanAft ; pSubEdge1->GetPointD1D2( dU, ICurve::FROM_PLUS, ptCurr1, &vtTanAft) ;
Vector3d vtTanNext ; pSubEdge1->GetPointD1D2( dU, ICurve::FROM_PLUS, ptCurr1, &vtTanNext) ;
// Verifico se ho uno spigolo
vtTanPrev.Normalize() ;
vtTanAft.Normalize() ;
if ( vtTanPrev * vtTanAft < COS_EDGE_LIMIT)
vSharpEdges1.emplace_back( dU, ptCurr1, vtTanPrev, vtTanAft) ;
vtTanNext.Normalize() ;
if ( vtTanPrev * vtTanNext < COS_EDGE_LIMIT)
vSharpEdges1.emplace_back( dU, ptCurr1, vtTanPrev, vtTanNext) ;
}
}
// --- Cerco gli spigoli sul Sottotratto del secondo bordo
// Se la Curva B ha solo una sottocurva, non faccio nulla
// Se la Curva B ha più di una sottocurva, verifico l'esistena di Spigoli (Sharp Edges)
if ( nCrv2 > 1) {
Point3d ptCurr2 ;
// Scorro le sottocurve
@@ -6048,24 +6170,24 @@ ManageEdgesInQuadrangulation( const ICurveComposite* pSubEdge1, const ICurveComp
double dU = double( nCrv + 1) ;
// Recupero le tangenti prima e dopo tale parametro
Vector3d vtTanPrev ; pSubEdge2->GetPointD1D2( dU, ICurve::FROM_MINUS, ptCurr2, &vtTanPrev) ;
Vector3d vtTanAft ; pSubEdge2->GetPointD1D2( dU, ICurve::FROM_PLUS, ptCurr2, &vtTanAft) ;
Vector3d vtTanNext ; pSubEdge2->GetPointD1D2( dU, ICurve::FROM_PLUS, ptCurr2, &vtTanNext) ;
// Verifico se ho uno spigolo
vtTanPrev.Normalize() ;
vtTanAft.Normalize() ;
if ( vtTanPrev * vtTanAft < COS_EDGE_LIMIT)
vSharpEdges2.emplace_back( dU, ptCurr2, vtTanPrev, vtTanAft) ;
vtTanNext.Normalize() ;
if ( vtTanPrev * vtTanNext < COS_EDGE_LIMIT)
vSharpEdges2.emplace_back( dU, ptCurr2, vtTanPrev, vtTanNext) ;
}
}
// Se non ho alcuno spigolo, allora non faccio nulla
// Se non ho alcuno spigolo per entrambe le curve di Bordo, non faccio nulla
if ( vSharpEdges1.empty() && vSharpEdges2.empty())
return true ;
// Recupero le Curve di Sincronizzazione dell'Edge 1 sull'Edge 2
// Recupero le Curve di Sincronizzazione associate agli spigoli dal primo Bordo al secondo Bordo
BIPNTVECTOR vSyncLines1 ;
CalcSyncPointFromEdge( vSharpEdges1, pSubEdge2, vSharpEdges2, vSyncLines1) ;
// Recupero le Curve di Sincronizzazione dell'Edge 2 sull'Egde 1
// Recupero le Curve di Sincronizzazione associate agli spigoli dal secondo Bordo al primo Bordo
BIPNTVECTOR vSyncLines2 ;
CalcSyncPointFromEdge( vSharpEdges2, pSubEdge1, vSharpEdges1, vSyncLines2) ;
@@ -6078,7 +6200,7 @@ ManageEdgesInQuadrangulation( const ICurveComposite* pSubEdge1, const ICurveComp
}) ;
}) ;
// Ordino tutte le curve in base al parametro dU della curva principale
// Ordino tutte le curve in base al parametro dU della prima curva di Bordo
vector<pair<BIPOINT, double>> vSyncLinesPar ; vSyncLinesPar.reserve( vSyncLines1.size() + vSyncLines2.size()) ;
for ( BIPOINT& SyncLine1 : vSyncLines1) {
vSyncLinesPar.emplace_back( make_pair( make_pair( SyncLine1.first, SyncLine1.second),
@@ -6094,9 +6216,16 @@ ManageEdgesInQuadrangulation( const ICurveComposite* pSubEdge1, const ICurveComp
return SyncLineParA.second < SyncLineParB.second ;
}) ;
// Restituisco il risultato
for ( auto Iter = vSyncLinesPar.begin() ; Iter != vSyncLinesPar.end() ; ++ Iter)
vSyncLines.emplace_back( Iter->first) ;
// Controllo che le linee di sincronizzazione non si intreccino e restituisco il risultato
// NB. Inserisco solo che curve che progressivamente non si intrecciano sulla seconda curva di Bordo
double dUSecondPrev = EPS_PARAM ;
for ( auto Iter = vSyncLinesPar.begin() ; Iter != vSyncLinesPar.end() ; ++ Iter) {
double dUSecondCurr ; pSubEdge2->GetParamAtPoint( ( *Iter).first.second, dUSecondCurr, TOL) ;
if ( dUSecondCurr > dUSecondPrev + EPS_SMALL) {
vSyncLines.emplace_back( Iter->first) ;
dUSecondPrev = dUSecondCurr ;
}
}
return true ;
}
@@ -6110,75 +6239,188 @@ ManageTwistInQuadrangulation( const ICurveComposite* pSubEdge1, const ICurveComp
if ( pSubEdge1 == nullptr || ! pSubEdge1->IsValid() || pSubEdge2 == nullptr || ! pSubEdge2->IsValid())
return false ;
// --- Verifico il cambiamento di Deviazione angolare tra Inizio-Fine del tratto ( ne basta uno dei due fuori dalla tolleranza)
const double COS_LIMIT = cos( 50. * DEGTORAD) ;
Vector3d vtStart1 ; pSubEdge1->GetStartDir( vtStart1) ;
Vector3d vtEnd1 ; pSubEdge1->GetEndDir( vtEnd1) ;
bool bSplit = ( vtStart1 * vtEnd1 < COS_LIMIT) ;
if ( ! bSplit) {
Vector3d vtStart2 ; pSubEdge2->GetStartDir( vtStart2) ;
Vector3d vtEnd2 ; pSubEdge2->GetEndDir( vtEnd2) ;
bSplit = ( vtStart2 * vtEnd2 < COS_LIMIT) ;
const int NUM_SAMPLE_PNT = 20 ;
#if DEBUG_CURVATURE
vector<pair<Point3d, double>> _vPtK1 ;
vector<pair<Point3d, double>> _vPtK2 ;
IGeomDB* pGeomDB = GetCurrGeomDB() ;
VERIFY_GEOMDB( pGeomDB, false)
#endif
// --- Verifico il cambiamento di Deviazione angolare tra Inizio-Fine della prima curva di Bordo
Vector3d vtS1 ; pSubEdge1->GetStartDir( vtS1) ;
Vector3d vtE1 ; pSubEdge1->GetEndDir( vtE1) ;
double dLen1 = - EPS_SMALL ;
bool bSplit1 = ( vtS1 * vtE1 < COS_LIMIT) ;
if ( bSplit1) {
// Individuo il punto a Curvatura Massima
pSubEdge1->GetLength( dLen1) ;
double dKMax1 = - INFINITO + 1, dUK1Max = - EPS_SMALL ;
Point3d ptKMax1 ; Vector3d vtTanKMax1Prev, vtTanKMax1Next ;
for ( int i = 0 ; i <= NUM_SAMPLE_PNT ; ++ i) {
// Ricavo la Lunghezza corrente e il parametro dU associato
double dCurrLen1 = Clamp( i * ( dLen1 / NUM_SAMPLE_PNT), 0., dLen1) ;
double dUCurr1 ; pSubEdge1->GetParamAtLength( dCurrLen1, dUCurr1) ;
// Calcolo la Curvatura
Point3d ptCurrSub1 ;
Vector3d vtCurrSub1Der1Prev, vtCurrSub1Der2Prev, vtCurrSub1Der1Next, vtCurrSub1Der2Next ;
pSubEdge1->GetPointD1D2( dUCurr1, ICurve::FROM_MINUS, ptCurrSub1, &vtCurrSub1Der1Prev, &vtCurrSub1Der2Prev) ;
pSubEdge1->GetPointD1D2( dUCurr1, ICurve::FROM_PLUS, ptCurrSub1, &vtCurrSub1Der1Next, &vtCurrSub1Der2Next) ;
Vector3d vtCurrSub1Der1 = Media( vtCurrSub1Der1Prev, vtCurrSub1Der1Next) ;
Vector3d vtCurrSub1Der2 = Media( vtCurrSub1Der2Prev, vtCurrSub1Der2Next) ;
double dCurrK1 = ( vtCurrSub1Der1 ^ vtCurrSub1Der2).Len() / max( EPS_SMALL, Pow( vtCurrSub1Der1.Len(), 3)) ;
// Se maggiore della massima trovata, aggiorno la massima
if ( dCurrK1 > dKMax1) {
dKMax1 = dCurrK1 ;
ptKMax1 = ptCurrSub1 ;
dUK1Max = dUCurr1 ;
vtTanKMax1Prev = vtCurrSub1Der1Prev ;
vtTanKMax1Next = vtCurrSub1Der1Next ;
}
#if DEBUG_CURVATURE
_vPtK1.emplace_back( make_pair( ptCurrSub1, dCurrK1)) ;
#endif
}
vtTanKMax1Prev.Normalize() ;
vtTanKMax1Next.Normalize() ;
// Calcolo la nuova linea di sincronizzazione
CurvaturePoint myCurvaturePoint( dUK1Max, ptKMax1, vtTanKMax1Prev, vtTanKMax1Next) ;
BIPOINT SyncLine ;
CalcSyncPointFromCurvature( myCurvaturePoint, pSubEdge2, SyncLine) ;
vSyncLines.emplace_back( SyncLine) ;
}
// Sew non devo Splittare, non faccio nulla
if ( ! bSplit)
// --- Verifico il cambiamento di Deviazione angolare tra Inizio-Fine del tratto per la prima curva di Bordo
Vector3d vtS2 ; pSubEdge2->GetStartDir( vtS2) ;
Vector3d vtE2 ; pSubEdge2->GetEndDir( vtE2) ;
double dLen2 = - EPS_SMALL ;
bool bSplit2 = ( vtS2 * vtE2 < COS_LIMIT) ;
if ( bSplit2) {
// Individuo il punto a Curvatura Massima
pSubEdge2->GetLength( dLen2) ;
double dKMax2 = - INFINITO + 1, dUK2Max = - EPS_SMALL ;
Point3d ptKMax2 ; Vector3d vtTanKMax2Prev, vtTanKMax2Next ;
for ( int i = 0 ; i <= NUM_SAMPLE_PNT ; ++ i) {
// Ricavo la Lunghezza corrente e il parametro dU associato
double dCurrLen2 = Clamp( i * ( dLen2 / NUM_SAMPLE_PNT), 0., dLen2) ;
double dUCurr2 ; pSubEdge2->GetParamAtLength( dCurrLen2, dUCurr2) ;
// Calcolo la Curvatura
Point3d ptCurrSub2 ;
Vector3d vtCurrSub2Der1Prev, vtCurrSub2Der2Prev, vtCurrSub2Der1Next, vtCurrSub2Der2Next ;
pSubEdge2->GetPointD1D2( dUCurr2, ICurve::FROM_MINUS, ptCurrSub2, &vtCurrSub2Der1Prev, &vtCurrSub2Der2Prev) ;
pSubEdge2->GetPointD1D2( dUCurr2, ICurve::FROM_PLUS, ptCurrSub2, &vtCurrSub2Der1Next, &vtCurrSub2Der2Next) ;
Vector3d vtCurrSub2Der1 = Media( vtCurrSub2Der1Prev, vtCurrSub2Der1Next) ;
Vector3d vtCurrSub2Der2 = Media( vtCurrSub2Der2Prev, vtCurrSub2Der2Next) ;
double dCurrK2 = ( vtCurrSub2Der1 ^ vtCurrSub2Der2).Len() / max( EPS_SMALL, Pow( vtCurrSub2Der1.Len(), 3)) ;
// Se maggiore della massima trovata, aggiorno la massima
if ( dCurrK2 > dKMax2) {
dKMax2 = dCurrK2 ;
ptKMax2 = ptCurrSub2 ;
dUK2Max = dUCurr2 ;
vtTanKMax2Prev = vtCurrSub2Der1Prev ;
vtTanKMax2Next = vtCurrSub2Der1Next ;
}
#if DEBUG_CURVATURE
_vPtK1.emplace_back( make_pair( ptCurrSub2, dCurrK2)) ;
#endif
}
vtTanKMax2Prev.Normalize() ;
vtTanKMax2Next.Normalize() ;
// Calcolo la nuova linea di sincronizzazione
CurvaturePoint myCurvaturePoint( dUK2Max, ptKMax2, vtTanKMax2Prev, vtTanKMax2Next) ;
BIPOINT SyncLine ;
CalcSyncPointFromCurvature( myCurvaturePoint, pSubEdge1, SyncLine) ;
// !<-- Inverto la curva di Sincronizzaione per coerenza con la prima -->!
swap( SyncLine.first, SyncLine.second) ;
vSyncLines.emplace_back( SyncLine) ;
}
#if DEBUG_CURVATURE // Curvatura minima -> AQUA | Curvatura massima -> ORANGE
auto [itMin1, itMax1] = std::minmax_element( _vPtK1.begin(), _vPtK1.end(), [](auto const& A, auto const& B) {
return A.second < B.second ;
}) ;
double _dK1Max = itMax1->second ;
double _dK1Min = itMin1->second ;
for ( int i = 0 ; i < ssize( _vPtK1) ; ++ i) {
double _t = 0. ;
if ( _dK1Max > _dK1Min)
_t = ( _vPtK1[i].second - _dK1Min) / ( _dK1Max - _dK1Min) ;
double _dh = 30 - 180. ; // AQUA -> HSV( 180, 1, 1) | ORANGE -> HSV( 30, 1, 1)
if ( _dh > 180.) _dh -= 360.0 ;
if ( _dh < -180.0) _dh += 360.0 ;
double _h = 180. + _t * _dh ;
if ( _h < 0.0) _h += 360.0 ;
if ( _h >= 360.0) _h -= 360.0 ;
PtrOwner<IGeoPoint3d> _ptC( CreateGeoPoint3d()) ; _ptC->Set( _vPtK1[i].first) ;
int _nId = pGeomDB->AddGeoObj( GDB_ID_NULL, GDB_ID_ROOT, Release( _ptC)) ;
pGeomDB->SetMaterial( _nId, Color( GetColorFromHSV( HSV( _h, 1., 1.)))) ;
}
auto [itMin2, itMax2] = std::minmax_element( _vPtK2.begin(), _vPtK2.end(), [](auto const& A, auto const& B) {
return A.second < B.second ;
}) ;
double _dK2Max = itMax2->second ;
double _dK2Min = itMin2->second ;
for ( int i = 0 ; i < ssize( _vPtK2) ; ++ i) {
double _t = 0. ;
if ( _dK2Max > _dK2Min)
_t = ( _vPtK2[i].second - _dK2Min) / ( _dK2Max - _dK2Min) ;
double _dh = 30 - 180. ; // CYAN -> HSV( 180, 1, 1) | ORANGE -> HSV( 30, 1, 1)
if ( _dh > 180.) _dh -= 360.0 ;
if ( _dh < -180.0) _dh += 360.0 ;
double _h = 180. + _t * _dh ;
if ( _h < 0.0) _h += 360.0 ;
if ( _h >= 360.0) _h -= 360.0 ;
PtrOwner<IGeoPoint3d> _ptC( CreateGeoPoint3d()) ; _ptC->Set( _vPtK2[i].first) ;
int _nId = pGeomDB->AddGeoObj( GDB_ID_NULL, GDB_ID_ROOT, Release( _ptC)) ;
pGeomDB->SetMaterial( _nId, Color( GetColorFromHSV( HSV( _h, 1., 1.)))) ;
}
#endif
// Se non ho alcuna linea di sincronizzazione, non faccio nulla
if ( vSyncLines.empty())
return true ;
double dLen1 ; pSubEdge1->GetLength( dLen1) ;
double dLen2 ; pSubEdge2->GetLength( dLen2) ;
// Individuo sulle due curve i punti a curvatura massima e definisco una Curva di Sync tra essi
Point3d ptKMax1, ptKMax2 ;
double dUKMax1, dUKMin1 ;
double dKMax1 = - INFINITO + 1, dKMax2 = - INFINITO + 1 ;
const int NUM_SAMPLE_PNT = 20 ;
for ( int i = 0 ; i <= NUM_SAMPLE_PNT ; ++ i) {
// Ricavo i parametri
double dU1 ; pSubEdge1->GetParamAtLength( Clamp( i * ( dLen1 / NUM_SAMPLE_PNT), 0., dLen1), dU1) ;
double dU2 ; pSubEdge2->GetParamAtLength( Clamp( i * ( dLen2 / NUM_SAMPLE_PNT), 0., dLen2), dU2) ;
// Calcolo la Curvatura
Point3d ptSub1 ; Vector3d vtSub1Der1, vtSub1Der2 ;
pSubEdge1->GetPointD1D2( dU1, ICurve::FROM_MINUS, ptSub1, &vtSub1Der1, &vtSub1Der2) ;
double dK1 = ( vtSub1Der1 ^ vtSub1Der2).Len() / max( EPS_SMALL, Pow( vtSub1Der1.Len(), 3)) ;
if ( dK1 > dKMax1) {
dKMax1 = dK1 ;
ptKMax1 = ptSub1 ;
const double TOL = 250. * EPS_SMALL ;
// Elimino tutte le curve di Sincronizzazione a ridosso degli estremi della Quadrangolazione
// NB. Risultano ridondanti, creano SottoQuadrangolazioni molto strette o possono Inclinare eccssivamente l'Utensile
// NB. Così facendo evito anche di creare curve di Sync lungo le diagonali della Quadrangolazione
const double LEN_TOL = 2. ;
for ( int i = 0 ; i < ssize( vSyncLines) ; ++ i) {
double dSyncLen1 ; pSubEdge1->GetLengthAtPoint( vSyncLines[i].first, dSyncLen1, TOL) ;
bool bErase = ( dSyncLen1 < max( LEN_TOL, 0.2 * dLen1) || dSyncLen1 > min( dLen1 - LEN_TOL, 0.8 * dLen1)) ;
if ( ! bErase) {
double dSyncLen2 ; pSubEdge2->GetLengthAtPoint( vSyncLines[i].second, dSyncLen2, TOL) ;
bErase = ( dSyncLen2 < max( LEN_TOL, 0.2 * dLen2) || dSyncLen2 > min( dLen2 - LEN_TOL, 0.8 * dLen2)) ;
}
Point3d ptSub2 ; Vector3d vtSub2Der1, vtSub2Der2 ;
pSubEdge2->GetPointD1D2( dU2, ICurve::FROM_MINUS, ptSub2, &vtSub2Der1, &vtSub2Der2) ;
double dK2 = ( vtSub2Der1 ^ vtSub2Der2).Len() / max( EPS_SMALL, Pow( vtSub2Der1.Len(), 3)) ;
if ( dK2 > dKMax2) {
dKMax2 = dK2 ;
ptKMax2 = ptSub2 ;
if ( bErase) {
vSyncLines.erase( vSyncLines.begin() + i) ;
-- i ;
}
}
if ( vSyncLines.empty())
return true ;
// Avendo fissato il punto a curvatura massima su entrambe le curve, cerco di sportarmi lungo la seconda curva
// ( nell'intorno del suo punto a massima curvatura) per cercare una direzione coerente
Point3d ptCurr1 ; Vector3d vtCurr1 ;
const double MAX_DIST = 2.5 ;
double dLenAtK2 ; pSubEdge2->GetLengthAtPoint( ptKMax2, dLenAtK2) ;
//double dLimInfLen = Clamp( dLenAtK2 - MAX_DIST, 0., dLen2) ;
//double dLimSupLen = Clamp( dLenAtK2 + MAX_DIST, 0., dLen2) ;
//int nSamplePnt = int( ceil( NUM_SAMPLE_PNT / 2.)) ;
//double dMinCos = -1 - EPS_SMALL ;
//for ( int i = 0 ; i <= nSamplePnt ; ++ i) {
// double dCurrLen = dLimInfLen + i * ( dLimSupLen - dLimInfLen) / nSamplePnt ;
// double dU ; pSubEdge2->GetParamAtLength( dCurrLen, dU) ;
// Point3d ptCurr ; Vector3d vtTan ;
// pSubEdge2->GetPointD1D2( dU, ICurve::FROM_MINUS, ptCurr, &vtTan) ; vtTan.Normalize() ;
// double dCos = ( vtTan *
//}
// Verifico che entrambi gli estremi della Linea di Sync siano distanti dai bordi della Quadrangolazione corrente
double dLenAtK1 ; pSubEdge1->GetLengthAtPoint( ptKMax1, dLenAtK1) ;
const double DIST_TOL = 2. ;
if ( ( dLenAtK1 > DIST_TOL - EPS_SMALL && dLenAtK1 < dLen1 - DIST_TOL + EPS_SMALL) &&
( dLenAtK2 > DIST_TOL - EPS_SMALL && dLenAtK2 < dLen2 - DIST_TOL + EPS_SMALL)) {
// Verifico che la Linea di Sicnronizzazione non tagli in diagonale la Quadrangolazione corrente
if ( ( dLenAtK1 < dLen1 - dLenAtK1 && dLenAtK2 < dLen2 - dLenAtK2) ||
( dLenAtK1 > dLen1 - dLenAtK1 && dLenAtK2 > dLen2 - dLenAtK2))
vSyncLines.emplace_back( make_pair( ptKMax1, ptKMax2)) ;
// Se due curve di Sincronizzazione rimaste
if ( ssize( vSyncLines) == 2) {
// Se si sovrappongono, ne elimino una
if ( AreSamePointEpsilon( vSyncLines[0].first, vSyncLines[1].first, LEN_TOL) &&
AreSamePointEpsilon( vSyncLines[0].second, vSyncLines[1].second, LEN_TOL))
vSyncLines.pop_back() ;
}
// Se due curve di Sincronizzazione rimaste
if ( ssize( vSyncLines) == 2) {
// Ordino tutte le linee di sincronizzazione in base al parametro dU della curva principale
double dU0 ; pSubEdge1->GetParamAtPoint( vSyncLines[0].first, dU0, TOL) ;
double dU1 ; pSubEdge1->GetParamAtPoint( vSyncLines[1].first, dU1, TOL) ;
if ( dU0 > dU1 - EPS_SMALL)
swap( vSyncLines[0], vSyncLines[1]) ;
// Verifico che le curve non si intreccino
pSubEdge2->GetParamAtPoint( vSyncLines[0].second, dU0, TOL) ;
pSubEdge2->GetParamAtPoint( vSyncLines[1].second, dU1, TOL) ;
if ( dU0 > dU1 - EPS_SMALL)
vSyncLines.pop_back() ; // lascio solo la prima... ( bisognerebbe scegliere quale lasciare con un criterio migliore ?)
}
return true ;
@@ -6189,12 +6431,20 @@ bool
SurfBezier::CreateSmoothRuledByTwoCurves( const ICurve* pCurve0, const ICurve* pCurve1, double dSampleLen)
{
// converto in bezier le curve iniziali
PtrOwner<ICurveBezier> pCrvEdge1( GetCurveBezier( CurveToBezierCurve( pCurve0, 3, false))) ;
PtrOwner<ICurveBezier> pCrvEdge2( GetCurveBezier( CurveToBezierCurve( pCurve1, 3, false))) ;
PtrOwner<ICurve> pCrvEdge1( CurveToBezierCurve( pCurve0, 3, false)) ;
PtrOwner<ICurve> pCrvEdge2( CurveToBezierCurve( pCurve1, 3, false)) ;
if ( IsNull( pCrvEdge1) || IsNull( pCrvEdge2))
return false ;
#if SAVEPACEDISO
vGeo.clear() ;
vCol.clear() ;
#endif
// Verifico che la distanza di campionamento sia ammissibile
double dSampleDist = Clamp( dSampleLen, 2., 30.) ; // 20.0 sembra un passo di campionamento ideale
// Recupero parametri iniziali
double dLen1 ; pCrvEdge1->GetLength( dLen1) ;
double dLen2 ; pCrvEdge2->GetLength( dLen2) ;
@@ -6207,10 +6457,10 @@ SurfBezier::CreateSmoothRuledByTwoCurves( const ICurve* pCurve0, const ICurve* p
Point3d ptPrev1, ptCurr1 ; pCrvEdge1->GetStartPoint( ptPrev1) ;
Point3d ptPrev2, ptCurr2 ; pCrvEdge2->GetStartPoint( ptPrev2) ;
Vector3d vtCurr1 = V_NULL, vtCurr2 = V_NULL ;
BIPNTVECTOR vEdgeSyncLines ;
while ( dLenPrev1 + dSampleLen < dLen1 - EPS_ZERO) {
BIPNTVECTOR vSyncLines ;
while ( dLenPrev1 + dSampleDist < dLen1 - EPS_ZERO) {
// Recupero dU, Point3d e dLen corrente sul primo bordo, per un incremento del passo di campionamento
dLenCurr1 = Clamp( dLenPrev1 + dSampleLen, 0., dLen1) ;
dLenCurr1 = Clamp( dLenPrev1 + dSampleDist, 0., dLen1) ;
pCrvEdge1->GetParamAtLength( dLenCurr1, dUCurr1) ;
pCrvEdge1->GetPointD1D2( dUCurr1, ICurve::FROM_MINUS, ptCurr1, &vtCurr1) ;
vtCurr1.Normalize() ;
@@ -6269,7 +6519,7 @@ SurfBezier::CreateSmoothRuledByTwoCurves( const ICurve* pCurve0, const ICurve* p
}
// Verifico di non essermi allontanato troppo
double dLen ; pCrvEdge2->GetLengthAtParam( dUCurr2, dLen) ;
bOkPlane = ( dLen < dLenPrev2 + 2. * dSampleLen) ;
bOkPlane = ( dLen < dLenPrev2 + 2. * dSampleDist) ;
}
if ( ! bOkPlane) {
// --- Altrimenti, cerco il punto a minima distanza
@@ -6279,11 +6529,11 @@ SurfBezier::CreateSmoothRuledByTwoCurves( const ICurve* pCurve0, const ICurve* p
// Verifico di non essermi allontanato troppo
if ( bOkMinDist) {
double dLen ; pCrvEdge2->GetLengthAtParam( dUCurr2, dLen) ;
bOkMinDist = ( dLen < dLenPrev2 + 2. * dSampleLen) ;
bOkMinDist = ( dLen < dLenPrev2 + 2. * dSampleDist) ;
}
if ( ! bOkMinDist) {
// --- Aumento la distanza corrente del passo di campionamento
double dLen = Clamp( dLenPrev2 + dSampleLen, 0., dLen2) ;
double dLen = Clamp( dLenPrev2 + dSampleDist, 0., dLen2) ;
pCrvEdge2->GetParamAtLength( dLen, dUCurr2) ;
}
}
@@ -6296,10 +6546,10 @@ SurfBezier::CreateSmoothRuledByTwoCurves( const ICurve* pCurve0, const ICurve* p
// Verifico se le direzioni tangenti sono tra di loro circa parallele
const double COS_ANG_TOL = cos( 15. * DEGTORAD) ;
if ( vtCurr1 * vtCurr2 < COS_ANG_TOL) {
// Se fuori dalla tolleranza, recupero il miglior versore tangente sul secondo bordo nell'intervallo successivo di lunghezza ( 2. * dSampleLen)
// Se fuori dalla tolleranza, recupero il miglior versore tangente sul secondo bordo nell'intervallo successivo di lunghezza ( 2. * dMyDist)
pCrvEdge2->GetLengthAtPoint( ptCurr2, dLenCurr2) ;
double dLimInfLen2 = Clamp( dLenCurr2 - dSampleLen, dLenPrev2, dLen2) ;
double dLimSupLen2 = Clamp( dLenCurr2 + dSampleLen, dLenPrev2, dLen2) ;
double dLimInfLen2 = Clamp( dLenCurr2 - dSampleDist, dLenPrev2, dLen2) ;
double dLimSupLen2 = Clamp( dLenCurr2 + dSampleDist, dLenPrev2, dLen2) ;
// [Controllo migliorabile, magari mendiante metodo di bisezione (?)]
const int NUM_STEP = 20 ;
double dMinCos = - 1. - EPS_ZERO ;
@@ -6329,30 +6579,32 @@ SurfBezier::CreateSmoothRuledByTwoCurves( const ICurve* pCurve0, const ICurve* p
pGeomDB->AddGeoObj( GDB_ID_NULL, nLay2, Release( vtGeo2)) ;
#endif
// debug
//// Inserisco le curve di sincronizzazione nel Layer di destinazine
// PtrOwner<ICurveLine> pLine( CreateCurveLine()) ; pLine->Set( ptCurr1, ptCurr2) ;
// pGeomDB->AddGeoObj( GDB_ID_NULL, nDestGrp, Release( pLine)) ;
#if SAVEPACEDISO
// Inserisco le curve di sincronizzazione nel Layer di destinazine
PtrOwner<ICurveLine> pLine( CreateCurveLine()) ; pLine->Set( ptCurr1, ptCurr2) ;
vGeo.push_back( Release( pLine)) ;
vCol.push_back( LIME) ;
#endif
// --- Analisi degli spigoli all'interno della Quadrangolazione ---
// Perchè si fa questa cosa ? Perchè parametrizzando per la lunghezza i SottoTratti ricavati, non è sempre detto
// che uno spigolo di una curva sia sincronizzato con lo spigolo di un'altra ( se questi esistono)... Pertanto
// la Bezier Ruled ricavata non è detto che sia in grado di approssimare lo spigolo correttamente, potrebbe sdondarlo
// --- Analisi degli spigoli all'interno della Quadrangolazione corrente ---
// NB. Non è sempre detto che uno spigolo di una curva sia sincronizzato con lo spigolo di un'altra ( se questi esistono)...
// Pertanto la Bezier Ruled ricavata non è detto che sia in grado di approssimare lo spigolo correttamente, potrebbe sdondarlo
PtrOwner<ICurveComposite> pCrvQuad1( ConvertCurveToComposite( pCrvEdge1->CopyParamRange( dUPrev1, dUCurr1))) ;
PtrOwner<ICurveComposite> pCrvQuad2( ConvertCurveToComposite( pCrvEdge2->CopyParamRange( dUPrev2, dUCurr2))) ;
BIPNTVECTOR vEdgeSyncLines ;
ManageEdgesInQuadrangulation( pCrvQuad1, pCrvQuad2, vEdgeSyncLines) ;
#if SAVEPACEDISO
for ( int i = 0 ; i < ssize( vEdgeSyncLines) ; ++ i) {
PtrOwner<ICurveLine> pLine( CreateCurveLine()) ; pLine->Set( vEdgeSyncLines[i].first, vEdgeSyncLines[i].second) ;
vGeo.push_back( Release( pLine)) ;
vCol.push_back( GREEN) ;
}
#endif
//debug
//for ( int i = 0 ; i < ssize( vEdgeSyncLines) ; ++ i) {
// PtrOwner<ICurveLine> pLine( CreateCurveLine()) ; pLine->Set( vEdgeSyncLines[i].first, vEdgeSyncLines[i].second) ;
// int nNewId = pGeomDB->AddGeoObj( GDB_ID_NULL, nDestGrp, Release( pLine)) ;
// pGeomDB->SetMaterial( nNewId, GREEN) ;
//}
// --- Aggiunta di Linee di Sync all'interno della quandrangolazione corrente ---
// Perchè si fa questa cosa ? Perchè parametrizzando per la lunghezza i SottoTratti ricavati, nel caso di elevate variazioni
// angolari tra l'inizio e la fine ( tra due curva di Sync) si potrebbero generare delle torsioni non volute
// NB. Queste linee vengono create considerando le SubQuadrangolazioni con gli Spigoli.
// --- Aggiunta di Linee di Sync all'interno della Quandrangolazione corrente ---
// Perchè parametrizzando per la lunghezza i SottoTratti ricavati, nel caso di elevate variazioni angolari tra l'inizio e la fine
// ( tra due curva di Sync) si potrebbero generare delle torsioni non volute
// -->! NB. Queste linee vengono create considerando le SubQuadrangolazioni con gli Spigoli. !<--
BIPNTVECTOR vTwistSyncLines ;
if ( vEdgeSyncLines.empty())
ManageTwistInQuadrangulation( pCrvQuad1, pCrvQuad2, vTwistSyncLines) ;
@@ -6382,19 +6634,24 @@ SurfBezier::CreateSmoothRuledByTwoCurves( const ICurve* pCurve0, const ICurve* p
SyncLinePrev = SyncLineNext ;
}
}
//for ( int i = 0 ; i < ssize( vTwistSyncLines) ; ++ i) {
// PtrOwner<ICurveLine> pLine( CreateCurveLine()) ; pLine->Set( vTwistSyncLines[i].first, vTwistSyncLines[i].second) ;
// int nNewId = pGeomDB->AddGeoObj( GDB_ID_NULL, nDestGrp, Release( pLine)) ;
// pGeomDB->SetMaterial( nNewId, OLIVE) ;
//}
#if SAVEPACEDISO
for ( int i = 0 ; i < ssize( vTwistSyncLines) ; ++ i) {
PtrOwner<ICurveLine> pLine( CreateCurveLine()) ; pLine->Set( vTwistSyncLines[i].first, vTwistSyncLines[i].second) ;
vGeo.push_back( Release( pLine)) ;
vCol.push_back( OLIVE) ;
}
#endif
// Aggiorno i parametri
ptPrev1 = ptCurr1 ; dUPrev1 = dUCurr1 ; dLenPrev1 = dLenCurr1 ;
ptPrev2 = ptCurr2 ; dUPrev2 = dUCurr2 ; dLenPrev2 = dLenCurr2 ;
}
return CreateByIsoParamSet( pCrvEdge1, pCrvEdge2, vEdgeSyncLines) ;
#if SAVEPACEDISO
SaveGeoObj( vGeo, vCol, "D:\\Temp\\bezier\\ruled\\trimming\\smooth.nge") ;
#endif
return CreateByIsoParamSet( pCrvEdge1, pCrvEdge2, vSyncLines) ;
}
//----------------------------------------------------------------------------