Files
Dario Sassi 2ed2a34d55 EgtGeomKernel :
- modifiche per DistPointLine con interfaccia portata in Include.
2024-05-22 08:19:10 +02:00

372 lines
16 KiB
C++

#include "stdafx.h"
#include "CDeUtility.h"
#include "/EgtDev/Include/EGkDistPointLine.h"
#include "/EgtDev/Include/ENkPolynomialRoots.h"
using namespace std ;
//----------------------------------------------------------------------------
// Dati un punto e una retta, restituisce il quadrato della loro distanza.
// La retta è descritta da un suo punto e dal suo versore tangente.
double
GetPointLineSqDist( const Point3d& ptP, const Point3d& ptLine, const Vector3d& vtLine)
{
return ( ( ptP - ptLine) - ( ptP - ptLine) * vtLine * vtLine).SqLen() ;
}
//----------------------------------------------------------------------------
// Dati un punto e un piano, ne restituisce la distanza con segno.
// Il piano è descritto da un suo punto e dal suo versore normale.
double
PointPlaneSignedDist( const Point3d& ptP, const Point3d& ptPlane, const Vector3d& vtNorm)
{
return ( ptP - ptPlane) * vtNorm ;
}
//----------------------------------------------------------------------------
// Dati un sistema di tre punti e un piano, ne calcola le tre distanze con segno e ne restituisce la
// maggiore. I tre punti sono passati mediante un triangolo, il piano è descritto da un suo punto e
// dal suo versore normale.
double
ThreePointPlaneSignedDist( const Triangle3d& trTria, const Point3d& ptPlane, const Vector3d& vtNorm)
{
double dMaxSgnDist = - INFINITO ;
for ( int nV = 0 ; nV < 3 ; ++ nV) {
double dCurrSgnDist = PointPlaneSignedDist( trTria.GetP( nV), ptPlane, vtNorm) ;
if ( dCurrSgnDist > dMaxSgnDist)
dMaxSgnDist = dCurrSgnDist ;
}
return dMaxSgnDist ;
}
//----------------------------------------------------------------------------
// Calcola, se esiste, il punto di una retta a distanza con segno data da un piano.
// Il piano è descritto da punto e versore normale.
// La retta è descritta da punto e versore direzione.
// I casi possibili sono:
// - Nessun punto della retta dista d dal piano (retta parallela al piano con distanza diversa da d),
// viene restituito 0 e il valore di dPar non ha senso.
// - Un solo punto della retta dista d dal piano (retta non parallela al piano), viene restituito 1
// e il valore di dPar è il parametro del punto sulla retta.
// - Tutti i punti della retta distano d dal piano (retta parallela al piano con distanza d), viene
// restituito 2 e qualunque valore di dPar ha senso.
int
LinePlaneDDistPar( const Point3d& ptPlane, const Vector3d& vtPlane, const Point3d& ptLine, const Vector3d& vtLine,
double dDist, double& dPar)
{
// Vettore congiungente il punto del piano con il punto iniziale della retta
Vector3d vtPlLn = ptLine - ptPlane ;
// Distanza con segno del punto iniziale della retta dal piano
double dSgnDist0 = vtPlLn * vtPlane ;
// Prodotto scalare fra versore della direzione della retta e versore normale al piano
double dDotDirNorm = vtLine * vtPlane ;
// Retta parallela al piano
if ( abs( dDotDirNorm) < EPS_ZERO) {
if ( abs( dSgnDist0 - dDist) < EPS_SMALL)
return 2 ;
else
return 0 ;
}
// Caso generale
else {
dPar = ( dDist - dSgnDist0) / dDotDirNorm ;
return 1 ;
}
}
//----------------------------------------------------------------------------
// Date due rette nello spazio restituisce il quadrato della loro distanza.
// Le rette sono definite da un punto e dal versore direzione.
double
LineLineSqDist( const Point3d& ptP1, const Vector3d& vtD1, const Point3d& ptP2, const Vector3d& vtD2)
{
// Se parallele
if ( abs( 1. - abs( vtD1 * vtD2)) < EPS_SMALL)
return ( ( ptP2 - ptP1) - ( ptP2 - ptP1) * vtD1 * vtD1).SqLen() ;
// Caso generale
else {
Vector3d vtOrtD2 = vtD2 - vtD2 * vtD1 * vtD1 ;
Vector3d vtOrtP1P2 = ( ptP2 - ptP1) - (ptP2 - ptP1) * vtD1 * vtD1 ;
double dD = ( vtOrtD2 * vtOrtP1P2) / vtOrtP1P2.SqLen() ;
return ( vtOrtP1P2.SqLen() + dD * dD * vtOrtD2.SqLen() + 2 * dD * vtOrtD2 * vtOrtP1P2) ;
}
}
//----------------------------------------------------------------------------
// Data una retta e un segmento, ne restituisce il quadrato della distanza.
// La retta è definita da un punto e dal versore direzione.
// Il segmento è definito da un estremo, dal versore direzione verso l'altro estremo e dalla lunghezza.
double
LineSegmentSqDist( const Point3d& ptPLn, const Vector3d& vtDLn,
const Point3d& ptPSg, const Vector3d& vtDSg, double dSgLen)
{
// Se paralleli
if ( AreSameOrOppositeVectorApprox( vtDLn, vtDSg))
return ( ( ptPSg - ptPLn) - ( ptPSg - ptPLn) * vtDLn * vtDLn).SqLen() ;
// Caso generale
else {
double dDLn = ( ptPSg - ptPLn) * vtDLn ;
double dDSg = ( ptPSg - ptPLn) * vtDSg ;
double dDLnSg = vtDLn * vtDSg ;
// posizione parametrica del punto sul segmento
double dSgPar = ( dDLn * dDLnSg - dDSg) / ( 1. - dDLnSg * dDLnSg) ;
// se prima dell'inizio, è distanza dell'inizio dalla linea
if ( dSgPar <= 0.)
return GetPointLineSqDist( ptPSg, ptPLn, vtDLn) ;
// se dopo fine, è distanza fine dalla linea
else if ( dSgPar >= dSgLen)
return GetPointLineSqDist( ptPSg + dSgLen * vtDSg, ptPLn, vtDLn) ;
// altrimenti, determino i due punti a minima distanza
else {
double dLnPar = ( dDLn - dDSg * dDLnSg) / ( 1. - dDLnSg * dDLnSg) ;
Point3d ptMinLn = ptPLn + dLnPar * vtDLn ;
Point3d ptMinSg = ptPSg + dSgPar * vtDSg ;
return SqDist( ptMinLn, ptMinSg) ;
}
}
}
//----------------------------------------------------------------------------
// Valuta l'interferenza di una circonferenza (disco vuoto) e un triangolo (pieno) complanari.
// Si assume che disco e piano siano paralleli, ovvero che valga a n = 1 con
// a versore dell'asse del disco e n versore normale al triangolo.
// L'informazione su n è contenuta nell'oggetto triangolo; quella su a non è passata
// in quanto inutile sotto le nostre assunzioni.
bool
CoplanarCircleTriangleInterference( const Point3d& ptCen, double dRad, const Triangle3d& trTria)
{
// Se il centro del disco non sta sul piano del triangolo, non sono complanari
if ( abs( PointPlaneSignedDist( ptCen, trTria.GetP( 0), trTria.GetN())) > EPS_SMALL)
return false ;
// Classifico i vertici del triangolo in base alla loro posizione rispetto alla circonferenza.
double dSqRad = dRad * dRad ;
double dSqRadInf = dSqRad - 2 * dRad * EPS_SMALL ;
double dSqRadSup = dSqRad + 2 * dRad * EPS_SMALL ;
int nInOutVec[3] ;
for ( int n = 0 ; n < 3 ; ++ n) {
Point3d ptTriaVert = trTria.GetP( n) ;
nInOutVec[n] = SqDist( ptTriaVert, ptCen) < dSqRadInf ? 1 : SqDist( ptTriaVert, ptCen) < dSqRadSup ? 0 : - 1 ;
}
// Se i vertici sono tutti dentro, non c'è interferenza.
if ( nInOutVec[0] == 1 && nInOutVec[1] == 1 && nInOutVec[2] == 1)
return false ;
// Se i vertici sono tutti fuori, valuto se il centro della circonferenza è interno al triangolo.
else if ( nInOutVec[0] == - 1 && nInOutVec[1] == - 1 && nInOutVec[2] == - 1) {
return IsPointInsideTriangle( ptCen, trTria, TriangleType::EXACT) ;
}
// Almeno un vertice tocca la circonferenza oppure due segmenti la attraversano, quindi c'è interferenza.
else
return true ;
}
//----------------------------------------------------------------------------
// Valuta l'interferenza di un disco e un triangolo complanari.
// Si assume che disco e piano siano paralleli, ovvero che valga a n = 1 con
// a versore dell'asse del disco e n versore normale al triangolo.
// L'informazione su n è contenuta nell'oggetto triangolo; quella su a non è passata
// in quanto inutile sotto le nostre assunzioni.
bool
CoplanarDiscTriangleInterference( const Point3d& ptCen, double dRad, const Triangle3d& trTria, int nTriType)
{
// Se il centro del disco non sta sul piano del triangolo, non sono complanari
if ( abs( PointPlaneSignedDist( ptCen, trTria.GetP( 0), trTria.GetN())) > EPS_SMALL)
return false ;
// Se il centro giace nel triangolo
if ( IsPointInsideTriangle( ptCen, trTria, TriangleType::EXACT))
return true ;
double dSqMinDist = 0 ;
switch ( nTriType) {
case OPEN:
dSqMinDist = dRad * dRad - 2 * dRad * EPS_SMALL ;
break ;
case EXACT:
dSqMinDist = dRad * dRad ;
break ;
case CLOSED:
dSqMinDist = dRad * dRad + 2 * dRad * EPS_SMALL ;
break ;
}
// Se il centro non si avvicina più del raggio dalle rette associate ai segmenti, non c'è interferenza
double dSegPointDist0, dSegPointDist1, dSegPointDist2 ;
( DistPointLine( ptCen, trTria.GetP( 0), trTria.GetP( 1))).GetSqDist( dSegPointDist0) ;
( DistPointLine( ptCen, trTria.GetP( 1), trTria.GetP( 2))).GetSqDist( dSegPointDist1) ;
( DistPointLine( ptCen, trTria.GetP( 2), trTria.GetP( 0))).GetSqDist( dSegPointDist2) ;
if ( dSegPointDist0 > dSqMinDist && dSegPointDist1 > dSqMinDist && dSegPointDist2 > dSqMinDist)
return false ;
// interferenza
return true ;
}
//----------------------------------------------------------------------------
// Date due rette, descritte da un punto e un vettore direzione,
// trova i parametri corrispondenti ai punti di minima distanza.
// Se le rette non sono parallele abbiamo una singola coppia di
// punti e viene restituito true, altrimenti ne abbiamo infinite
// e viene restituito false. Si noti che i parametri dU1, dU2
// non hanno necessariamente il significato di lunghezza;
// perché non è richiesto che i vettori siano normalizzati.
bool
FindLineLineMinDistPar( const Point3d& ptL1, const Vector3d& vtV1,
const Point3d& ptL2, const Vector3d& vtV2,
double& dU1, double& dU2)
{
// Se le rette sono parallele
if ( AreSameOrOppositeVectorExact( vtV1, vtV2))
return false ;
// Vettore congiungente i punti iniziali
Vector3d vtD = ptL2 - ptL1 ;
// Proiezioni di vtD sui versori direzione
double dD1 = vtD * vtV1 ;
double dD2 = vtD * vtV2 ;
// Prodotto scalare fra i versori direzione
double dV = vtV1 * vtV2 ;
// Se le rette sono perpendicolari
if ( abs( vtV1 * vtV2) < EPS_ZERO) {
dU1 = dD1 ;
dU2 = - dD2 ;
}
// Caso generale
else {
dU1 = ( dD1 - dD2 * dV) / ( 1 - dV * dV) ;
dU2 = ( - dD2 + dD1 * dV) / ( 1 - dV * dV) ;
}
return true ;
}
//----------------------------------------------------------------------------
// Calcolo delle posizioni di tangenza di una sfera che si muove dalla posizione
// originale lungo una direzione prefissata rispetto ad una retta.
// Le posizioni sono rappresentate dalla distanza percorsa.
// Il parametro di ritorno può assumere i seguenti valori :
// 0 : la sfera non è mai tangente alla retta; dU1 e dU2 non hanno senso
// 1 : la sfera è tangente alla retta in una posizione; solo dU1 ha senso
// 2 : la sfera è tangente alla retta in due posizioni; dU1 e dU2 valide
// 3 : la sfera è sempre tangente
int
SphereLineTangentPoints( const Point3d& ptSpheCen, double dSpheRad,
const Point3d& ptSeg, const Vector3d& vtSegDir, double dSegLen,
const Vector3d& vtMove, double& dU1, double& dU2)
{
// Definisco l'equazione
Vector3d vtMotOrt = vtMove - vtMove * vtSegDir * vtSegDir ;
Vector3d vtLineSeg = ptSeg - ptSpheCen ;
Vector3d vtLineSegOrt = vtLineSeg - vtLineSeg * vtSegDir * vtSegDir ;
// Setto i coefficienti dell'equazione
DBLVECTOR vdCoeff( 3) ;
vdCoeff[0] = vtLineSegOrt.SqLen() - dSpheRad * dSpheRad ;
vdCoeff[1] = - 2 * vtMotOrt * vtLineSegOrt ;
vdCoeff[2] = vtMotOrt.SqLen() ;
if ( vdCoeff[2] < EPS_ZERO * EPS_ZERO) {
if ( abs( vdCoeff[0]) < EPS_SMALL)
return 3 ;
else
return 0 ;
}
// Risolvo l'equazione
DBLVECTOR vdRoots ;
int nRoots = PolynomialRoots( 2, vdCoeff, vdRoots) ;
// Studio le soluzioni
if ( nRoots == 1)
dU1 = vdRoots[0] ;
else if ( nRoots == 2) {
dU1 = min( vdRoots[0], vdRoots[1]) ;
dU2 = max( vdRoots[0], vdRoots[1]) ;
}
return nRoots ;
}
//----------------------------------------------------------------------------
// Valuta l'allontanamento di un disco, lungo il proprio asse di simmetria, da un piano.
// Il disco è descritto dal centro nella posizione iniziale e dal raggio (la normale è il versore traslazione).
// Il piano è descritto da un suo punto e dal suo versore normale.
// Il moto è descritto dal versore di traslazione.
// Il valore restituito è un intero che descrive la situazione di allontanamento e,
// quando sensato, per referenza restituisce il punto di ultimo contatto.
// 0: Nessun contatto
// 1: Un contatto
// 2: Disco e piano ortogonali, con disco tg al piano, il contatto è una semiretta
// 3: Disco e piano ortogonali, con disco secante il piano, il contatto è una striscia
// 4: Disco e piano paralleli, come ultimo contatto si restituisce il centro.
// Nel caso 0 il parametro ptTouch non ha senso, così come nel caso 2 e nel caso 3
int
DiskPlaneLastContactLongMot( const Point3d& ptDiskCen, double dDiskRad,
const Point3d& ptPlane, const Vector3d& vtPlane,
const Vector3d& vtMove, Point3d& ptTouch)
{
// Se disco e piano sono paralleli
if ( AreSameOrOppositeVectorApprox( vtMove, vtPlane)) {
double dDist = PointPlaneSignedDist( ptPlane, ptDiskCen, vtMove) ;
if ( dDist > - EPS_SMALL) {
ptTouch = ptDiskCen + dDist * vtMove ;
return 4 ;
}
return 0 ;
}
// Vettore radiale
Vector3d vtRadLine = vtMove * ( vtPlane * vtMove) - vtPlane ;
// Disco e piano ortogonali
if ( vtRadLine.IsNormalized()) {
double dDist = abs( PointPlaneSignedDist( ptPlane, ptDiskCen, vtMove)) ;
if ( abs( dDist - dDiskRad) < EPS_SMALL)
return 2 ;
else if ( dDist < dDiskRad)
return 3 ;
return 0 ;
}
// Cerco un punto di contatto nell'interno del triangolo. Se tale punto
// esiste, la retta intersezione fra il piano del triangolo e quello del
// disco è tangente alla circonferenza.
vtRadLine.Normalize() ;
// Punti delle due rette candidate all'intersezione col triangolo
Point3d ptStPlus = ptDiskCen + dDiskRad * vtRadLine ;
Point3d ptStMinus = ptDiskCen - dDiskRad * vtRadLine ;
// Parametri d'intersezione delle rette col piano
double dDistPlus = ( ( ptPlane - ptStPlus) * vtPlane) / ( vtMove * vtPlane) ;
double dDistMinus = ( ( ptPlane - ptStMinus) * vtPlane) / ( vtMove * vtPlane) ;
double dDist ;
if ( dDistPlus > dDistMinus) {
dDist = dDistPlus ;
ptTouch = ptStPlus + dDist * vtMove ;
}
else {
dDist = dDistMinus ;
ptTouch = ptStMinus + dDist * vtMove ;
}
if ( dDist > - EPS_SMALL)
return 1 ;
return 0 ;
}
//----------------------------------------------------------------------------
// Si limita il segmento alla parte di spazio con distanza positiva dal piano.
// Se segmento ancora valido si restituisce true, altrimenti false.
bool
ClampSegmentOutPlane( const Plane3d& plPlane, Point3d& ptSegP, const Vector3d& vtSegV, double& dSegLen, double dSignTol)
{
// Distanze degli estremi del segmento dal piano
double dDistStart = DistPointPlane( ptSegP, plPlane) ;
double dDistEnd = DistPointPlane( ptSegP + dSegLen * vtSegV, plPlane) ;
// Se il segmento sta nel piano o è tutto esterno, va bene
if ( dDistStart > dSignTol - EPS_ZERO && dDistEnd > dSignTol - EPS_ZERO)
return true ;
// Se il segmento è tutto interno, va eliminato
if ( dDistStart < dSignTol && dDistEnd < dSignTol) {
dSegLen = 0 ;
return false ;
}
// Altrimenti il segmento attraversa il piano e va limitato
double dIntersLen = dSegLen * abs( dDistStart) / ( abs( dDistStart) + abs( dDistEnd)) ;
if ( dDistStart > 0)
dSegLen = dIntersLen ;
else {
ptSegP += dIntersLen * vtSegV ;
dSegLen -= dIntersLen ;
}
return true ;
}