Files
EgtGeomKernel/BiArcs.cpp
T
Dario Sassi 903f0c69bc EgtGeomKernel 2.5l2 :
- aggiunto calcolo edge di superfici trimesh
- piccole modifiche per usare direttamente oggetti anzichè le loro interfacce.
2023-12-11 10:23:30 +01:00

222 lines
8.6 KiB
C++

//----------------------------------------------------------------------------
// EgalTech 2013-2014
//----------------------------------------------------------------------------
// File : BiArcs.cpp Data : 30.07.14 Versione : 1.5g4
// Contenuto : Implementazione funzioni per calcolo biarchi.
//
//
//
// Modifiche : 30.07.14 DS Creazione modulo.
//
//
//----------------------------------------------------------------------------
//--------------------------- Include ----------------------------------------
#include "stdafx.h"
#include "BiArcs.h"
#include "CurveLine.h"
#include "CurveArc.h"
#include "CurveComposite.h"
#include "/EgtDev/Include/EGkAngle.h"
#include "/EgtDev/Include/EGkArcSpecial.h"
#include "/EgtDev/Include/EGkDistPointCurve.h"
#include "/EgtDev/Include/EgtNumUtils.h"
#include "/EgtDev/Include/EgtPointerOwner.h"
using namespace std ;
//----------------------------------------------------------------------------
static ICurve* CalcJCurve( const Point3d& ptP0, double dDir0Deg, const Point3d& ptP1, double dDir1Deg) ;
//----------------------------------------------------------------------------
ICurve*
GetBiArc( const Point3d& ptP0, double dDir0Deg, const Point3d& ptP1, double dDir1Deg, double dU)
{
// calcolo la curva dove giacciono i punti di giunzione tra i due archi del biarco
PtrOwner<ICurve> pJCrv( CalcJCurve( ptP0, dDir0Deg, ptP1, dDir1Deg)) ;
if ( IsNull( pJCrv))
return nullptr ;
// limito il parametro nell'intervallo 0 - 1
if ( dU < 0)
dU = 0 ;
else if ( dU > 1)
dU = 1 ;
// recupero il punto di giunzione
Point3d ptJ ;
if ( ! pJCrv->GetPointD1D2( dU, ICurve::FROM_MINUS, ptJ))
return nullptr ;
// preparo la curva composita per i biarchi
PtrOwner<CurveComposite> pBiArc( CreateBasicCurveComposite()) ;
if ( IsNull( pBiArc))
return nullptr ;
// calcolo la curva dal punto iniziale alla giunzione
if ( ! AreSamePointApprox( ptP0, ptJ)) {
ICurve* pCrv = GetArc2PD( ptP0, ptJ, dDir0Deg) ;
if ( pCrv == nullptr)
return nullptr ;
pBiArc->AddCurve( pCrv) ;
}
// calcolo la curva dalla giunzione al punto finale
if ( ! AreSamePointApprox( ptJ, ptP1)) {
// curva dal punto finale, direzione opposta alla giunzione
ICurve* pCrv = GetArc2PD( ptP1, ptJ, dDir1Deg + ANG_STRAIGHT) ;
if ( pCrv == nullptr)
return nullptr ;
// inverto la curva
pCrv->Invert() ;
pBiArc->AddCurve( pCrv) ;
}
// se il biarco non esiste
if ( pBiArc->GetCurveCount() == 0)
return nullptr ;
return ::Release( pBiArc) ;
}
//----------------------------------------------------------------------------
ICurve*
GetBiArc( const Point3d& ptP0, double dDir0Deg, const Point3d& ptP1, double dDir1Deg,
const PolyLine& PL, double& dDist)
{
// calcolo la curva dove giacciono i punti di giunzione tra i due archi del biarco
PtrOwner<ICurve> pJCrv( CalcJCurve( ptP0, dDir0Deg, ptP1, dDir1Deg)) ;
if ( IsNull( pJCrv))
return nullptr ;
// calcolo le intersezioni tra la curva delle giunzioni (segmento o arco) e la polilinea
PtrOwner<ICurve> pBiArc ;
if ( pJCrv->GetType() == CRV_LINE) {
pBiArc.Set( GetBiArc( ptP0, dDir0Deg, ptP1, dDir1Deg, 0.5)) ;
}
else {
const CurveArc* pArc = GetBasicCurveArc( pJCrv) ;
if ( pArc == nullptr)
return nullptr ;
double dU = -1 ;
double dRad = pArc->GetRadius() ;
double dSqRad = dRad * dRad ;
Point3d ptCen = pArc->GetCenter() ;
Point3d ptIni, ptFin ;
for ( bool bLine = PL.GetFirstLine( ptIni, ptFin) ;
bLine ;
bLine = PL.GetNextLine( ptIni, ptFin)) {
double dSqDistIni = SqDistXY( ptIni, ptCen) ;
double dSqDistFin = SqDistXY( ptFin, ptCen) ;
if ( ( dSqDistIni > dSqRad && dSqDistFin < dSqRad) ||
( dSqDistIni < dSqRad && dSqDistFin > dSqRad)) {
double dDiffIni = DistXY( ptIni, ptCen) - dRad ;
double dDiffFin = DistXY( ptFin, ptCen) - dRad ;
Point3d ptMid = Media( ptIni, ptFin, abs( dDiffIni) / ( abs( dDiffIni) + abs( dDiffFin))) ;
int nPos ;
double dTmp ;
if ( pArc->CalcPointParamPosiz( ptMid, dTmp, nPos)) {
if ( nPos != ICurve::PP_START && nPos != ICurve::PP_END &&
abs( dTmp - 0.5) < abs( dU - 0.5))
dU = dTmp ;
}
}
}
// non c'è intersezione, assegno valore medio
if ( dU < -0.5)
dU = 0.5 ;
// elimino casi vicino agli estremi, danno solo problemi
dU = Clamp( dU, 0.1, 0.9) ;
pBiArc.Set( GetBiArc( ptP0, dDir0Deg, ptP1, dDir1Deg, dU)) ;
}
// se biarco non trovato, errore
if ( IsNull( pBiArc))
return nullptr ;
// determino la massima distanza tra la curva e il biarco
double dSqDist = 0 ;
const double STEP = 10 ;
Point3d ptCurr ;
bool bPnt = PL.GetFirstPoint( ptCurr) ;
Point3d ptPrev = ptCurr ;
while ( bPnt) {
double dLen = Dist( ptCurr, ptPrev) ;
int nStep = ( dLen < STEP ? 2 : 1) * int( dLen / STEP) + 1 ;
for ( int i = 1 ; i <= nStep ; ++ i) {
double dCoeff = double( i) / nStep ;
Point3d ptP = Media( ptPrev, ptCurr, dCoeff) ;
DistPointCurve dstPC( ptP, *pBiArc) ;
double dSqDistPC ;
if ( dstPC.GetSqDist( dSqDistPC) && dSqDistPC > dSqDist)
dSqDist = dSqDistPC ;
}
ptPrev = ptCurr ;
bPnt = PL.GetNextPoint( ptCurr) ;
}
dDist = sqrt( dSqDist) ;
return Release( pBiArc) ;
}
//----------------------------------------------------------------------------
static ICurve*
CalcJCurve( const Point3d& ptP0, double dDir0Deg, const Point3d& ptP1, double dDir1Deg)
{
// se i due punti coincidono, non si può fare alcunché
if ( AreSamePointApprox( ptP0, ptP1))
return nullptr ;
// angolo di rotazione dalla prima direzione alla seconda -> angolo al centro
double dAngDeg = DiffAngle( dDir1Deg, dDir0Deg) ;
// se rotazione nulla, allora segmento di retta tra i due punti
if ( abs( dAngDeg) < EPS_ANG_SMALL) {
PtrOwner<CurveLine> pLine( CreateBasicCurveLine()) ;
if ( IsNull( pLine) || ! pLine->Set( ptP0, ptP1))
return nullptr ;
// inverto per avere parametrizzazione crescente allontanandosi da Dir0 e avvicinandosi a Dir1
pLine->Invert() ;
return ::Release( pLine) ;
}
// caso generico
Point3d ptMed = 0.5 * ( ptP0 + ptP1) ;
ptMed.z = ptP0.z ;
Vector3d vtDiff = ptMed - ptP0 ;
double dHalfDist = vtDiff.LenXY() ;
vtDiff /= dHalfDist ;
double dDirDiffDeg ;
vtDiff.ToSpherical( nullptr, nullptr, &dDirDiffDeg) ;
double dH = dHalfDist / tan( 0.5 * dAngDeg * DEGTORAD) ;
vtDiff.Rotate( Z_AX, 0, 1) ;
Point3d ptCen = ptMed + dH * vtDiff ;
Vector3d vtStart = ptP0 - ptCen ;
double dRad, dAngStart ;
vtStart.ToSpherical( &dRad, nullptr, &dAngStart) ;
PtrOwner<CurveArc> pArc( CreateBasicCurveArc()) ;
if ( IsNull( pArc) || ! pArc->SetXY( ptCen, dRad, dAngStart, dAngDeg, ( ptP1.z - ptP0.z)))
return nullptr ;
double dDirStartDeg = dAngStart + ( dAngDeg > 0 ? ANG_RIGHT : - ANG_RIGHT) ;
// determinazione regione tra le curve estreme in cui contenere l'arco (tramite angoli su start)
// direzione iniziale arco rispetto a direzione P0->P1
double dDirStartRelDeg = DiffAngle( dDirStartDeg, dDirDiffDeg) ;
// direzione iniziale primo arco limite rispetto a direzione P0->P1
double dDir0RelDeg = DiffAngle( dDir0Deg, dDirDiffDeg) ;
// direzione iniziale secondo arco limite rispetto a direzione P0->P1 (dalla finale simmetrico e invert)
double dDir1RelDeg = - DiffAngle( dDir1Deg, dDirDiffDeg) ;
// nel caso di direzioni a 180deg si sceglie la più compatta
if ( abs( abs( dDir1RelDeg) - ANG_STRAIGHT) < EPS_SMALL)
dDir1RelDeg = ( dDir0RelDeg > 0 ? ANG_STRAIGHT : - ANG_STRAIGHT) ;
else if ( abs( abs( dDir0RelDeg) - ANG_STRAIGHT) < EPS_SMALL)
dDir0RelDeg = ( dDir1RelDeg > 0 ? ANG_STRAIGHT : - ANG_STRAIGHT) ;
// intervallo angolare ammissibile a partire da direzione iniziale primo arco ammissibile ( == Dir0)
double dDeltaAngDeg = - dDir0RelDeg + dDir1RelDeg ;
// se non è nella regione, prendo l'altra parte di arco
if ( ! AngleInSpan( dDirStartRelDeg, dDir0RelDeg, dDeltaAngDeg))
pArc->ToExplementary() ;
// inverto per avere parametrizzazione crescente allontanandosi da Dir0 e avvicinandosi a Dir1
pArc->Invert() ;
return ::Release( pArc) ;
}