Files
EgtGeomKernel/CurveCompositeOffset.cpp
T
Dario Sassi 4d66e98ba9 EgtGeomKernel 1.6s4 :
- piccola modifica a offset semplice di composite per conservare inizio.
2016-07-18 08:04:10 +00:00

521 lines
20 KiB
C++

//----------------------------------------------------------------------------
// EgalTech 2013-2013
//----------------------------------------------------------------------------
// File : CurveCompositeOffset.cpp Data : 07.12.14 Versione : 1.5l1
// Contenuto : Metodi per offset semplice della classe CCurveComposite.
//
//
//
// Modifiche : 07.12.14 DS Creazione modulo.
//
//
//----------------------------------------------------------------------------
//--------------------------- Include ----------------------------------------
#include "stdafx.h"
#include "CurveComposite.h"
#include "CurveLine.h"
#include "CurveArc.h"
#include "GeoConst.h"
#include "/EgtDev/Include/EgkCurve.h"
#include "/EgtDev/Include/EgkIntersCurves.h"
#include "/EgtDev/Include/EgtPointerOwner.h"
using namespace std ;
//----------------------------------------------------------------------------
static const int TP_IS_VERT_LINE = 1 ;
//----------------------------------------------------------------------------
static bool IsVerticalLine( const ICurve* pCrv, double* pdLenZ) ;
static bool VerifyAndAdjustSamePoint( ICurve* pCrv1, ICurve* pCrv2, CurveComposite& ccAux) ;
static bool VerifyAndAdjustInternalAngle( ICurve* pCrv1, ICurve* pCrv2, CurveComposite& ccAux) ;
static bool VerifyAndAdjustExternalAngle( ICurve* pCrv1, ICurve* pCrv2, double dDist, int nType,
CurveComposite& ccAux) ;
static bool AddFirstLastVerticalLines( CurveComposite& ccOffs, double dLenVertFirst, double dLenVertLast) ;
static bool MediaInternalAngleDeltaZ( CurveComposite& ccOffs) ;
//----------------------------------------------------------------------------
bool
CurveComposite::SimpleOffsetXY( double dDist, int nType)
{
// creo una copia formata solo da rette e archi che giacciono nel piano XY (VtExtr è Z+)
CurveComposite ccCopy ;
if ( ! ccCopy.CopyFrom( *this) ||
! ccCopy.ArcsBezierCurvesToArcsPerpExtr( 10 * EPS_SMALL, ANG_TOL_STD_DEG))
return false ;
// determino se vietate linee verticali (perpendicolari a piano offset)
bool bNoVertLine = (( nType & ICurve::OFF_NO_VERTICAL) != 0) ;
// determino se richiesti angoli interni con deltaZ mediata
bool bMedIntDz = (( nType & ICurve::OFF_MEDIA_INTDZ) != 0) ;
// verifico se curva chiusa
bool bClosed = ccCopy.IsClosed() && ( nType & ICurve::OFF_FORCE_OPEN) == 0 ;
// creo la curva di offset
CurveComposite ccOffs ;
ccOffs.m_VtExtr = m_VtExtr ;
ccOffs.m_dThick = m_dThick ;
// eseguo l'offset ( estraggo entità dalla copia, le modifiche e le inserisco nell'offset)
// lunghezze di eventuali linee verticali iniziale e finale
double dLenVertFirst = 0 ;
double dLenVertLast = 0 ;
// recupero la prima curva
PtrOwner<ICurve> pCrv1( ccCopy.RemoveFirstOrLastCurve( false)) ;
if ( IsNull( pCrv1))
return false ;
// se la curva è una linea verticale in Z, passo alla successiva
if ( IsVerticalLine( pCrv1, &dLenVertFirst)) {
if ( bNoVertLine)
return false ;
pCrv1.Set( ccCopy.RemoveFirstOrLastCurve( false)) ;
}
// offset della prima curva
if ( IsNull( pCrv1))
return false ;
if ( ! pCrv1->SimpleOffset( dDist, nType))
return false ;
ICurve* pCrvPrev = pCrv1 ;
if ( ! ccOffs.AddSimpleCurve( Release( pCrv1)))
return false ;
// curve successive
PtrOwner<ICurve> pCrv2( ccCopy.RemoveFirstOrLastCurve( false)) ;
while ( ! IsNull( pCrv2)) {
// se la curva è una linea verticale in Z, passo alla successiva
if ( IsVerticalLine( pCrv2, &dLenVertLast)) {
if ( bNoVertLine)
return false ;
pCrv2.Set( ccCopy.RemoveFirstOrLastCurve( false)) ;
continue ;
}
// eseguo semplice offset
if ( ! pCrv2->SimpleOffset( dDist, nType))
return false ;
// verifico relazione con la curva precedente e aggiungo eventuali curve intermedie
CurveComposite ccTemp ;
if ( VerifyAndAdjustSamePoint( pCrvPrev, pCrv2, ccTemp) ||
VerifyAndAdjustInternalAngle( pCrvPrev, pCrv2, ccTemp) ||
VerifyAndAdjustExternalAngle( pCrvPrev, pCrv2, dDist, nType, ccTemp)) {
if ( ccTemp.GetCurveCount() > 0 && ! ccOffs.AddCurveByRelocate( ccTemp))
return false ;
}
// nessun caso è andato a buon fine, errore
else
return false ;
// aggiorno curva precedente
pCrvPrev = pCrv2 ;
// inserisco nell'offset
if ( ! ccOffs.AddSimpleCurve( Release( pCrv2)))
return false ;
// passo alla curva successiva
pCrv2.Set( ccCopy.RemoveFirstOrLastCurve( false)) ;
}
// se originale chiuso, devo confrontare anche ultima e prima curva
if ( bClosed && ccOffs.GetCurveCount() > 0) {
// la curva successiva ora è la prima dell'offset
ICurve* pCrvNext = ccOffs.m_CrvSmplS.front() ;
// verifico relazione con la curva precedente e aggiungo eventuali curve intermedie
CurveComposite ccTemp ;
if ( VerifyAndAdjustSamePoint( pCrvPrev, pCrvNext, ccTemp) ||
VerifyAndAdjustInternalAngle( pCrvPrev, pCrvNext, ccTemp) ||
VerifyAndAdjustExternalAngle( pCrvPrev, pCrvNext, dDist, nType, ccTemp)) {
int nCrvCount = ccTemp.GetCurveCount() ;
if ( nCrvCount > 0 && ! ccOffs.AddCurveByRelocate( ccTemp))
return false ;
if ( nCrvCount > 1) {
ccOffs.ChangeStartPoint( double( ccOffs.m_CrvSmplS.size()) - 1) ;
}
}
// nessun caso è andato a buon fine, errore
else
return false ;
}
// se originale aperto, devo rimettere eventuali lineee verticali iniziale e finale
if ( ! bClosed && ccOffs.GetCurveCount() > 0 &&
! AddFirstLastVerticalLines( ccOffs, dLenVertFirst, dLenVertLast))
return false ;
// se richiesti angoli interni con Z mediata, verifico e sistemo
if ( bMedIntDz && ! MediaInternalAngleDeltaZ( ccOffs))
return false ;
// sposto le curve dall'offset alla composita attuale
return RelocateFrom( ccOffs) ;
}
//----------------------------------------------------------------------------
bool
IsVerticalLine( const ICurve* pCrv, double* pdLenZ)
{
// verifico sia una linea
const CurveLine* pLine = GetBasicCurveLine( pCrv) ;
if ( pLine == nullptr)
return false ;
// verifico sia diretta come l'asse Z
if ( AreSamePointXYApprox( pLine->GetStart(), pLine->GetEnd())) {
if ( pdLenZ != nullptr)
*pdLenZ = pLine->GetEnd().z - pLine->GetStart().z ;
return true ;
}
else {
if ( pdLenZ != nullptr)
*pdLenZ = 0 ;
return false ;
}
}
//----------------------------------------------------------------------------
bool
VerifyAndAdjustSamePoint( ICurve* pCrv1, ICurve* pCrv2, CurveComposite& ccAux)
{
// verifica dei puntatori
if ( pCrv1 == nullptr || pCrv2 == nullptr || &ccAux == nullptr)
return false ;
// pulisco la curva ausiliaria
ccAux.Clear() ;
// calcolo dei punti estremi (finale per prima curva, iniziale per seconda)
Point3d ptP1, ptP2 ;
if ( ! pCrv1->GetEndPoint( ptP1) || ! pCrv2->GetStartPoint( ptP2))
return false ;
// verifica distanza tra estremi
if ( ! AreSamePointXYEpsilon( ptP1, ptP2, 10 * EPS_SMALL))
return false ;
// se punti quasi coincidenti in Z
if ( fabs( ptP1.z - ptP2.z) < 10 * EPS_SMALL) {
// se coincidono esattamente, va bene così
if ( AreSamePointExact( ptP1, ptP2))
return true ;
// sono in tolleranza, ma devo ricongiungere gli estremi
Point3d ptMid = 0.5 * ( ptP1 + ptP2) ;
return ( pCrv1->ModifyEnd( ptMid) && pCrv2->ModifyStart( ptMid)) ;
}
// devo allineare i punti in XY e aggiungere un tratto verticale
else {
// facio coincidere gli estremi in XY
Point3d ptP1a = 0.5 * ( ptP1 + ptP2) ;
ptP1a.z = ptP1.z ;
Point3d ptP2a = 0.5 * ( ptP1 + ptP2) ;
ptP2a.z = ptP2.z ;
if ( ! pCrv1->ModifyEnd( ptP1a) || ! pCrv2->ModifyStart( ptP2a))
return false ;
// aggiungo una retta in Z
PtrOwner<CurveLine> pCrv( CreateBasicCurveLine()) ;
if ( IsNull( pCrv) || ! pCrv->Set( ptP1a, ptP2a))
return false ;
return ccAux.AddCurve( Release( pCrv)) ;
}
}
//----------------------------------------------------------------------------
bool
VerifyAndAdjustInternalAngle( ICurve* pCrv1, ICurve* pCrv2, CurveComposite& ccAux)
{
// verifica dei puntatori
if ( pCrv1 == nullptr || pCrv2 == nullptr || &ccAux == nullptr)
return false ;
// pulisco la curva ausiliaria
ccAux.Clear() ;
// calcolo l'intersezione tra le due curve
IntersCurveCurve intCC( *pCrv1, *pCrv2) ;
if ( intCC.GetIntersCount() == 0)
return false ;
// prendo l'intersezione più vicina al punto medio tra gli estremi delle curve
Point3d ptP1, ptP2 ;
if ( ! pCrv1->GetEndPoint( ptP1) || ! pCrv2->GetStartPoint( ptP2))
return false ;
Point3d ptMid = 0.5 * ( ptP1 + ptP2) ;
Point3d ptNew1, ptNew2 ;
if ( ! intCC.GetIntersPointNearTo( 0, ptMid, ptNew1) ||
! intCC.GetIntersPointNearTo( 1, ptMid, ptNew2))
return false ;
// se punti coincidenti in Z
if ( fabs( ptNew1.z - ptNew2.z) < EPS_SMALL) {
// modifico le due curve sul punto medio
Point3d ptNew = 0.5 * ( ptNew1 + ptNew2) ;
return ( pCrv1->ModifyEnd( ptNew) && pCrv2->ModifyStart( ptNew)) ;
}
// altrimenti
else {
// modifico le due curve sui rispettivi punti di intersezione
if ( ! pCrv1->ModifyEnd( ptNew1) || ! pCrv2->ModifyStart( ptNew2))
return false ;
// aggiungo una retta in Z
PtrOwner<CurveLine> pCrv( CreateBasicCurveLine()) ;
if ( IsNull( pCrv) || ! pCrv->Set( ptNew1, ptNew2))
return false ;
pCrv->SetTempProp( TP_IS_VERT_LINE) ;
return ccAux.AddCurve( Release( pCrv)) ;
}
}
//----------------------------------------------------------------------------
bool
VerifyAndAdjustExternalAngle( ICurve* pCrv1, ICurve* pCrv2, double dDist, int nType,
CurveComposite& ccAux)
{
// verifica dei puntatori
if ( pCrv1 == nullptr || pCrv2 == nullptr || &ccAux == nullptr)
return false ;
// pulisco la curva ausiliaria
ccAux.Clear() ;
// elimino dal tipo le parti estranee all'angolo esterno
nType &= ( ICurve::OFF_FILLET | ICurve::OFF_CHAMFER | ICurve::OFF_EXTEND) ;
// calcolo direzioni tangenti sull'estremo in comune
Vector3d vtDir1, vtDir2 ;
if ( ! pCrv1->GetEndDir( vtDir1) || ! pCrv2->GetStartDir( vtDir2))
return false ;
// verifico se presente componente in Z
bool bDZ1 = fabs( vtDir1.z) > EPS_ANG_ZERO ;
bool bDZ2 = fabs( vtDir2.z) > EPS_ANG_ZERO ;
// le porto nel piano XY
vtDir1.z = 0 ;
vtDir2.z = 0 ;
if ( ! vtDir1.Normalize() || ! vtDir2.Normalize())
return false ;
// calcolo l'angolo di rotazione dalla prima direzione alla seconda
double dAngDeg ;
if ( ! vtDir1.GetAngleXY( vtDir2, dAngDeg))
return false ;
// se vicino all'angolo piatto, si devono ricalcolare usando le curve originali e spostandosi un poco
if ( fabs( dAngDeg) > ( ANG_STRAIGHT - EPS_ANG_SMALL)) {
// ritorno alle curve originali
PtrOwner<ICurve> pCrvOri1( pCrv1->Clone()) ;
PtrOwner<ICurve> pCrvOri2( pCrv2->Clone()) ;
if ( IsNull( pCrvOri1) || ! pCrvOri1->SimpleOffset( - dDist, nType))
return false ;
if ( IsNull( pCrvOri2) || ! pCrvOri2->SimpleOffset( - dDist, nType))
return false ;
// eseguo calcolo spostato su curve originali
double dU1 = 1 ;
double dU2 = 0 ;
Point3d ptDummy ;
Vector3d vtDir1b ;
Vector3d vtDir2b ;
if ( ! MoveParamToAvoidTg( dU1, ICurve::FROM_MINUS, *pCrvOri1) ||
! pCrvOri1->GetPointTang( dU1, ICurve::FROM_MINUS, ptDummy, vtDir1b) ||
! MoveParamToAvoidTg( dU2, ICurve::FROM_PLUS, *pCrvOri2) ||
! pCrvOri2->GetPointTang( dU2, ICurve::FROM_PLUS, ptDummy, vtDir2b))
return false ;
if ( ! vtDir1b.GetAngleXY( vtDir2b, dAngDeg))
return false ;
}
// verifico sia angolo esterno (accetto se entità quasi esattamente sovrapposto)
if ( fabs( dAngDeg) < ( ANG_STRAIGHT - 10 * EPS_ANG_ZERO) &&
( ( dDist < 0 && dAngDeg > 0) ||
( dDist > 0 && dAngDeg < 0)))
return false ;
// se l'angolo esterno supera il retto, offset extend diventa offset chamfer
if ( nType == ICurve::OFF_EXTEND && fabs( dAngDeg) > ANG_RIGHT + EPS_ANG_SMALL)
nType = ICurve::OFF_CHAMFER ;
// se angolo esterno molto piccolo, semplifico tutto
const double SMALL_EXT_ANG = 1.0 ;
bool bAngSmall = ( fabs( dAngDeg) < SMALL_EXT_ANG) ;
if ( bAngSmall)
nType = ICurve::OFF_EXTEND ;
// congiungo le due curve
switch ( nType) {
case ICurve::OFF_FILLET :
{
Point3d ptP1, ptP2 ;
if ( ! pCrv1->GetEndPoint( ptP1) || ! pCrv2->GetStartPoint( ptP2))
return false ;
double dAngStart ;
vtDir1.ToSpherical( nullptr, nullptr, &dAngStart) ;
PtrOwner<CurveArc> pCrv( CreateBasicCurveArc()) ;
if ( IsNull( pCrv) || ! pCrv->Set2PD( ptP1, ptP2, dAngStart))
return false ;
// restituisco la curva
return ccAux.AddCurve( Release( pCrv)) ;
}
break ;
case ICurve::OFF_CHAMFER :
{
// lunghezza aggiuntiva in tangenza
double dLen = fabs( dDist) * tan( fabs( dAngDeg) / 4 * DEGTORAD) ;
// punti di costruzione smusso
Point3d ptP1, ptP1a, ptP2a, ptP2 ;
if ( ! pCrv1->GetEndPoint( ptP1) || ! pCrv2->GetStartPoint( ptP2))
return false ;
ptP1a = ptP1 + vtDir1 * dLen ;
ptP2a = ptP2 - vtDir2 * dLen ;
// se sull'angolo c'era un dislivello (linea verticale eliminata)
double dDeltaZ = ptP2.z - ptP1.z ;
if ( fabs( dDeltaZ) > EPS_SMALL) {
ptP1a.z += dDeltaZ / 4 ;
ptP2a.z -= dDeltaZ / 4 ;
bDZ1 = true ;
bDZ2 = true ;
}
// se prima c'è linea senza DZ posso allungarla
if ( pCrv1->GetType() == CRV_LINE && ! bDZ1)
pCrv1->ModifyEnd( ptP1a) ;
// altrimenti, devo aggiungere una nuova linea
else {
PtrOwner<CurveLine> pCrv( CreateBasicCurveLine()) ;
if ( IsNull( pCrv) || ! pCrv->Set( ptP1, ptP1a))
return false ;
if ( ! ccAux.AddCurve( Release( pCrv)))
return false ;
}
// tratto intermedio
{
PtrOwner<CurveLine> pCrv( CreateBasicCurveLine()) ;
if ( IsNull( pCrv) || ! pCrv->Set( ptP1a, ptP2a))
return false ;
if ( ! ccAux.AddCurve( Release( pCrv)))
return false ;
}
// se dopo c'è linea senza DZ posso allungarla
if ( pCrv2->GetType() == CRV_LINE && ! bDZ2)
pCrv2->ModifyStart( ptP2a) ;
// altrimenti, devo aggiungere una nuova linea
else {
PtrOwner<CurveLine> pCrv( CreateBasicCurveLine()) ;
if ( IsNull( pCrv) || ! pCrv->Set( ptP2a, ptP2))
return false ;
if ( ! ccAux.AddCurve( Release( pCrv)))
return false ;
}
return true ;
}
break ;
case ICurve::OFF_EXTEND :
{
// lunghezza aggiuntiva in tangenza
double dLen = fabs( dDist) * tan( fabs( dAngDeg) / 2 * DEGTORAD) ;
// punti di costruzione estensione
Point3d ptP1, ptPc, ptP2 ;
if ( ! pCrv1->GetEndPoint( ptP1) || ! pCrv2->GetStartPoint( ptP2))
return false ;
ptPc = ptP1 + vtDir1 * dLen ;
// se sull'angolo c'era un dislivello (linea verticale eliminata)
double dDeltaZ = ptP2.z - ptP1.z ;
if ( fabs( dDeltaZ) > EPS_SMALL) {
ptPc.z += dDeltaZ / 2 ;
bDZ1 = true ;
bDZ2 = true ;
}
// se prima c'è linea senza DZ posso allungarla o angolo molto piccolo
if ( ( pCrv1->GetType() == CRV_LINE && ! bDZ1) || bAngSmall)
pCrv1->ModifyEnd( ptPc) ;
// altrimenti, devo aggiungere una nuova linea
else {
PtrOwner<CurveLine> pCrv( CreateBasicCurveLine()) ;
if ( IsNull( pCrv) || ! pCrv->Set( ptP1, ptPc))
return false ;
if ( ! ccAux.AddCurve( Release( pCrv)))
return false ;
}
// se dopo c'è linea senza DZ posso allungarla o angolo molto piccolo
if ( ( pCrv2->GetType() == CRV_LINE && ! bDZ2) || bAngSmall)
pCrv2->ModifyStart( ptPc) ;
// altrimenti, devo aggiungere una nuova linea
else {
PtrOwner<CurveLine> pCrv( CreateBasicCurveLine()) ;
if ( IsNull( pCrv) || ! pCrv->Set( ptPc, ptP2))
return false ;
if ( ! ccAux.AddCurve( Release( pCrv)))
return false ;
}
return true ;
}
}
return false ;
}
//----------------------------------------------------------------------------
bool
AddFirstLastVerticalLines( CurveComposite& ccOffs, double dLenVertFirst, double dLenVertLast)
{
// se richiesto inserimento prima retta verticale
if ( fabs( dLenVertFirst) > EPS_SMALL) {
Point3d ptP2 ;
if ( ! ccOffs.GetStartPoint( ptP2))
return false ;
Point3d ptP1 = ptP2 ;
ptP1.z -= dLenVertFirst ;
PtrOwner<CurveLine> pCrv( CreateBasicCurveLine()) ;
if ( IsNull( pCrv) || ! pCrv->Set( ptP1, ptP2))
return false ;
if ( ! ccOffs.AddCurve( Release( pCrv), false))
return false ;
}
// se richiesto inserimento ultima retta verticale
if ( fabs( dLenVertLast) > EPS_SMALL) {
Point3d ptP1 ;
if ( ! ccOffs.GetEndPoint( ptP1))
return false ;
Point3d ptP2 = ptP1 ;
ptP2.z += dLenVertLast ;
PtrOwner<CurveLine> pCrv( CreateBasicCurveLine()) ;
if ( IsNull( pCrv) || ! pCrv->Set( ptP1, ptP2))
return false ;
if ( ! ccOffs.AddCurve( Release( pCrv)))
return false ;
}
return true ;
}
//----------------------------------------------------------------------------
bool
MediaInternalAngleDeltaZ( CurveComposite& ccOffs)
{
// definisco una composita temporanea
CurveComposite ccTemp ;
// ciclo sulle curve
bool bModifyNext = false ;
Point3d ptMid ;
ICurve* pCrv = ccOffs.RemoveFirstOrLastCurve( false) ;
while ( pCrv != nullptr) {
// verifico se curva di raccordo in Z di angolo interno
if ( pCrv->GetTempProp() == TP_IS_VERT_LINE) {
// ne recupero il punto medio e la cancello
pCrv->GetMidPoint( ptMid) ;
delete pCrv ;
// modifico il finale della curva precedente
if ( ! ccTemp.ModifyEnd( ptMid))
return false ;
// imposto flag per modifica iniziale della curva successiva
bModifyNext = true ;
}
// altrimenti
else {
// se da modificare
if ( bModifyNext && ! pCrv->ModifyStart( ptMid)) {
delete pCrv ;
return false ;
}
bModifyNext = false ;
// la sposto nella composita di offset
if ( ! ccTemp.AddCurve( pCrv))
return false ;
}
// passo alla curva successiva
pCrv = ccOffs.RemoveFirstOrLastCurve( false) ;
}
// se modifica in sospeso, allora curva originale chiusa e modifico inizio
if ( bModifyNext && ! ccTemp.ModifyStart( ptMid))
return false ;
// riporto le curve nella composita di offset
return ccOffs.RelocateFrom( ccTemp) ;
}