EgtGeomKernel :
- VM 5 assi con volumi chiusi, con bilineari.
This commit is contained in:
+430
-181
@@ -1131,10 +1131,10 @@ GetAlongAcrossRotation( const Vector3d& vtDir1, const Vector3d& vtDir2, const Ve
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool
|
||||
VolZmap::UpdateMaxMin( Point3d ptBez, Vector3d vtN, PNTVECTVECTOR& vInters) const
|
||||
VolZmap::UpdateMaxMin( Point3d& ptBez, Vector3d& vtN, PNTVEC3DVECTOR& vInters) const
|
||||
{
|
||||
if ( ! AreSameVectorExact( vtN, V_NULL))
|
||||
vInters.emplace_back( PNTVECT( ptBez, vtN)) ;
|
||||
vInters.emplace_back( PNTVEC3D( ptBez, vtN)) ;
|
||||
return true ;
|
||||
}
|
||||
|
||||
@@ -1308,10 +1308,13 @@ VolZmap::GenTool_5AxisMilling( int nGrid, const PNTVECTOR& ptS, const PNTVECTOR&
|
||||
return true ;
|
||||
}
|
||||
|
||||
//debug
|
||||
static vector<IGeoObj*> vGeo ;
|
||||
//static int nCount = 0 ;
|
||||
//debug
|
||||
#if true
|
||||
//debug
|
||||
#define TEST
|
||||
static vector<IGeoObj*> vGeo ;
|
||||
//static int nCount = 0 ;
|
||||
//debug
|
||||
#endif
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool
|
||||
@@ -1337,11 +1340,11 @@ VolZmap::Comp_5AxisMilling( int nGrid, const PNTVECTOR& ptS, const PNTVECTOR& pt
|
||||
DBLVECTOR A1, B1, C1, A2, B2, C2 ;
|
||||
int nTotSurf = 4;
|
||||
if ( n5AxisType == VolZmap::Move5Axis::NO_BASE_INTERS)
|
||||
nTotSurf = 2 + nSub * 2 ;
|
||||
nTotSurf = 2 * nStepCnt + nSub * 2 * nStepCnt + nSub * 2 ; // sup laterali, sup superiori e inferiori, superfici verticali di inizio e fine
|
||||
else if( n5AxisType == VolZmap::Move5Axis::ALONG_CONVEX || n5AxisType == VolZmap::Move5Axis::ALONG_CONCAVE)
|
||||
nTotSurf = 2 + nSub * 2 + nStepCnt ;
|
||||
nTotSurf = 2 * nStepCnt + nSub * 2 * nStepCnt + nStepCnt + nSub * 2 + nSub * 2 ; // verticali esterne e verticali interne
|
||||
else if( n5AxisType == VolZmap::Move5Axis::ACROSS)
|
||||
nTotSurf = 2 + nSub * 4 ;
|
||||
nTotSurf = 2 * nStepCnt + nSub * 4 * nStepCnt + nSub * 4 ;
|
||||
|
||||
ISURFBEZPOVECTOR vSurfBez( nTotSurf * nStepCnt) ;
|
||||
BOXVECTOR vSurfBox( nTotSurf * nStepCnt) ;
|
||||
@@ -1356,10 +1359,12 @@ VolZmap::Comp_5AxisMilling( int nGrid, const PNTVECTOR& ptS, const PNTVECTOR& pt
|
||||
Vector3d vtDirTipStartEx ;
|
||||
Vector3d vtDirTipEndEx ;
|
||||
|
||||
#ifdef TEST
|
||||
//debug
|
||||
if( nGrid == 0)
|
||||
vGeo.clear() ;
|
||||
//debug
|
||||
#endif
|
||||
|
||||
for ( int s = 0 ; s < nStepCnt ; ++s) {
|
||||
// punti di riferimento sul tool
|
||||
@@ -1432,6 +1437,12 @@ VolZmap::Comp_5AxisMilling( int nGrid, const PNTVECTOR& ptS, const PNTVECTOR& pt
|
||||
vtDirTopStartEx = vtTopBaseStart ;
|
||||
Point3d ptRefStart = ptS[s] + vtTopBaseStart ;
|
||||
dSide = (ptRefEnd - ptRefStart) * vtLs[s] ;
|
||||
|
||||
//debug
|
||||
string sOut = ToString( dSide) ;
|
||||
LOG_INFO( GetEGkLogger(), sOut.c_str())
|
||||
//debug
|
||||
|
||||
// calcolo anche i vettori per le basi inferiori
|
||||
Vector3d vtTipBaseStart = - (vtLs[s] ^ vtDirTip) ;
|
||||
vtTipBaseStart.Normalize() ;
|
||||
@@ -1539,6 +1550,13 @@ VolZmap::Comp_5AxisMilling( int nGrid, const PNTVECTOR& ptS, const PNTVECTOR& pt
|
||||
for ( int i = 0 ; i < nSub ; ++i) {
|
||||
vvPtCtrl.emplace_back( PNTVECTOR({ vPntTopStart[i], vPntTopStart[i+1], vPntTopEnd[i], vPntTopEnd[i+1]})) ;
|
||||
}
|
||||
// chiudo il volume con le superici verticali
|
||||
if( n5AxisType == VolZmap::Move5Axis::NO_BASE_INTERS) {
|
||||
for ( int i = 0 ; i < nSub ; ++i ) {
|
||||
vvPtCtrl.emplace_back( PNTVECTOR({ vPntTipStart[i+1], vPntTipStart[i], vPntTopStart[nSub - i - 1], vPntTopStart[nSub - i]})) ;
|
||||
vvPtCtrl.emplace_back( PNTVECTOR({ vPntTipEnd[i], vPntTipEnd[i+1], vPntTopEnd[nSub - i], vPntTopEnd[nSub - i- 1]})) ;
|
||||
}
|
||||
}
|
||||
// aggiungo le superfici bilineari anche nell'altro verso sia per il top che per il tip
|
||||
if( n5AxisType == VolZmap::Move5Axis::ACROSS) {
|
||||
// inferiori
|
||||
@@ -1549,6 +1567,16 @@ VolZmap::Comp_5AxisMilling( int nGrid, const PNTVECTOR& ptS, const PNTVECTOR& pt
|
||||
for ( int i = 0 ; i < nSub ; ++i) {
|
||||
vvPtCtrl.emplace_back( PNTVECTOR({ vPntTopStart2[i], vPntTopStart2[i+1], vPntTopEnd2[i], vPntTopEnd2[i+1]})) ;
|
||||
}
|
||||
// chiudo il volume con le superici verticali in avanti
|
||||
for ( int i = 0 ; i < nSub ; ++i) {
|
||||
vvPtCtrl.emplace_back( PNTVECTOR({ vPntTipStart[i+1], vPntTipStart[i], vPntTopStart2[nSub - i - 1], vPntTopStart2[nSub - i]})) ;
|
||||
vvPtCtrl.emplace_back( PNTVECTOR({ vPntTipEnd2[i], vPntTipEnd2[i+1], vPntTopEnd[nSub - i], vPntTopEnd[nSub - i -1]})) ;
|
||||
}
|
||||
// chiudo il volume con le superici verticali indietro
|
||||
for ( int i = 0 ; i < nSub ; ++i) {
|
||||
vvPtCtrl.emplace_back( PNTVECTOR({ vPntTipStart2[i+1], vPntTipStart2[i], vPntTopStart[nSub - i - 1], vPntTopStart[nSub - i]})) ;
|
||||
vvPtCtrl.emplace_back( PNTVECTOR({ vPntTipEnd[i], vPntTipEnd[i+1], vPntTopEnd2[nSub - i], vPntTopEnd2[nSub - i - 1]})) ;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( n5AxisType == VolZmap::Move5Axis::ALONG_CONVEX) {
|
||||
@@ -1562,6 +1590,26 @@ VolZmap::Comp_5AxisMilling( int nGrid, const PNTVECTOR& ptS, const PNTVECTOR& pt
|
||||
}
|
||||
// superfici inferiori semplici
|
||||
vvPtCtrl.emplace_back( PNTVECTOR({ vPntTipStart.front(), vPntTipStart.back(), vPntTipEnd.front(), vPntTipEnd.back()})) ;
|
||||
|
||||
// calcolo i punti di split dei diametri
|
||||
PNTVECTOR vSplitDiamStart ;
|
||||
PNTVECTOR vSplitDiamEnd ;
|
||||
Vector3d vDirDiamStart = (vPntTipStart.back() - vPntTipStart.front()) / nSub ;
|
||||
Vector3d vDirDiamEnd = (vPntTipEnd.back() - vPntTipEnd.front()) / nSub ;
|
||||
for ( int i = 0 ; i <= nSub ; ++i) {
|
||||
vSplitDiamStart.push_back( vPntTipStart.front() + i * vDirDiamStart) ;
|
||||
vSplitDiamEnd.push_back( vPntTipEnd.front() + i * vDirDiamEnd) ;
|
||||
}
|
||||
// aggiungo le superfici verticali esterne
|
||||
for ( int i = 0 ; i < nSub ; ++i) {
|
||||
vvPtCtrl.emplace_back( PNTVECTOR({ vSplitDiamStart[i+1], vSplitDiamStart[i], vPntTopStart[nSub - i - 1], vPntTopStart[nSub - i]})) ;
|
||||
vvPtCtrl.emplace_back( PNTVECTOR({ vSplitDiamEnd[i], vSplitDiamEnd[i+1], vPntTopEnd2[nSub - i], vPntTopEnd2[nSub - i- 1]})) ;
|
||||
}
|
||||
// aggiungo le superfici verticali interne
|
||||
for ( int i = 0 ; i < nSub ; ++i) {
|
||||
vvPtCtrl.emplace_back( PNTVECTOR({ vSplitDiamStart[i+1], vSplitDiamStart[i], vPntTopStart2[nSub - i - 1], vPntTopStart2[nSub - i]})) ;
|
||||
vvPtCtrl.emplace_back( PNTVECTOR({ vSplitDiamEnd[i], vSplitDiamEnd[i+1], vPntTopEnd[nSub - i], vPntTopEnd[nSub - i- 1]})) ;
|
||||
}
|
||||
}
|
||||
else if ( n5AxisType == VolZmap::Move5Axis::ALONG_CONCAVE) {
|
||||
// superfici inferiori
|
||||
@@ -1574,6 +1622,26 @@ VolZmap::Comp_5AxisMilling( int nGrid, const PNTVECTOR& ptS, const PNTVECTOR& pt
|
||||
}
|
||||
// superfici superiori semplici
|
||||
vvPtCtrl.emplace_back( PNTVECTOR({ vPntTopStart.front(), vPntTopStart.back(), vPntTopEnd.front(), vPntTopEnd.back()})) ;
|
||||
|
||||
// calcolo i punti di split dei diametri
|
||||
PNTVECTOR vSplitDiamStart ;
|
||||
PNTVECTOR vSplitDiamEnd ;
|
||||
Vector3d vDirDiamStart = (vPntTopStart.back() - vPntTopStart.front()) / nSub ;
|
||||
Vector3d vDirDiamEnd = (vPntTopEnd.back() - vPntTopEnd.front()) / nSub ;
|
||||
for ( int i = 0 ; i <= nSub ; ++i) {
|
||||
vSplitDiamStart.push_back( vPntTopStart.front() + i * vDirDiamStart) ;
|
||||
vSplitDiamEnd.push_back( vPntTopEnd.front() + i * vDirDiamEnd) ;
|
||||
}
|
||||
// aggiungo le superfici verticali esterne
|
||||
for ( int i = 0 ; i < nSub ; ++i) {
|
||||
vvPtCtrl.emplace_back( PNTVECTOR({ vSplitDiamStart[i+1], vSplitDiamStart[i], vPntTipStart[nSub - i - 1], vPntTipStart[nSub - i]})) ;
|
||||
vvPtCtrl.emplace_back( PNTVECTOR({ vSplitDiamEnd[i], vSplitDiamEnd[i+1], vPntTipEnd2[nSub - i], vPntTipEnd2[nSub - i- 1]})) ;
|
||||
}
|
||||
// aggiungo le superfici verticali interne
|
||||
for ( int i = 0 ; i < nSub ; ++i) {
|
||||
vvPtCtrl.emplace_back( PNTVECTOR({ vSplitDiamStart[i+1], vSplitDiamStart[i], vPntTipStart2[nSub - i - 1], vPntTipStart2[nSub - i]})) ;
|
||||
vvPtCtrl.emplace_back( PNTVECTOR({ vSplitDiamEnd[i], vSplitDiamEnd[i+1], vPntTipEnd[nSub - i], vPntTipEnd[nSub - i- 1]})) ;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1599,16 +1667,23 @@ VolZmap::Comp_5AxisMilling( int nGrid, const PNTVECTOR& ptS, const PNTVECTOR& pt
|
||||
A2.push_back( a.y * q.z - a.z * q.y) ;
|
||||
B2.push_back( b.y * q.z - b.z * q.y) ;
|
||||
C2.push_back( c.y * q.z - c.z * q.y) ;
|
||||
if ( nGrid == 0)
|
||||
vGeo.push_back( vSurfBez[nSurfInd]->Clone()) ;
|
||||
#ifdef TEST
|
||||
if ( nGrid == 0)
|
||||
vGeo.push_back( vSurfBez[nSurfInd]->Clone()) ;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef TEST
|
||||
//debug
|
||||
if ( nGrid == 0) {
|
||||
SaveGeoObj( vGeo, "D:/Temp/VirtualMilling/5axisAdvanced/finalApprox.nge", 2) ;
|
||||
vector<Color> vCol( vGeo.size()) ;
|
||||
fill(vCol.begin(), vCol.end(), Color( 255,128,0)) ;
|
||||
SaveGeoObj( vGeo, vCol, "D:/Temp/VirtualMilling/5axisAdvanced/finalApprox.nge", 2) ;
|
||||
}
|
||||
//debug
|
||||
#endif
|
||||
|
||||
|
||||
BBox3d bbStartCyl = GetCylMoveBBox( ptS[0], ptS[0], vtLs[0], dMaxRad, dHeight) ;
|
||||
BBox3d bbEndCyl = GetCylMoveBBox( ptE.back(), ptE.back(), vtLe.back(), dMaxRad, dHeight) ;
|
||||
@@ -1668,7 +1743,11 @@ VolZmap::Comp_5AxisMilling( int nGrid, const PNTVECTOR& ptS, const PNTVECTOR& pt
|
||||
double dX = ( i + 0.5) * m_dStep ;
|
||||
double dY = ( j + 0.5) * m_dStep ;
|
||||
Point3d r( dX, dY, 0) ;
|
||||
PNTVECTVECTOR vInters ;
|
||||
PNTVEC3DVECTOR vInters ;
|
||||
|
||||
//debug
|
||||
INTVECTOR vSurfInters ;
|
||||
|
||||
// interseco con le bilineari
|
||||
for( int s = 0 ; s < nAllStepsSurfs ; ++s) {
|
||||
// verifico che lo spillone faccia interferenza con il box della superficie
|
||||
@@ -1689,7 +1768,11 @@ VolZmap::Comp_5AxisMilling( int nGrid, const PNTVECTOR& ptS, const PNTVECTOR& pt
|
||||
Point3d ptBez1 ;
|
||||
Vector3d vtN1 ;
|
||||
vSurfBez[s]->GetPointNrmD1D2(dU1, dV1, ISurfBezier::Side::FROM_MINUS, ISurfBezier::Side::FROM_MINUS, ptBez1, vtN1) ;
|
||||
UpdateMaxMin( ptBez1, -vtN1, vInters) ;
|
||||
vtN1 *= -1 ;
|
||||
UpdateMaxMin( ptBez1, vtN1, vInters) ;
|
||||
|
||||
//debug
|
||||
vSurfInters.push_back( s) ;
|
||||
}
|
||||
}
|
||||
if ( nRoots > 1 && vdRoots[1] > 0 - EPS_ZERO && vdRoots[1] < 1 + EPS_ZERO) {
|
||||
@@ -1699,13 +1782,53 @@ VolZmap::Comp_5AxisMilling( int nGrid, const PNTVECTOR& ptS, const PNTVECTOR& pt
|
||||
Point3d ptBez2 ;
|
||||
Vector3d vtN2 ;
|
||||
vSurfBez[s]->GetPointNrmD1D2(dU2, dV2, ISurfBezier::Side::FROM_MINUS, ISurfBezier::Side::FROM_MINUS, ptBez2, vtN2) ;
|
||||
UpdateMaxMin( ptBez2, -vtN2, vInters) ;
|
||||
vtN2 *= -1 ;
|
||||
UpdateMaxMin( ptBez2, vtN2, vInters) ;
|
||||
|
||||
//debug
|
||||
vSurfInters.push_back( s) ;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
INTINTVECTOR vStartEnds ;
|
||||
if ( vInters.size() > 2) {
|
||||
sort( vInters.begin(), vInters.end(), []( PNTVEC3D& a, PNTVEC3D& b){ return a.first.z < b.first.z ;}) ;
|
||||
INTINT pStartEnd ;
|
||||
bool bDirStart = vInters.front().second * Z_AX > 0 ;
|
||||
bool bFirstEndFound = false ;
|
||||
pStartEnd.first = 0 ;
|
||||
for ( int w = 1 ; w < int( vInters.size()) ; ++w) {
|
||||
bool bDir = vInters[w].second * Z_AX > 0 ;
|
||||
// se è l'ultimo punto esco
|
||||
if ( w == int( vInters.size() - 1) && bDirStart != bDir) {
|
||||
pStartEnd.second = w ;
|
||||
vStartEnds.push_back( pStartEnd) ;
|
||||
break ;
|
||||
}
|
||||
// sennò scorro finché non trovo l'uscita più lontana ( prima di un altro ingresso o eventualmente l'ultima inters)
|
||||
else if( bDirStart != bDir)
|
||||
bFirstEndFound = true ;
|
||||
// se trovo di nuovo un'intersezione orientata come la prima dello spillone allora ho un nuovo start e posso chiudere l'intervallo precedente
|
||||
else if( bFirstEndFound && bDirStart == bDir) {
|
||||
pStartEnd.second = w - 1 ;
|
||||
bFirstEndFound = false ;
|
||||
vStartEnds.push_back( pStartEnd) ;
|
||||
pStartEnd.first = w ;
|
||||
pStartEnd.second = -1 ;
|
||||
}
|
||||
}
|
||||
for( auto p : vStartEnds)
|
||||
SubtractIntervals(nGrid, i, j, vInters[p.first].first.z, vInters[p.second].first.z, vInters[p.first].second, vInters[p.second].second, nToolNum) ;
|
||||
}
|
||||
else if ( vInters.size() == 2)
|
||||
SubtractIntervals( nGrid, i, j, vInters.front().first.z, vInters.back().first.z, vInters.front().second, vInters.back().second, nToolNum) ;
|
||||
else if ( vInters.size() == 1)
|
||||
return false ;
|
||||
vInters.clear() ;
|
||||
|
||||
// interseco con la prima metà del tool in posizione iniziale e con la seconda metà del tool in posizione finale
|
||||
// se sto trattando un cilindro
|
||||
BOOLVECTOR vbUpdate( 2) ;
|
||||
@@ -1735,180 +1858,306 @@ VolZmap::Comp_5AxisMilling( int nGrid, const PNTVECTOR& ptS, const PNTVECTOR& pt
|
||||
bIntersFound = IntersLineConus( r, Z_AX, frTool, dTan, dMinH, dMaxH, false, false, vPntInters[0], vVtIntersNorm[0], vPntInters[1], vVtIntersNorm[1]) ;
|
||||
|
||||
if ( bIntersFound) {
|
||||
// ciclo sulle due intersezioni
|
||||
fill(vbUpdate.begin(), vbUpdate.end(), false) ;
|
||||
for( int p = 0 ; p < 2 ; ++p) {
|
||||
bool bOnTopBase = ( vPntInters[p] - frTool.Orig() ) * frTool.VersZ() > dHeight - EPS_SMALL ;
|
||||
bool bOnBottomBase = ( vPntInters[p] - frTool.Orig() ) * frTool.VersZ() < EPS_SMALL ;
|
||||
// se ho un'intersezione su una base devo verificare se la devo considerare o no
|
||||
if ( bOnBottomBase || bOnTopBase) {
|
||||
// verifico se sono nella metà della base il cui contorno è in contatto con le bilineari
|
||||
//base inferiore
|
||||
double dSectorAng = 0 ;
|
||||
bool bApproxedSideBottom = false ;
|
||||
bool bApproxedSideTop = false ;
|
||||
double dBottomBaseSide = 0 ;
|
||||
if ( t == 0)
|
||||
dBottomBaseSide = ( vPntInters[p] - frTool.Orig()) * vtDirTipStartEx ;
|
||||
else if ( t == 1)
|
||||
dBottomBaseSide = ( vPntInters[p] - frTool.Orig()) * vtDirTipEndEx ;
|
||||
double dTopBaseSide = 0 ;
|
||||
if ( t == 0)
|
||||
dTopBaseSide = ( vPntInters[p] - ( frTool.Orig() + frTool.VersZ() * dHeight)) * vtDirTopStartEx ;
|
||||
else if ( t == 1)
|
||||
dTopBaseSide = ( vPntInters[p] - ( frTool.Orig() + frTool.VersZ() * dHeight)) * vtDirTopEndEx ;
|
||||
fill(vbUpdate.begin(), vbUpdate.end(), true) ; // tengo tutte le intersezioni col cilindro
|
||||
|
||||
if ( n5AxisType == VolZmap::Move5Axis::NO_BASE_INTERS) {
|
||||
if ( dSide < 0) {
|
||||
bApproxedSideBottom = dBottomBaseSide < 0 ;
|
||||
bApproxedSideTop = dTopBaseSide > 0 ;
|
||||
}
|
||||
else {
|
||||
bApproxedSideBottom = dBottomBaseSide > 0 ;
|
||||
bApproxedSideTop = dTopBaseSide < 0 ;
|
||||
}
|
||||
}
|
||||
else if ( n5AxisType == VolZmap::Move5Axis::ACROSS) {
|
||||
if ( t == 0) {
|
||||
bApproxedSideBottom = dBottomBaseSide < 0 ;
|
||||
bApproxedSideTop = dTopBaseSide < 0 ;
|
||||
}
|
||||
else if ( t == 1) {
|
||||
bApproxedSideBottom = dBottomBaseSide > 0 ;
|
||||
bApproxedSideTop = dTopBaseSide > 0 ;
|
||||
}
|
||||
}
|
||||
else if ( n5AxisType == VolZmap::Move5Axis::ALONG_CONVEX){
|
||||
if ( bOnBottomBase) {
|
||||
if ( t == 0)
|
||||
vbUpdate[p] = dBottomBaseSide < 0 ;
|
||||
else if ( t == 1)
|
||||
vbUpdate[p] = dBottomBaseSide > 0 ;
|
||||
}
|
||||
else if ( bOnTopBase)
|
||||
bApproxedSideTop = true ;
|
||||
}
|
||||
else if ( n5AxisType == VolZmap::Move5Axis::ALONG_CONCAVE) {
|
||||
if ( bOnBottomBase)
|
||||
bApproxedSideBottom = true ;
|
||||
else if ( bOnTopBase) {
|
||||
if ( t == 0)
|
||||
vbUpdate[p] = dTopBaseSide < 0 ;
|
||||
else if( t == 1)
|
||||
vbUpdate[p] = dTopBaseSide > 0 ;
|
||||
}
|
||||
}
|
||||
if ( bOnBottomBase && bApproxedSideBottom) {
|
||||
if ( t == 0)
|
||||
vvtTipStartAux.front().GetAngle( vPntInters[p] - frTool.Orig(), dSectorAng) ;
|
||||
else if( t == 1)
|
||||
vvtTipEndAux.front().GetAngle( vPntInters[p] - frTool.Orig(), dSectorAng) ;
|
||||
// determino in quale settore del semicerchio approssimato mi trovo
|
||||
int nSector = int( dSectorAng / ( 180. / nSub) + 1) ;
|
||||
// verifico se l'intersezione linea-base è interna o esterna
|
||||
if ( n5AxisType == VolZmap::Move5Axis::NO_BASE_INTERS) {
|
||||
double dDist = 0 ;
|
||||
if ( t == 0)
|
||||
dDist = ( vPntInters[p] - frTool.Orig() ) * vvtTipStartAux[nSector] ;
|
||||
else if( t == 1)
|
||||
dDist = ( vPntInters[p] - frTool.Orig() ) * vvtTipEndAux[nSector] ;
|
||||
//// ciclo sulle due intersezioni
|
||||
//fill(vbUpdate.begin(), vbUpdate.end(), false) ;
|
||||
//for( int p = 0 ; p < 2 ; ++p) {
|
||||
// bool bOnTopBase = ( vPntInters[p] - frTool.Orig() ) * frTool.VersZ() > dHeight - EPS_SMALL ;
|
||||
// bool bOnBottomBase = ( vPntInters[p] - frTool.Orig() ) * frTool.VersZ() < EPS_SMALL ;
|
||||
// // se ho un'intersezione su una base devo verificare se la devo considerare o no
|
||||
// if ( bOnBottomBase || bOnTopBase) {
|
||||
// // verifico se sono nella metà della base il cui contorno è in contatto con le bilineari
|
||||
// //base inferiore
|
||||
// double dSectorAng = 0 ;
|
||||
// bool bApproxedSideBottom = false ;
|
||||
// bool bApproxedSideTop = false ;
|
||||
// double dBottomBaseSide = 0 ;
|
||||
// if ( t == 0)
|
||||
// dBottomBaseSide = ( vPntInters[p] - frTool.Orig()) * vtDirTipStartEx ;
|
||||
// else if ( t == 1)
|
||||
// dBottomBaseSide = ( vPntInters[p] - frTool.Orig()) * vtDirTipEndEx ;
|
||||
// double dTopBaseSide = 0 ;
|
||||
// if ( t == 0)
|
||||
// dTopBaseSide = ( vPntInters[p] - ( frTool.Orig() + frTool.VersZ() * dHeight)) * vtDirTopStartEx ;
|
||||
// else if ( t == 1)
|
||||
// dTopBaseSide = ( vPntInters[p] - ( frTool.Orig() + frTool.VersZ() * dHeight)) * vtDirTopEndEx ;
|
||||
|
||||
if ( dSide > 0)
|
||||
vbUpdate[p] = dDist < dMinRadApprox - EPS_SMALL ;
|
||||
else
|
||||
vbUpdate[p] = dDist > dMinRadApprox - EPS_SMALL && dDist < dMinRad ;
|
||||
}
|
||||
else if ( n5AxisType == VolZmap::Move5Axis::ALONG_CONCAVE) {
|
||||
double dDist = 0 ;
|
||||
// per il caso ALONG_CONCAVE dovrei avere dSide < 0, quindi sulla base bottom avere i punti ausiliari nella parte posteriore del cerchio di base
|
||||
if ( dBottomBaseSide > 0) {
|
||||
if ( t == 0) {
|
||||
// in questo caso devo prendere il vettore Aux dell'angolo supplementare e con segno opposto
|
||||
dDist = ( vPntInters[p] - frTool.Orig() ) * ( -vvtTipStartAux[( nSub + 1 ) - nSector] ) ;
|
||||
vbUpdate[p] = dDist < dMinRadApprox - EPS_SMALL ;
|
||||
}
|
||||
else if ( t == 1)
|
||||
vbUpdate[p] = true ;
|
||||
}
|
||||
else {
|
||||
if ( t == 0)
|
||||
vbUpdate[p] = true ;
|
||||
else if ( t == 1 ) {
|
||||
dDist = (vPntInters[p] - frTool.Orig()) * vvtTipEndAux[nSector] ;
|
||||
vbUpdate[p] = dDist < dMinRadApprox - EPS_SMALL ;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( n5AxisType == VolZmap::Move5Axis::ACROSS)
|
||||
vbUpdate[p] = true ;
|
||||
}
|
||||
// base superiore
|
||||
else if( bOnTopBase && bApproxedSideTop) {
|
||||
if ( t == 0)
|
||||
vvtTopStartAux.front().GetAngle( vPntInters[p] - ( frTool.Orig() + frTool.VersZ() * dHeight ), dSectorAng) ;
|
||||
else if( t == 1)
|
||||
vvtTopEndAux.front().GetAngle( vPntInters[p] - ( frTool.Orig() + frTool.VersZ() * dHeight ), dSectorAng) ;
|
||||
// determino in quale settore del semicerchio approssimato mi trovo
|
||||
int nSector = int( dSectorAng / ( 180. / nSub)) + 1 ;
|
||||
// verifico se l'intersezione linea-base è interna o esterna
|
||||
if ( n5AxisType == VolZmap::Move5Axis::NO_BASE_INTERS) {
|
||||
double dDist = 0 ;
|
||||
if ( t == 0)
|
||||
dDist = ( vPntInters[p] - ( frTool.Orig() + frTool.VersZ() * dHeight)) * vvtTopStartAux[nSector] ;
|
||||
else if( t == 1)
|
||||
dDist = ( vPntInters[p] - ( frTool.Orig() + frTool.VersZ() * dHeight)) * vvtTopEndAux[nSector] ;
|
||||
// if ( n5AxisType == VolZmap::Move5Axis::NO_BASE_INTERS) {
|
||||
// if ( dSide < 0) {
|
||||
// bApproxedSideBottom = dBottomBaseSide < 0 ;
|
||||
// bApproxedSideTop = dTopBaseSide > 0 ;
|
||||
// }
|
||||
// else {
|
||||
// bApproxedSideBottom = dBottomBaseSide > 0 ;
|
||||
// bApproxedSideTop = dTopBaseSide < 0 ;
|
||||
// }
|
||||
// }
|
||||
// else if ( n5AxisType == VolZmap::Move5Axis::ACROSS) {
|
||||
// //if ( t == 0) {
|
||||
// // bApproxedSideBottom = dBottomBaseSide < 0 ;
|
||||
// // bApproxedSideTop = dTopBaseSide < 0 ;
|
||||
// //}
|
||||
// //else if ( t == 1) {
|
||||
// // bApproxedSideBottom = dBottomBaseSide > 0 ;
|
||||
// // bApproxedSideTop = dTopBaseSide > 0 ;
|
||||
// //}
|
||||
|
||||
if( dSide < 0)
|
||||
vbUpdate[p] = dDist < dMaxRadApprox - EPS_SMALL ;
|
||||
else
|
||||
vbUpdate[p] = dDist > dMaxRadApprox - EPS_SMALL && dDist < dMaxRad ;
|
||||
}
|
||||
else if ( n5AxisType == VolZmap::Move5Axis::ALONG_CONVEX) {
|
||||
double dDist = 0 ;
|
||||
// per il caso ALONG_CONVEX dovrei avere dSide > 0, quindi sulla base top avere i punti ausiliari nella parte posteriore del cerchio di base
|
||||
if ( dTopBaseSide > 0) {
|
||||
// in questo caso devo prendere il vettore Aux dell'angolo supplementare e con segno opposto
|
||||
if ( t == 0)
|
||||
dDist = ( vPntInters[p] - frTool.Orig()) * ( -vvtTipStartAux[( nSub + 1 ) - nSector]) ;
|
||||
else if( t == 1)
|
||||
dDist = ( vPntInters[p] - frTool.Orig()) * ( -vvtTipEndAux[( nSub + 1 ) - nSector]) ;
|
||||
vbUpdate[p] = dDist < dMinRadApprox - EPS_SMALL ;
|
||||
}
|
||||
else
|
||||
vbUpdate[p] = true ;
|
||||
}
|
||||
else if ( n5AxisType == VolZmap::Move5Axis::ACROSS)
|
||||
vbUpdate[p] = true ;
|
||||
}
|
||||
else if ( n5AxisType == VolZmap::Move5Axis::NO_BASE_INTERS &&
|
||||
(( t == 0 && ((bOnTopBase && dSide < 0) || ( bOnBottomBase && dSide > 0))) ||
|
||||
( t == 1 && ((bOnTopBase && dSide > 0) || ( bOnBottomBase && dSide < 0)))))
|
||||
vbUpdate[p] = true ;
|
||||
}
|
||||
else {
|
||||
double dHInters = 0 ;
|
||||
if ( t == 0)
|
||||
dHInters = (vPntInters[p] - ptP1T) * vtLs.front() ;
|
||||
else if( t == 1)
|
||||
dHInters = (vPntInters[p] - ptP2T) * vtLe.back() ;
|
||||
Vector3d vtDir = Media( vtDirTip, vtDirTop, dHInters / dHeight) ;
|
||||
if ( t == 0)
|
||||
vbUpdate[p] = vVtIntersNorm[p] * vtDir > 0 ;
|
||||
else if ( t == 1)
|
||||
vbUpdate[p] = vVtIntersNorm[p] * vtDir < 0 ;
|
||||
}
|
||||
}
|
||||
if ( vbUpdate[0])
|
||||
// bApproxedSideBottom = true ;
|
||||
// bApproxedSideTop = true ;
|
||||
// }
|
||||
// else if ( n5AxisType == VolZmap::Move5Axis::ALONG_CONVEX){
|
||||
// if ( bOnBottomBase) {
|
||||
// if ( t == 0)
|
||||
// vbUpdate[p] = dBottomBaseSide < 0 ;
|
||||
// else if ( t == 1)
|
||||
// vbUpdate[p] = dBottomBaseSide > 0 ;
|
||||
// }
|
||||
// else if ( bOnTopBase)
|
||||
// bApproxedSideTop = true ;
|
||||
// }
|
||||
// else if ( n5AxisType == VolZmap::Move5Axis::ALONG_CONCAVE) {
|
||||
// if ( bOnBottomBase)
|
||||
// bApproxedSideBottom = true ;
|
||||
// else if ( bOnTopBase) {
|
||||
// if ( t == 0)
|
||||
// vbUpdate[p] = dTopBaseSide < 0 ;
|
||||
// else if( t == 1)
|
||||
// vbUpdate[p] = dTopBaseSide > 0 ;
|
||||
// }
|
||||
// }
|
||||
// if ( bOnBottomBase && bApproxedSideBottom) {
|
||||
// if ( t == 0)
|
||||
// vvtTipStartAux.front().GetAngle( vPntInters[p] - frTool.Orig(), dSectorAng) ;
|
||||
// else if( t == 1)
|
||||
// vvtTipEndAux.front().GetAngle( vPntInters[p] - frTool.Orig(), dSectorAng) ;
|
||||
// // determino in quale settore del semicerchio approssimato mi trovo
|
||||
// int nSector = int( dSectorAng / ( 180. / nSub) + 1) ;
|
||||
// // verifico se l'intersezione linea-base è interna o esterna
|
||||
// if ( n5AxisType == VolZmap::Move5Axis::NO_BASE_INTERS) {
|
||||
// double dDist = 0 ;
|
||||
// if ( t == 0)
|
||||
// dDist = ( vPntInters[p] - frTool.Orig() ) * vvtTipStartAux[nSector] ;
|
||||
// else if( t == 1)
|
||||
// dDist = ( vPntInters[p] - frTool.Orig() ) * vvtTipEndAux[nSector] ;
|
||||
|
||||
// if ( dSide > 0)
|
||||
// vbUpdate[p] = dDist < dMinRadApprox - EPS_SMALL ;
|
||||
// else
|
||||
// vbUpdate[p] = dDist > dMinRadApprox - EPS_SMALL && dDist < dMinRad ;
|
||||
// }
|
||||
// else if ( n5AxisType == VolZmap::Move5Axis::ALONG_CONCAVE) {
|
||||
// double dDist = 0 ;
|
||||
// // per il caso ALONG_CONCAVE dovrei avere dSide < 0, quindi sulla base bottom avere i punti ausiliari nella parte posteriore del cerchio di base
|
||||
// if ( dBottomBaseSide > 0) {
|
||||
// if ( t == 0) {
|
||||
// // in questo caso devo prendere il vettore Aux dell'angolo supplementare e con segno opposto
|
||||
// dDist = ( vPntInters[p] - frTool.Orig() ) * ( -vvtTipStartAux[( nSub + 1 ) - nSector] ) ;
|
||||
// vbUpdate[p] = dDist < dMinRadApprox - EPS_SMALL ;
|
||||
// }
|
||||
// else if ( t == 1)
|
||||
// vbUpdate[p] = true ;
|
||||
// }
|
||||
// else {
|
||||
// if ( t == 0)
|
||||
// vbUpdate[p] = true ;
|
||||
// else if ( t == 1 ) {
|
||||
// dDist = (vPntInters[p] - frTool.Orig()) * vvtTipEndAux[nSector] ;
|
||||
// vbUpdate[p] = dDist < dMinRadApprox - EPS_SMALL ;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// else if ( n5AxisType == VolZmap::Move5Axis::ACROSS) {
|
||||
// double dDist = 0 ;
|
||||
// // con dSide < 0 sulla base bottom/top ho i punti ausiliari nella parte posteriore/anteriore del cerchio di base
|
||||
// // con dSide > 0 sulla base bottom/top ho i punti ausiliari nella parte anteriore/posteriore del cerchio di base
|
||||
//
|
||||
// if ( dBottomBaseSide > 0) {
|
||||
// if ( t == 0) {
|
||||
// if ( dSide < 0) {
|
||||
// // in questo caso devo prendere il vettore Aux dell'angolo supplementare e con segno opposto
|
||||
// dDist = ( vPntInters[p] - frTool.Orig() ) * ( -vvtTipStartAux[( nSub + 1 ) - nSector] ) ;
|
||||
// }
|
||||
// else {
|
||||
// dDist = ( vPntInters[p] - frTool.Orig() ) * vvtTipStartAux[nSector] ;
|
||||
// }
|
||||
// vbUpdate[p] = dDist < dMinRadApprox - EPS_SMALL ;
|
||||
// }
|
||||
// else if ( t == 1)
|
||||
// vbUpdate[p] = true ;
|
||||
// }
|
||||
// else {
|
||||
// if ( t == 0)
|
||||
// vbUpdate[p] = true ;
|
||||
// else if ( t == 1) {
|
||||
// if ( dSide < 0) {
|
||||
// dDist = ( vPntInters[p] - frTool.Orig() ) * vvtTipStartAux[nSector] ;
|
||||
// }
|
||||
// else {
|
||||
// // in questo caso devo prendere il vettore Aux dell'angolo supplementare e con segno opposto
|
||||
// dDist = ( vPntInters[p] - frTool.Orig() ) * ( -vvtTipStartAux[( nSub + 1 ) - nSector] ) ;
|
||||
// }
|
||||
// vbUpdate[p] = dDist < dMinRadApprox - EPS_SMALL ;
|
||||
// }
|
||||
// }
|
||||
|
||||
// //vbUpdate[p] = true ;
|
||||
// }
|
||||
// }
|
||||
// // base superiore
|
||||
// else if( bOnTopBase && bApproxedSideTop) {
|
||||
// if ( t == 0)
|
||||
// vvtTopStartAux.front().GetAngle( vPntInters[p] - ( frTool.Orig() + frTool.VersZ() * dHeight ), dSectorAng) ;
|
||||
// else if( t == 1)
|
||||
// vvtTopEndAux.front().GetAngle( vPntInters[p] - ( frTool.Orig() + frTool.VersZ() * dHeight ), dSectorAng) ;
|
||||
// // determino in quale settore del semicerchio approssimato mi trovo
|
||||
// int nSector = int( dSectorAng / ( 180. / nSub)) + 1 ;
|
||||
// // verifico se l'intersezione linea-base è interna o esterna
|
||||
// if ( n5AxisType == VolZmap::Move5Axis::NO_BASE_INTERS) {
|
||||
// double dDist = 0 ;
|
||||
// if ( t == 0)
|
||||
// dDist = ( vPntInters[p] - ( frTool.Orig() + frTool.VersZ() * dHeight)) * vvtTopStartAux[nSector] ;
|
||||
// else if( t == 1)
|
||||
// dDist = ( vPntInters[p] - ( frTool.Orig() + frTool.VersZ() * dHeight)) * vvtTopEndAux[nSector] ;
|
||||
|
||||
// if( dSide < 0)
|
||||
// vbUpdate[p] = dDist < dMaxRadApprox - EPS_SMALL ;
|
||||
// else
|
||||
// vbUpdate[p] = dDist > dMaxRadApprox - EPS_SMALL && dDist < dMaxRad ;
|
||||
// }
|
||||
// else if ( n5AxisType == VolZmap::Move5Axis::ALONG_CONVEX) {
|
||||
// double dDist = 0 ;
|
||||
// // per il caso ALONG_CONVEX dovrei avere dSide > 0, quindi sulla base top avere i punti ausiliari nella parte posteriore del cerchio di base
|
||||
// if ( dTopBaseSide > 0) {
|
||||
// // in questo caso devo prendere il vettore Aux dell'angolo supplementare e con segno opposto
|
||||
// if ( t == 0)
|
||||
// dDist = ( vPntInters[p] - frTool.Orig()) * ( -vvtTopStartAux[( nSub + 1 ) - nSector]) ;
|
||||
// else if( t == 1)
|
||||
// dDist = ( vPntInters[p] - frTool.Orig()) * ( -vvtTopEndAux[( nSub + 1 ) - nSector]) ;
|
||||
// vbUpdate[p] = dDist < dMaxRadApprox - EPS_SMALL ;
|
||||
// }
|
||||
// else
|
||||
// vbUpdate[p] = true ;
|
||||
// }
|
||||
// else if ( n5AxisType == VolZmap::Move5Axis::ACROSS) {
|
||||
// double dDist = 0 ;
|
||||
// // con dSide < 0 sulla base bottom/top ho i punti ausiliari nella parte posteriore/anteriore del cerchio di base
|
||||
// // con dSide > 0 sulla base bottom/top ho i punti ausiliari nella parte anteriore/posteriore del cerchio di base
|
||||
// if ( dTopBaseSide > 0) {
|
||||
// if ( t == 0) {
|
||||
// if ( dSide < 0) {
|
||||
// dDist = ( vPntInters[p] - frTool.Orig()) * vvtTopStartAux[nSector] ;
|
||||
// }
|
||||
// else {
|
||||
// // in questo caso devo prendere il vettore Aux dell'angolo supplementare e con segno opposto
|
||||
// dDist = ( vPntInters[p] - frTool.Orig()) * ( -vvtTopStartAux[( nSub + 1 ) - nSector] ) ;
|
||||
// }
|
||||
// vbUpdate[p] = dDist < dMaxRadApprox - EPS_SMALL ;
|
||||
// }
|
||||
// else if ( t == 1)
|
||||
// vbUpdate[p] = true ;
|
||||
// }
|
||||
// else {
|
||||
// if ( t == 0)
|
||||
// vbUpdate[p] = true ;
|
||||
// else if ( t == 1) {
|
||||
// if ( dSide < 0) {
|
||||
// // in questo caso devo prendere il vettore Aux dell'angolo supplementare e con segno opposto
|
||||
// dDist = ( vPntInters[p] - frTool.Orig()) * ( -vvtTopStartAux[( nSub + 1 ) - nSector] ) ;
|
||||
// }
|
||||
// else {
|
||||
// dDist = ( vPntInters[p] - frTool.Orig()) * vvtTopStartAux[nSector] ;
|
||||
// }
|
||||
// vbUpdate[p] = dDist < dMaxRadApprox - EPS_SMALL ;
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
// //vbUpdate[p] = true ;
|
||||
// }
|
||||
// }
|
||||
// else if ( n5AxisType == VolZmap::Move5Axis::NO_BASE_INTERS &&
|
||||
// (( t == 0 && ((bOnTopBase && dSide < 0) || ( bOnBottomBase && dSide > 0))) ||
|
||||
// ( t == 1 && ((bOnTopBase && dSide > 0) || ( bOnBottomBase && dSide < 0)))))
|
||||
// vbUpdate[p] = true ;
|
||||
// }
|
||||
// else {
|
||||
// double dHInters = 0 ;
|
||||
// if ( t == 0)
|
||||
// dHInters = (vPntInters[p] - ptP1T) * vtLs.front() ;
|
||||
// else if( t == 1)
|
||||
// dHInters = (vPntInters[p] - ptP2T) * vtLe.back() ;
|
||||
// Vector3d vtDir = Media( vtDirTip, vtDirTop, dHInters / dHeight) ;
|
||||
// if ( t == 0)
|
||||
// vbUpdate[p] = vVtIntersNorm[p] * vtDir > 0 ; // le normali dei punti di intersezione sul cilindro sono rivolte verso l'interno del cilindro
|
||||
// else if ( t == 1)
|
||||
// vbUpdate[p] = vVtIntersNorm[p] * vtDir < 0 ;
|
||||
// }
|
||||
//}
|
||||
if ( vbUpdate[0]) {
|
||||
UpdateMaxMin( vPntInters[0], vVtIntersNorm[0], vInters) ;
|
||||
if ( vbUpdate[1])
|
||||
|
||||
//debug
|
||||
vSurfInters.push_back( t == 0 ? -1 : -2) ;
|
||||
}
|
||||
if ( vbUpdate[1]) {
|
||||
UpdateMaxMin( vPntInters[1], vVtIntersNorm[1], vInters) ;
|
||||
|
||||
//debug
|
||||
vSurfInters.push_back( t == 0 ? -1 : -2) ;
|
||||
}
|
||||
}
|
||||
|
||||
if ( vInters.size() == 2) {
|
||||
sort( vInters.begin(), vInters.end(), []( PNTVEC3D& a, PNTVEC3D& b){ return a.first.z < b.first.z ;}) ;
|
||||
SubtractIntervals( nGrid, i, j, vInters.front().first.z, vInters.back().first.z, vInters.front().second, vInters.back().second, nToolNum) ;
|
||||
vInters.clear() ;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( vInters.size() != 0 ) {
|
||||
sort( vInters.begin(), vInters.end(), []( PNTVECT& a, PNTVECT& b){ return a.first.z < b.first.z ;}) ;
|
||||
SubtractIntervals( nGrid, i, j, vInters.front().first.z, vInters.back().first.z, vInters.front().second, vInters.back().second, nToolNum) ;
|
||||
}
|
||||
|
||||
//if ( vInters.size() != 0 ) {
|
||||
// // se ho un numero dispari di intersezioni devo migliorare la gestione
|
||||
|
||||
// // riordino le intersezioni rispetto alla loro z
|
||||
// sort( vInters.begin(), vInters.end(), []( PNTVEC3D& a, PNTVEC3D& b){ return a.first.z < b.first.z ;}) ;
|
||||
// INTINTVECTOR vStartEnds ;
|
||||
// if ( vInters.size() > 2) {
|
||||
// INTINT pStartEnd ;
|
||||
// bool bDirStart = vInters.front().second * Z_AX > 0 ;
|
||||
// bool bFirstEndFound = false ;
|
||||
// pStartEnd.first = 0 ;
|
||||
// for ( int w = 1 ; w < int( vInters.size()) ; ++w) {
|
||||
// bool bDir = vInters[w].second * Z_AX > 0 ;
|
||||
// // se è l'ultimo punto esco
|
||||
// if ( w == int( vInters.size() - 1) && bDirStart != bDir) {
|
||||
// pStartEnd.second = w ;
|
||||
// vStartEnds.push_back( pStartEnd) ;
|
||||
// break ;
|
||||
// }
|
||||
// // sennò scorro finché non trovo l'uscita più lontana ( prima di un altro ingresso o eventualmente l'ultima inters)
|
||||
// else if( bDirStart != bDir)
|
||||
// bFirstEndFound = true ;
|
||||
// // se trovo di nuovo un'intersezione orientata come la prima dello spillone allora ho un nuovo start e posso chiudere l'intervallo precedente
|
||||
// else if( bFirstEndFound && bDirStart == bDir) {
|
||||
// pStartEnd.second = w - 1 ;
|
||||
// bFirstEndFound = false ;
|
||||
// vStartEnds.push_back( pStartEnd) ;
|
||||
// pStartEnd.first = w ;
|
||||
// pStartEnd.second = -1 ;
|
||||
// }
|
||||
// }
|
||||
// for( auto p : vStartEnds)
|
||||
// SubtractIntervals(nGrid, i, j, vInters[p.first].first.z, vInters[p.second].first.z, vInters[p.first].second, vInters[p.second].second, nToolNum) ;
|
||||
// }
|
||||
// else
|
||||
// SubtractIntervals( nGrid, i, j, vInters.front().first.z, vInters.back().first.z, vInters.front().second, vInters.back().second, nToolNum) ;
|
||||
//}
|
||||
|
||||
//if ( vInters.size() != 0 ) {
|
||||
// sort( vInters.begin(), vInters.end(), []( PNTVEC3D& a, PNTVEC3D& b){ return get<0>(a).z < get<0>(b).z ;}) ;
|
||||
// SubtractIntervals( nGrid, i, j, vInters.front().first.z, vInters.back().first.z, vInters.front().second, vInters.back().second, nToolNum) ;
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user