Compare commits
39 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a69be04cf7 | |||
| fa71f3c38d | |||
| 488bdd3ca2 | |||
| 0f0397c20c | |||
| c1988dd442 | |||
| b5f5a22145 | |||
| 7f4bd1ef24 | |||
| afe3000635 | |||
| 5bd2b869f8 | |||
| 4d3b7ea6cf | |||
| f0137d26f4 | |||
| dc46c92c95 | |||
| 53cd3c26b9 | |||
| e194679232 | |||
| fca95f609a | |||
| 063322e528 | |||
| 01342aebe5 | |||
| c41dde0da0 | |||
| ef12fe8bd1 | |||
| c1a2912970 | |||
| 576eb1cb73 | |||
| d3ce75b2e8 | |||
| 337d5b6d80 | |||
| 487ed0b61f | |||
| 8c67f1aebf | |||
| 1c4198d31c | |||
| 0c7f3d5fd8 | |||
| 4cdcfe1b8b | |||
| 96cb7e5cc3 | |||
| dd28697e48 | |||
| d7d19a07c6 | |||
| 25e2a903c7 | |||
| 8e18211735 | |||
| a53ead679e | |||
| 9b80d32f34 | |||
| 52b95982fb | |||
| 92d8f4414e | |||
| f48bf06f64 | |||
| 490f3fc54c |
+1000
-431
File diff suppressed because it is too large
Load Diff
+31
-2
@@ -41,7 +41,7 @@ GEOOBJ_REGISTER( CRV_ARC, NGE_C_ARC, CurveArc) ;
|
||||
class ArcApproxer
|
||||
{
|
||||
public :
|
||||
ArcApproxer( double dLinTol, double dAngTolDeg, bool bInside, const CurveArc& arArc) ;
|
||||
ArcApproxer( double dLinTol, double dAngTolDeg, bool bInside, const CurveArc& arArc, double dMaxLen = INFINITO) ;
|
||||
bool GetPoint( double& dU, Point3d& ptP) ;
|
||||
|
||||
private :
|
||||
@@ -1177,6 +1177,30 @@ CurveArc::ApproxWithLines( double dLinTol, double dAngTolDeg, int nType, PolyLin
|
||||
return true ;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool
|
||||
CurveArc::ApproxWithLimitedLines( double dLinTol, double dAngTolDeg, int nType, double dMaxLen, PolyLine& PL) const
|
||||
{
|
||||
// pulisco la polilinea
|
||||
PL.Clear() ;
|
||||
|
||||
// la curva deve essere validata
|
||||
if ( m_nStatus != OK)
|
||||
return false ;
|
||||
|
||||
// eseguo approssimazione
|
||||
bool bInside = true ;
|
||||
ArcApproxer aAppr( dLinTol, dAngTolDeg, bInside, *this, dMaxLen) ;
|
||||
double dU ;
|
||||
Point3d ptPos ;
|
||||
while ( aAppr.GetPoint( dU, ptPos)) {
|
||||
if ( ! PL.AddUPoint( dU, ptPos))
|
||||
return false ;
|
||||
}
|
||||
|
||||
return true ;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool
|
||||
CurveArc::ApproxWithArcs( double dLinTol, double dAngTolDeg, PolyArc& PA) const
|
||||
@@ -2170,7 +2194,7 @@ CurveArc::Flip( void)
|
||||
// usando il versore medio dal centro e moltiplicandolo per il coefficiente ( 2 / ( 1 + cosA)).
|
||||
// Il versore dell'ultimo punto è già stato calcolato per il penultimo.
|
||||
//----------------------------------------------------------------------------
|
||||
ArcApproxer::ArcApproxer( double dLinTol, double dAngTolDeg, bool bInside, const CurveArc& arArc)
|
||||
ArcApproxer::ArcApproxer( double dLinTol, double dAngTolDeg, bool bInside, const CurveArc& arArc, double dMaxLen)
|
||||
{
|
||||
// inizializzazioni
|
||||
m_nTotPnt = 0 ;
|
||||
@@ -2195,6 +2219,11 @@ ArcApproxer::ArcApproxer( double dLinTol, double dAngTolDeg, bool bInside, const
|
||||
dAngStepDeg = sqrt( 8 * dLinTolRel) * RADTODEG ;
|
||||
else
|
||||
dAngStepDeg = sqrt( 8 * dLinTolRel / ( 1 + dLinTolRel)) * RADTODEG ;
|
||||
|
||||
if ( dMaxLen < INFINITO) {
|
||||
double dAngStepMaxLen = 2 * asin( dMaxLen / ( 2 * arArc.GetRadius())) * RADTODEG ;
|
||||
dAngStepDeg = min( dAngStepDeg, dAngStepMaxLen) ;
|
||||
}
|
||||
dAngStepDeg = min( dAngStepDeg, dAngTolDeg) ;
|
||||
|
||||
// dall'angolo al centro ricavo il numero di passi
|
||||
|
||||
@@ -111,6 +111,7 @@ class CurveArc : public ICurveArc, public IGeoObjRW
|
||||
bool GetArea( Plane3d& plPlane, double& dArea) const override
|
||||
{ return CurveGetArea( *this, plPlane, dArea) ; }
|
||||
bool ApproxWithLines( double dLinTol, double dAngTolDeg, int nType, PolyLine& PL) const override ;
|
||||
bool ApproxWithLimitedLines( double dLinTol, double dAngTolDeg, int nType, double dMaxLen, PolyLine& PL) const override ;
|
||||
bool ApproxWithArcs( double dLinTol, double dAngTolDeg, PolyArc& PA) const override ;
|
||||
bool ApproxWithArcsEx( double dLinTol, double dAngTolDeg, double dLinFea, PolyArc& PA) const override
|
||||
{ return ApproxWithArcs( dLinTol, dAngTolDeg, PA) ; }
|
||||
|
||||
+2
-2
@@ -2652,7 +2652,7 @@ ResetCurveVoronoi( const ICurve& crvC)
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool
|
||||
GetChainedCurves( ICRVCOMPOPOVECTOR& vCrv, double dChainTol, bool bAllowInvert)
|
||||
GetChainedCurves( ICRVCOMPOPOVECTOR& vCrv, double dChainTol, bool bAllowInvert, bool bHaltOnFork)
|
||||
{
|
||||
if ( ssize( vCrv) == 1)
|
||||
return true ;
|
||||
@@ -2670,7 +2670,7 @@ GetChainedCurves( ICRVCOMPOPOVECTOR& vCrv, double dChainTol, bool bAllowInvert)
|
||||
}
|
||||
INTVECTOR vIds ;
|
||||
Point3d ptStart = ORIG ;
|
||||
while ( chainCrv.GetChainFromNear( ptStart, false, vIds)) {
|
||||
while ( chainCrv.GetChainFromNear( ptStart, bHaltOnFork, vIds)) {
|
||||
int nFirst = vIds[0] ;
|
||||
bool bInvert = false ;
|
||||
if ( nFirst < 0)
|
||||
|
||||
+1
-2
@@ -34,5 +34,4 @@ bool CurveDump( const ICurve& crvC, std::string& sOut, bool bMM, const char* szN
|
||||
bool CopyExtrusion( const ICurve* pSouCrv, ICurve* pDestCrv) ;
|
||||
bool CopyThickness( const ICurve* pSouCrv, ICurve* pDestCrv) ;
|
||||
ICurveBezier* ApproxCurveBezierWithSingleCubic( const ICurve* pCrv) ;
|
||||
Voronoi* GetCurveVoronoi( const ICurve& crvC) ;
|
||||
bool GetChainedCurves( ICRVCOMPOPOVECTOR& vCrv, double dChainTol, bool bAllowInvert) ;
|
||||
Voronoi* GetCurveVoronoi( const ICurve& crvC) ;
|
||||
+54
-4
@@ -1432,7 +1432,7 @@ CurveBezier::ApproxWithLines( double dLinTol, double dAngTolDeg, int nType, Poly
|
||||
//----------------------------------------------------------------------------
|
||||
bool
|
||||
CurveBezier::FlatOrSplit( int nLev, const CurveBezier& crvBez, double dParStart, double dParEnd,
|
||||
double dLinTol, double dAngTolDeg, PolyLine& PL) const
|
||||
double dLinTol, double dAngTolDeg, PolyLine& PL, double dMaxLen) const
|
||||
{
|
||||
// se raggiunto il massimo livello di recursione ...
|
||||
const int MAX_LEV = 10 ;
|
||||
@@ -1461,7 +1461,10 @@ CurveBezier::FlatOrSplit( int nLev, const CurveBezier& crvBez, double dParStart,
|
||||
}
|
||||
|
||||
// se distanza entro tolleranza
|
||||
if ( dMaxSqDist <= ( dLinTol * dLinTol)) {
|
||||
bool bSplit = dMaxSqDist > ( dLinTol * dLinTol) ;
|
||||
if ( dMaxLen < INFINITO)
|
||||
bSplit = bSplit || Dist( crvBez.m_vPtCtrl[0], crvBez.m_vPtCtrl[m_nDeg]) > dMaxLen ;
|
||||
if ( ! bSplit) {
|
||||
// deviazione angolare tra primo e ultimo tratto del poligono di controllo (grado >= 1)
|
||||
Vector3d vtDirI = crvBez.m_vPtCtrl[1] - crvBez.m_vPtCtrl[0] ;
|
||||
Vector3d vtDirF = crvBez.m_vPtCtrl[m_nDeg] - crvBez.m_vPtCtrl[m_nDeg-1] ;
|
||||
@@ -1495,18 +1498,65 @@ CurveBezier::FlatOrSplit( int nLev, const CurveBezier& crvBez, double dParStart,
|
||||
// prima metà
|
||||
crvBez1 = crvBez ;
|
||||
crvBez1.TrimEndAtParam( dParDiv) ;
|
||||
if ( ! FlatOrSplit( nLev + 1, crvBez1, dParStart, dParMid, dLinTol, dAngTolDeg, PL))
|
||||
if ( ! FlatOrSplit( nLev + 1, crvBez1, dParStart, dParMid, dLinTol, dAngTolDeg, PL, dMaxLen))
|
||||
return false ;
|
||||
// seconda metà
|
||||
crvBez1 = crvBez ;
|
||||
crvBez1.TrimStartAtParam( dParDiv) ;
|
||||
if ( ! FlatOrSplit( nLev + 1, crvBez1, dParMid, dParEnd, dLinTol, dAngTolDeg, PL))
|
||||
if ( ! FlatOrSplit( nLev + 1, crvBez1, dParMid, dParEnd, dLinTol, dAngTolDeg, PL, dMaxLen))
|
||||
return false ;
|
||||
}
|
||||
|
||||
return true ;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool
|
||||
CurveBezier::ApproxWithLimitedLines( double dLinTol, double dAngTolDeg, int nType, double dMaxLen, PolyLine& PL) const
|
||||
{
|
||||
// pulisco la polilinea
|
||||
PL.Clear() ;
|
||||
|
||||
// la curva deve essere validata
|
||||
if ( m_nStatus != OK)
|
||||
return false ;
|
||||
|
||||
// se di primo grado, basta inserire gli estremi
|
||||
if ( m_nDeg == 1) {
|
||||
PL.AddUPoint( 0, m_vPtCtrl[0]) ;
|
||||
PL.AddUPoint( 1, m_vPtCtrl[m_nDeg]) ;
|
||||
PL.AdjustForMaxSegmentLen( dMaxLen) ;
|
||||
return true ;
|
||||
}
|
||||
|
||||
// limiti minimi su tolleranza e deviazione angolare
|
||||
dLinTol = max( dLinTol, LIN_TOL_MIN) ;
|
||||
dAngTolDeg = max( dAngTolDeg, ANG_TOL_MIN_DEG) ;
|
||||
|
||||
// inserisco il punto iniziale
|
||||
if ( ! PL.AddUPoint( 0, m_vPtCtrl[0]))
|
||||
return false ;
|
||||
|
||||
// verifico se va divisa
|
||||
if ( ! FlatOrSplit( 0, *this, 0, 1, dLinTol, dAngTolDeg, PL, dMaxLen))
|
||||
return false ;
|
||||
|
||||
// se è stato inserito un solo punto, aggiungo il finale
|
||||
if ( PL.GetPointNbr() == 1)
|
||||
return PL.AddUPoint( 1, m_vPtCtrl[m_nDeg]) ;
|
||||
// altrimenti, se l'ultimo punto non coincide con il finale lo sostituisco (distano al max di dLinTol)
|
||||
else {
|
||||
Point3d ptLast ;
|
||||
PL.GetLastPoint( ptLast) ;
|
||||
if ( ! AreSamePointApprox( ptLast, m_vPtCtrl[m_nDeg])) {
|
||||
PL.EraseLastUPoint() ;
|
||||
return PL.AddUPoint( 1, m_vPtCtrl[m_nDeg]) ;
|
||||
}
|
||||
}
|
||||
|
||||
return true ;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool
|
||||
CurveBezier::ApproxWithArcs( double dLinTol, double dAngTolDeg, PolyArc& PA) const
|
||||
|
||||
+2
-1
@@ -111,6 +111,7 @@ class CurveBezier : public ICurveBezier, public IGeoObjRW
|
||||
bool GetArea( Plane3d& plPlane, double& dArea) const override
|
||||
{ return CurveGetArea( *this, plPlane, dArea) ; }
|
||||
bool ApproxWithLines( double dLinTol, double dAngTolDeg, int nType, PolyLine& PL) const override ;
|
||||
bool ApproxWithLimitedLines( double dLinTol, double dAngTolDeg, int nType, double dMaxLen, PolyLine& PL) const override ;
|
||||
bool ApproxWithArcs( double dLinTol, double dAngTolDeg, PolyArc& PA) const override ;
|
||||
bool ApproxWithArcsEx( double dLinTol, double dAngTolDeg, double dLinFea, PolyArc& PA) const override
|
||||
{ return ApproxWithArcs( dLinTol, dAngTolDeg, PA) ; }
|
||||
@@ -188,7 +189,7 @@ class CurveBezier : public ICurveBezier, public IGeoObjRW
|
||||
bool GetSegmentParam( double dLen, double& dCurrLen, double& dSegLen,
|
||||
double& dUIni, double& dUFin) const ;
|
||||
bool FlatOrSplit( int nLev, const CurveBezier& crvBez, double dParStart, double dParEnd,
|
||||
double dLinTol, double dAngTolDeg, PolyLine& PL) const ;
|
||||
double dLinTol, double dAngTolDeg, PolyLine& PL, double dMaxLen = INFINITO) const ;
|
||||
bool ApproxWithArcsXY(double dLinTol, double dAngTolDeg, PolyArc& PA) const;
|
||||
bool BiArcOrSplit(int nLev, PolyLine& PL, double dLinTol, double dAngTolDeg, PolyArc& PA) const;
|
||||
bool ToPowerBase( PolynomialPoint3d& pol3P) const ;
|
||||
|
||||
@@ -1460,6 +1460,68 @@ CurveComposite::ApproxWithLines( double dLinTol, double dAngTolDeg, int nType, P
|
||||
return PL.RemoveAlignedPoints( dLinTol) ;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool
|
||||
CurveComposite::ApproxWithLimitedLines( double dLinTol, double dAngTolDeg, int nType, double dMaxLen, PolyLine& PL) const
|
||||
{
|
||||
// funziona solo con le modalità standard e special
|
||||
if ( nType != APL_STD && nType != APL_SPECIAL && nType != APL_SPECIAL_INT)
|
||||
return false ;
|
||||
|
||||
// pulisco la polilinea
|
||||
PL.Clear() ;
|
||||
|
||||
// verifico lo stato
|
||||
if ( m_nStatus != OK)
|
||||
return false ;
|
||||
|
||||
// limiti minimi su tolleranza e deviazione angolare
|
||||
dLinTol = max( dLinTol, LIN_TOL_MIN) ;
|
||||
dAngTolDeg = max( dAngTolDeg, ANG_TOL_MIN_DEG) ;
|
||||
|
||||
// se speciale, approssimo ogni singola entità e conservo le estremità interne (joint)
|
||||
if ( nType == APL_SPECIAL || nType == APL_SPECIAL_INT) {
|
||||
// eseguo approssimazione
|
||||
double dStartPar = 0 ;
|
||||
for ( auto& pCrv : m_CrvSmplS) {
|
||||
// assegno estrusione e spessore della curva composita
|
||||
pCrv->SetExtrusion( m_VtExtr) ;
|
||||
pCrv->SetThickness( m_dThick) ;
|
||||
// recupero approssimazione per curva semplice
|
||||
PolyLine PLSmpl ;
|
||||
if ( ! pCrv->ApproxWithLimitedLines( dLinTol, dAngTolDeg, nType, dMaxLen, PLSmpl))
|
||||
return false ;
|
||||
// se richiesto almeno un punto interno con curve non rettilinee e ci sono solo gli estremi
|
||||
if ( nType == APL_SPECIAL_INT && pCrv->GetType() != CRV_LINE && PLSmpl.GetPointNbr() == 2) {
|
||||
// aggiungo il punto interno
|
||||
Point3d ptMid ;
|
||||
if ( ! pCrv->GetMidPoint( ptMid))
|
||||
return false ;
|
||||
double dU ;
|
||||
PLSmpl.GetLastU( dU) ;
|
||||
dU /= 2 ;
|
||||
PNTULIST& List = PLSmpl.GetUPointList() ;
|
||||
List.insert( ++ List.begin(), { ptMid, dU}) ;
|
||||
}
|
||||
// ripristino estrusione e spessore della curva semplice (annullandoli)
|
||||
pCrv->SetExtrusion( V_NULL) ;
|
||||
pCrv->SetThickness( 0) ;
|
||||
// la accodo opportunamente a quella della curva composita
|
||||
if ( ! PL.Join( PLSmpl, dStartPar))
|
||||
return false ;
|
||||
// incremento inizio parametro per prossima curva semplice
|
||||
dStartPar += 1 ;
|
||||
}
|
||||
}
|
||||
else if ( nType == APL_STD) {
|
||||
if ( ! ApproxWithLimitedLines( dLinTol, dAngTolDeg, APL_SPECIAL, dMaxLen, PL))
|
||||
return false ;
|
||||
PL.RemoveAlignedPoints( dLinTol, false, dMaxLen) ;
|
||||
}
|
||||
|
||||
return true ;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool
|
||||
CurveComposite::ApproxWithArcs( double dLinTol, double dAngTolDeg, PolyArc& PA) const
|
||||
|
||||
@@ -112,6 +112,7 @@ class CurveComposite : public ICurveComposite, public IGeoObjRW
|
||||
bool GetArea( Plane3d& plPlane, double& dArea) const override
|
||||
{ return CurveGetArea( *this, plPlane, dArea) ; }
|
||||
bool ApproxWithLines( double dLinTol, double dAngTolDeg, int nType, PolyLine& PL) const override ;
|
||||
bool ApproxWithLimitedLines( double dLinTol, double dAngTolDeg, int nType, double dMaxLen, PolyLine& PL) const override ;
|
||||
bool ApproxWithArcs( double dLinTol, double dAngTolDeg, PolyArc& PA) const override ;
|
||||
bool ApproxWithArcsEx( double dLinTol, double dAngTolDeg, double dLinFea, PolyArc& PA) const override ;
|
||||
ICurve* CopyParamRange( double dUStart, double dUEnd) const override ;
|
||||
|
||||
@@ -514,6 +514,34 @@ CurveLine::ApproxWithLines( double dLinTol, double dAngTolDeg, int nType, PolyLi
|
||||
return true ;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool
|
||||
CurveLine::ApproxWithLimitedLines( double dLinTol, double dAngTolDeg, int nType, double dMaxLen, PolyLine& PL) const
|
||||
{
|
||||
// pulisco la polilinea
|
||||
PL.Clear() ;
|
||||
|
||||
// la curva deve essere validata
|
||||
if ( m_nStatus != OK)
|
||||
return false ;
|
||||
|
||||
// inserisco lo start
|
||||
PL.AddUPoint( 0, m_PtStart) ;
|
||||
|
||||
double dLen = Dist( m_PtStart, m_PtEnd) ;
|
||||
if ( dLen > dMaxLen) {
|
||||
int nStep = int( ceil( dLen / dMaxLen)) ;
|
||||
for ( int i = 1 ; i < nStep ; ++i) {
|
||||
double dPar = 1. / nStep * i ;
|
||||
PL.AddUPoint( dPar, Media( m_PtStart, m_PtEnd, dPar)) ;
|
||||
}
|
||||
}
|
||||
// inserisco l'end
|
||||
PL.AddUPoint( 1, m_PtEnd) ;
|
||||
|
||||
return true ;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool
|
||||
CurveLine::ApproxWithArcs( double dLinTol, double dAngTolDeg, PolyArc& PA) const
|
||||
|
||||
@@ -112,6 +112,7 @@ class CurveLine : public ICurveLine, public IGeoObjRW
|
||||
bool GetArea( Plane3d& plPlane, double& dArea) const override
|
||||
{ return false ; }
|
||||
bool ApproxWithLines( double dLinTol, double dAngTolDeg, int nType, PolyLine& PL) const override ;
|
||||
bool ApproxWithLimitedLines( double dLinTol, double dAngTolDeg, int nType, double dMaxLen, PolyLine& PL) const override ;
|
||||
bool ApproxWithArcs( double dLinTol, double dAngTolDeg, PolyArc& PA) const override ;
|
||||
bool ApproxWithArcsEx( double dLinTol, double dAngTolDeg, double dLinFea, PolyArc& PA) const override
|
||||
{ return ApproxWithArcs( dLinTol, dAngTolDeg, PA) ; }
|
||||
|
||||
Binary file not shown.
+475
-228
@@ -20,15 +20,17 @@
|
||||
#include "/EgtDev/Include/EGkPoint3d.h"
|
||||
#include "/EgtDev/Include/EGkPolyLine.h"
|
||||
#include "/EgtDev/Include/EGkOffsetCurve3d.h"
|
||||
#include "/EgtDev/Include/EGkIntersLinePlane.h"
|
||||
#include "/EgtDev/Include/EgtPointerOwner.h"
|
||||
#include <algorithm>
|
||||
|
||||
using namespace std ;
|
||||
|
||||
#define SAVECVRORIG 0
|
||||
#define SAVECRVORIG 0
|
||||
#define SAVEOFFDIR 0
|
||||
#define SAVECYL 0
|
||||
#if SAVECVRORIG || SAVEOFFDIR || SAVECYL
|
||||
#define SAVEOFFSET 0
|
||||
#if SAVECRVORIG || SAVEOFFDIR || SAVECYL || SAVEOFFSET
|
||||
#include "/EgtDev/Include/EGkColor.h"
|
||||
#include "/EgtDev/Include/EGkGeoVector3d.h"
|
||||
vector<IGeoObj*> vGeo ;
|
||||
@@ -38,20 +40,6 @@ using namespace std ;
|
||||
#include "/EgtDev/Include/EGkStmFromCurves.h"
|
||||
#endif
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
struct OffsetSegm {
|
||||
PtrOwner<ICurve> pCrv ;
|
||||
int nFlag ;
|
||||
int nParent ;
|
||||
OffsetSegm( ICurve* _pCrv, int _nFlag, int _nParent) :
|
||||
nFlag( _nFlag), nParent( _nParent) { pCrv.Set( _pCrv); } ;
|
||||
} ;
|
||||
|
||||
typedef vector<OffsetSegm> vOffsetSeg ;
|
||||
|
||||
bool AdjustConcavePartsInPath( const ICurveComposite* pCrv, vOffsetSeg& vOffsetCrvs, double dRad) ;
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
OffsetCurve3d::~OffsetCurve3d( void)
|
||||
{
|
||||
@@ -74,7 +62,7 @@ OffsetCurve3d::Reset( void)
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool
|
||||
OffsetCurve3d::Make( const PolyLine& PL, const VCT3DVECTOR& vOffDir, double dOffDist, int nType)
|
||||
OffsetCurve3d::Make( const PNT5AXVECTOR& vPnt5Ax, double dOffDist, int nType)
|
||||
{
|
||||
// la funzione è pensata per lavorare con il risultato dell'operazione ProjectCurveOnSurf
|
||||
|
||||
@@ -83,11 +71,24 @@ OffsetCurve3d::Make( const PolyLine& PL, const VCT3DVECTOR& vOffDir, double dOff
|
||||
|
||||
// pulisco tutto
|
||||
Reset() ;
|
||||
|
||||
INTVECTOR vFlag ;
|
||||
VCT3DVECTOR vOffDir ;
|
||||
PolyLine PL ;
|
||||
for ( int i = 0 ; i < ssize( vPnt5Ax) ; ++i) {
|
||||
PL.AddUPoint( i, vPnt5Ax[i].ptP) ;
|
||||
vOffDir.push_back( vPnt5Ax[i].vtDir1) ;
|
||||
if ( vPnt5Ax[i].nFlag > 0)
|
||||
vFlag.push_back( vPnt5Ax[i].nFlag) ;
|
||||
else
|
||||
return false ;
|
||||
}
|
||||
|
||||
PtrOwner<CurveComposite> pCrv( CreateBasicCurveComposite()) ;
|
||||
if ( ! pCrv->FromPolyLine( PL))
|
||||
return false ;
|
||||
|
||||
#if SAVECVRORIG || SAVEOFFDIR
|
||||
#if SAVECRVORIG || SAVEOFFDIR
|
||||
vGeo.clear() ;
|
||||
vCol.clear() ;
|
||||
|
||||
@@ -103,6 +104,9 @@ OffsetCurve3d::Make( const PolyLine& PL, const VCT3DVECTOR& vOffDir, double dOff
|
||||
vCol.push_back( BLUE) ;
|
||||
}
|
||||
#endif
|
||||
#if ! SAVECYL && ! SAVEOFFSET
|
||||
SaveGeoObj( vGeo, vCol, "C:\\Temp\\curve offset 3d\\crvoffset.nge") ;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// verifico se la curva è un segmento di retta
|
||||
@@ -126,23 +130,45 @@ OffsetCurve3d::Make( const PolyLine& PL, const VCT3DVECTOR& vOffDir, double dOff
|
||||
|
||||
bool bClosed = pCrv->IsClosed() ;
|
||||
|
||||
INTVECTOR vFlag ;
|
||||
vFlag.push_back( OffsetCurve3d::AngType::ANG_STR) ;
|
||||
const ICurve* pSubCrv = pCrv->GetFirstCurve() ;
|
||||
const double dSinAngSmall = sin( 1 * DEGTORAD) ;
|
||||
for ( int i = 1 ; i < pCrv->GetCurveCount() ; ++i) {
|
||||
pSubCrv = pCrv->GetNextCurve() ;
|
||||
Vector3d vtDirCurr ; pSubCrv->GetStartDir( vtDirCurr) ;
|
||||
vtDirCurr.Normalize() ;
|
||||
int nFlag ;
|
||||
double dProj = vtDirCurr * vOffDir[i-1] ;
|
||||
if ( dProj > dSinAngSmall)
|
||||
nFlag = OffsetCurve3d::AngType::ANG_SMOOTH_CONC ;
|
||||
else if ( dProj > - dSinAngSmall)
|
||||
nFlag = OffsetCurve3d::AngType::ANG_STR ;
|
||||
else
|
||||
nFlag = OffsetCurve3d::AngType::ANG_CVEX ;
|
||||
vFlag.push_back( nFlag) ;
|
||||
// identifico le zone circostanti un angolo interno
|
||||
for ( int i = 0 ; i < ssize( vPnt5Ax) ; ++i) {
|
||||
if ( vFlag[i] == OffsetCurve3d::AngType::ANG_CONC) {
|
||||
// scorro indietro e avanti flaggando i tratti da controllare
|
||||
int nPrev = i - 1 ;
|
||||
if ( nPrev < 0)
|
||||
nPrev = ssize( vPnt5Ax) - 1;
|
||||
int nNext = i + 1 ;
|
||||
if ( nNext >= ssize( vPnt5Ax))
|
||||
nNext = 0 ;
|
||||
double dAng = acos( vPnt5Ax[nPrev].vtDir1 * vPnt5Ax[nNext].vtDir1) * RADTODEG ;
|
||||
double dDistAngConc = dOffDist * tan( ( dAng / 2) * DEGTORAD) ;
|
||||
int c = nPrev ;
|
||||
while ( Dist( vPnt5Ax[i].ptP, vPnt5Ax[c].ptP) < dDistAngConc) {
|
||||
vFlag[c] = OffsetCurve3d::AngType::ANG_BEFORE_CONC ;
|
||||
--c ;
|
||||
if ( c < 0) {
|
||||
if ( bClosed)
|
||||
c = ssize( vPnt5Ax) - 1 ;
|
||||
else
|
||||
break ;
|
||||
}
|
||||
}
|
||||
vFlag[c] = OffsetCurve3d::AngType::ANG_BEFORE_CONC ;
|
||||
// scorro in avanti
|
||||
c = nNext ;
|
||||
while ( c < ssize( vPnt5Ax) - 1 && Dist( vPnt5Ax[i].ptP, vPnt5Ax[c].ptP) < dDistAngConc) {
|
||||
vFlag[c] = OffsetCurve3d::AngType::ANG_AFTER_CONC ;
|
||||
++c ;
|
||||
if ( c == ssize( vPnt5Ax) - 1) {
|
||||
if ( bClosed)
|
||||
c = 0 ;
|
||||
else
|
||||
break ;
|
||||
}
|
||||
}
|
||||
vFlag[c] = OffsetCurve3d::AngType::ANG_AFTER_CONC ;
|
||||
i = c ;
|
||||
}
|
||||
}
|
||||
|
||||
for ( int i = 1 ; i < ssize( vFlag) - 1 ; ++i) {
|
||||
@@ -155,43 +181,70 @@ OffsetCurve3d::Make( const PolyLine& PL, const VCT3DVECTOR& vOffDir, double dOff
|
||||
double dRadCorr ;
|
||||
Point3d ptPrev ; pCrv->GetStartPoint( ptPrev) ;
|
||||
ptPrev += vOffDir[0] * dOffDist ;
|
||||
Vector3d vtNormPrev ;
|
||||
Vector3d vtCorrPrev ;
|
||||
Vector3d vtTangPrev ;
|
||||
Vector3d vtDirPrev ; pCrv->GetStartDir( vtDirPrev) ;
|
||||
if ( bClosed) {
|
||||
Vector3d vtDirLast ; pCrv->GetEndDir( vtDirLast) ;
|
||||
if ( vFlag[0] != OffsetCurve3d::AngType::ANG_CVEX)
|
||||
vtTangPrev = Media( vtDirLast, vtDirPrev) ;
|
||||
else if ( vFlag[0] == OffsetCurve3d::AngType::ANG_CVEX && vFlag.back() != OffsetCurve3d::AngType::ANG_CVEX)
|
||||
vtTangPrev = vtDirLast ;
|
||||
else
|
||||
vtTangPrev = vtDirPrev ;
|
||||
}
|
||||
const ICurve* pCrvPrev = pCrv->GetFirstCurve() ;
|
||||
const ICurve* pCrvCurr ;
|
||||
vOffsetSeg vOffsetCrvs ;
|
||||
OFFSETSEGVEC vOffsetCrvs ;
|
||||
Vector3d vtDirPrevOff = V_INVALID ;
|
||||
for ( int i = 1 ; i < pCrv->GetCurveCount() ; ++i) {
|
||||
pCrvCurr = pCrv->GetNextCurve() ;
|
||||
Vector3d vtOffDir = vOffDir[i] ;
|
||||
Vector3d vtDirCurr ; pCrvCurr->GetStartDir( vtDirCurr) ;
|
||||
int nRejected = 0 ;
|
||||
int nClosure = bClosed ? 1 : 0 ;
|
||||
bool bCheckingClosure = false ;
|
||||
bool bPlanarConcCvex = false ;
|
||||
for ( int i = 1 ; i <= pCrv->GetCurveCount() + nClosure ; ++i) {
|
||||
int nCurr = i ;
|
||||
int nPrev = i - 1 ;
|
||||
if ( i > pCrv->GetCurveCount()) {
|
||||
nCurr = 0 ;
|
||||
bCheckingClosure = true ;
|
||||
}
|
||||
if ( ! bCheckingClosure)
|
||||
pCrvCurr = pCrv->GetNextCurve() ;
|
||||
if ( pCrvCurr == nullptr && bClosed)
|
||||
pCrvCurr = pCrv->GetFirstCurve() ;
|
||||
Vector3d vtOffDir = vOffDir[nCurr] ;
|
||||
Vector3d vtDirCurr ;
|
||||
if ( pCrvCurr != nullptr)
|
||||
pCrvCurr->GetStartDir( vtDirCurr) ;
|
||||
else
|
||||
vtDirCurr = vtDirPrev ;
|
||||
pCrvPrev->GetStartDir( vtDirPrev) ;
|
||||
Vector3d vtTang ;
|
||||
if ( vFlag[i] != OffsetCurve3d::AngType::ANG_CVEX)
|
||||
// so che ci sono due punti in stretta prossimità dell'angolo, quindi discrimino tra la tangeten prima e dopo l'angolo
|
||||
// negli altri casi faccio la media
|
||||
if ( vFlag[nCurr] != OffsetCurve3d::AngType::ANG_CVEX)
|
||||
vtTang = Media( vtDirCurr, vtDirPrev) ;
|
||||
else if ( vFlag[i] == OffsetCurve3d::AngType::ANG_CVEX && vFlag[i-1] != OffsetCurve3d::AngType::ANG_CVEX)
|
||||
else if ( vFlag[nCurr] == OffsetCurve3d::AngType::ANG_CVEX && vFlag[nPrev] != OffsetCurve3d::AngType::ANG_CVEX)
|
||||
vtTang = vtDirPrev ;
|
||||
else
|
||||
vtTang = vtDirCurr ;
|
||||
vtTang.Normalize() ;
|
||||
Vector3d vtNorm = vtOffDir ;
|
||||
vtNorm.Rotate( vtTang, -90) ;
|
||||
//Vector3d vtCorr = vtTang ^ vtNorm ; vtCorr.Normalize() ;
|
||||
// devo invertire vtCorr??? se sì, quando?/////////////////////////////////////////
|
||||
Vector3d vtCorr = vtOffDir ;
|
||||
double dCorrK = 1 ;
|
||||
if ( vFlag[i] == OffsetCurve3d::AngType::ANG_CONC) {
|
||||
double dHalfAlfa = acos( vtTang * vtTangPrev) ;
|
||||
dCorrK = 1 / sin( 90 - dHalfAlfa) ;
|
||||
}
|
||||
//if ( vFlag[nCurr] == OffsetCurve3d::AngType::ANG_CONC) {
|
||||
// double dHalfAlfa = acos( vtTang * vtTangPrev) * RADTODEG ;
|
||||
// dCorrK = 1 / sin( ( 90 - dHalfAlfa) * DEGTORAD) ;
|
||||
//}
|
||||
|
||||
Point3d ptP ; pCrvCurr->GetStartPoint( ptP) ;
|
||||
Point3d ptP ;
|
||||
if ( pCrvCurr != nullptr)
|
||||
pCrvCurr->GetStartPoint( ptP) ;
|
||||
else
|
||||
pCrvPrev->GetEndPoint( ptP) ;
|
||||
dRadCorr = dOffDist ;
|
||||
ptP = ptP + dRadCorr * dCorrK * vtCorr ;
|
||||
// se secondo punto di angolo esterno di fianco, inserisco movimenti intermedi
|
||||
if ( vFlag[i] == OffsetCurve3d::AngType::ANG_CVEX && vFlag[i-1] == OffsetCurve3d::AngType::ANG_CVEX) {
|
||||
if ( vFlag[nCurr] == OffsetCurve3d::AngType::ANG_CVEX && vFlag[nPrev] == OffsetCurve3d::AngType::ANG_CVEX && bPlanarConcCvex) {
|
||||
double dAlfa = acos( vtTang * vtTangPrev) ;
|
||||
double dDelta = dOffDist * tan( dAlfa / 4) ;
|
||||
Point3d ptAdd1 = ptPrev + dDelta * vtTangPrev ;
|
||||
@@ -204,77 +257,111 @@ OffsetCurve3d::Make( const PolyLine& PL, const VCT3DVECTOR& vOffDir, double dOff
|
||||
vOffsetCrvs.emplace_back( pCL2, OffsetCurve3d::AngType::ANG_CVEX, -1) ;
|
||||
ptPrev = ptAdd2 ;
|
||||
}
|
||||
Vector3d vtAng = vtDirPrev ^ vtDirCurr ;
|
||||
const double COS_ANG_MAX_PLANAR = 0.7 ;
|
||||
bPlanarConcCvex = false ;
|
||||
if ( ! vtAng.IsSmall() && vtAng.Normalize())
|
||||
bPlanarConcCvex = abs( vtAng * vOffDir[nCurr]) < COS_ANG_MAX_PLANAR ;
|
||||
|
||||
//// se punto di angolo interno di fianco, elimino eventuali movimenti precedenti invertiti
|
||||
//if ( vFlag[i] == OffsetCurve3d::AngType::ANG_CVEX == 3) {
|
||||
// local nLastId = EgtGetLastInGroup( nClPathId)
|
||||
// while nLastId do
|
||||
// local vtMlast = ptP - EgtEP( nLastId, GDB_ID.ROOT) ; vtMlast:normalize()
|
||||
// if vtMlast * vtGpre < 0.5 then
|
||||
// ptPpre = EgtSP( nLastId, GDB_ID.ROOT)
|
||||
// EgtErase( nLastId)
|
||||
// else
|
||||
// break
|
||||
// end
|
||||
// nLastId = EgtGetLastInGroup( nClPathId)
|
||||
// end
|
||||
//}
|
||||
|
||||
//// se appena dopo angolo interno di fianco, verifico se da aggiungere
|
||||
//bool bToAdd = true
|
||||
//if ( nFlpre == 3 and abs( dSideAng) > GEO.EPS_ANG_SMALL) {
|
||||
// local vtMove = ptP - ptPpre ; vtMove:normalize()
|
||||
// if vtMove * vtTang < 0.5 then
|
||||
// bToAdd = false
|
||||
// end
|
||||
//}
|
||||
|
||||
Vector3d vtDirCurrOff = ptP - ptPrev ; vtDirCurrOff.Normalize() ;
|
||||
double dProj = 1 ;
|
||||
if ( vtDirPrevOff.IsValid())
|
||||
dProj = vtDirCurrOff * vtDirPrevOff ;
|
||||
if ( dProj > - 0.5 || vFlag[i] == OffsetCurve3d::AngType::ANG_SMOOTH_CONC) {
|
||||
// aggiungo tratto
|
||||
ICurveLine* pCL = CreateBasicCurveLine() ;
|
||||
pCL->Set( ptPrev, ptP) ;
|
||||
vOffsetCrvs.emplace_back( pCL, vFlag[i], i - 1) ;
|
||||
// aggiorno punto precedente
|
||||
ptPrev = ptP ;
|
||||
vtNormPrev = vtNorm ;
|
||||
vtCorrPrev = vtCorr ;
|
||||
vtTangPrev = vtTang ;
|
||||
vtDirPrev = vtDirCurr ;
|
||||
pCrvPrev = pCrvCurr ;
|
||||
vtDirPrevOff = vtDirCurrOff ;
|
||||
if ( ! bCheckingClosure) {
|
||||
Vector3d vtDirCurrOff = ptP - ptPrev ; vtDirCurrOff.Normalize() ;
|
||||
double dProj = 1 ;
|
||||
if ( vtDirPrevOff.IsValid())
|
||||
dProj = vtDirCurrOff * vtDirPrevOff ;
|
||||
// prima di inserirlo controllo che il tratto non torni indietro
|
||||
if ( ( vFlag[nCurr] != ANG_STR || dProj > - 0.966) && Dist( ptPrev, ptP) > 2 * EPS_SMALL) {
|
||||
// aggiungo tratto
|
||||
ICurveLine* pCL = CreateBasicCurveLine() ;
|
||||
pCL->Set( ptPrev, ptP) ;
|
||||
vOffsetCrvs.emplace_back( pCL, vFlag[nCurr], i - 1) ;
|
||||
// aggiorno punto precedente
|
||||
ptPrev = ptP ;
|
||||
vtCorrPrev = vtCorr ;
|
||||
vtTangPrev = vtTang ;
|
||||
vtDirPrev = vtDirCurr ;
|
||||
if ( i < pCrv->GetCurveCount())
|
||||
pCrvPrev = pCrvCurr ;
|
||||
vtDirPrevOff = vtDirCurrOff ;
|
||||
}
|
||||
else
|
||||
++nRejected ;
|
||||
}
|
||||
}
|
||||
|
||||
// se chiusa aggiungo il tratto di chiusura
|
||||
if ( bClosed) {
|
||||
Point3d ptP ; vOffsetCrvs.front().pCrv->GetStartPoint( ptP) ;
|
||||
Point3d ptPS ; vOffsetCrvs.front().pCrv->GetStartPoint( ptPS) ;
|
||||
ICurveLine* pCL = CreateBasicCurveLine() ;
|
||||
pCL->Set( ptPrev, ptP) ;
|
||||
vOffsetCrvs.emplace_back( pCL, OffsetCurve3d::AngType::ANG_STR, -1) ;
|
||||
Point3d ptPE ; vOffsetCrvs.back().pCrv->GetEndPoint( ptPE) ;
|
||||
pCL->Set( ptPE, ptPS) ;
|
||||
// aggiungo solo se valida, quindi se non sono già coincidenti
|
||||
if ( pCL->IsValid())
|
||||
vOffsetCrvs.emplace_back( pCL, OffsetCurve3d::AngType::ANG_STR, -1) ;
|
||||
}
|
||||
|
||||
// qui faccio la correzione per gli angoli interni
|
||||
if ( ! AdjustConcavePartsInPath( pCrv, vOffsetCrvs, dOffDist))
|
||||
EDITCRVINFOVEC vEditInfo ;
|
||||
if ( ! CalcAdjustConcavePartsInPath( pCrv, vOffsetCrvs, dOffDist, vEditInfo))
|
||||
return false ;
|
||||
|
||||
#if SAVECVRORIG || SAVEOFFDIR || SAVECYL
|
||||
for ( int i = 0 ; i < ssize( vOffsetCrvs) ; ++i) {
|
||||
vGeo.push_back( vOffsetCrvs[i].pCrv->Clone()) ;
|
||||
if ( vOffsetCrvs[i].nFlag == OffsetCurve3d::AngType::ANG_SMOOTH_CONC)
|
||||
vCol.push_back( GREEN) ;
|
||||
else if ( vOffsetCrvs[i].nFlag == OffsetCurve3d::AngType::ANG_STR)
|
||||
vCol.push_back( PURPLE) ;
|
||||
else if ( vOffsetCrvs[i].nFlag == OffsetCurve3d::AngType::ANG_CVEX)
|
||||
vCol.push_back( RED) ;
|
||||
if ( nType == OFFTYPE::PLANECUT) {
|
||||
if ( ! CalcAdjustConcavePartsPlaneCut( pCrv, vPnt5Ax, vOffsetCrvs, dOffDist, vEditInfo))
|
||||
return false ;
|
||||
}
|
||||
SaveGeoObj( vGeo, vCol, "C:\\Temp\\curve offset 3d\\crvoffset.nge") ;
|
||||
return true ;
|
||||
#endif
|
||||
|
||||
// applico le modifiche calcolate
|
||||
for ( int i = ssize( vEditInfo) - 1 ; i >= 0 ; --i) {
|
||||
if ( vEditInfo[i].nFlag == EditCrvInfo::NOEDIT)
|
||||
continue ;
|
||||
else if ( vEditInfo[i].nFlag == EditCrvInfo::DEL)
|
||||
vOffsetCrvs.erase( vOffsetCrvs.begin() + i) ;
|
||||
else if ( vEditInfo[i].nFlag == EditCrvInfo::EDIT) {
|
||||
if ( vEditInfo[i].ptStart.IsValid())
|
||||
vOffsetCrvs[vEditInfo[i].nId].pCrv->ModifyStart( vEditInfo[i].ptStart) ;
|
||||
if ( vEditInfo[i].ptEnd.IsValid())
|
||||
vOffsetCrvs[vEditInfo[i].nId].pCrv->ModifyEnd( vEditInfo[i].ptEnd) ;
|
||||
vOffsetCrvs[vEditInfo[i].nId].nParent = -2 ;
|
||||
}
|
||||
}
|
||||
|
||||
// scorro tutto il vettore delle linee di offset e unisco aggiungendo una linea dove ne ho cancellate
|
||||
for ( int i = 0 ; i < ssize( vOffsetCrvs) - 1 ; ++i) {
|
||||
Point3d ptEndCurr, ptStartNext ;
|
||||
vOffsetCrvs[i].pCrv->GetEndPoint( ptEndCurr) ;
|
||||
vOffsetCrvs[i+1].pCrv->GetStartPoint( ptStartNext) ;
|
||||
if ( ! AreSamePointApprox( ptEndCurr, ptStartNext)) {
|
||||
ICurveLine* pCL = CreateBasicCurveLine() ;
|
||||
pCL->Set( ptEndCurr, ptStartNext) ;
|
||||
vOffsetCrvs.emplace_back( pCL, OffsetCurve3d::AngType::ANG_STR,-1) ;
|
||||
rotate( vOffsetCrvs.begin() + i + 1, vOffsetCrvs.end() - 1, vOffsetCrvs.end()) ;
|
||||
++i ;
|
||||
}
|
||||
}
|
||||
|
||||
#if SAVEOFFSET || SAVECYL
|
||||
#if SAVEOFFSET
|
||||
for ( int i = 0 ; i < ssize( vOffsetCrvs) ; ++i) {
|
||||
vGeo.push_back( vOffsetCrvs[i].pCrv->Clone()) ;
|
||||
if ( vOffsetCrvs[i].nParent == -2)
|
||||
vCol.push_back( LIME) ;
|
||||
else if ( vOffsetCrvs[i].nFlag == OffsetCurve3d::AngType::ANG_SMOOTH_CONC)
|
||||
vCol.push_back( GREEN) ;
|
||||
else if ( vOffsetCrvs[i].nFlag == OffsetCurve3d::AngType::ANG_STR)
|
||||
vCol.push_back( PURPLE) ;
|
||||
else if ( vOffsetCrvs[i].nFlag == OffsetCurve3d::AngType::ANG_CVEX)
|
||||
vCol.push_back( RED) ;
|
||||
else if ( vOffsetCrvs[i].nFlag == OffsetCurve3d::AngType::ANG_BEFORE_CONC)
|
||||
vCol.push_back( OLIVE) ;
|
||||
else if ( vOffsetCrvs[i].nFlag == OffsetCurve3d::AngType::ANG_AFTER_CONC)
|
||||
vCol.push_back( YELLOW) ;
|
||||
else if ( vOffsetCrvs[i].nFlag == OffsetCurve3d::AngType::ANG_CONC)
|
||||
vCol.push_back( Color(0,64,0)) ; //dark green
|
||||
}
|
||||
#endif
|
||||
SaveGeoObj( vGeo, vCol, "C:\\Temp\\curve offset 3d\\crvoffset.nge") ;
|
||||
#endif
|
||||
|
||||
// qua andrebbe gestito con una chain ( ptorebbero essere più di una curva)
|
||||
PtrOwner<ICurveComposite> pCrvOffset( CreateBasicCurveComposite()) ;
|
||||
for ( int i = 0 ; i < ssize( vOffsetCrvs) ; ++i) {
|
||||
if ( ! pCrvOffset->AddCurve( Release( vOffsetCrvs[i].pCrv)))
|
||||
@@ -284,21 +371,8 @@ OffsetCurve3d::Make( const PolyLine& PL, const VCT3DVECTOR& vOffDir, double dOff
|
||||
|
||||
return true ;
|
||||
|
||||
|
||||
// raccordi
|
||||
|
||||
// angoli interni
|
||||
|
||||
// angoli esterni
|
||||
|
||||
// auto intersezioni
|
||||
|
||||
//// sesto passo : se curva aperta, elimino i tratti che stanno nella circonferenza di offset dei punti estremi
|
||||
|
||||
//// ottavo passo : concateno i percorsi risultanti (senza cambiare verso)
|
||||
|
||||
//// nono passo : se con smusso o estensione, sostituisco i fillet con questi
|
||||
|
||||
//// ordino le curve in ordine decrescente di lunghezza
|
||||
//if ( m_CrvLst.size() > 1) {
|
||||
// for ( auto pCrv : m_CrvLst) {
|
||||
@@ -354,22 +428,6 @@ OffsetCurve3d::GetShorterCurve( void)
|
||||
return pCrv ;
|
||||
}
|
||||
|
||||
struct Cyl {
|
||||
Cyl( void): frCyl( GLOB_FRM), dH( 0.), dRad( 0.) {;} ;
|
||||
Cyl( const Frame3d& _frCyl, double _dH, double _dRad, double _dLinTol) :
|
||||
frCyl( _frCyl), dH( _dH), dRad( _dRad) { ;}
|
||||
Cyl( const Point3d& _ptBase, const Vector3d& vtZ, double _dH, double _dRad, double _dLinTol) :
|
||||
dH( _dH), dRad( _dRad){
|
||||
frCyl.Set( _ptBase, vtZ); }
|
||||
public :
|
||||
Frame3d frCyl ;
|
||||
public:
|
||||
double dH ;
|
||||
double dRad ;
|
||||
};
|
||||
|
||||
typedef vector<Cyl> CYLVECT ;
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool
|
||||
IsPointInsideCylinder( const Point3d& ptTest, const Cyl& offCyl, double dLinTol)
|
||||
@@ -384,21 +442,46 @@ IsPointInsideCylinder( const Point3d& ptTest, const Cyl& offCyl, double dLinTol)
|
||||
return true ;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool
|
||||
AdjustConcavePartsInPath( const ICurveComposite* pCrv, vOffsetSeg& vOffsetCrvs, double dRad)
|
||||
CalcAdjustConcavePartsInPath( const ICurveComposite* pCrv, const OFFSETSEGVEC& vOffsetCrvs, double dRad, EDITCRVINFOVEC& vEditInfo)
|
||||
{
|
||||
const double dLinTol = 5 * EPS_SMALL ;
|
||||
INTVECTOR vErase ;
|
||||
const double dLinTol = 0 * EPS_SMALL ;
|
||||
for ( int i = 0 ; i < ssize( vOffsetCrvs) ; ++i) {
|
||||
int nFlag = vOffsetCrvs[i].nFlag ;
|
||||
if ( nFlag == OffsetCurve3d::AngType::ANG_SMOOTH_CONC) {
|
||||
// considero tutt le zone concave
|
||||
if ( nFlag >= OffsetCurve3d::AngType::ANG_CONC) {
|
||||
// scorro i prossimi finchè trovo la fine della zona concava
|
||||
// controllo se devo considerare anche tratti prima dello start
|
||||
INTVECTOR vLines ;
|
||||
while ( nFlag == OffsetCurve3d::AngType::ANG_SMOOTH_CONC) {
|
||||
if ( i == 0) {
|
||||
INTVECTOR vLinesAdd ;
|
||||
int c = ssize( vOffsetCrvs) - 1 ;
|
||||
nFlag = vOffsetCrvs[c].nFlag ;
|
||||
while ( nFlag >= OffsetCurve3d::AngType::ANG_CONC) {
|
||||
vLinesAdd.push_back( c) ;
|
||||
--c ;
|
||||
if ( c > 0)
|
||||
nFlag = vOffsetCrvs[c].nFlag ;
|
||||
}
|
||||
// inserisco il vettore al contrario, in modo che sia in ordine crescente
|
||||
vLines.insert( vLines.end(), vLinesAdd.rbegin(), vLinesAdd.rend()) ;
|
||||
}
|
||||
bool bDone = false ;
|
||||
nFlag = vOffsetCrvs[i].nFlag ;
|
||||
while ( nFlag >= OffsetCurve3d::AngType::ANG_CONC) {
|
||||
vLines.push_back( i) ;
|
||||
++i ;
|
||||
nFlag = vOffsetCrvs[i].nFlag ;
|
||||
if ( i < ssize( vOffsetCrvs))
|
||||
nFlag = vOffsetCrvs[i].nFlag ;
|
||||
else {
|
||||
bDone = true ;
|
||||
break ;
|
||||
}
|
||||
}
|
||||
// se sto ricominciando, esco dal for ( il tratto finale è già stato considerato insieme al tratto iniziale)
|
||||
if ( bDone)
|
||||
break ;
|
||||
CYLVECT vCyl ;
|
||||
// creo un cilindro della dimensione del raggio
|
||||
for ( int j = 0 ; j < ssize( vLines) ; ++j) {
|
||||
@@ -408,13 +491,14 @@ AdjustConcavePartsInPath( const ICurveComposite* pCrv, vOffsetSeg& vOffsetCrvs,
|
||||
Point3d ptStart, ptEnd ;
|
||||
pSubCrv->GetStartPoint( ptStart) ;
|
||||
pSubCrv->GetEndPoint( ptEnd) ;
|
||||
// cilindri con asse sul parent
|
||||
Vector3d vtHeight = ptEnd - ptStart ;
|
||||
double dHeight = vtHeight.Len() ;
|
||||
vtHeight.Normalize() ;
|
||||
vCyl.emplace_back( ptStart, vtHeight, dHeight, dRad, dLinTol) ;
|
||||
#if SAVECYL
|
||||
CurveArc ca ; ca.Set( ptStart, vtHeight, dRad) ;
|
||||
ISurfTriMesh* pSurfTm = GetSurfTriMeshByExtrusion( &ca, vtHeight, false, 2 * EPS_SMALL) ;
|
||||
ISurfTriMesh* pSurfTm = GetSurfTriMeshByExtrusion( &ca, vtHeight * dHeight, false, 2 * EPS_SMALL) ;
|
||||
vGeo.push_back( pSurfTm) ;
|
||||
vCol.push_back( LGRAY) ;
|
||||
#endif
|
||||
@@ -423,8 +507,8 @@ AdjustConcavePartsInPath( const ICurveComposite* pCrv, vOffsetSeg& vOffsetCrvs,
|
||||
// controllo l'end di ogni linea per verificare se sta nel cilindro definito da uno degli altri tratti
|
||||
// controllo tutto i punti
|
||||
bool bErasedSomePart = false ;
|
||||
bool bErasedPrev = false ;
|
||||
INTVECTOR vInters ;
|
||||
bool bCheckStart = false ;
|
||||
vector<INTINTVECTOR> vEditZones ;
|
||||
for ( int j = 0 ; j < ssize( vLines) ; ++j) {
|
||||
Point3d ptStart, ptEnd ;
|
||||
const ICurve* pSubCrv = vOffsetCrvs[vLines[j]].pCrv ;
|
||||
@@ -433,115 +517,278 @@ AdjustConcavePartsInPath( const ICurveComposite* pCrv, vOffsetSeg& vOffsetCrvs,
|
||||
pSubCrv->GetEndPoint( ptEnd) ;
|
||||
pSubCrv->GetStartPoint( ptStart) ;
|
||||
// se stanno in uno dei cilindri degli altri tratti della zona concava
|
||||
for ( int k = 0 ; k < ssize( vLines) ; ++k) {
|
||||
bool bToErase = false ;
|
||||
for ( int k = 0 ; k < ssize( vCyl) ; ++k) {
|
||||
if ( j == k)
|
||||
continue ;
|
||||
bool bToErase = IsPointInsideCylinder( ptEnd, vCyl[k], dLinTol) ;
|
||||
if ( bErasedPrev && ! bToErase)
|
||||
bToErase = bToErase || IsPointInsideCylinder( ptStart, vCyl[k], dLinTol) ;
|
||||
bToErase = IsPointInsideCylinder( ptEnd, vCyl[k], dLinTol) ;
|
||||
bool bStartInsideCyl = false ;
|
||||
if ( bCheckStart && ! bToErase) {
|
||||
bStartInsideCyl = IsPointInsideCylinder( ptStart, vCyl[k], dLinTol) ;
|
||||
bToErase = bToErase || bStartInsideCyl ;
|
||||
}
|
||||
if ( bToErase) {
|
||||
bErasedSomePart = true ;
|
||||
bErasedPrev = true ;
|
||||
vInters.push_back( vLines[j]) ;
|
||||
if ( j < ssize( vLines) - 1)
|
||||
vInters.push_back( vLines[j+1]) ;
|
||||
++j ;
|
||||
if ( ! bStartInsideCyl)
|
||||
bCheckStart = true ;
|
||||
|
||||
if ( vEditZones.empty() || vEditZones.back().back().first != vLines[j-1])
|
||||
vEditZones.emplace_back() ;
|
||||
vEditZones.back().emplace_back( vLines[j], j) ;
|
||||
if ( ! bStartInsideCyl && j < ssize( vLines) - 1) {
|
||||
vEditZones.back().emplace_back( vLines[j+1], j + 1) ;
|
||||
++j ;
|
||||
}
|
||||
break ;
|
||||
}
|
||||
else
|
||||
bErasedPrev = false ;
|
||||
}
|
||||
if ( ! bToErase) {
|
||||
bCheckStart = false ;
|
||||
vEditInfo.emplace_back( vLines[j], EditCrvInfo::NOEDIT) ;
|
||||
}
|
||||
}
|
||||
if ( bErasedSomePart) {
|
||||
// calcolo le intersezioni effettive del primo e ultimo tratto cancellati con i cilindri che li hanno cancellati
|
||||
// controllo che effettivamente tutti i tratti cancellati siano consecutivi
|
||||
for ( int j = 1 ; j < ssize( vInters) ; ++j) {
|
||||
if ( vInters[j] != vInters[j-1] + 1)
|
||||
return false ;
|
||||
}
|
||||
for ( int j = 0 ; j < ssize( vInters) ; ++j) {
|
||||
// cancello i tratti intermedi
|
||||
if ( j > 0 && j < ssize( vInters) - 1) {
|
||||
vErase.push_back( vInters[j]) ;
|
||||
for ( int z = 0 ; z < ssize( vEditZones) ; ++z) {
|
||||
INTINTVECTOR& vInters = vEditZones[z] ;
|
||||
if ( ssize( vInters) == 1)
|
||||
continue ;
|
||||
}
|
||||
// per il primo e ultimo controllo le intersezioni con tutti i cilindri
|
||||
ICurve* pCL = vOffsetCrvs[vInters[j]].pCrv ;
|
||||
Point3d ptStart ; pCL->GetStartPoint( ptStart) ;
|
||||
Vector3d vtStart ; pCL->GetStartDir( vtStart) ;
|
||||
double dLen ; pCL->GetLength( dLen) ;
|
||||
double dUTrim = ( j == 0 ? INFINITO : 0) ;
|
||||
Point3d ptTrim = P_INVALID ;
|
||||
for ( int k = 0 ; k < ssize( vCyl) ; ++k) {
|
||||
if ( vInters[j] == k)
|
||||
for ( int j = 0 ; j < ssize( vInters) ; ++j) {
|
||||
// cancello i tratti intermedi
|
||||
if ( j > 0 && j < ssize( vInters) - 1) {
|
||||
vEditInfo.emplace_back( vInters[j].first, EditCrvInfo::DEL) ;
|
||||
continue ;
|
||||
Point3d ptInt1 = P_INVALID, ptInt2 = P_INVALID ;
|
||||
double dU1, dU2 ;
|
||||
Vector3d vtN1, vtN2 ;
|
||||
if ( IntersLineCyl( ptStart, vtStart * dLen, vCyl[k].frCyl, vCyl[k].dH, vCyl[k].dRad, false, false, dU1, ptInt1, vtN1, dU2, ptInt2, vtN2, true)) {
|
||||
bool bUpdate = ( j == 0 ? dU1 < dUTrim : dU1 > dUTrim) ;
|
||||
bUpdate = bUpdate && ptInt1.IsValid() && dU1 > 0 && dU1 < 1 ;
|
||||
bUpdate = bUpdate && vtN1 * vtStart < 0 ;
|
||||
if ( bUpdate) {
|
||||
dUTrim = dU1 ;
|
||||
ptTrim = ptInt1 ;
|
||||
}
|
||||
bUpdate = ( j == 0 ? dU2 < dUTrim : dU2 > dUTrim) ;
|
||||
bUpdate = bUpdate && ptInt2.IsValid() && dU2 > 0 && dU2 < 1 ;
|
||||
bUpdate = bUpdate && vtN2 * vtStart > 0 ;
|
||||
if ( bUpdate) {
|
||||
dUTrim = dU2 ;
|
||||
ptTrim = ptInt2 ;
|
||||
}
|
||||
// per il primo e ultimo controllo le intersezioni con tutti i cilindri
|
||||
PtrOwner<ICurve> pCL ( vOffsetCrvs[vInters[j].first].pCrv->Clone()) ;
|
||||
Point3d ptStart ; pCL->GetStartPoint( ptStart) ;
|
||||
Vector3d vtStart ; pCL->GetStartDir( vtStart) ;
|
||||
double dLen ; pCL->GetLength( dLen) ;
|
||||
double dUTrim = ( j == 0 ? INFINITO : 0) ;
|
||||
Point3d ptTrim = P_INVALID ;
|
||||
for ( int k = 0 ; k < ssize( vCyl) ; ++k) {
|
||||
if ( vInters[j].second == k)
|
||||
continue ;
|
||||
Point3d ptInt1 = P_INVALID, ptInt2 = P_INVALID ;
|
||||
double dU1, dU2 ;
|
||||
Vector3d vtN1, vtN2 ;
|
||||
if ( IntersLineCyl( ptStart, vtStart * dLen, vCyl[k].frCyl, vCyl[k].dH, vCyl[k].dRad, false, false, dU1, ptInt1, vtN1, dU2, ptInt2, vtN2, true)) {
|
||||
bool bUpdate = ( j == 0 ? dU1 < dUTrim : dU1 > dUTrim) ;
|
||||
bUpdate = bUpdate && ptInt1.IsValid() && dU1 > 0 && dU1 < 1 ;
|
||||
bUpdate = bUpdate && vtN1 * vtStart < 0 ;
|
||||
if ( bUpdate) {
|
||||
dUTrim = dU1 ;
|
||||
ptTrim = ptInt1 ;
|
||||
}
|
||||
bUpdate = ( j == 0 ? dU2 < dUTrim : dU2 > dUTrim) ;
|
||||
bUpdate = bUpdate && ptInt2.IsValid() && dU2 > 0 && dU2 < 1 ;
|
||||
bUpdate = bUpdate && vtN2 * vtStart > 0 ;
|
||||
if ( bUpdate) {
|
||||
dUTrim = dU2 ;
|
||||
ptTrim = ptInt2 ;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( ptTrim.IsValid()) {
|
||||
if ( j == 0) {
|
||||
pCL->ModifyEnd( ptTrim) ;
|
||||
double dNewLen ; pCL->GetLength( dNewLen) ;
|
||||
if ( dNewLen < 0.1 && vInters[0] != 0) { // se fosse il primo allora potrei modificare il successivo
|
||||
int nPrev = vInters[0] - 1 ;
|
||||
vErase.push_back( vInters[0]) ;
|
||||
vInters[0] = nPrev ;
|
||||
ICurve* pCLPrev = vOffsetCrvs[nPrev].pCrv ;
|
||||
pCLPrev->ModifyEnd( ptTrim) ;
|
||||
if ( ptTrim.IsValid()) {
|
||||
bool bOk = pCL->ModifyEnd( ptTrim) ;
|
||||
double dNewLen ;
|
||||
if ( bOk)
|
||||
pCL->GetLength( dNewLen) ;
|
||||
if ( ( ! bOk || dNewLen < 0.1) && vInters[0].first != 0) { // se fosse il primo allora potrei modificare il successivo
|
||||
int nPrev = vInters[0].first - 1 ;
|
||||
vInters[0].first = nPrev ;
|
||||
int c = 1 ;
|
||||
while( vEditInfo.end()[-c].nId != nPrev)
|
||||
++c ;
|
||||
vEditInfo.end()[-c].nFlag = EditCrvInfo::EDIT ;
|
||||
vEditInfo.end()[-c].ptEnd = ptTrim ;
|
||||
vEditInfo.emplace_back( nPrev + 1, EditCrvInfo::DEL) ;
|
||||
}
|
||||
else
|
||||
vEditInfo.emplace_back( vInters[0].first, EditCrvInfo::EDIT, P_INVALID, ptTrim) ;
|
||||
}
|
||||
else
|
||||
vEditInfo.emplace_back( vInters[0].first, EditCrvInfo::EDIT, P_INVALID, P_INVALID) ;
|
||||
}
|
||||
else {
|
||||
pCL->ModifyStart( ptTrim) ;
|
||||
double dNewLen ; pCL->GetLength( dNewLen) ;
|
||||
if ( dNewLen < 0.1 && vInters[j] != ssize( vOffsetCrvs) - 1) { // se fosse l'ultima curva allora potrei modificare la precedente
|
||||
int nNext = vInters[j] + 1 ;
|
||||
vErase.push_back( vInters[j]) ;
|
||||
vInters[j] = nNext ;
|
||||
ICurve* pCLNext = vOffsetCrvs[nNext].pCrv ;
|
||||
pCLNext->ModifyStart( ptTrim) ;
|
||||
if ( ptTrim.IsValid()) {
|
||||
bool bOk = pCL->ModifyStart( ptTrim) ;
|
||||
double dNewLen ;
|
||||
if ( bOk)
|
||||
pCL->GetLength( dNewLen) ;
|
||||
if ( ( ! bOk || dNewLen < 0.1) && vInters[j].first != ssize( vOffsetCrvs) - 1) { // se fosse l'ultima curva allora potrei modificare la precedente
|
||||
int nNext = vInters[j].first + 1 ;
|
||||
vInters[j].first = nNext ;
|
||||
vEditInfo.emplace_back( nNext - 1, EditCrvInfo::DEL) ;
|
||||
int c = 1 ;
|
||||
while ( vEditInfo.end()[-c].nId != nNext && c < ssize( vEditInfo))
|
||||
++c ;
|
||||
if ( vEditInfo.end()[-c].nId == nNext) {
|
||||
vEditInfo.end()[-c].nFlag = EditCrvInfo::EDIT ;
|
||||
vEditInfo.end()[-c].ptStart = ptTrim ;
|
||||
}
|
||||
else
|
||||
vEditInfo.emplace_back( nNext, EditCrvInfo::EDIT, ptTrim, P_INVALID) ;
|
||||
}
|
||||
else
|
||||
vEditInfo.emplace_back( vInters[j].first, EditCrvInfo::EDIT, ptTrim, P_INVALID) ;
|
||||
}
|
||||
else
|
||||
vEditInfo.emplace_back( vInters[j].first, EditCrvInfo::EDIT, P_INVALID, P_INVALID) ;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
i = vLines.back() ;
|
||||
}
|
||||
else {
|
||||
if ( vEditInfo.empty() || vEditInfo.back().nId != i)
|
||||
vEditInfo.emplace_back( i, EditCrvInfo::NOEDIT) ;
|
||||
}
|
||||
}
|
||||
|
||||
// cancello le curve indicate in vErase
|
||||
for ( int i = ssize( vErase) - 1 ; i >= 0 ; --i) {
|
||||
vOffsetCrvs.erase( vOffsetCrvs.begin() + vErase[i]) ;
|
||||
}
|
||||
sort( vEditInfo.begin(), vEditInfo.end(), []( EditCrvInfo& a, EditCrvInfo& b) { return a.nId < b.nId ;}) ;
|
||||
return true ;
|
||||
}
|
||||
|
||||
// scorro tutto il vettore delle linee di offset e unisco aggiungendo una linea dove ne ho cancellate
|
||||
for ( int i = 0 ; i < ssize( vOffsetCrvs) - 1 ; ++i) {
|
||||
Point3d ptEndCurr, ptStartNext ;
|
||||
vOffsetCrvs[i].pCrv->GetEndPoint( ptEndCurr) ;
|
||||
vOffsetCrvs[i+1].pCrv->GetStartPoint( ptStartNext) ;
|
||||
if ( ! AreSamePointApprox( ptEndCurr, ptStartNext)) {
|
||||
ICurveLine* pCL = CreateBasicCurveLine() ;
|
||||
pCL->Set( ptEndCurr, ptStartNext) ;
|
||||
vOffsetCrvs.emplace_back( pCL, OffsetCurve3d::AngType::ANG_STR,-1) ;
|
||||
rotate( vOffsetCrvs.begin() + i + 1, vOffsetCrvs.end() - 1, vOffsetCrvs.end()) ;
|
||||
++i ;
|
||||
//----------------------------------------------------------------------------
|
||||
bool
|
||||
CalcAdjustConcavePartsPlaneCut( const ICurveComposite* pCrv, const PNT5AXVECTOR& vPnt5Ax, const OFFSETSEGVEC& vOffsetCrvs,
|
||||
double dRad, EDITCRVINFOVEC& vEditInfo)
|
||||
{
|
||||
// calcolo come tagliare il percorso negli angoli misti concavo/convesso
|
||||
// il taglio al percorso lo faccio usando il piano definito dalla direzione di offset
|
||||
// se una delle due direzioni è perpendicolare al piano formato dai due tratti, la cui joint è il punto corrente, allora non faccio nulla
|
||||
for ( int i = 0 ; i < ssize( vOffsetCrvs) ; ++i) {
|
||||
if ( vOffsetCrvs[i].nFlag == OffsetCurve3d::AngType::ANG_CVEX) {
|
||||
// se ho 2 tratti convex di fila, ho un convex non planar e se ho anche un angolo importante tra i due lati, devo correggere
|
||||
// se ho 4 tratti convex di fila, ho un convex planar e non devo fare nulla
|
||||
int c = 1 ;
|
||||
while ( i + c < ssize( vOffsetCrvs) && vOffsetCrvs[i + c].nFlag == OffsetCurve3d::AngType::ANG_CVEX)
|
||||
++c ;
|
||||
if ( c == 4) {
|
||||
i += 3 ;
|
||||
continue ;
|
||||
}
|
||||
|
||||
// trovo i due piani e taglio i tratti prima e dopo lo spigolo
|
||||
int nParent = vOffsetCrvs[i].nParent ; // sottocurva di pCrv che ha generato il tratto di offset
|
||||
Point3d pt ; pCrv->GetCurve( nParent)->GetEndPoint( pt) ;
|
||||
int nNextParent = nParent + 1 ;
|
||||
bool bCheckingStart = false ;
|
||||
if ( nNextParent > pCrv->GetCurveCount() - 1) {
|
||||
if ( ! pCrv->IsClosed())
|
||||
return false ;
|
||||
nNextParent = 0 ;
|
||||
bCheckingStart = true ;
|
||||
}
|
||||
Vector3d vtDirNext ; pCrv->GetCurve( nNextParent)-> GetStartDir( vtDirNext) ;
|
||||
Vector3d vtDirPrev ; pCrv->GetCurve( nParent)-> GetEndDir( vtDirPrev) ;
|
||||
// punti che rappresentano la pCrv ( quindi pCrv->GetCurveCount() == ssize( vPnt5Ax) - 1)
|
||||
Vector3d vtNNext ;
|
||||
Vector3d vtNPrev ;
|
||||
if ( ! bCheckingStart) {
|
||||
vtNNext = vPnt5Ax[nNextParent].vtDir2 ^ vtDirNext ;
|
||||
vtNPrev = vPnt5Ax[nNextParent].vtDir1 ^ vtDirPrev ;
|
||||
}
|
||||
else {
|
||||
vtNNext = vPnt5Ax[nNextParent].vtDir1 ^ vtDirNext ;
|
||||
vtNPrev = vPnt5Ax[nNextParent].vtDir2 ^ vtDirPrev ;
|
||||
}
|
||||
Plane3d plNext ; plNext.Set( pt, vtNNext) ;
|
||||
Plane3d plPrev ; plPrev.Set( pt, vtNPrev) ;
|
||||
Point3d ptPrev ;
|
||||
Point3d ptNext ;
|
||||
// interseco con il piano precedente prendendo i tratti successivi
|
||||
bool bFound = false ;
|
||||
// parto a confrontare i segmenti successivi a quelli identificati come concavi
|
||||
if ( ! bCheckingStart)
|
||||
c = i + 2 ;
|
||||
else // se sono arrivato a fine curva di una curva chiusa controllo con lo start
|
||||
c = 1 ;
|
||||
while ( ! bFound && c > 0) {
|
||||
Point3d ptInt ;
|
||||
Point3d ptStart, ptEnd ;
|
||||
vOffsetCrvs[c].pCrv->GetStartPoint( ptStart) ;
|
||||
vOffsetCrvs[c].pCrv->GetEndPoint( ptEnd) ;
|
||||
IntersLinePlane( ptStart, ptEnd, plPrev, ptInt, false) ;
|
||||
Vector3d vtDir ; vOffsetCrvs[c].pCrv->GetStartDir( vtDir) ;
|
||||
double dLen ; vOffsetCrvs[c].pCrv->GetLength( dLen) ;
|
||||
double dU = ( ptInt - ptStart).Len() / dLen ;
|
||||
if ( vtDir * ( ptInt - ptStart) < 0)
|
||||
dU *= -1 ;
|
||||
if ( dU < 1) {
|
||||
ptNext = ptInt ;
|
||||
bFound = true ;
|
||||
}
|
||||
else
|
||||
++c ;
|
||||
}
|
||||
// salvo la modifica nel vettore edit
|
||||
int nNext = c ;
|
||||
vEditInfo[c].nFlag = EditCrvInfo::EditFlag::EDIT ;
|
||||
vEditInfo[c].ptStart = ptNext ;
|
||||
|
||||
// interseco con il piano successivo prendendo i tratti precedenti
|
||||
bFound = false ;
|
||||
c = i - 1 ;
|
||||
while ( ! bFound && c < ssize( vOffsetCrvs)) {
|
||||
Point3d ptInt ;
|
||||
Point3d ptStart, ptEnd ;
|
||||
vOffsetCrvs[c].pCrv->GetStartPoint( ptStart) ;
|
||||
vOffsetCrvs[c].pCrv->GetEndPoint( ptEnd) ;
|
||||
IntersLinePlane( ptStart, ptEnd, plNext, ptInt, false) ;
|
||||
Vector3d vtDir ; vOffsetCrvs[c].pCrv->GetStartDir( vtDir) ;
|
||||
double dLen ; vOffsetCrvs[c].pCrv->GetLength( dLen) ;
|
||||
double dU = ( ptInt - ptStart).Len() / dLen ;
|
||||
if ( vtDir * ( ptInt - ptStart) < 0)
|
||||
dU *= -1 ;
|
||||
if ( dU > 0) {
|
||||
ptPrev = ptInt ;
|
||||
bFound = true ;
|
||||
}
|
||||
else
|
||||
--c ;
|
||||
}
|
||||
// salvo la modifica nel vettore edit
|
||||
int nPrev = c ;
|
||||
vEditInfo[c].nFlag = EditCrvInfo::EditFlag::EDIT ;
|
||||
vEditInfo[c].ptEnd = ptPrev ;
|
||||
|
||||
// tengo solo il punto più esterno
|
||||
double dLen1 = Dist( ptPrev, pt) ;
|
||||
double dLen2 = Dist( ptNext, pt) ;
|
||||
if ( dLen1 > dLen2)
|
||||
vEditInfo[nNext].ptStart = ptPrev ;
|
||||
else
|
||||
vEditInfo[nPrev].ptStart = ptNext ;
|
||||
|
||||
// setto come da cancellare tutti i tratti nel mezzo
|
||||
if ( ! bCheckingStart) {
|
||||
c = nPrev + 1 ;
|
||||
while ( c < nNext) {
|
||||
vEditInfo[c].nFlag = EditCrvInfo::EditFlag::DEL ;
|
||||
++c ;
|
||||
}
|
||||
}
|
||||
else {
|
||||
c = nPrev + 1 ;
|
||||
while ( c < ssize( vEditInfo)) {
|
||||
vEditInfo[c].nFlag = EditCrvInfo::EditFlag::DEL ;
|
||||
++c ;
|
||||
}
|
||||
c = 0 ;
|
||||
while ( c < nNext) {
|
||||
vEditInfo[c].nFlag = EditCrvInfo::EditFlag::DEL ;
|
||||
++c ;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! bCheckingStart)
|
||||
i = nNext ;
|
||||
}
|
||||
}
|
||||
|
||||
return true ;
|
||||
}
|
||||
+70
-29
@@ -761,7 +761,7 @@ DouglasPeuckerSimplification( const PNTUVECTOR& vPtU, const double dSqTol, const
|
||||
|
||||
// se la distanza massima trovata è sopra la tolleranza, allora controllo la parte di PolyLine tra
|
||||
// (nIndStart, nMaxInd) e quella tra (nMaxInd, nIndEnd)
|
||||
if ( dMaxSqDist > dSqTol) {
|
||||
if ( dMaxSqDist > dSqTol) {
|
||||
// inserisco il punto
|
||||
vInd.push_back( nMaxInd) ;
|
||||
// split
|
||||
@@ -775,7 +775,7 @@ DouglasPeuckerSimplification( const PNTUVECTOR& vPtU, const double dSqTol, const
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool
|
||||
PolyLine::RemoveAlignedPoints( double dToler, bool bStartEnd)
|
||||
PolyLine::RemoveAlignedPoints( double dToler, bool bStartEnd, double dMaxLen)
|
||||
{
|
||||
// se non ci sono almeno 3 punti, esco subito
|
||||
if ( m_lUPoints.size() < 3)
|
||||
@@ -792,34 +792,65 @@ PolyLine::RemoveAlignedPoints( double dToler, bool bStartEnd)
|
||||
// vettore indici dei punti rimanenti
|
||||
INTVECTOR vInd ; vInd.reserve( vPtU.size()) ;
|
||||
|
||||
// se aperta
|
||||
if ( ! IsClosed()) {
|
||||
// considero tutti i punti della PolyLine
|
||||
vInd.push_back( 0) ;
|
||||
if ( ! DouglasPeuckerSimplification( vPtU, dSqTol, 0, int( vPtU.size()) - 1, vInd))
|
||||
return false ;
|
||||
vInd.push_back( int( vPtU.size()) - 1) ;
|
||||
}
|
||||
// altrimenti chiusa
|
||||
else {
|
||||
// cerco il punto più distante dal primo
|
||||
double dMaxDist = 0. ;
|
||||
int nMaxInd = 0 ;
|
||||
for ( int i = 1 ; i < int( vPtU.size()) ; ++ i) {
|
||||
double dCurrDist = Dist( vPtU[0].first, vPtU[i].first) ;
|
||||
if ( dCurrDist > dMaxDist) {
|
||||
dMaxDist = dCurrDist ;
|
||||
nMaxInd = i ;
|
||||
}
|
||||
if ( dMaxLen > INFINITO - 1) {
|
||||
// se aperta
|
||||
if ( ! IsClosed()) {
|
||||
// considero tutti i punti della PolyLine
|
||||
vInd.push_back( 0) ;
|
||||
if ( ! DouglasPeuckerSimplification( vPtU, dSqTol, 0, int( vPtU.size()) - 1, vInd))
|
||||
return false ;
|
||||
vInd.push_back( int( vPtU.size()) - 1) ;
|
||||
}
|
||||
// altrimenti chiusa
|
||||
else {
|
||||
// cerco il punto più distante dal primo
|
||||
double dMaxDist = 0. ;
|
||||
int nMaxInd = 0 ;
|
||||
for ( int i = 1 ; i < int( vPtU.size()) ; ++ i) {
|
||||
double dCurrDist = Dist( vPtU[0].first, vPtU[i].first) ;
|
||||
if ( dCurrDist > dMaxDist) {
|
||||
dMaxDist = dCurrDist ;
|
||||
nMaxInd = i ;
|
||||
}
|
||||
}
|
||||
// recupero due PolyLine di approssimazione
|
||||
vInd.push_back( 0) ;
|
||||
if ( ! DouglasPeuckerSimplification( vPtU, dSqTol, 0, nMaxInd, vInd))
|
||||
return false ;
|
||||
vInd.push_back( nMaxInd) ;
|
||||
if ( ! DouglasPeuckerSimplification( vPtU, dSqTol, nMaxInd, int( vPtU.size()) - 1, vInd))
|
||||
return false ;
|
||||
vInd.push_back( int( vPtU.size()) - 1) ;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// raggruppo i punti della polyline a gruppi che stanno entro la lunghezza massima e ad ognuno applico Douglas
|
||||
vInd.push_back(0) ;
|
||||
|
||||
int i = 0 ;
|
||||
while ( i < ssize(vPtU) - 1) {
|
||||
int nLast = i + 1 ;
|
||||
while ( nLast < ssize( vPtU) && Dist( vPtU[i].first, vPtU[nLast].first) <= dMaxLen)
|
||||
++nLast ;
|
||||
|
||||
// l'ultimo punto era oltre il limite
|
||||
--nLast ;
|
||||
|
||||
// sicurezza: almeno un passo avanti
|
||||
if ( nLast == i)
|
||||
nLast = i + 1 ;
|
||||
|
||||
// mantieni l'estremo del tratto
|
||||
if ( vInd.back() != nLast)
|
||||
vInd.push_back( nLast) ;
|
||||
|
||||
// semplifica il tratto interno
|
||||
if ( nLast - i > 1) {
|
||||
if ( ! DouglasPeuckerSimplification( vPtU, dSqTol, i, nLast, vInd))
|
||||
return false ;
|
||||
}
|
||||
i = nLast ;
|
||||
}
|
||||
// recupero due PolyLine di approssimazione
|
||||
vInd.push_back( 0) ;
|
||||
if ( ! DouglasPeuckerSimplification( vPtU, dSqTol, 0, nMaxInd, vInd))
|
||||
return false ;
|
||||
vInd.push_back( nMaxInd) ;
|
||||
if ( ! DouglasPeuckerSimplification( vPtU, dSqTol, nMaxInd, int( vPtU.size()) - 1, vInd))
|
||||
return false ;
|
||||
vInd.push_back( int( vPtU.size()) - 1) ;
|
||||
}
|
||||
|
||||
// ordino in senso crescente
|
||||
@@ -1618,6 +1649,9 @@ AssociatePolyLinesMinDistPoints( const PolyLine& PL1, const PolyLine& PL2, PNTIV
|
||||
int nPnt2 = PL2.GetPointNbr() ;
|
||||
if ( nPnt1 == 0 || nPnt2 == 0)
|
||||
return false ;
|
||||
|
||||
bool bClosed1 = PL1.IsClosed() ;
|
||||
bool bClosed2 = PL2.IsClosed() ;
|
||||
|
||||
// indica la presenza di punti interni in comune tra le due polylines
|
||||
bCommonInternalPoints = false ;
|
||||
@@ -1663,6 +1697,10 @@ AssociatePolyLinesMinDistPoints( const PolyLine& PL1, const PolyLine& PL2, PNTIV
|
||||
if ( nMinJ < nLastJ)
|
||||
nMinJ = nLastJ ;
|
||||
|
||||
// se la curva è chiusa il primo e l'ultimo punto coincidono, verifico quale conviene considerare
|
||||
if ( bClosed2 && nMinJ == nTotP2 - 1 && nLastJ == 0)
|
||||
nMinJ = 0 ;
|
||||
|
||||
// verifica se è un punto interno in comune con l'altra polyline
|
||||
if ( i < nTotP1 - 1 && dDist < EPS_SMALL && abs( dMinDistPar - floor( dMinDistPar + 0.5)) < EPS_SMALL)
|
||||
bCommonInternalPoints = true ;
|
||||
@@ -1693,6 +1731,9 @@ AssociatePolyLinesMinDistPoints( const PolyLine& PL1, const PolyLine& PL2, PNTIV
|
||||
|
||||
if ( nMinI < nLastI)
|
||||
nMinI = nLastI ;
|
||||
|
||||
if ( bClosed1 && nMinI == nTotP1 - 1 && nLastI == 0)
|
||||
nMinI = 0 ;
|
||||
|
||||
if ( j < nTotP2 - 1 && dDist < EPS_SMALL && abs( dMinDistPar - floor( dMinDistPar + 0.5)) < EPS_SMALL)
|
||||
bCommonInternalPoints = true ;
|
||||
|
||||
+320
-30
@@ -13,6 +13,7 @@
|
||||
|
||||
//--------------------------- Include ----------------------------------------
|
||||
#include "stdafx.h"
|
||||
#include <cmath>
|
||||
#include "SurfTriMesh.h"
|
||||
#include "SurfBezier.h"
|
||||
#include "GeoConst.h"
|
||||
@@ -32,15 +33,6 @@ using namespace std ;
|
||||
const double COS_ANG_LIM = 0.0175 ;
|
||||
// Angolo massimo tra normali per effettuare bisezione su spigolo
|
||||
const double COS_ANG_MAX_CORNER = 0.8660 ;
|
||||
// Tipologia di punto
|
||||
const int P5AX_TO_DELETE = -1 ; // da cancellare
|
||||
const int P5AX_OUT = 0 ; // aggiunto prima di inizio o dopo fine
|
||||
const int P5AX_STD = 1 ; // standard
|
||||
const int P5AX_CVEX = 2 ; // su angolo convesso
|
||||
const int P5AX_CONC = 3 ; // in angolo concavo
|
||||
const int P5AX_BEFORE_CONC = 4 ; // adiacente ad angolo concavo
|
||||
const int P5AX_AFTER_CONC = 5 ; // adiacente ad angolo concavo
|
||||
const int P5AX_SMOOTH_CONC = 6 ; // zona concava curva, senza spigolo netto
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
static double
|
||||
@@ -65,7 +57,7 @@ PointsInTolerance( const PNT5AXVECTOR& vPt5ax, int nPrec, int nCurr, int nNext,
|
||||
static bool
|
||||
AddPointsOnCorners( PNT5AXVECTOR& vPt5ax)
|
||||
{
|
||||
const double dSinSmallAngle = sin( 0 * DEGTORAD) ;
|
||||
const double dSinSmallAngle = sin( 0.5 * DEGTORAD) ;
|
||||
for ( int i = 1 ; i < ssize( vPt5ax) ; ++ i) {
|
||||
// precedente
|
||||
int j = i - 1 ;
|
||||
@@ -81,24 +73,47 @@ AddPointsOnCorners( PNT5AXVECTOR& vPt5ax)
|
||||
Point3d ptInt ;
|
||||
if ( IntersLinePlane( ptEdge, vtEdge, 1, plPlane3, ptInt, false) == ILPT_YES) {
|
||||
// verifico se spigolo convesso o concavo
|
||||
bool bConvex = (vPt5ax[i].ptP - vPt5ax[j].ptP) * vPt5ax[j].vtDir1 < 0 ;
|
||||
bool bValidInters = true ;
|
||||
if ( i > 2) {
|
||||
int k = i - 2 ;
|
||||
// verifico la concavità anche tornando indietro lungo la linea
|
||||
if ( ( ( vPt5ax[k].ptP - vPt5ax[j].ptP) * vPt5ax[i].vtDir1 < 0) != bConvex) {
|
||||
LOG_WARN( GetEGkLogger(), "La superficie su cui si sta proiettando la curva ha delle normali incoerenti")
|
||||
return false ;
|
||||
}
|
||||
// verifico che l'intersezione sia tra i e j e non prima di j
|
||||
bValidInters = ( ptInt - vPt5ax[j].ptP) * ( vPt5ax[j].ptP - vPt5ax[k].ptP) > 0 ;
|
||||
double dProjPrev = ( vPt5ax[i].ptP - vPt5ax[j].ptP) * vPt5ax[j].vtDir1 ;
|
||||
double dProjCurr = ( vPt5ax[j].ptP - vPt5ax[i].ptP) * vPt5ax[i].vtDir1 ;
|
||||
bool bConvex = ( abs( dProjCurr) > abs( dProjPrev) ? dProjCurr < 0 : dProjPrev < 0) ;
|
||||
int nPrev = i - 1 ;
|
||||
if ( i < ssize( vPt5ax) - 2 && ! AreSameVectorExact(vPt5ax[i - 1].vtDir1, vPt5ax[i - 1].vtDir2)) {
|
||||
int z = nPrev - 1 ;
|
||||
// se vtDir1 e vtDir2 sono diverse allora sono su uno spigolo
|
||||
double dProjPrev2 = ( vPt5ax[z].ptP - vPt5ax[nPrev].ptP) * vPt5ax[nPrev].vtDir2 ;
|
||||
double dProjCurr2 = ( vPt5ax[i].ptP - vPt5ax[nPrev].ptP) * vPt5ax[nPrev].vtDir1 ;
|
||||
// se uno dei due è convesso allora considero convesso l'angolo
|
||||
bConvex = dProjPrev2 < EPS_SMALL || dProjCurr2 < EPS_SMALL ;
|
||||
}
|
||||
bool bValidIntersAfterJ = true ;
|
||||
bool bValidIntersBeforeI = true ;
|
||||
if ( i > 2) {
|
||||
// verifico che l'intersezione sia tra i e j e non prima di j
|
||||
int k = i - 2 ;
|
||||
bValidIntersAfterJ = ( ptInt - vPt5ax[j].ptP) * ( vPt5ax[j].ptP - vPt5ax[k].ptP) > 0 ;
|
||||
bool bBetweenIAndJ = ( ptInt - vPt5ax[j].ptP) * ( vPt5ax[i].ptP - vPt5ax[j].ptP) > 0 ;
|
||||
bValidIntersAfterJ = bValidIntersAfterJ && bBetweenIAndJ ;
|
||||
}
|
||||
if ( i < ssize( vPt5ax) - 1) {
|
||||
// verifico anche che l'intersezione non sia dopo i
|
||||
int h = i + 1 ;
|
||||
bValidIntersBeforeI = ( ptInt - vPt5ax[i].ptP) * ( vPt5ax[h].ptP - vPt5ax[i].ptP) < 0 ;
|
||||
bool bBetweenIAndJ = ( ptInt - vPt5ax[i].ptP) * ( vPt5ax[i].ptP - vPt5ax[j].ptP) < 0 ;
|
||||
bValidIntersBeforeI = bValidIntersBeforeI && bBetweenIAndJ ;
|
||||
}
|
||||
bool bValidInters = ( bValidIntersAfterJ && bValidIntersBeforeI) ;
|
||||
// se convesso, metto due punti con direzione appena prima e appena dopo
|
||||
if ( bConvex) {
|
||||
Vector3d vtLine1 = ptInt - vPt5ax[j].ptP ; double dLen1 = vtLine1.Len() ;
|
||||
Vector3d vtLine2 = vPt5ax[i].ptP - ptInt ; double dLen2 = vtLine2.Len() ;
|
||||
if ( dLen1 > 10 * EPS_SMALL && bValidInters) {
|
||||
if ( dLen1 > 10 * EPS_SMALL && bValidIntersAfterJ) {
|
||||
Point5ax Pt5ax ;
|
||||
if ( bValidInters)
|
||||
Pt5ax.ptP = ptInt - vtLine1 / dLen1 * 2 * EPS_SMALL ;
|
||||
else {
|
||||
Vector3d vtNewLine = vPt5ax[i].ptP - vPt5ax[j].ptP ; vtNewLine.Normalize() ;
|
||||
Pt5ax.ptP = vPt5ax[i].ptP - vtNewLine * 2 * EPS_SMALL ;
|
||||
}
|
||||
Pt5ax.vtDir1 = vPt5ax[j].vtDir1 ;
|
||||
Pt5ax.vtDir2 = vPt5ax[j].vtDir2 ;
|
||||
Pt5ax.vtDirU = vPt5ax[j].vtDirU ;
|
||||
@@ -110,7 +125,7 @@ AddPointsOnCorners( PNT5AXVECTOR& vPt5ax)
|
||||
}
|
||||
else
|
||||
vPt5ax[j].nFlag = P5AX_CVEX ;
|
||||
if ( dLen2 > 10 * EPS_SMALL) {
|
||||
if ( dLen2 > 10 * EPS_SMALL && bValidIntersBeforeI) {
|
||||
Point5ax Pt5ax ;
|
||||
if ( bValidInters)
|
||||
Pt5ax.ptP = ptInt + vtLine2 / dLen2 * 2 * EPS_SMALL ;
|
||||
@@ -150,13 +165,64 @@ AddPointsOnCorners( PNT5AXVECTOR& vPt5ax)
|
||||
// guardo se la proiezione il tratto successivo, lungo la normale precedente + maggiore di un angolo minimo ( angolo interno smooth)
|
||||
Vector3d vtDirNext = vPt5ax[i].ptP - vPt5ax[j].ptP ;
|
||||
vtDirNext.Normalize() ;
|
||||
if ( vtDirNext * vPt5ax[j].vtDir1 > dSinSmallAngle) {
|
||||
double dProj1 = vtDirNext * vPt5ax[j].vtDir1 ;
|
||||
double dProj2 = ( - vtDirNext) * vPt5ax[i].vtDir1 ;
|
||||
|
||||
if ( ( abs( dProj1) > abs( dProj2) ? dProj1 > dSinSmallAngle : dProj2 > dSinSmallAngle)) {
|
||||
// se concavo senza spigolo netto segnalo zona concava smooth
|
||||
vPt5ax[i].nFlag = P5AX_SMOOTH_CONC ;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int i = 0 ;
|
||||
int j = ssize( vPt5ax) - 1 ;
|
||||
bool bClosed = AreSamePointApprox( vPt5ax[i].ptP, vPt5ax[j].ptP) ;
|
||||
// se la curva è chiusa controllo inizio e fine
|
||||
if ( bClosed) {
|
||||
// se normali tra corrente e precedente oltre limite
|
||||
int k = i + 1 ;
|
||||
int z = j - 1 ;
|
||||
double dProjPrev = ( vPt5ax[z].ptP - vPt5ax[j].ptP) * vPt5ax[i].vtDir1 ;
|
||||
double dProjCurr = ( vPt5ax[k].ptP - vPt5ax[i].ptP) * vPt5ax[j].vtDir1 ;
|
||||
if ( vPt5ax[i].vtDir1 * vPt5ax[j].vtDir1 < COS_ANG_MAX_CORNER) {
|
||||
bool bConvex = ( abs( dProjCurr) > abs( dProjPrev) ? dProjCurr < 0 : dProjPrev < 0) ;
|
||||
if ( i < ssize( vPt5ax) - 2 && ! AreSameVectorExact(vPt5ax.back().vtDir1, vPt5ax.back().vtDir2)) {
|
||||
int z = j - 1 ;
|
||||
// se vtDir1 e vtDir2 sono diverse allora sono su uno spigolo
|
||||
double dProjPrev2 = ( vPt5ax[z].ptP - vPt5ax[j].ptP) * vPt5ax[j].vtDir2 ;
|
||||
double dProjCurr2 = ( vPt5ax[i].ptP - vPt5ax[j].ptP) * vPt5ax[j].vtDir1 ;
|
||||
// se uno dei due è convesso allora considero convesso l'angolo
|
||||
bConvex = dProjPrev2 < EPS_SMALL || dProjCurr2 < EPS_SMALL ;
|
||||
}
|
||||
if ( bConvex) {
|
||||
vPt5ax[i].nFlag = P5AX_CVEX ;
|
||||
vPt5ax[i].vtDir2 = vPt5ax[j].vtDir1 ;
|
||||
vPt5ax[j].nFlag = P5AX_CVEX ;
|
||||
}
|
||||
else {
|
||||
vPt5ax[i].nFlag = P5AX_CONC ;
|
||||
vPt5ax[j].nFlag = P5AX_CONC ;
|
||||
Vector3d vtDir1 = Media( vPt5ax[i].vtDir1, vPt5ax[j].vtDir1) ; vtDir1.Normalize() ;
|
||||
Vector3d vtDir2 = Media( vPt5ax[i].vtDir2, vPt5ax[j].vtDir2) ; vtDir2.Normalize() ;
|
||||
Vector3d vtDirU = Media( vPt5ax[i].vtDirU, vPt5ax[j].vtDirU) ; vtDirU.Normalize() ;
|
||||
Vector3d vtDirV = Media( vPt5ax[i].vtDirV, vPt5ax[j].vtDirV) ; vtDirV.Normalize() ;
|
||||
vPt5ax[i].vtDir1 = vtDir1 ;
|
||||
vPt5ax[j].vtDir1 = vtDir1 ;
|
||||
vPt5ax[i].vtDir2 = vtDir2 ;
|
||||
vPt5ax[j].vtDir2 = vtDir2 ;
|
||||
vPt5ax[i].vtDirU = vtDirU ;
|
||||
vPt5ax[j].vtDirU = vtDirU ;
|
||||
vPt5ax[i].vtDirV = vtDirV ;
|
||||
vPt5ax[j].vtDirV = vtDirV ;
|
||||
}
|
||||
}
|
||||
else if ( abs( dProjPrev) > abs( dProjCurr) ? dProjPrev > dSinSmallAngle : dProjCurr > dSinSmallAngle) {
|
||||
vPt5ax[i].nFlag = P5AX_SMOOTH_CONC ;
|
||||
vPt5ax[j].nFlag = P5AX_SMOOTH_CONC ;
|
||||
}
|
||||
}
|
||||
|
||||
// riscorro tutto il vettore per vedere se ho creato delle zone concave smooth frammentate (separate solo da un tratto non classificato concavo), che quindi uniformo
|
||||
for ( int i = 1 ; i < ssize( vPt5ax) - 1 ; ++ i) {
|
||||
if ( vPt5ax[i].nFlag != P5AX_SMOOTH_CONC && vPt5ax[i-1].nFlag == P5AX_SMOOTH_CONC && vPt5ax[i+1].nFlag == P5AX_SMOOTH_CONC)
|
||||
@@ -372,7 +438,7 @@ ProjectPointOnSurf( const Point3d& ptP, const CISURFPVECTOR& vpSurf, double dPar
|
||||
//----------------------------------------------------------------------------
|
||||
bool
|
||||
ProjectCurveOnSurf( const ICurve& crCrv, const CISURFPVECTOR& vpSurf,
|
||||
double dLinTol, double dMaxSegmLen, bool bSharpEdges, PNT5AXVECTOR& vPt5ax)
|
||||
double dLinTol, double dMaxSegmLen, bool bSharpEdges, PNT5AXVECTOR& vPt5ax, bool bNormOrTang)
|
||||
{
|
||||
// controllo le tolleranze
|
||||
dLinTol = max( dLinTol, LIN_TOL_MIN) ;
|
||||
@@ -392,16 +458,111 @@ ProjectCurveOnSurf( const ICurve& crCrv, const CISURFPVECTOR& vpSurf,
|
||||
vPt5ax.reserve( PL.GetPointNbr()) ;
|
||||
|
||||
// proietto i punti della polilinea sulla superficie secondo la direzione di minima distanza
|
||||
double dPar ;
|
||||
Point3d ptP ;
|
||||
bool bFound = PL.GetFirstUPoint( &dPar, &ptP) ;
|
||||
double dPar, dParNext ;
|
||||
Point3d ptP, ptPNext ;
|
||||
Vector3d vtDirPrev = V_INVALID ;
|
||||
if ( PL.IsClosed()) {
|
||||
Point3d pt1, pt2 ;
|
||||
PL.GetLastLine( pt1, pt2) ;
|
||||
vtDirPrev = pt2 - pt1 ;
|
||||
}
|
||||
Vector3d vtLast ; PL.GetLastULine( &dPar, &ptP, &dParNext, &ptPNext) ;
|
||||
vtLast = ptPNext - ptP ; vtLast.Normalize() ;
|
||||
bool bFound = PL.GetFirstULine( &dPar, &ptP, &dParNext, &ptPNext) ;
|
||||
Vector3d vtFirst = ptPNext - ptP ; vtFirst.Normalize() ;
|
||||
bool bClosed = PL.IsClosed() ;
|
||||
bool bLast = false ;
|
||||
Vector3d vtNormPrev = V_INVALID ;
|
||||
Point3d ptPrev ;
|
||||
bool bFirst = true ;
|
||||
while ( bFound) {
|
||||
// se trovo proiezione, la salvo
|
||||
Point5ax Pt5ax ;
|
||||
if ( ProjectPointOnSurf( ptP, vpSurf, dPar, Pt5ax))
|
||||
vPt5ax.emplace_back( Pt5ax) ;
|
||||
// passo al successivo
|
||||
bFound = PL.GetNextUPoint( &dPar, &ptP) ;
|
||||
// controllo che la normale trovata sia in linea con la precedente, se i due tratti erano abbastanza allineati
|
||||
bool bRecalc = false ;
|
||||
if ( bFirst)
|
||||
bRecalc = true ;
|
||||
if ( ssize( vPt5ax) > 2) {
|
||||
Point5ax& pt5Curr = vPt5ax.back() ;
|
||||
Point5ax& pt5Prev = vPt5ax.end()[-2] ;
|
||||
Point5ax& pt5PrevPrev = vPt5ax.end()[-3] ;
|
||||
Vector3d vtDirCurr = pt5Curr.ptP - pt5Prev.ptP ; vtDirCurr.Normalize() ;
|
||||
Vector3d vtDirPrev = pt5Prev.ptP - pt5PrevPrev.ptP ; vtDirPrev.Normalize() ;
|
||||
double dProjDir = vtDirCurr * vtDirPrev ;
|
||||
if ( dProjDir > COS_ANG_MAX_CORNER) {
|
||||
double dProjNorm = vPt5ax.back().vtDir1 * vtNormPrev ;
|
||||
if ( dProjNorm < COS_ANG_MAX_CORNER)
|
||||
bRecalc = true ;
|
||||
}
|
||||
}
|
||||
Vector3d vtDirNext = ptPNext - ptP ; vtDirNext.Normalize() ;
|
||||
// se sono arrivato ad uno spigolo tengo la normale appena prima dello spigolo
|
||||
if ( bRecalc) {
|
||||
Point3d ptAlter ;
|
||||
if ( ! bFirst) {
|
||||
Vector3d vtDirPrev = ptP - ptPrev ; vtDirPrev.Normalize() ;
|
||||
ptAlter = ptP - vtDirPrev * 5 * EPS_SMALL ;
|
||||
}
|
||||
else
|
||||
ptAlter = ptP + vtDirNext * 5 * EPS_SMALL ;
|
||||
Point5ax Pt5axRec ;
|
||||
if ( ProjectPointOnSurf( ptAlter, vpSurf, dPar, Pt5axRec)) {
|
||||
// aggiorno solo la vtDir1, la vtDir2 la mantengo con la normale successiva allo spigolo
|
||||
if ( Pt5axRec.vtDir1 * Pt5ax.vtDir1 < COS_ANG_MAX_CORNER)
|
||||
vPt5ax.back().vtDir1 = Pt5axRec.vtDir1 ;
|
||||
}
|
||||
}
|
||||
else if ( ssize( vPt5ax) > 1 && vPt5ax.back().vtDir1 * vPt5ax.end()[-2].vtDir1 < COS_ANG_MAX_CORNER) {
|
||||
// se la normale corrente è sufficientemente diversa dalla precedente e i tratti non erano allineati
|
||||
// controllo se il punto precedente erano su uno spigolo, ricalcolando in un punto appena oltre
|
||||
Point3d ptAlter = ptPrev + ( ptP - ptPrev) * 5 * EPS_SMALL ;
|
||||
Point5ax Pt5axRec ;
|
||||
if ( ProjectPointOnSurf( ptAlter, vpSurf, dPar, Pt5axRec)) {
|
||||
// aggiorno solo la vtDir1, la vtDir2 la mantengo con la normale successiva allo spigolo
|
||||
if ( Pt5axRec.vtDir1 * Pt5ax.vtDir1 < COS_ANG_MAX_CORNER)
|
||||
vPt5ax.end()[-2].vtDir1 = Pt5axRec.vtDir1 ;
|
||||
}
|
||||
}
|
||||
|
||||
if ( bLast && bClosed) {
|
||||
// controllo se il punto finale è di spigolo
|
||||
if ( vPt5ax.back().vtDir1 * vPt5ax.front().vtDir1 < COS_ANG_MAX_CORNER) {
|
||||
vPt5ax.back().vtDir2 = vPt5ax.front().vtDir1 ;
|
||||
vPt5ax.front().vtDir2 = vPt5ax.back().vtDir1 ;
|
||||
}
|
||||
}
|
||||
vtNormPrev = vPt5ax.back().vtDir1 ;
|
||||
// se richiesta la tangente anziché la normale allora modifico il vettore associato al punto
|
||||
Vector3d vtDir ;
|
||||
if ( ! bNormOrTang) {
|
||||
Vector3d vtNorm = vPt5ax.back().vtDir1 ;
|
||||
if ( bFirst)
|
||||
vtDir = vtDirNext ;
|
||||
else
|
||||
vtDir = vtDirPrev ;
|
||||
// in caso sia stato superato l'angolo limite dò priorità alla direzione precedente
|
||||
if ( vtDirPrev.IsValid() && (! bFirst || ( bFirst && bClosed))) {
|
||||
double dProj = vtDir * vtDirPrev ;
|
||||
if ( dProj > COS_ANG_MAX_CORNER)
|
||||
vtDir = Media( vtDir, vtDirPrev) ;
|
||||
}
|
||||
vtDirPrev = vtDirNext ;
|
||||
Vector3d vtTang = vtDir ^ vtNorm ; vtTang.Normalize() ;
|
||||
vPt5ax.back().vtDir1 = vtTang ;
|
||||
vPt5ax.back().vtDir2 = vtTang ;
|
||||
}
|
||||
// passo al successivo
|
||||
ptPrev = ptP ;
|
||||
bFound = PL.GetNextULine( &dPar, &ptP, &dParNext, &ptPNext) ;
|
||||
// se sono arrivato alla fine aggiungo l'ultimo punto
|
||||
if ( ! bFound && ! bLast) {
|
||||
bLast = true ;
|
||||
bFound = true ;
|
||||
}
|
||||
if ( bFirst)
|
||||
bFirst = false ;
|
||||
}
|
||||
|
||||
// se richiesto, inserimento punti intermedi in presenza di spigoli
|
||||
@@ -1131,3 +1292,132 @@ ProjectCurveOnSurf( const ICurve& crCrv, const CISURFPVECTOR& vpSurf, const ISur
|
||||
|
||||
return true ;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool
|
||||
GetCurveOnSurfInfo( const ICurve& crCrv, const ISurfTriMesh& pSurf,
|
||||
double dLinTol, double dMaxSegmLen, bool bSharpEdges, PNT5AXVECTOR& vPt5ax, bool bNormOrTang)
|
||||
{
|
||||
// controllo le tolleranze
|
||||
dLinTol = max( dLinTol, LIN_TOL_MIN) ;
|
||||
dMaxSegmLen = max( dMaxSegmLen, 10 * EPS_SMALL) ;
|
||||
|
||||
// approssimo la curva con una polilinea entro la tolleranza
|
||||
PolyLine PL ;
|
||||
const double MAX_SEG_LEN = min( dMaxSegmLen, 0.977) ;
|
||||
if ( ! crCrv.ApproxWithLimitedLines( 10 * EPS_SMALL, ANG_TOL_STD_DEG, ICurve::APL_SPECIAL, MAX_SEG_LEN, PL))
|
||||
return false ;
|
||||
|
||||
PL.RemoveAlignedPoints( dLinTol, false, MAX_SEG_LEN) ;
|
||||
|
||||
// Pulisco e riservo spazio nel vettore dei punti risultanti
|
||||
vPt5ax.clear() ;
|
||||
vPt5ax.reserve( PL.GetPointNbr()) ;
|
||||
|
||||
// proietto i punti della polilinea sulla superficie secondo la direzione di minima distanza
|
||||
double dPar, dParNext ;
|
||||
Point3d ptP, ptPNext ;
|
||||
Vector3d vtDirPrev = V_INVALID ;
|
||||
if ( PL.IsClosed()) {
|
||||
Point3d pt1, pt2 ;
|
||||
PL.GetLastLine( pt1, pt2) ;
|
||||
vtDirPrev = pt2 - pt1 ;
|
||||
}
|
||||
Vector3d vtLast ; PL.GetLastULine( &dPar, &ptP, &dParNext, &ptPNext) ;
|
||||
vtLast = ptPNext - ptP ; vtLast.Normalize() ;
|
||||
bool bFound = PL.GetFirstULine( &dPar, &ptP, &dParNext, &ptPNext) ;
|
||||
Vector3d vtFirst = ptPNext - ptP ; vtFirst.Normalize() ;
|
||||
bool bClosed = PL.IsClosed() ;
|
||||
bool bLast = false ;
|
||||
Point3d ptPrev ;
|
||||
bool bFirst = true ;
|
||||
const ICurveComposite* pCC = GetCurveComposite( &crCrv) ;
|
||||
int nTria = -1 ;
|
||||
Triangle3dEx trTria ;
|
||||
while ( bFound) {
|
||||
// se trovo proiezione, la salvo
|
||||
Point5ax Pt5ax ;
|
||||
Pt5ax.ptP = ptP ;
|
||||
Pt5ax.dPar = dPar ;
|
||||
double dDecimal ;
|
||||
bool bJoint = modf( dPar, &dDecimal) == 0.0 ;
|
||||
int nCrv = int( bJoint ? dPar - 1 : dPar) ;
|
||||
if ( nCrv < 0)
|
||||
nCrv = 0 ;
|
||||
int nTriaCurr ; pCC->GetCurveTempProp( nCrv, nTriaCurr, 0) ;
|
||||
if ( nTriaCurr != nTria)
|
||||
pSurf.GetTriangle( nTriaCurr, trTria) ;
|
||||
|
||||
if ( ! CalcNormal( ptP, trTria, Pt5ax.vtDir1))
|
||||
Pt5ax.vtDir1 = trTria.GetN() ;
|
||||
nTria = nTriaCurr ;
|
||||
if ( bJoint) {
|
||||
int nPar = int( dPar) ;
|
||||
if ( bClosed && nPar > pCC->GetCurveCount() - 1)
|
||||
nPar = 0 ;
|
||||
int nTria2 ; pCC->GetCurveTempProp( nPar, nTria2, 0) ;
|
||||
Triangle3dEx trTria2 ; pSurf.GetTriangle( nTria2, trTria2) ;
|
||||
if ( ! CalcNormal( ptP, trTria2, Pt5ax.vtDir2))
|
||||
Pt5ax.vtDir2 = trTria2.GetN() ;
|
||||
nTria = nTria2 ;
|
||||
trTria = trTria2 ;
|
||||
}
|
||||
else
|
||||
Pt5ax.vtDir2 = Pt5ax.vtDir1 ;
|
||||
Pt5ax.vtDirU = V_NULL ;
|
||||
Pt5ax.vtDirV = V_NULL ;
|
||||
Pt5ax.nFlag = P5AX_STD ;
|
||||
vPt5ax.emplace_back( Pt5ax) ;
|
||||
|
||||
Vector3d vtDirNext = ptPNext - ptP ; vtDirNext.Normalize() ;
|
||||
// se richiesta la tangente anziché la normale allora modifico il vettore associato al punto
|
||||
Vector3d vtDir ;
|
||||
if ( ! bNormOrTang) {
|
||||
Vector3d vtNorm = vPt5ax.back().vtDir1 ;
|
||||
if ( bFirst)
|
||||
vtDir = vtDirNext ;
|
||||
else
|
||||
vtDir = vtDirPrev ;
|
||||
// in caso sia stato superato l'angolo limite dò priorità alla direzione precedente
|
||||
bool bEdge = false ;
|
||||
if ( vtDirPrev.IsValid() && (! bFirst || ( bFirst && bClosed))) {
|
||||
double dProj = vtDirNext * vtDirPrev ;
|
||||
bEdge = dProj < COS_ANG_MAX_CORNER ;
|
||||
if ( ! bEdge)
|
||||
vtDir = Media( vtDirNext, vtDirPrev) ;
|
||||
}
|
||||
vtDirPrev = vtDirNext ;
|
||||
bEdge = bEdge || ! AreSameVectorExact( vPt5ax.back().vtDir1, vPt5ax.back().vtDir2) ;
|
||||
Vector3d vtTang = vtDir ^ vtNorm ; vtTang.Normalize() ;
|
||||
vPt5ax.back().vtDir1 = vtTang ;
|
||||
if ( bEdge) {
|
||||
Vector3d vtTang2 = vtDirNext ^ vPt5ax.back().vtDir2 ;
|
||||
vtTang2.Normalize() ;
|
||||
vPt5ax.back().vtDir2 = vtTang2 ;
|
||||
}
|
||||
else
|
||||
vPt5ax.back().vtDir2 = vtTang ;
|
||||
}
|
||||
// passo al successivo
|
||||
ptPrev = ptP ;
|
||||
bFound = PL.GetNextULine( &dPar, &ptP, &dParNext, &ptPNext) ;
|
||||
// se sono arrivato alla fine aggiungo l'ultimo punto
|
||||
if ( ! bFound && ! bLast) {
|
||||
bLast = true ;
|
||||
bFound = true ;
|
||||
}
|
||||
if ( bFirst)
|
||||
bFirst = false ;
|
||||
}
|
||||
|
||||
// se richiesto, inserimento punti intermedi in presenza di spigoli
|
||||
if ( bSharpEdges) {
|
||||
if ( ! AddPointsOnCorners( vPt5ax))
|
||||
return false ;
|
||||
}
|
||||
|
||||
// rimozione punti in eccesso rispetto alle tolleranze
|
||||
RemovePointsInExcess( vPt5ax, dLinTol, dMaxSegmLen, bSharpEdges) ;
|
||||
|
||||
return true ;
|
||||
}
|
||||
|
||||
+852
-625
File diff suppressed because it is too large
Load Diff
+23
-18
@@ -6102,7 +6102,8 @@ ManageTwistInQuadrangulation( const ICurveComposite* pSubEdge1, const ICurveComp
|
||||
IGeomDB* pGeomDB = GetCurrGeomDB() ;
|
||||
VERIFY_GEOMDB( pGeomDB, false)
|
||||
#endif
|
||||
|
||||
|
||||
int nLastSync = ssize( vSyncLines) ;
|
||||
// --- Verifico il cambiamento di Deviazione angolare tra Inizio-Fine della prima curva di Bordo
|
||||
Vector3d vtS1 ; pSubEdge1->GetStartDir( vtS1) ;
|
||||
Vector3d vtE1 ; pSubEdge1->GetEndDir( vtE1) ;
|
||||
@@ -6234,7 +6235,7 @@ ManageTwistInQuadrangulation( const ICurveComposite* pSubEdge1, const ICurveComp
|
||||
#endif
|
||||
|
||||
// Se non ho alcuna linea di sincronizzazione, non faccio nulla
|
||||
if ( vSyncLines.empty())
|
||||
if ( vSyncLines.empty() || ( ! bSplit2 && ! bSplit1))
|
||||
return true ;
|
||||
|
||||
const double TOL = 250. * EPS_SMALL ;
|
||||
@@ -6245,7 +6246,7 @@ ManageTwistInQuadrangulation( const ICurveComposite* pSubEdge1, const ICurveComp
|
||||
const double LEN_TOL = 2. ;
|
||||
const double dParTol = 0.1 ;
|
||||
double dLenPrev1 = - LEN_TOL, dLenPrev2 = - LEN_TOL ;
|
||||
for ( int i = 0 ; i < ssize( vSyncLines) ; ++ i) {
|
||||
for ( int i = nLastSync ; i < ssize( vSyncLines) ; ++ i) {
|
||||
double dSyncLen1 ; pSubEdge1->GetLengthAtPoint( vSyncLines[i].first, dSyncLen1, TOL) ;
|
||||
bool bErase = ( dSyncLen1 < max( LEN_TOL, dParTol * dLen1) || dSyncLen1 - dLenPrev1 < LEN_TOL) ;
|
||||
bEraseLastIso = dSyncLen1 > min( dLen1 - LEN_TOL, ( 1 - dParTol) * dLen1) ;
|
||||
@@ -6267,28 +6268,28 @@ ManageTwistInQuadrangulation( const ICurveComposite* pSubEdge1, const ICurveComp
|
||||
return true ;
|
||||
|
||||
// Se due curve di Sincronizzazione rimaste
|
||||
if ( ssize( vSyncLines) == 2) {
|
||||
if ( ssize( vSyncLines) - nLastSync == 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))
|
||||
if ( AreSamePointEpsilon( vSyncLines.end()[-2].first, vSyncLines.end()[-1].first, LEN_TOL) &&
|
||||
AreSamePointEpsilon( vSyncLines.end()[-2].second, vSyncLines.end()[-1].second, LEN_TOL))
|
||||
vSyncLines.pop_back() ;
|
||||
}
|
||||
// Se due curve di Sincronizzazione rimaste
|
||||
if ( ssize( vSyncLines) == 2) {
|
||||
if ( ssize( vSyncLines) - nLastSync == 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) ;
|
||||
double dU0 ; pSubEdge1->GetParamAtPoint( vSyncLines.end()[-2].first, dU0, TOL) ;
|
||||
double dU1 ; pSubEdge1->GetParamAtPoint( vSyncLines.end()[-1].first, dU1, TOL) ;
|
||||
if ( dU0 > dU1 - EPS_SMALL)
|
||||
swap( vSyncLines[0], vSyncLines[1]) ;
|
||||
swap( vSyncLines.end()[-2], vSyncLines[-1]) ;
|
||||
// Verifico che le curve non si intreccino
|
||||
pSubEdge2->GetParamAtPoint( vSyncLines[0].second, dU0, TOL) ;
|
||||
pSubEdge2->GetParamAtPoint( vSyncLines[1].second, dU1, TOL) ;
|
||||
pSubEdge2->GetParamAtPoint( vSyncLines.end()[-2].second, dU0, TOL) ;
|
||||
pSubEdge2->GetParamAtPoint( vSyncLines.end()[-1].second, dU1, TOL) ;
|
||||
if ( dU0 > dU1 - EPS_SMALL)
|
||||
vSyncLines.pop_back() ; // lascio solo la prima... ( bisognerebbe scegliere quale lasciare con un criterio migliore ?)
|
||||
}
|
||||
|
||||
// se ne ho due tengo solo la prima
|
||||
if ( ssize( vSyncLines) == 2)
|
||||
if ( ssize( vSyncLines) - nLastSync == 2)
|
||||
vSyncLines.pop_back() ;
|
||||
|
||||
return true ;
|
||||
@@ -6395,8 +6396,8 @@ SurfBezier::CreateSmoothRuledByTwoCurves( const ICurve* pCurve0, const ICurve* p
|
||||
BIPOINT SyncLinePrev, SyncLineNext ;
|
||||
pCrvQuad1->GetStartPoint( SyncLinePrev.first) ;
|
||||
pCrvQuad2->GetStartPoint( SyncLinePrev.second) ;
|
||||
for ( int i = 0 ; i < ssize( vEdgeSyncLines) ; ++ i) {
|
||||
if ( i == ssize( vEdgeSyncLines) - 1) {
|
||||
for ( int i = 0 ; i <= ssize( vEdgeSyncLines) ; ++ i) {
|
||||
if ( i == ssize( vEdgeSyncLines)) {
|
||||
pCrvQuad1->GetEndPoint( SyncLineNext.first) ;
|
||||
pCrvQuad2->GetEndPoint( SyncLineNext.second) ;
|
||||
}
|
||||
@@ -6411,7 +6412,7 @@ SurfBezier::CreateSmoothRuledByTwoCurves( const ICurve* pCurve0, const ICurve* p
|
||||
pCrvQuad1->GetParamAtPoint( SyncLineNext.first, dUE1) ;
|
||||
pCrvQuad2->GetParamAtPoint( SyncLineNext.second, dUE2) ;
|
||||
PtrOwner<ICurveComposite> pCrvSubQuad1( ConvertCurveToComposite( pCrvQuad1->CopyParamRange( dUS1, dUE1))) ;
|
||||
PtrOwner<ICurveComposite> pCrvSubQuad2( ConvertCurveToComposite( pCrvQuad2->CopyParamRange( dUE1, dUE2))) ;
|
||||
PtrOwner<ICurveComposite> pCrvSubQuad2( ConvertCurveToComposite( pCrvQuad2->CopyParamRange( dUS2, dUE2))) ;
|
||||
ManageTwistInQuadrangulation( pCrvSubQuad1, pCrvSubQuad2, vTwistSyncLines, bEraseLastIso) ;
|
||||
// Aggiorno i parametri
|
||||
SyncLinePrev = SyncLineNext ;
|
||||
@@ -6945,14 +6946,18 @@ SurfBezier::CreateByIsoParamSet( const ICurve* pCurve0, const ICurve* pCurve1, c
|
||||
if ( vIso[c].dParam0 < dLastParam0) {
|
||||
if ( dLastParam0 - vIso[c].dParam0 < EPS_ZERO)
|
||||
vIso[c].dParam0 = dLastParam0 + EPS_PARAM ;
|
||||
else
|
||||
else {
|
||||
LOG_DBG_ERR( GetEGkLogger(), "ERROR : Bezier Surface couldn't be created with the isocurves provided") ;
|
||||
return false ;
|
||||
}
|
||||
}
|
||||
if ( vIso[c].dParam1 < dLastParam1) {
|
||||
if ( dLastParam1 - vIso[c].dParam1 < EPS_ZERO)
|
||||
vIso[c].dParam1 = dLastParam1 + EPS_PARAM ;
|
||||
else
|
||||
else {
|
||||
LOG_DBG_ERR( GetEGkLogger(), "ERROR : Bezier Surface couldn't be created with the isocurves provided") ;
|
||||
return false ;
|
||||
}
|
||||
}
|
||||
dLastParam0 = vIso[c].dParam0 ;
|
||||
dLastParam1 = vIso[c].dParam1 ;
|
||||
|
||||
+8
-6
@@ -1150,7 +1150,7 @@ SurfTriMesh::MarchOneTria( int& nT, int& nV, int nTimeStamp,
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool
|
||||
SurfTriMesh::GetSilhouette( const Vector3d& vtDir, double dTol, POLYLINEVECTOR& vPL, bool bAllTria) const
|
||||
SurfTriMesh::GetSilhouette( const Vector3d& vtDir, double dTol, POLYLINEVECTOR& vPL, bool bAllTria, double dLinTol) const
|
||||
{
|
||||
// Verifico lo stato
|
||||
if ( m_nStatus != OK)
|
||||
@@ -1163,6 +1163,7 @@ SurfTriMesh::GetSilhouette( const Vector3d& vtDir, double dTol, POLYLINEVECTOR&
|
||||
|
||||
// Controlli su tolleranza
|
||||
dTol = max( dTol, 100 * EPS_SMALL) ;
|
||||
double dMyLinTol = max( EPS_SMALL, dLinTol) ;
|
||||
|
||||
// Determino il riferimento di proiezione
|
||||
Frame3d frOCS ; frOCS.Set( ORIG, vtVers) ;
|
||||
@@ -1182,7 +1183,7 @@ SurfTriMesh::GetSilhouette( const Vector3d& vtDir, double dTol, POLYLINEVECTOR&
|
||||
( ! bAllTria && Tria.GetN() * vtVers > EPS_ZERO)) &&
|
||||
Tria.Scale( frOCS, 1, 1, 0) && Tria.GetSqMinHeight() > SQ_EPS_SMALL) {
|
||||
PtrOwner<SurfFlatRegion> pSfrTria( GetBasicSurfFlatRegion( GetSurfFlatRegionFromTriangle( Tria))) ;
|
||||
if ( ! IsNull( pSfrTria)) {
|
||||
if ( ! IsNull( pSfrTria) && pSfrTria->IsValid()) {
|
||||
if ( bAllTria && Tria.GetN() * vtVers < 0)
|
||||
pSfrTria->Invert() ;
|
||||
pSfrTria->Offset( dTol, ICurve::OFF_FILLET) ;
|
||||
@@ -1210,7 +1211,7 @@ SurfTriMesh::GetSilhouette( const Vector3d& vtDir, double dTol, POLYLINEVECTOR&
|
||||
for ( int i = 0 ; i < pSfr->GetChunkCount() ; ++ i) {
|
||||
for ( int j = 0 ; j < pSfr->GetLoopCount( i) ; ++ j) {
|
||||
PolyLine PL ;
|
||||
if ( ! pSfr->ApproxLoopWithLines( i, j, LIN_TOL_STD, ANG_TOL_STD_DEG, ICurve::APL_STD, PL)) {
|
||||
if ( ! pSfr->ApproxLoopWithLines( i, j, dMyLinTol, ANG_TOL_STD_DEG, ICurve::APL_STD, PL)) {
|
||||
vPL.clear() ;
|
||||
return false ;
|
||||
}
|
||||
@@ -1223,7 +1224,7 @@ SurfTriMesh::GetSilhouette( const Vector3d& vtDir, double dTol, POLYLINEVECTOR&
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool
|
||||
SurfTriMesh::GetSilhouette( const Plane3d& plPlane, double dTol, POLYLINEVECTOR& vPL, bool bAllTria) const
|
||||
SurfTriMesh::GetSilhouette( const Plane3d& plPlane, double dTol, POLYLINEVECTOR& vPL, bool bAllTria, double dLinTol) const
|
||||
{
|
||||
// Verifico lo stato
|
||||
if ( m_nStatus != OK)
|
||||
@@ -1236,6 +1237,7 @@ SurfTriMesh::GetSilhouette( const Plane3d& plPlane, double dTol, POLYLINEVECTOR&
|
||||
|
||||
// Controlli su tolleranza
|
||||
dTol = max( dTol, 100 * EPS_SMALL) ;
|
||||
double dMyLinTol = max( EPS_SMALL, dLinTol) ;
|
||||
|
||||
// Determino il riferimento di proiezione
|
||||
Frame3d frOCS ; frOCS.Set( plPlane.GetPoint(), vtVers) ;
|
||||
@@ -1259,7 +1261,7 @@ SurfTriMesh::GetSilhouette( const Plane3d& plPlane, double dTol, POLYLINEVECTOR&
|
||||
// lo proietto sul piano e creo la regione
|
||||
if ( pgTria.Scale( frOCS, 1, 1, 0)) {
|
||||
PtrOwner<SurfFlatRegion> pSfrTria( GetBasicSurfFlatRegion( GetSurfFlatRegionFromPolyLine( pgTria.GetPolyLine()))) ;
|
||||
if ( ! IsNull( pSfrTria)) {
|
||||
if ( ! IsNull( pSfrTria) && pSfrTria->IsValid()) {
|
||||
if ( bAllTria && Tria.GetN() * vtVers < 0)
|
||||
pSfrTria->Invert() ;
|
||||
pSfrTria->Offset( dTol, ICurve::OFF_FILLET) ;
|
||||
@@ -1289,7 +1291,7 @@ SurfTriMesh::GetSilhouette( const Plane3d& plPlane, double dTol, POLYLINEVECTOR&
|
||||
for ( int i = 0 ; i < pSfr->GetChunkCount() ; ++ i) {
|
||||
for ( int j = 0 ; j < pSfr->GetLoopCount( i) ; ++ j) {
|
||||
PolyLine PL ;
|
||||
if ( ! pSfr->ApproxLoopWithLines( i, j, LIN_TOL_STD, ANG_TOL_STD_DEG, ICurve::APL_STD, PL)) {
|
||||
if ( ! pSfr->ApproxLoopWithLines( i, j, dMyLinTol, ANG_TOL_STD_DEG, ICurve::APL_STD, PL)) {
|
||||
vPL.clear() ;
|
||||
return false ;
|
||||
}
|
||||
|
||||
+3
-2
@@ -16,6 +16,7 @@
|
||||
#include "ObjGraphicsMgr.h"
|
||||
#include "DllMain.h"
|
||||
#include "GeoObjRW.h"
|
||||
#include "GeoConst.h"
|
||||
#include "/EgtDev/Include/EGkSurfTriMesh.h"
|
||||
#include "/EgtDev/Include/EGkHashGrids3d.h"
|
||||
#include "/EgtDev/Include/EGkPointGrid3d.h"
|
||||
@@ -295,8 +296,8 @@ class SurfTriMesh : public ISurfTriMesh, public IGeoObjRW
|
||||
bool GetTriangleSmoothNormals( int nId, TriNormals3d& TNrms) const override ;
|
||||
SurfTriMesh* CloneTriangle( int nT) const override ;
|
||||
bool GetLoops( POLYLINEVECTOR& vPL) const override ;
|
||||
bool GetSilhouette( const Vector3d& vtDir, double dTol, POLYLINEVECTOR& vPL, bool bAllTria = false) const override ;
|
||||
bool GetSilhouette( const Plane3d& plPlane, double dTol, POLYLINEVECTOR& vPL, bool bAllTria = false) const override ;
|
||||
bool GetSilhouette( const Vector3d& vtDir, double dTol, POLYLINEVECTOR& vPL, bool bAllTria = false, double dLinTol = LIN_TOL_STD) const override ;
|
||||
bool GetSilhouette( const Plane3d& plPlane, double dTol, POLYLINEVECTOR& vPL, bool bAllTria = false, double dLinTol = LIN_TOL_STD) const override ;
|
||||
int GetFacetCount( void) const override ;
|
||||
int GetFacetSize( void) const override
|
||||
{ return int( m_vFacet.size()) ; }
|
||||
|
||||
@@ -37,6 +37,19 @@ struct AppliedVector {
|
||||
int nPropIndex ;
|
||||
} ;
|
||||
|
||||
struct IntersToCheck {
|
||||
Point3d ptInters ;
|
||||
Vector3d vtN ;
|
||||
double dU ;
|
||||
int nG ;
|
||||
int nI ;
|
||||
int nJ ;
|
||||
IntersToCheck( const Point3d& _ptI, const Vector3d& _vtN, double _dU, int _nG, int _nI, int _nJ) :
|
||||
ptInters( _ptI), vtN( _vtN), dU( _dU), nG( _nG), nI( _nI), nJ( _nJ) { ;}
|
||||
};
|
||||
|
||||
typedef std::vector<IntersToCheck> INTTOCHECKVEC ;
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
class VolZmap : public IVolZmap, public IGeoObjRW
|
||||
{
|
||||
@@ -84,7 +97,7 @@ class VolZmap : public IVolZmap, public IGeoObjRW
|
||||
bool Create( const Point3d& ptO, double dDimX, double dDimY, double dDimZ, double dStep, bool bTriDex, int* nError = nullptr) override ;
|
||||
bool CreateEmpty( const Point3d& ptO, double dDimX, double dDimY, double dDimZ, double dStep, bool bTriDex, int* nError = nullptr) override ;
|
||||
bool CreateFromFlatRegion( const ISurfFlatRegion& Surf, double dDimZ, double dStep, bool bTriDex, int* nError = nullptr) override ;
|
||||
bool CreateFromTriMesh( const ISurfTriMesh& Surf, double dStep, bool bTriDex, double dExtraBox = 0, int* nError = nullptr) override ;
|
||||
bool CreateFromTriMesh( const ISurfTriMesh& Surf, double dStep, bool bTriDex, double dExtraBox = 0, int* nError = nullptr, bool bForceClosed = false) override ;
|
||||
int GetBlockCount( void) const override ;
|
||||
int GetBlockUpdatingCounter( int nBlock) const override ;
|
||||
bool GetBlockTriangles( int nBlock, TRIA3DEXVECTOR& vTria) const override ;
|
||||
@@ -463,11 +476,14 @@ class VolZmap : public IVolZmap, public IGeoObjRW
|
||||
bool CDeSimpleTorus( const Frame3d& frTorus, double dMaxRad, double dMinRad, bool bPrecise = false) const ;
|
||||
// Funzione per crezione solido in parallelo
|
||||
bool CreateMapPart( int nMap, int nInfI, int nSupI, int nInfJ, int nSupJ, const Vector3d& vtLen, const Point3d& ptMapOrig,
|
||||
const ISurfTriMesh& Surf, IntersParLinesSurfTm& intPLSTM) ;
|
||||
const ISurfTriMesh& Surf, IntersParLinesSurfTm& intPLSTM, bool bForceClosed) ;
|
||||
bool AddMapPart( int nMap, int nInfI, int nSupI, int nInfJ, int nSupJ, const Vector3d& vtLen, const Point3d& ptMapOrig,
|
||||
const ISurfTriMesh& Surf, IntersParLinesSurfTm& intPLSTM) ;
|
||||
bool SubtractMapPart( int nMap, int nInfI, int nSupI, int nInfJ, int nSupJ, const Vector3d& vtLen, const Point3d& ptMapOrig,
|
||||
const ISurfTriMesh& Surf, IntersParLinesSurfTm& intPLSTM) ;
|
||||
bool AdjustDexelThroughCracks( int nG, const Vector3d& vtLen, const INTTOCHECKVEC& vIntToCheck, const Point3d& ptMapOrig,
|
||||
const ISurfTriMesh& Surf, const IntersParLinesSurfTm& intPLSTM) ;
|
||||
DBLDBLVECTOR GetNeighbours( int nG, int nI, int nJ, double dPar) ;
|
||||
// Funzioni per Offset di superfici
|
||||
bool InitVolZMapOffset( const CISURFTMPVECTOR& vSurf, double dOffs, double dTol) ;
|
||||
bool InitVolZMapThickeningOffset( const CISURFTMPVECTOR& vSurf, double dOffs, double dTol) ;
|
||||
|
||||
+361
-10
@@ -19,6 +19,7 @@
|
||||
#include "GeoConst.h"
|
||||
#include "/EgtDev/Include/EGkStmFromCurves.h"
|
||||
#include "/EgtDev/Include/EGkIntersLineSurfTm.h"
|
||||
#include "/EgtDev/Include/EGkIntersLinePlane.h"
|
||||
#include "/EgtDev/Include/EgtNumUtils.h"
|
||||
#include <future>
|
||||
|
||||
@@ -542,7 +543,7 @@ VolZmap::CreateFromFlatRegion( const ISurfFlatRegion& Surf, double dDimZ, double
|
||||
//----------------------------------------------------------------------------
|
||||
bool
|
||||
VolZmap::CreateMapPart( int nMap, int nInfI, int nSupI, int nInfJ, int nSupJ, const Vector3d& vtLen, const Point3d& ptMapOrig,
|
||||
const ISurfTriMesh& Surf, IntersParLinesSurfTm& intPLSTM)
|
||||
const ISurfTriMesh& Surf, IntersParLinesSurfTm& intPLSTM, bool bForceClosed)
|
||||
{
|
||||
if ( nMap < 0 || nMap > 2 ||
|
||||
nInfI < 0 || nInfI > m_nNx[nMap] ||
|
||||
@@ -553,6 +554,8 @@ VolZmap::CreateMapPart( int nMap, int nInfI, int nSupI, int nInfJ, int nSupJ, co
|
||||
|
||||
double dCosSmall = sin( EPS_ANG_SMALL * DEGTORAD) ;
|
||||
|
||||
INTTOCHECKVEC vIntToCheck ;
|
||||
|
||||
// Determinazione e ridimensionamento dei dexel interni alla trimesh
|
||||
for ( int i = nInfI ; i < nSupI ; ++ i) {
|
||||
for ( int j = nInfJ ; j < nSupJ ; ++ j) {
|
||||
@@ -584,6 +587,14 @@ VolZmap::CreateMapPart( int nMap, int nInfI, int nSupI, int nInfJ, int nSupJ, co
|
||||
|
||||
int nInt = int( IntersectionResults.size()) ;
|
||||
|
||||
if ( nInt == 1) {
|
||||
int nT = IntersectionResults[0].nT ;
|
||||
int nF = Surf.GetFacetFromTria( nT) ;
|
||||
Vector3d vtN ; Surf.GetFacetNormal( nF, vtN) ;
|
||||
vIntToCheck.emplace_back( IntersectionResults[0].ptI, vtN, IntersectionResults[0].dU, nMap, i, j) ;
|
||||
continue ;
|
||||
}
|
||||
|
||||
int nPos = j * m_nNx[nMap] + i ;
|
||||
|
||||
bool bInside = false ;
|
||||
@@ -592,6 +603,14 @@ VolZmap::CreateMapPart( int nMap, int nInfI, int nSupI, int nInfJ, int nSupJ, co
|
||||
|
||||
for ( int k = 0 ; k < nInt ; ++ k) {
|
||||
|
||||
if ( k > 0) {
|
||||
int z = k - 1 ;
|
||||
// controllo se l'intersezione corrente è coincidente con la precedente, per esempio se ILTT == 4 ( intersezione su un lato di un triangolo, quindi con 2 triangoli)
|
||||
if ( IntersectionResults[k].dU - IntersectionResults[z].dU < EPS_SMALL &&
|
||||
IntersectionResults[k].dCosDN - IntersectionResults[z].dCosDN < EPS_SMALL)
|
||||
continue ;
|
||||
}
|
||||
|
||||
int nIntType = IntersectionResults[k].nILTT ;
|
||||
|
||||
// Se c'è intersezione
|
||||
@@ -601,7 +620,12 @@ VolZmap::CreateMapPart( int nMap, int nInfI, int nSupI, int nInfJ, int nSupJ, co
|
||||
|
||||
// entro nella superficie trimesh
|
||||
if ( dCos < - dCosSmall) {
|
||||
|
||||
if ( bInside) {
|
||||
// salvo la precedente, perché vuol dire che manca la sua uscita
|
||||
// poi procedo con l'intersezione corrente
|
||||
int z = k - 1 ;
|
||||
vIntToCheck.emplace_back( IntersectionResults[z].ptI, vtInN, IntersectionResults[z].dU, nMap, i, j) ;
|
||||
}
|
||||
ptIn = IntersectionResults[k].ptI ;
|
||||
|
||||
int nT = IntersectionResults[k].nT ;
|
||||
@@ -613,8 +637,7 @@ VolZmap::CreateMapPart( int nMap, int nInfI, int nSupI, int nInfJ, int nSupJ, co
|
||||
}
|
||||
|
||||
// esco dalla superficie trimesh
|
||||
else if ( dCos > dCosSmall && bInside) {
|
||||
|
||||
else if ( dCos > dCosSmall) {
|
||||
Point3d ptOut = IntersectionResults[k].ptI ;
|
||||
|
||||
int nT = IntersectionResults[k].nT ;
|
||||
@@ -623,6 +646,13 @@ VolZmap::CreateMapPart( int nMap, int nInfI, int nSupI, int nInfJ, int nSupJ, co
|
||||
Vector3d vtOutN ;
|
||||
Surf.GetFacetNormal( nF, vtOutN) ;
|
||||
|
||||
if ( ! bInside) {
|
||||
// se l'intersezione è uscente, ma non ho un'entrata allora devo salvare l'intersezione corrente, perché è andata persa la sua entrata
|
||||
// procedo poi con la prossima intersezione
|
||||
vIntToCheck.emplace_back( IntersectionResults[k].ptI, vtOutN, IntersectionResults[k].dU, nMap, i, j) ;
|
||||
continue ;
|
||||
}
|
||||
|
||||
int nCurrentSize = int( m_Values[nMap][nPos].size()) ;
|
||||
|
||||
// Aggiungo un tratto al dexel
|
||||
@@ -643,6 +673,12 @@ VolZmap::CreateMapPart( int nMap, int nInfI, int nSupI, int nInfJ, int nSupJ, co
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( bForceClosed) {
|
||||
if ( ! AdjustDexelThroughCracks( nMap, vtLen, vIntToCheck, ptMapOrig, Surf, intPLSTM))
|
||||
return false ;
|
||||
}
|
||||
|
||||
return true ;
|
||||
}
|
||||
|
||||
@@ -834,11 +870,14 @@ VolZmap::SubtractMapPart( int nMap, int nInfI, int nSupI, int nInfJ, int nSupJ,
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool
|
||||
VolZmap::CreateFromTriMesh( const ISurfTriMesh& Surf, double dStep, bool bTriDex, double dExtraBox, int* nError)
|
||||
VolZmap::CreateFromTriMesh( const ISurfTriMesh& Surf, double dStep, bool bTriDex, double dExtraBox, int* nError, bool bForceClosed)
|
||||
{
|
||||
// Se la superficie non è chiusa oppure orientata al contrario non ha senso continuare
|
||||
double dVol ;
|
||||
if ( ! Surf.IsClosed() || ! Surf.GetVolume( dVol) || dVol < 0)
|
||||
if ( ( ! Surf.IsClosed() || ! Surf.GetVolume( dVol) || dVol < 0) && ! bForceClosed)
|
||||
return false ;
|
||||
|
||||
if ( bForceClosed && ! bTriDex)
|
||||
return false ;
|
||||
|
||||
// Assegno la dimensione della mappa 1 o 3
|
||||
@@ -940,7 +979,7 @@ VolZmap::CreateFromTriMesh( const ISurfTriMesh& Surf, double dStep, bool bTriDex
|
||||
IntersParLinesSurfTm intPLSTM( frMapFrame, Surf) ;
|
||||
|
||||
// Standarda è multithread
|
||||
constexpr bool MULTITHREAD = true ;
|
||||
constexpr bool MULTITHREAD = false ;
|
||||
if ( MULTITHREAD) {
|
||||
|
||||
// Numero massimo di thread
|
||||
@@ -956,7 +995,7 @@ VolZmap::CreateFromTriMesh( const ISurfTriMesh& Surf, double dStep, bool bTriDex
|
||||
nInfI = nSupI ;
|
||||
nSupI = nInfI + ( nThread < nRemainder ? nDexNum + 1 : nDexNum) ;
|
||||
vRes[nThread] = async( launch::async, &VolZmap::CreateMapPart, this, nG,
|
||||
nInfI, nSupI, 0, m_nNy[nG], ref( vtLen), ref( ptMapOrig), ref( Surf), ref( intPLSTM)) ;
|
||||
nInfI, nSupI, 0, m_nNy[nG], ref( vtLen), ref( ptMapOrig), ref( Surf), ref( intPLSTM), bForceClosed) ;
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -968,7 +1007,7 @@ VolZmap::CreateFromTriMesh( const ISurfTriMesh& Surf, double dStep, bool bTriDex
|
||||
nInfJ = nSupJ ;
|
||||
nSupJ = nInfJ + ( nThread < nRemainder ? nDexNum + 1 : nDexNum) ;
|
||||
vRes[nThread] = async( launch::async, &VolZmap::CreateMapPart, this, nG,
|
||||
0, m_nNx[nG], nInfJ, nSupJ, ref( vtLen), ref( ptMapOrig), ref( Surf),ref( intPLSTM)) ;
|
||||
0, m_nNx[nG], nInfJ, nSupJ, ref( vtLen), ref( ptMapOrig), ref( Surf),ref( intPLSTM), bForceClosed) ;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -987,7 +1026,7 @@ VolZmap::CreateFromTriMesh( const ISurfTriMesh& Surf, double dStep, bool bTriDex
|
||||
|
||||
// !!!! NON MULTITHREAD : SOLO PER DEBUG !!!!
|
||||
else {
|
||||
CreateMapPart( nG, 0, m_nNx[nG], 0, m_nNy[nG], vtLen, ptMapOrig, Surf, intPLSTM) ;
|
||||
CreateMapPart( nG, 0, m_nNx[nG], 0, m_nNy[nG], vtLen, ptMapOrig, Surf, intPLSTM, bForceClosed) ;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1008,3 +1047,315 @@ VolZmap::CreateFromTriMesh( const ISurfTriMesh& Surf, double dStep, bool bTriDex
|
||||
|
||||
return bCompleted ;
|
||||
}
|
||||
|
||||
DBLDBLVECTOR
|
||||
VolZmap::GetNeighbours( int nG, int nI, int nJ, double dPar)
|
||||
{
|
||||
DBLDBLVECTOR vNeigh ;
|
||||
// parto da in basso a sinistra e procedo per righe, saltando il punto corrente
|
||||
if ( nI > 0 && nJ > 0)
|
||||
vNeigh.emplace_back( nI - dPar, nJ - dPar) ;
|
||||
if ( nJ > 0) {
|
||||
vNeigh.emplace_back( nI, nJ - dPar) ;
|
||||
if ( nI < m_nNx[nG] - dPar)
|
||||
vNeigh.emplace_back( nI + dPar, nJ - dPar) ;
|
||||
}
|
||||
// passo alla fila contenente il punto corrente
|
||||
if ( nI > 0)
|
||||
vNeigh.emplace_back( nI - dPar, nJ) ;
|
||||
if ( nI < m_nNx[nG] - dPar)
|
||||
vNeigh.emplace_back( nI + dPar, nJ) ;
|
||||
// passo alla fila successiva a quella del corrente
|
||||
if ( nJ < m_nNy[nG] - dPar) {
|
||||
if ( nI > 0)
|
||||
vNeigh.emplace_back( nI - dPar, nJ + dPar) ;
|
||||
vNeigh.emplace_back( nI, nJ + dPar) ;
|
||||
if ( nI < m_nNx[nG])
|
||||
vNeigh.emplace_back( nI + dPar, nJ + dPar) ;
|
||||
}
|
||||
return vNeigh ;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool
|
||||
VolZmap::AdjustDexelThroughCracks( int nG, const Vector3d& vtLen, const INTTOCHECKVEC& vIntToCheck, const Point3d& ptMapOrig,
|
||||
const ISurfTriMesh& Surf, const IntersParLinesSurfTm& intPLSTM)
|
||||
{
|
||||
double dAngSameFace = 45 ;
|
||||
double dCosSmall = sin( EPS_ANG_SMALL * DEGTORAD) ;
|
||||
// per ogni intersezione segnalata devo cercare sugli spilloni vicini l'intervallo con l'estremo più vicino a questa intersezione
|
||||
// e usare l'altro estremo, insieme a quello degli altri spilloni vicini, per estrapolare quello mancante sullo spillone corrente
|
||||
Vector3d vtAx ;
|
||||
if ( nG == 0)
|
||||
vtAx = Z_AX ;
|
||||
else if ( nG == 1)
|
||||
vtAx = X_AX ;
|
||||
else if ( nG == 2)
|
||||
vtAx = Y_AX ;
|
||||
for ( const auto& itc : vIntToCheck) {
|
||||
int nPos = itc.nJ * m_nNx[nG] + itc.nI ;
|
||||
double dPosRef = itc.ptInters.v[( nG + 2) % 3] ;
|
||||
// controllo se esiste già un intervallo con un estremo quasi coincidente a quello segnalato
|
||||
// ed entrambi sono entrate o uscite
|
||||
bool bNeedToReconstruct = true ;
|
||||
for ( const auto& data: m_Values[nG][nPos]) {
|
||||
if ( (( abs( dPosRef - data.dMin) < 10 * EPS_SMALL && ( itc.vtN * vtAx) * ( data.vtMinN * vtAx) > 0)) ||
|
||||
(( abs( dPosRef - data.dMax) < 10 * EPS_SMALL && ( itc.vtN * vtAx) * ( data.vtMaxN * vtAx) > 0))) {
|
||||
bNeedToReconstruct = false ;
|
||||
break ;
|
||||
}
|
||||
}
|
||||
if ( ! bNeedToReconstruct)
|
||||
continue ;
|
||||
DBLDBLVECTOR vNeigh = GetNeighbours( nG, itc.nI, itc.nJ, m_dStep) ;
|
||||
PNTVECTOR vNeighInters ;
|
||||
Vector3d vtMeanN = V_NULL ;
|
||||
bool bSearchingInOrOut = itc.vtN * vtAx > 0 ;
|
||||
bool bUseNeighInfo = true ;
|
||||
for ( const auto& [i,j] : vNeigh) {
|
||||
double dMinDist = INFINITO ;
|
||||
double dCorrespInters = INFINITO ;
|
||||
double dNearestPos = NAN ;
|
||||
Vector3d vtNearestN ;
|
||||
int nNeighPos = int( j) * m_nNx[nG] + int( i) ;
|
||||
for ( const auto& data: m_Values[nG][nNeighPos]) {
|
||||
double dDist1 = abs( dPosRef - data.dMin) ;
|
||||
double dDist2 = abs( dPosRef - data.dMax) ;
|
||||
if ( dDist1 < dMinDist || dDist2 < dMinDist) {
|
||||
dMinDist = min( dDist1, dDist2) ;
|
||||
|
||||
// se sto cercando un ingresso
|
||||
if ( bSearchingInOrOut) {
|
||||
if ( data.vtMaxN * vtAx < 0) {
|
||||
dNearestPos = data.dMax ;
|
||||
vtNearestN = data.vtMaxN ;
|
||||
dCorrespInters = data.dMin ;
|
||||
}
|
||||
else if ( data.vtMinN * vtAx < 0) {
|
||||
dNearestPos = data.dMin ;
|
||||
vtNearestN = data.vtMinN ;
|
||||
dCorrespInters = data.dMax ;
|
||||
}
|
||||
}
|
||||
// se sto cercando un'uscita
|
||||
else {
|
||||
if ( data.vtMaxN * vtAx > 0) {
|
||||
dNearestPos = data.dMax ;
|
||||
vtNearestN = data.vtMaxN ;
|
||||
dCorrespInters = data.dMin ;
|
||||
}
|
||||
else if ( data.vtMinN * vtAx > 0) {
|
||||
dNearestPos = data.dMin ;
|
||||
vtNearestN = data.vtMinN ;
|
||||
dCorrespInters = data.dMax ;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// verifico che il dNearestPos sia effettivamente associato al tratto che sto cercando di ricostruire
|
||||
// e non ad un altro presente sullo spillone corrente
|
||||
bool bAddNearest = true ;
|
||||
for ( const auto& data: m_Values[nG][nPos]) {
|
||||
double dDist1 = abs( dCorrespInters - data.dMin) ;
|
||||
double dDist2 = abs( dCorrespInters - data.dMax) ;
|
||||
if ( dDist1 < dMinDist || dDist2 < dMinDist) {
|
||||
bAddNearest = false ;
|
||||
break ;
|
||||
}
|
||||
}
|
||||
|
||||
if ( bAddNearest && isfinite( dNearestPos)) {
|
||||
// controllo anche che i punti non siano troppo lontani tra loro, altrimenti
|
||||
// il piano calcolato potrebbe essere una media troppo grezza
|
||||
double dMaxDist = 3 * m_dStep ;
|
||||
const bool bTooFar = any_of( vNeighInters.begin(), vNeighInters.end(),
|
||||
[ dNearestPos, dMaxDist]( const Point3d& pt) {
|
||||
return abs( pt.z - dNearestPos) > dMaxDist ;
|
||||
}) ;
|
||||
double dAng ;
|
||||
if ( ! bTooFar) {
|
||||
// controllo anche che gli angoli delle normali non cambino troppo
|
||||
// calcolo l'angolo con la media delle precedenti perché il valore oltre cui
|
||||
// considero le normali diverse è grande
|
||||
vtNearestN.GetAngle( vtMeanN, dAng) ;
|
||||
}
|
||||
bAddNearest = ! bTooFar && dAng < dAngSameFace ;
|
||||
if ( bAddNearest) {
|
||||
double dX = ( i + 0.5) * m_dStep ;
|
||||
double dY = ( j + 0.5) * m_dStep ;
|
||||
vNeighInters.emplace_back( dX, dY, dNearestPos) ;
|
||||
vtMeanN += vtNearestN ;
|
||||
}
|
||||
else {
|
||||
bUseNeighInfo = false ;
|
||||
break ;
|
||||
}
|
||||
}
|
||||
}
|
||||
Point3d ptInt ;
|
||||
// se riesco uso i vicini per calcolare l'intersezione mancante
|
||||
if ( bUseNeighInfo) {
|
||||
// se non ci sono almeno tre punti, non allineati, non riesco a determinare il piano con cui tagliare lo spillone
|
||||
if ( ssize( vNeighInters) < 3)
|
||||
continue ;
|
||||
else if ( ssize( vNeighInters) == 3){
|
||||
// verifico che non siano allineati
|
||||
if ( ( vNeighInters[0].x == vNeighInters[1].x && vNeighInters[0].x == vNeighInters[1].x) ||
|
||||
( vNeighInters[0].y == vNeighInters[1].y && vNeighInters[0].y == vNeighInters[1].y))
|
||||
continue ;
|
||||
}
|
||||
// se sono più di tre sicuramente non sono allieanti, visto che sono intorno allo spillone corrente
|
||||
vtMeanN.Normalize() ;
|
||||
Plane3d plLoc ;
|
||||
PolyLine PL ; PL.FromPointVector( vNeighInters) ;
|
||||
PL.IsFlat( plLoc, INFINITO) ;
|
||||
|
||||
// porto anche il punto dell'intersezione nel frame della griglia
|
||||
Point3d ptKnown = itc.ptInters ;
|
||||
Frame3d frGrid ;
|
||||
if ( nG == 0)
|
||||
frGrid.Set( ORIG, Frame3d::TOP) ;
|
||||
else if ( nG == 1)
|
||||
frGrid.Set( ORIG, Frame3d::RIGHT) ;
|
||||
else if ( nG == 2)
|
||||
frGrid.Set( ORIG, Z_AX, X_AX, Y_AX) ;
|
||||
ptKnown.ToLoc( frGrid) ;
|
||||
IntersLinePlane( ptKnown, Z_AX, INFINITO, plLoc, ptInt, false) ;
|
||||
}
|
||||
// altrimenti sposto di poco lo spillone e ricalcolo le intersezioni con la superficie, finché trovo l'intersezione mancante
|
||||
else {
|
||||
bool bFound = false ;
|
||||
DBLVECTOR vSmallStep ;
|
||||
vSmallStep.push_back( Clamp( 0.05 * m_dStep, 50 * EPS_SMALL, 1.)) ;
|
||||
vSmallStep.push_back( Clamp( 0.2 * m_dStep, 50 * EPS_SMALL, 1.)) ;
|
||||
// ciclo una raggiera di spilloni ( a distanza inferiore allo m_dStep) attorno allo spillone
|
||||
// appena trova un'intersezione buona mi fermo
|
||||
// se ciclando la raggiera non trovo un'intersezione valida allora potrei essermi spostato troppo poco dallo spillone originale e essere caduto ancora nella fessura
|
||||
// quindi ciclo nuovamente aumentando un poco la distanza dallo spillone originale ( comunque sempre meno della distanza m_dStep)
|
||||
for ( int c = 0 ; c < ssize( vSmallStep) ; ++c) {
|
||||
DBLDBLVECTOR vSmallMoveNeigh = GetNeighbours( nG, itc.nI, itc.nJ, vSmallStep[c]) ;
|
||||
for ( int n = 0 ; n < 8 ; ++n) {
|
||||
double dX = ( vSmallMoveNeigh[n].first + 0.5) * m_dStep ;
|
||||
double dY = ( vSmallMoveNeigh[n].second + 0.5) * m_dStep ;
|
||||
Point3d ptP0( dX, dY, 0) ;
|
||||
ILSIVECTOR IntersectionResults ;
|
||||
intPLSTM.GetInters( ptP0, vtLen.v[(nG+2)%3], IntersectionResults) ;
|
||||
// intervalli dalle intersezioni
|
||||
vector<Data> vNewIntervals ;
|
||||
bool bInside = false ;
|
||||
Vector3d vtInN ;
|
||||
Point3d ptIn ;
|
||||
for ( int i = 0 ; i < ssize( IntersectionResults) ; ++i) {
|
||||
// Se c'è intersezione
|
||||
if ( IntersectionResults[i].nILTT != ILTT_NO) {
|
||||
if ( i > 0) {
|
||||
int z = i - 1 ;
|
||||
// controllo se l'intersezione corrente è coincidente con la precedente, per esempio se ILTT == 4 ( intersezione su un lato di un triangolo, quindi con 2 triangoli)
|
||||
if ( IntersectionResults[i].dU - IntersectionResults[z].dU < EPS_SMALL &&
|
||||
IntersectionResults[i].dCosDN - IntersectionResults[z].dCosDN < EPS_SMALL)
|
||||
continue ;
|
||||
}
|
||||
double dCos = IntersectionResults[i].dCosDN ;
|
||||
// entro nella superficie trimesh
|
||||
if ( dCos < - dCosSmall) {
|
||||
ptIn = IntersectionResults[i].ptI ;
|
||||
|
||||
int nT = IntersectionResults[i].nT ;
|
||||
int nF = Surf.GetFacetFromTria( nT) ;
|
||||
|
||||
Surf.GetFacetNormal( nF, vtInN) ;
|
||||
|
||||
bInside = true ;
|
||||
}
|
||||
|
||||
// esco dalla superficie trimesh
|
||||
else if ( dCos > dCosSmall && bInside) {
|
||||
Point3d ptOut = IntersectionResults[i].ptI ;
|
||||
|
||||
int nT = IntersectionResults[i].nT ;
|
||||
int nF = Surf.GetFacetFromTria( nT) ;
|
||||
|
||||
Vector3d vtOutN ;
|
||||
Surf.GetFacetNormal( nF, vtOutN) ;
|
||||
|
||||
// Aggiungo un tratto al dexel
|
||||
vNewIntervals.emplace_back() ;
|
||||
|
||||
// Aggiorno dati del tratto di dexel
|
||||
vNewIntervals.back().dMin = ptIn.v[(nG+2)%3] - ptMapOrig.v[(nG+2)%3] ;
|
||||
vNewIntervals.back().dMax = ptOut.v[(nG+2)%3] - ptMapOrig.v[(nG+2)%3] ;
|
||||
vNewIntervals.back().vtMinN = vtInN ;
|
||||
vNewIntervals.back().vtMaxN = vtOutN ;
|
||||
vNewIntervals.back().nToolMin = 0 ;
|
||||
vNewIntervals.back().nToolMax = 0 ;
|
||||
vNewIntervals.back().nCompo = 0 ;
|
||||
|
||||
bInside = false ;
|
||||
}
|
||||
}
|
||||
}
|
||||
// controllo che abbia un intervallo in più rispetto a quelli di prima
|
||||
if ( ssize( vNewIntervals) == ssize( m_Values[nG][nPos]) + 1) {
|
||||
// dovrei verificare che quello in più sia effettivamente quello ricercato????
|
||||
double dMinDist = INFINITO ;
|
||||
for ( const auto& data : vNewIntervals) {
|
||||
if ( abs( data.dMin - dPosRef) < dMinDist) {
|
||||
dMinDist = abs( data.dMin - dPosRef) ;
|
||||
ptInt.Set( itc.nI + 0.5, itc.nJ + 0.5, data.dMax) ;
|
||||
vtMeanN = data.vtMaxN ;
|
||||
bFound = true ;
|
||||
}
|
||||
if ( abs( data.dMax - dPosRef) < dMinDist) {
|
||||
dMinDist = abs( data.dMax - dPosRef) ;
|
||||
ptInt.Set( itc.nI + 0.5, itc.nJ + 0.5, data.dMin) ;
|
||||
vtMeanN = data.vtMinN ;
|
||||
bFound = true ;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( bFound)
|
||||
break ;
|
||||
}
|
||||
if ( bFound)
|
||||
break ;
|
||||
}
|
||||
// se cercando nei d'intorni dello spillone non ho trovato l'intersezione mancante, mi arrendo
|
||||
if ( ! bFound)
|
||||
continue ;
|
||||
}
|
||||
|
||||
double dMin, dMax ;
|
||||
Vector3d vtMinN, vtMaxN ;
|
||||
if ( itc.dU < ptInt.z) {
|
||||
dMin = itc.dU ;
|
||||
dMax = ptInt.z ;
|
||||
vtMinN = itc.vtN ;
|
||||
vtMaxN = vtMeanN ;
|
||||
}
|
||||
else {
|
||||
dMin = ptInt.z ;
|
||||
dMax = itc.dU ;
|
||||
vtMinN = vtMeanN ;
|
||||
vtMaxN = itc.vtN ;
|
||||
}
|
||||
//// prima di aggiungere il tratto verifico che non si sovrapponga con un tratto già presente
|
||||
//for ( const auto& data: m_Values[nG][nPos]) {
|
||||
// if ( ( dMin > data.dMin && dMin < data.dMax) ||
|
||||
// ( dMax > data.dMin && dMax < data.dMax) ||
|
||||
// ( dMin < data.dMin && dMax > data.dMax))
|
||||
// continue ;
|
||||
//}
|
||||
|
||||
m_Values[nG][nPos].emplace_back() ;
|
||||
m_Values[nG][nPos].back().dMin = dMin;
|
||||
m_Values[nG][nPos].back().dMax = dMax ;
|
||||
m_Values[nG][nPos].back().vtMinN = vtMinN ;
|
||||
m_Values[nG][nPos].back().vtMaxN = vtMaxN ;
|
||||
m_Values[nG][nPos].back().nToolMin = 0 ;
|
||||
m_Values[nG][nPos].back().nToolMax = 0 ;
|
||||
m_Values[nG][nPos].back().nCompo = 0 ;
|
||||
std::sort( m_Values[nG][nPos].begin(), m_Values[nG][nPos].end(), []( Data& a, Data& b) { return a.dMin < b.dMin ;}) ;
|
||||
}
|
||||
|
||||
return true ;
|
||||
}
|
||||
+10
-2
@@ -2610,7 +2610,15 @@ VolZmap::MillingGeneralMotionStep( const Point3d& ptPs, const Vector3d& vtDs, co
|
||||
|
||||
// Calcolo angoli di rotazione utensile longitudinale e trasversale rispetto al movimento per eventuale suddivisione
|
||||
double dAlongAngDeg, dAcrossAngDeg ;
|
||||
GetAlongAcrossRotation( vtDs, vtDe, ptPe - ptPs, dAlongAngDeg, dAcrossAngDeg) ;
|
||||
if ( m_vTool[m_nCurrTool].GetRadius() < 40)
|
||||
GetAlongAcrossRotation( vtDs, vtDe, ptPe - ptPs, dAlongAngDeg, dAcrossAngDeg) ;
|
||||
else {
|
||||
// se è una lama uso un asse diverso per calcolare le rotaziona del vettore tool
|
||||
Vector3d vtAxis = vtDs ^ vtDe ;
|
||||
GetAlongAcrossRotation( vtDs, vtDe, vtAxis, dAlongAngDeg, dAcrossAngDeg) ;
|
||||
swap( dAlongAngDeg, dAcrossAngDeg) ;
|
||||
}
|
||||
|
||||
|
||||
// Calcolo numero degli step di movimento
|
||||
int nStepCnt = int( max( { abs( dAlongAngDeg) / ANG_ALONG_STEP, abs( dAcrossAngDeg) / ANG_ACROSS_STEP, 1.})) ;
|
||||
@@ -2719,7 +2727,7 @@ VolZmap::MillingGeneralMotionStep( const Point3d& ptPs, const Vector3d& vtDs, co
|
||||
InitializePointsAndVectors( ptSti, ptEni, vtDSi, vtDEi, ptLs, ptLe, vtLs, vtLe) ;
|
||||
|
||||
// Standard è multithread
|
||||
constexpr bool MULTITHREAD = false ;
|
||||
constexpr bool MULTITHREAD = true ;
|
||||
if ( MULTITHREAD) {
|
||||
// Ciclo sulle mappe
|
||||
vector<future<bool>> vRes( m_nMapNum) ;
|
||||
|
||||
+70
-3
@@ -434,6 +434,7 @@ Voronoi::GetBisectorCurve( int i)
|
||||
pLine->Set( ptS, ptE) ;
|
||||
pLine->SetTempParam( dParS, 0) ;
|
||||
pLine->SetTempParam( dParE, 1) ;
|
||||
pLine->SetTempProp( i) ;
|
||||
pLine->ToGlob( m_Frame) ;
|
||||
return pLine ;
|
||||
}
|
||||
@@ -454,6 +455,7 @@ Voronoi::GetBisectorCurve( int i)
|
||||
pArc->SetC2P( ptC, ptS, ptE) ;
|
||||
pArc->SetTempParam( dParS, 0) ;
|
||||
pArc->SetTempParam( dParS, 1) ; // dParE = dParS
|
||||
pArc->SetTempProp( i) ;
|
||||
pArc->ToGlob( m_Frame) ;
|
||||
return pArc ;
|
||||
}
|
||||
@@ -473,7 +475,7 @@ Voronoi::GetBisectorCurve( int i)
|
||||
bool bInvert = false ;
|
||||
double dPar1, dPar2 ;
|
||||
m_vroni->GetApproxedBisectorParams( i, dPar1, dPar2) ;
|
||||
if ( dPar1 > dPar2 + EPS_SMALL)
|
||||
if ( dPar1 > dPar2)
|
||||
bInvert = true ;
|
||||
|
||||
// punto iniziale
|
||||
@@ -496,6 +498,11 @@ Voronoi::GetBisectorCurve( int i)
|
||||
dParPrev = dPar ;
|
||||
nCrvCount ++ ;
|
||||
}
|
||||
else if ( ( bInvert && j == 0) || ( ! bInvert && j == nPoints - 1)) {
|
||||
// se estremo devo forzarlo
|
||||
pCompo->ModifyEnd( pt) ;
|
||||
dParPrev = dPar ;
|
||||
}
|
||||
// aggiorno per punto successivo
|
||||
if ( bInvert)
|
||||
j -- ;
|
||||
@@ -506,6 +513,7 @@ Voronoi::GetBisectorCurve( int i)
|
||||
// setto parametri sulla curva
|
||||
pCompo->SetTempParam( dParS, 0) ;
|
||||
pCompo->SetTempParam( dParPrev, 1) ;
|
||||
pCompo->SetTempProp( i) ;
|
||||
|
||||
pCompo->ToGlob( m_Frame) ;
|
||||
return pCompo ;
|
||||
@@ -700,8 +708,8 @@ Voronoi::CalcSingleCurvesOffset( ICURVEPOVECTOR& vOffs, double dOffs)
|
||||
// se necessario verifico se dal lato corretto rispetto ai siti di riferimento
|
||||
if ( bLeft && bRight) {
|
||||
// recupero i siti di riferimento
|
||||
int nOrigCrv1, nOrigSubCrv1, nOrigCrv2, nOrigSubCrv2 ;
|
||||
m_vroni->GetBisectorSites( i, nOrigCrv1, nOrigSubCrv1, nOrigCrv2, nOrigSubCrv2) ;
|
||||
int nOrigCrv1, nOrigSubCrv1, nOrigSubPnt1, nOrigCrv2, nOrigSubCrv2, nOrigSubPnt2 ;
|
||||
m_vroni->GetBisectorSites( i, nOrigCrv1, nOrigSubCrv1, nOrigSubPnt1, nOrigCrv2, nOrigSubCrv2, nOrigSubPnt2) ;
|
||||
if ( nOrigCrv1 != -1) {
|
||||
// verifico il lato rispetto al primo sito
|
||||
pCrv->SetTempProp( nOrigSubCrv1 + 1, 0) ;
|
||||
@@ -1261,3 +1269,62 @@ Voronoi::CalcLimitOffset( int nCrv, bool bLeft, double& dOffs)
|
||||
|
||||
return true ;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
bool
|
||||
Voronoi::GetBisectorPointAtParam( int nCrv, double dPar, Point3d& ptP)
|
||||
{
|
||||
if ( ! IsValid())
|
||||
return false ;
|
||||
|
||||
try {
|
||||
if ( ! m_bVDComputed)
|
||||
CalcVoronoi() ;
|
||||
|
||||
// verifico se il bisettore e il parametri richiesto sono sensati
|
||||
if ( nCrv >= m_vroni->GetNumberOfEdges())
|
||||
return false ;
|
||||
double dParS, dParE ;
|
||||
m_vroni->GetBisectorParams( nCrv, dParS, dParE) ;
|
||||
if ( dParS > dParE)
|
||||
swap( dParS, dParE) ;
|
||||
if ( dPar < dParS || dPar > dParE)
|
||||
return false ;
|
||||
|
||||
// calcolo il punto sul bisettore in corrispondenza dell'offset
|
||||
m_vroni->GetBisectorPointAtParam( nCrv, dPar, ptP.v) ;
|
||||
ptP.ToGlob( m_Frame) ;
|
||||
|
||||
return true ;
|
||||
}
|
||||
catch (...) {
|
||||
LOG_ERROR( GetEGkLogger(), m_vroni->GetExceptionMessage()) ;
|
||||
return false ;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
bool
|
||||
Voronoi::GetBisectorSites( int nCrv, int& nCrv1, int& nSubCrv1, int& nSubPnt1, int& nCrv2, int& nSubCrv2, int& nSubPnt2)
|
||||
{
|
||||
if ( ! IsValid())
|
||||
return false ;
|
||||
|
||||
try {
|
||||
if ( ! m_bVDComputed)
|
||||
CalcVoronoi() ;
|
||||
|
||||
// verifico se il bisettore è valido
|
||||
if ( nCrv >= m_vroni->GetNumberOfEdges())
|
||||
return false ;
|
||||
|
||||
// calcolo il punto sul bisettore in corrispondenza dell'offset
|
||||
m_vroni->GetBisectorSites( nCrv, nCrv1, nSubCrv1, nSubPnt1, nCrv2, nSubCrv2, nSubPnt2) ;
|
||||
return true ;
|
||||
}
|
||||
catch (...) {
|
||||
LOG_ERROR( GetEGkLogger(), m_vroni->GetExceptionMessage()) ;
|
||||
return false ;
|
||||
}
|
||||
}
|
||||
@@ -60,6 +60,8 @@ class Voronoi
|
||||
bool CalcFatCurve( ICURVEPOVECTOR& vOffs, double dOffs, bool bSquareEnds, bool bSquareMids, bool bMergeOnlySameProps = true) ;
|
||||
bool CalcMedialAxis( ICURVEPOVECTOR& vCrvs, int nSide) ;
|
||||
bool CalcLimitOffset( int nCrv, bool bLeft, double& dOffs) ;
|
||||
bool GetBisectorPointAtParam( int nCrv, double dPar, Point3d& ptP) ;
|
||||
bool GetBisectorSites( int nCrv, int& nCrv1, int& nSubCrv1, int& nSubPnt1, int& nCrv2, int& nSubCrv2, int& nSubPnt2) ;
|
||||
|
||||
bool Translate( const Vector3d& vtMove) ;
|
||||
bool Rotate( const Point3d& ptAx, const Vector3d& vtAx, double dAngDeg) ;
|
||||
|
||||
Reference in New Issue
Block a user