mirror of
https://git.dev.opencascade.org/repos/occt.git
synced 2025-04-06 18:26:22 +03:00
Redesign of selection mechanism: - implemented 3-level BVH tree for selection; - selection now calculates in 3D space; - intersection tests were moved to SelectMgr_BaseFrustum descendants; - removed .cdl files in Select3D and .cdl related to selection in MeshVS; - SelectMgr_ViewerSelectors are now shared between local and global contexts; - transformations of sensitive entities are now stored in SelectMgr_SelectableObject only. Sensitive entities are independent from transformations, it is applied to SelectMgr_SelectingVolumeManager instance only; - connected and multiple connected interactive objects are now represented by their child objects only for SelectMgr_SelectionManager; - if interactive object has child objects, they will be stored as separate objects in SelectMgr_SelectionManager now. - test cases bugs/vis/bug24623_1, bug24623_2, bug24623_3, bug24623_4 to test performance and memory issues.
793 lines
32 KiB
C++
793 lines
32 KiB
C++
// Created on: 2014-05-22
|
|
// Created by: Varvara POSKONINA
|
|
// Copyright (c) 2005-2014 OPEN CASCADE SAS
|
|
//
|
|
// This file is part of Open CASCADE Technology software library.
|
|
//
|
|
// This library is free software; you can redistribute it and/or modify it under
|
|
// the terms of the GNU Lesser General Public License version 2.1 as published
|
|
// by the Free Software Foundation, with special exception defined in the file
|
|
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
|
|
// distribution for complete text of the license and disclaimer of any warranty.
|
|
//
|
|
// Alternatively, this file may be used under the terms of Open CASCADE
|
|
// commercial license or contractual agreement.
|
|
|
|
#include <NCollection_Vector.hxx>
|
|
#include <Poly_Array1OfTriangle.hxx>
|
|
|
|
#include <SelectMgr_RectangularFrustum.hxx>
|
|
|
|
#define DOT(A, B) (A.x() * B.x() + A.y() * B.y() + A.z() * B.z())
|
|
#define DOTp(A, B) (A.x() * B.X() + A.y() * B.Y() + A.z() * B.Z())
|
|
#define DISTANCE(A, B) (std::sqrt ((A.x() - B.x()) * (A.x() - B.x()) + (A.y() - B.y()) * (A.y() - B.y()) + (A.z() - B.z()) * (A.z() - B.z())))
|
|
#define DISTANCEp(A, B) (std::sqrt ((A.x() - B.X()) * (A.x() - B.X()) + (A.y() - B.Y()) * (A.y() - B.Y()) + (A.z() - B.Z()) * (A.z() - B.Z())))
|
|
|
|
// =======================================================================
|
|
// function : segmentSegmentDistance
|
|
// purpose :
|
|
// =======================================================================
|
|
void SelectMgr_RectangularFrustum::segmentSegmentDistance (const gp_Pnt& theSegPnt1,
|
|
const gp_Pnt& theSegPnt2,
|
|
Standard_Real& theDepth)
|
|
{
|
|
SelectMgr_Vec3 anU = SelectMgr_Vec3 (theSegPnt2.X() - theSegPnt1.X(),
|
|
theSegPnt2.Y() - theSegPnt1.Y(),
|
|
theSegPnt2.Z() - theSegPnt1.Z());
|
|
SelectMgr_Vec3 aV = myViewRayDir;
|
|
SelectMgr_Vec3 aW = SelectMgr_Vec3 (theSegPnt1.X() - myNearPickedPnt.x(),
|
|
theSegPnt1.Y() - myNearPickedPnt.y(),
|
|
theSegPnt1.Z() - myNearPickedPnt.z());
|
|
Standard_Real anA = DOT (anU, anU);
|
|
Standard_Real aB = DOT (anU, aV);
|
|
Standard_Real aC = DOT (aV, aV);
|
|
Standard_Real aD = DOT (anU, aW);
|
|
Standard_Real anE = DOT (aV, aW);
|
|
Standard_Real aCoef = anA * aC - aB * aB;
|
|
Standard_Real aSc, aSn, aSd = aCoef;
|
|
Standard_Real aTc, aTn, aTd = aCoef;
|
|
|
|
if (aCoef < Precision::Confusion())
|
|
{
|
|
aSn = 0.0;
|
|
aSd = 1.0;
|
|
aTn = anE;
|
|
aTd = aC;
|
|
}
|
|
else
|
|
{
|
|
aSn = (aB * anE - aC * aD);
|
|
aTn = (anA * anE - aB * aD);
|
|
if (aSn < 0.0)
|
|
{
|
|
aSn = 0.0;
|
|
aTn = anE;
|
|
aTd = aC;
|
|
}
|
|
else if (aSn > aSd)
|
|
{
|
|
aSn = aSd;
|
|
aTn = anE + aB;
|
|
aTd = aC;
|
|
}
|
|
}
|
|
|
|
if (aTn < 0.0)
|
|
{
|
|
aTn = 0.0;
|
|
if (-aD < 0.0)
|
|
aSn = 0.0;
|
|
else if (-aD > anA)
|
|
aSn = aSd;
|
|
else {
|
|
aSn = -aD;
|
|
aSd = anA;
|
|
}
|
|
}
|
|
else if (aTn > aTd)
|
|
{
|
|
aTn = aTd;
|
|
if ((-aD + aB) < 0.0)
|
|
aSn = 0;
|
|
else if ((-aD + aB) > anA)
|
|
aSn = aSd;
|
|
else {
|
|
aSn = (-aD + aB);
|
|
aSd = anA;
|
|
}
|
|
}
|
|
aSc = (Abs (aSn) < Precision::Confusion() ? 0.0 : aSn / aSd);
|
|
aTc = (Abs (aTn) < Precision::Confusion() ? 0.0 : aTn / aTd);
|
|
|
|
SelectMgr_Vec3 aDiff = aW + (anU * aSc) - (aV * aTc);
|
|
SelectMgr_Vec3 aClosestPnt = myNearPickedPnt + myViewRayDir * aTc;
|
|
theDepth = DISTANCE (myNearPickedPnt, aClosestPnt);
|
|
}
|
|
|
|
// =======================================================================
|
|
// function : segmentPlaneIntersection
|
|
// purpose :
|
|
// =======================================================================
|
|
void SelectMgr_RectangularFrustum::segmentPlaneIntersection (const SelectMgr_Vec3& thePlane,
|
|
const gp_Pnt& thePntOnPlane,
|
|
Standard_Real& theDepth)
|
|
{
|
|
SelectMgr_Vec3 anU = myViewRayDir;
|
|
SelectMgr_Vec3 aW = SelectMgr_Vec3 (myNearPickedPnt.x() - thePntOnPlane.X(),
|
|
myNearPickedPnt.y() - thePntOnPlane.Y(),
|
|
myNearPickedPnt.z() - thePntOnPlane.Z());
|
|
Standard_Real aD = DOT (thePlane, anU);
|
|
Standard_Real aN = -DOT (thePlane, aW);
|
|
|
|
if (Abs (aD) < Precision::Confusion())
|
|
{
|
|
if (Abs (aN) < Precision::Angular())
|
|
{
|
|
theDepth = DBL_MAX;
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
theDepth = DBL_MAX;
|
|
return;
|
|
}
|
|
}
|
|
|
|
Standard_Real aParam = aN / aD;
|
|
if (aParam < 0.0 || aParam > 1.0)
|
|
{
|
|
theDepth = DBL_MAX;
|
|
return;
|
|
}
|
|
|
|
SelectMgr_Vec3 aClosestPnt = myNearPickedPnt + anU * aParam;
|
|
theDepth = DISTANCE (myNearPickedPnt, aClosestPnt);
|
|
}
|
|
|
|
// =======================================================================
|
|
// function : Build
|
|
// purpose : Build volume according to the point and given pixel
|
|
// tolerance
|
|
// =======================================================================
|
|
void SelectMgr_RectangularFrustum::Build (const gp_Pnt2d &thePoint)
|
|
{
|
|
myNearPickedPnt = myBuilder->ProjectPntOnViewPlane (thePoint.X(), thePoint.Y(), 0.0);
|
|
myFarPickedPnt = myBuilder->ProjectPntOnViewPlane (thePoint.X(), thePoint.Y(), 1.0);
|
|
myViewRayDir = myFarPickedPnt - myNearPickedPnt;
|
|
|
|
// LeftTopNear
|
|
myVertices[0] = myBuilder->ProjectPntOnViewPlane (thePoint.X() - myPixelTolerance / 2.0,
|
|
thePoint.Y() + myPixelTolerance / 2.0,
|
|
0.0);
|
|
// LeftTopFar
|
|
myVertices[1] = myBuilder->ProjectPntOnViewPlane (thePoint.X() - myPixelTolerance / 2.0,
|
|
thePoint.Y() + myPixelTolerance / 2.0,
|
|
1.0);
|
|
// LeftBottomNear
|
|
myVertices[2] = myBuilder->ProjectPntOnViewPlane (thePoint.X() - myPixelTolerance / 2.0,
|
|
thePoint.Y() - myPixelTolerance / 2.0,
|
|
0.0);
|
|
// LeftBottomFar
|
|
myVertices[3] = myBuilder->ProjectPntOnViewPlane (thePoint.X() - myPixelTolerance / 2.0,
|
|
thePoint.Y() - myPixelTolerance / 2.0,
|
|
1.0);
|
|
// RightTopNear
|
|
myVertices[4] = myBuilder->ProjectPntOnViewPlane (thePoint.X() + myPixelTolerance / 2.0,
|
|
thePoint.Y() + myPixelTolerance / 2.0,
|
|
0.0);
|
|
// RightTopFar
|
|
myVertices[5] = myBuilder->ProjectPntOnViewPlane (thePoint.X() + myPixelTolerance / 2.0,
|
|
thePoint.Y() + myPixelTolerance / 2.0,
|
|
1.0);
|
|
// RightBottomNear
|
|
myVertices[6] = myBuilder->ProjectPntOnViewPlane (thePoint.X() + myPixelTolerance / 2.0,
|
|
thePoint.Y() - myPixelTolerance / 2.0,
|
|
0.0);
|
|
// RightBottomFar
|
|
myVertices[7] = myBuilder->ProjectPntOnViewPlane (thePoint.X() + myPixelTolerance / 2.0,
|
|
thePoint.Y() - myPixelTolerance / 2.0,
|
|
1.0);
|
|
// Top
|
|
myPlanes[0] = myBuilder->PlaneEquation (myVertices[1],
|
|
myVertices[0],
|
|
myVertices[5],
|
|
myVertices[6]);
|
|
// Bottom
|
|
myPlanes[1] = myBuilder->PlaneEquation (myVertices[3],
|
|
myVertices[2],
|
|
myVertices[7],
|
|
myVertices[4]);
|
|
// Left
|
|
myPlanes[2] = myBuilder->PlaneEquation (myVertices[1],
|
|
myVertices[0],
|
|
myVertices[2],
|
|
myVertices[6]);
|
|
// Right
|
|
myPlanes[3] = myBuilder->PlaneEquation (myVertices[5],
|
|
myVertices[4],
|
|
myVertices[6],
|
|
myVertices[2]);
|
|
// Near
|
|
myPlanes[4] = myBuilder->PlaneEquation (myVertices[4],
|
|
myVertices[6],
|
|
myVertices[2],
|
|
myVertices[3]);
|
|
// Far
|
|
myPlanes[5] = myBuilder->PlaneEquation (myVertices[5],
|
|
myVertices[7],
|
|
myVertices[3],
|
|
myVertices[2]);
|
|
|
|
for (Standard_Integer aPlaneIdx = 0; aPlaneIdx < 6; ++aPlaneIdx)
|
|
{
|
|
Standard_Real aMax = -DBL_MAX;
|
|
Standard_Real aMin = DBL_MAX;
|
|
const SelectMgr_Vec3 aPlane = myPlanes[aPlaneIdx];
|
|
for (Standard_Integer aVertIdx = 0; aVertIdx < 8; ++aVertIdx)
|
|
{
|
|
Standard_Real aProjection = DOT (aPlane, myVertices[aVertIdx]);
|
|
aMax = Max (aMax, aProjection);
|
|
aMin = Min (aMin, aProjection);
|
|
}
|
|
myMaxVertsProjections[aPlaneIdx] = aMax;
|
|
myMinVertsProjections[aPlaneIdx] = aMin;
|
|
}
|
|
|
|
SelectMgr_Vec3 aDimensions[3] =
|
|
{
|
|
SelectMgr_Vec3 (1.0, 0.0, 0.0),
|
|
SelectMgr_Vec3 (0.0, 1.0, 0.0),
|
|
SelectMgr_Vec3 (0.0, 0.0, 1.0)
|
|
};
|
|
|
|
for (Standard_Integer aDim = 0; aDim < 3; ++aDim)
|
|
{
|
|
Standard_Real aMax = -DBL_MAX;
|
|
Standard_Real aMin = DBL_MAX;
|
|
for (Standard_Integer aVertIdx = 0; aVertIdx < 8; ++aVertIdx)
|
|
{
|
|
Standard_Real aProjection = DOT (aDimensions[aDim], myVertices[aVertIdx]);
|
|
aMax = Max (aProjection, aMax);
|
|
aMin = Min (aProjection, aMin);
|
|
}
|
|
myMaxOrthoVertsProjections[aDim] = aMax;
|
|
myMinOrthoVertsProjections[aDim] = aMin;
|
|
}
|
|
|
|
// Horizontal
|
|
myEdgeDirs[0] = myVertices[4] - myVertices[0];
|
|
// Vertical
|
|
myEdgeDirs[1] = myVertices[2] - myVertices[0];
|
|
// LeftLower
|
|
myEdgeDirs[2] = myVertices[2] - myVertices[3];
|
|
// RightLower
|
|
myEdgeDirs[3] = myVertices[6] - myVertices[7];
|
|
// LeftUpper
|
|
myEdgeDirs[4] = myVertices[0] - myVertices[1];
|
|
// RightUpper
|
|
myEdgeDirs[5] = myVertices[4] - myVertices[5];
|
|
}
|
|
|
|
// =======================================================================
|
|
// function : Build
|
|
// purpose : Build volume according to the selected rectangle
|
|
// =======================================================================
|
|
void SelectMgr_RectangularFrustum::Build (const gp_Pnt2d& theMinPnt,
|
|
const gp_Pnt2d& theMaxPnt)
|
|
{
|
|
myNearPickedPnt = myBuilder->ProjectPntOnViewPlane ((theMinPnt.X() + theMaxPnt.X()) * 0.5,
|
|
(theMinPnt.Y() + theMaxPnt.Y()) * 0.5,
|
|
0.0);
|
|
myFarPickedPnt = myBuilder->ProjectPntOnViewPlane ((theMinPnt.X() + theMaxPnt.X()) * 0.5,
|
|
(theMinPnt.Y() + theMaxPnt.Y()) * 0.5,
|
|
1.0);
|
|
myViewRayDir = myFarPickedPnt - myNearPickedPnt;
|
|
|
|
// LeftTopNear
|
|
myVertices[0] = myBuilder->ProjectPntOnViewPlane (theMinPnt.X(),
|
|
theMaxPnt.Y(),
|
|
0.0);
|
|
// LeftTopFar
|
|
myVertices[1] = myBuilder->ProjectPntOnViewPlane (theMinPnt.X(),
|
|
theMaxPnt.Y(),
|
|
1.0);
|
|
// LeftBottomNear
|
|
myVertices[2] = myBuilder->ProjectPntOnViewPlane (theMinPnt.X(),
|
|
theMinPnt.Y(),
|
|
0.0);
|
|
// LeftBottomFar
|
|
myVertices[3] = myBuilder->ProjectPntOnViewPlane (theMinPnt.X(),
|
|
theMinPnt.Y(),
|
|
1.0);
|
|
// RightTopNear
|
|
myVertices[4] = myBuilder->ProjectPntOnViewPlane (theMaxPnt.X(),
|
|
theMaxPnt.Y(),
|
|
0.0);
|
|
// RightTopFar
|
|
myVertices[5] = myBuilder->ProjectPntOnViewPlane (theMaxPnt.X(),
|
|
theMaxPnt.Y(),
|
|
1.0);
|
|
// RightBottomNear
|
|
myVertices[6] = myBuilder->ProjectPntOnViewPlane (theMaxPnt.X(),
|
|
theMinPnt.Y(),
|
|
0.0);
|
|
// RightBottomFar
|
|
myVertices[7] = myBuilder->ProjectPntOnViewPlane (theMaxPnt.X() ,
|
|
theMinPnt.Y(),
|
|
1.0);
|
|
|
|
// Top
|
|
myPlanes[0] = myBuilder->PlaneEquation (myVertices[1],
|
|
myVertices[0],
|
|
myVertices[5],
|
|
myVertices[6]);
|
|
// Bottom
|
|
myPlanes[1] = myBuilder->PlaneEquation (myVertices[3],
|
|
myVertices[2],
|
|
myVertices[7],
|
|
myVertices[4]);
|
|
// Left
|
|
myPlanes[2] = myBuilder->PlaneEquation (myVertices[1],
|
|
myVertices[0],
|
|
myVertices[2],
|
|
myVertices[6]);
|
|
// Right
|
|
myPlanes[3] = myBuilder->PlaneEquation (myVertices[5],
|
|
myVertices[4],
|
|
myVertices[6],
|
|
myVertices[2]);
|
|
// Near
|
|
myPlanes[4] = myBuilder->PlaneEquation (myVertices[4],
|
|
myVertices[6],
|
|
myVertices[2],
|
|
myVertices[3]);
|
|
// Far
|
|
myPlanes[5] = myBuilder->PlaneEquation (myVertices[5],
|
|
myVertices[7],
|
|
myVertices[3],
|
|
myVertices[2]);
|
|
|
|
for (Standard_Integer aPlaneIdx = 0; aPlaneIdx < 6; ++aPlaneIdx)
|
|
{
|
|
Standard_Real aMax = -DBL_MAX;
|
|
Standard_Real aMin = DBL_MAX;
|
|
const SelectMgr_Vec3 aPlane = myPlanes[aPlaneIdx];
|
|
for (Standard_Integer aVertIdx = 0; aVertIdx < 8; ++aVertIdx)
|
|
{
|
|
Standard_Real aProjection = DOT (aPlane, myVertices[aVertIdx]);
|
|
aMax = Max (aMax, aProjection);
|
|
aMin = Min (aMin, aProjection);
|
|
}
|
|
myMaxVertsProjections[aPlaneIdx] = aMax;
|
|
myMinVertsProjections[aPlaneIdx] = aMin;
|
|
}
|
|
|
|
SelectMgr_Vec3 aDimensions[3] =
|
|
{
|
|
SelectMgr_Vec3 (1.0, 0.0, 0.0),
|
|
SelectMgr_Vec3 (0.0, 1.0, 0.0),
|
|
SelectMgr_Vec3 (0.0, 0.0, 1.0)
|
|
};
|
|
|
|
for (Standard_Integer aDim = 0; aDim < 3; ++aDim)
|
|
{
|
|
Standard_Real aMax = -DBL_MAX;
|
|
Standard_Real aMin = DBL_MAX;
|
|
for (Standard_Integer aVertIdx = 0; aVertIdx < 8; ++aVertIdx)
|
|
{
|
|
Standard_Real aProjection = DOT (aDimensions[aDim], myVertices[aVertIdx]);
|
|
aMax = Max (aMax, aProjection);
|
|
aMin = Min (aMin, aProjection);
|
|
}
|
|
myMaxOrthoVertsProjections[aDim] = aMax;
|
|
myMinOrthoVertsProjections[aDim] = aMin;
|
|
}
|
|
|
|
// Horizontal
|
|
myEdgeDirs[0] = myVertices[4] - myVertices[0];
|
|
// Vertical
|
|
myEdgeDirs[1] = myVertices[2] - myVertices[0];
|
|
// LeftLower
|
|
myEdgeDirs[2] = myVertices[2] - myVertices[3];
|
|
// RightLower
|
|
myEdgeDirs[3] = myVertices[6] - myVertices[7];
|
|
// LeftUpper
|
|
myEdgeDirs[4] = myVertices[0] - myVertices[1];
|
|
// RightUpper
|
|
myEdgeDirs[5] = myVertices[4] - myVertices[5];
|
|
}
|
|
|
|
// =======================================================================
|
|
// function : Transform
|
|
// purpose : Returns a copy of the frustum transformed according to the matrix given
|
|
// =======================================================================
|
|
NCollection_Handle<SelectMgr_BaseFrustum> SelectMgr_RectangularFrustum::Transform (const gp_Trsf& theTrsf)
|
|
{
|
|
SelectMgr_RectangularFrustum* aRes = new SelectMgr_RectangularFrustum();
|
|
|
|
aRes->myNearPickedPnt = SelectMgr_MatOp::Transform (theTrsf, myNearPickedPnt);
|
|
aRes->myFarPickedPnt = SelectMgr_MatOp::Transform (theTrsf, myFarPickedPnt);
|
|
aRes->myViewRayDir = aRes->myFarPickedPnt - aRes->myNearPickedPnt;
|
|
|
|
aRes->myIsOrthographic = myIsOrthographic;
|
|
|
|
// LeftTopNear
|
|
aRes->myVertices[0] = SelectMgr_MatOp::Transform (theTrsf, myVertices[0]);
|
|
// LeftTopFar
|
|
aRes->myVertices[1] = SelectMgr_MatOp::Transform (theTrsf, myVertices[1]);
|
|
// LeftBottomNear
|
|
aRes->myVertices[2] = SelectMgr_MatOp::Transform (theTrsf, myVertices[2]);
|
|
// LeftBottomFar
|
|
aRes->myVertices[3] = SelectMgr_MatOp::Transform (theTrsf, myVertices[3]);
|
|
// RightTopNear
|
|
aRes->myVertices[4] = SelectMgr_MatOp::Transform (theTrsf, myVertices[4]);
|
|
// RightTopFar
|
|
aRes->myVertices[5] = SelectMgr_MatOp::Transform (theTrsf, myVertices[5]);
|
|
// RightBottomNear
|
|
aRes->myVertices[6] = SelectMgr_MatOp::Transform (theTrsf, myVertices[6]);
|
|
// RightBottomFar
|
|
aRes->myVertices[7] = SelectMgr_MatOp::Transform (theTrsf, myVertices[7]);
|
|
|
|
// Top
|
|
aRes->myPlanes[0] = myBuilder->PlaneEquation (aRes->myVertices[1],
|
|
aRes->myVertices[0],
|
|
aRes->myVertices[5],
|
|
aRes->myVertices[6]);
|
|
// Bottom
|
|
aRes->myPlanes[1] = myBuilder->PlaneEquation (aRes->myVertices[3],
|
|
aRes->myVertices[2],
|
|
aRes->myVertices[7],
|
|
aRes->myVertices[4]);
|
|
// Left
|
|
aRes->myPlanes[2] = myBuilder->PlaneEquation (aRes->myVertices[1],
|
|
aRes->myVertices[0],
|
|
aRes->myVertices[2],
|
|
aRes->myVertices[6]);
|
|
// Right
|
|
aRes->myPlanes[3] = myBuilder->PlaneEquation (aRes->myVertices[5],
|
|
aRes->myVertices[4],
|
|
aRes->myVertices[6],
|
|
aRes->myVertices[2]);
|
|
// Near
|
|
aRes->myPlanes[4] = myBuilder->PlaneEquation (aRes->myVertices[4],
|
|
aRes->myVertices[6],
|
|
aRes->myVertices[2],
|
|
aRes->myVertices[3]);
|
|
// Far
|
|
aRes->myPlanes[5] = myBuilder->PlaneEquation (aRes->myVertices[5],
|
|
aRes->myVertices[7],
|
|
aRes->myVertices[3],
|
|
aRes->myVertices[2]);
|
|
|
|
for (Standard_Integer aPlaneIdx = 0; aPlaneIdx < 6; ++aPlaneIdx)
|
|
{
|
|
Standard_Real aMax = -DBL_MAX;
|
|
Standard_Real aMin = DBL_MAX;
|
|
const SelectMgr_Vec3 aPlane = aRes->myPlanes[aPlaneIdx];
|
|
for (Standard_Integer aVertIdx = 0; aVertIdx < 8; ++aVertIdx)
|
|
{
|
|
Standard_Real aProjection = DOT (aPlane, aRes->myVertices[aVertIdx]);
|
|
aMax = Max (aMax, aProjection);
|
|
aMin = Min (aMin, aProjection);
|
|
}
|
|
aRes->myMaxVertsProjections[aPlaneIdx] = aMax;
|
|
aRes->myMinVertsProjections[aPlaneIdx] = aMin;
|
|
}
|
|
|
|
SelectMgr_Vec3 aDimensions[3] =
|
|
{
|
|
SelectMgr_Vec3 (1.0, 0.0, 0.0),
|
|
SelectMgr_Vec3 (0.0, 1.0, 0.0),
|
|
SelectMgr_Vec3 (0.0, 0.0, 1.0)
|
|
};
|
|
|
|
for (Standard_Integer aDim = 0; aDim < 3; ++aDim)
|
|
{
|
|
Standard_Real aMax = -DBL_MAX;
|
|
Standard_Real aMin = DBL_MAX;
|
|
for (Standard_Integer aVertIdx = 0; aVertIdx < 8; ++aVertIdx)
|
|
{
|
|
Standard_Real aProjection = DOT (aDimensions[aDim], aRes->myVertices[aVertIdx]);
|
|
aMax = Max (aMax, aProjection);
|
|
aMin = Min (aMin, aProjection);
|
|
}
|
|
aRes->myMaxOrthoVertsProjections[aDim] = aMax;
|
|
aRes->myMinOrthoVertsProjections[aDim] = aMin;
|
|
}
|
|
|
|
// Horizontal
|
|
aRes->myEdgeDirs[0] = aRes->myVertices[4] - aRes->myVertices[0];
|
|
// Vertical
|
|
aRes->myEdgeDirs[1] = aRes->myVertices[2] - aRes->myVertices[0];
|
|
// LeftLower
|
|
aRes->myEdgeDirs[2] = aRes->myVertices[2] - aRes->myVertices[3];
|
|
// RightLower
|
|
aRes->myEdgeDirs[3] = aRes->myVertices[6] - aRes->myVertices[7];
|
|
// LeftUpper
|
|
aRes->myEdgeDirs[4] = aRes->myVertices[0] - aRes->myVertices[1];
|
|
// RightUpper
|
|
aRes->myEdgeDirs[5] = aRes->myVertices[4] - aRes->myVertices[5];
|
|
|
|
return NCollection_Handle<SelectMgr_BaseFrustum> (aRes);
|
|
}
|
|
|
|
// =======================================================================
|
|
// function : Overlaps
|
|
// purpose : Returns true if selecting volume is overlapped by
|
|
// axis-aligned bounding box with minimum corner at point
|
|
// theMinPnt and maximum at point theMaxPnt
|
|
// =======================================================================
|
|
const Standard_Boolean SelectMgr_RectangularFrustum::Overlaps (const SelectMgr_Vec3& theMinPnt,
|
|
const SelectMgr_Vec3& theMaxPnt)
|
|
{
|
|
return hasOverlap (theMinPnt, theMaxPnt);
|
|
}
|
|
|
|
// =======================================================================
|
|
// function : Overlaps
|
|
// purpose : SAT intersection test between defined volume and
|
|
// given axis-aligned box
|
|
// =======================================================================
|
|
const Standard_Boolean SelectMgr_RectangularFrustum::Overlaps (const BVH_Box<Standard_Real, 3>& theBox,
|
|
Standard_Real& theDepth)
|
|
{
|
|
const SelectMgr_Vec3& aMinPnt = theBox.CornerMin();
|
|
const SelectMgr_Vec3& aMaxPnt = theBox.CornerMax();
|
|
if (!hasOverlap (aMinPnt, aMaxPnt))
|
|
return Standard_False;
|
|
|
|
SelectMgr_Vec3 aNearestPnt = SelectMgr_Vec3 (RealLast(), RealLast(), RealLast());
|
|
aNearestPnt.x() = Max (Min (myNearPickedPnt.x(), aMaxPnt.x()), aMinPnt.x());
|
|
aNearestPnt.y() = Max (Min (myNearPickedPnt.y(), aMaxPnt.y()), aMinPnt.y());
|
|
aNearestPnt.z() = Max (Min (myNearPickedPnt.z(), aMaxPnt.z()), aMinPnt.z());
|
|
|
|
theDepth = DISTANCE (aNearestPnt, myNearPickedPnt);
|
|
|
|
return Standard_True;
|
|
}
|
|
|
|
// =======================================================================
|
|
// function : Overlaps
|
|
// purpose : Intersection test between defined volume and given point
|
|
// =======================================================================
|
|
const Standard_Boolean SelectMgr_RectangularFrustum::Overlaps (const gp_Pnt& thePnt,
|
|
Standard_Real& theDepth)
|
|
{
|
|
if (!hasOverlap (thePnt))
|
|
return Standard_False;
|
|
|
|
SelectMgr_Vec3 aPnt (thePnt.X(), thePnt.Y(), thePnt.Z());
|
|
SelectMgr_Vec3 aV = aPnt - myNearPickedPnt;
|
|
SelectMgr_Vec3 aDetectedPnt = myNearPickedPnt + myViewRayDir * (DOT (aV, myViewRayDir) / DOT (myViewRayDir, myViewRayDir));
|
|
|
|
theDepth = DISTANCE (aDetectedPnt, myNearPickedPnt);
|
|
|
|
return Standard_True;
|
|
}
|
|
|
|
// =======================================================================
|
|
// function : Overlaps
|
|
// purpose : Checks if line segment overlaps selecting frustum
|
|
// =======================================================================
|
|
const Standard_Boolean SelectMgr_RectangularFrustum::Overlaps (const gp_Pnt& thePnt1,
|
|
const gp_Pnt& thePnt2,
|
|
Standard_Real& theDepth)
|
|
{
|
|
theDepth = -DBL_MAX;
|
|
if (!hasOverlap (thePnt1, thePnt2))
|
|
return Standard_False;
|
|
|
|
segmentSegmentDistance (thePnt1, thePnt2, theDepth);
|
|
return Standard_True;
|
|
}
|
|
|
|
// =======================================================================
|
|
// function : Overlaps
|
|
// purpose : SAT intersection test between defined volume and given
|
|
// ordered set of points, representing line segments. The test
|
|
// may be considered of interior part or boundary line defined
|
|
// by segments depending on given sensitivity type
|
|
// =======================================================================
|
|
const Standard_Boolean SelectMgr_RectangularFrustum::Overlaps (const Handle(TColgp_HArray1OfPnt)& theArrayOfPnts,
|
|
Select3D_TypeOfSensitivity theSensType,
|
|
Standard_Real& theDepth)
|
|
{
|
|
if (theSensType == Select3D_TOS_BOUNDARY)
|
|
{
|
|
Standard_Integer aMatchingSegmentsNb = -1;
|
|
theDepth = DBL_MAX;
|
|
Standard_Integer aLower = theArrayOfPnts->Lower();
|
|
Standard_Integer anUpper = theArrayOfPnts->Upper();
|
|
|
|
for (Standard_Integer aPntIter = aLower; aPntIter <= anUpper; ++aPntIter)
|
|
{
|
|
const gp_Pnt& aStartPnt = theArrayOfPnts->Value (aPntIter);
|
|
const gp_Pnt& aEndPnt = aPntIter == anUpper ? theArrayOfPnts->Value (aLower)
|
|
: theArrayOfPnts->Value (aPntIter + 1);
|
|
|
|
if (hasOverlap (aStartPnt, aEndPnt))
|
|
{
|
|
aMatchingSegmentsNb++;
|
|
Standard_Real aSegmentDepth = RealLast();
|
|
segmentSegmentDistance (aStartPnt, aEndPnt, aSegmentDepth);
|
|
theDepth = Min (theDepth, aSegmentDepth);
|
|
}
|
|
}
|
|
|
|
if (aMatchingSegmentsNb == -1)
|
|
return Standard_False;
|
|
}
|
|
else if (theSensType == Select3D_TOS_INTERIOR)
|
|
{
|
|
SelectMgr_Vec3 aPolyNorm (RealLast());
|
|
if (!hasOverlap (theArrayOfPnts, aPolyNorm))
|
|
return Standard_False;
|
|
|
|
segmentPlaneIntersection (aPolyNorm,
|
|
theArrayOfPnts->Value (theArrayOfPnts->Lower()),
|
|
theDepth);
|
|
}
|
|
|
|
return Standard_True;
|
|
}
|
|
|
|
// =======================================================================
|
|
// function : Overlaps
|
|
// purpose : SAT intersection test between defined volume and given
|
|
// triangle. The test may be considered of interior part or
|
|
// boundary line defined by triangle vertices depending on
|
|
// given sensitivity type
|
|
// =======================================================================
|
|
const Standard_Boolean SelectMgr_RectangularFrustum::Overlaps (const gp_Pnt& thePnt1,
|
|
const gp_Pnt& thePnt2,
|
|
const gp_Pnt& thePnt3,
|
|
Select3D_TypeOfSensitivity theSensType,
|
|
Standard_Real& theDepth)
|
|
{
|
|
if (theSensType == Select3D_TOS_BOUNDARY)
|
|
{
|
|
Handle(TColgp_HArray1OfPnt) aPntsArray = new TColgp_HArray1OfPnt(1, 4);
|
|
aPntsArray->SetValue (1, thePnt1);
|
|
aPntsArray->SetValue (2, thePnt2);
|
|
aPntsArray->SetValue (3, thePnt3);
|
|
aPntsArray->SetValue (4, thePnt1);
|
|
return Overlaps (aPntsArray, Select3D_TOS_BOUNDARY, theDepth);
|
|
}
|
|
else if (theSensType == Select3D_TOS_INTERIOR)
|
|
{
|
|
SelectMgr_Vec3 aTriangleNormal (RealLast());
|
|
if (!hasOverlap (thePnt1, thePnt2, thePnt3, aTriangleNormal))
|
|
return Standard_False;
|
|
|
|
// check if intersection point belongs to triangle's interior part
|
|
SelectMgr_Vec3 aPnt1 (thePnt1.X(), thePnt1.Y(), thePnt1.Z());
|
|
SelectMgr_Vec3 aTrEdges[3] = { SelectMgr_Vec3 (thePnt2.X() - thePnt1.X(), thePnt2.Y() - thePnt1.Y(), thePnt2.Z() - thePnt1.Z()),
|
|
SelectMgr_Vec3 (thePnt3.X() - thePnt2.X(), thePnt3.Y() - thePnt2.Y(), thePnt3.Z() - thePnt2.Z()),
|
|
SelectMgr_Vec3 (thePnt1.X() - thePnt3.X(), thePnt1.Y() - thePnt3.Y(), thePnt1.Z() - thePnt3.Z()) };
|
|
SelectMgr_Vec3 anEdge = (aPnt1 - myNearPickedPnt) * (1.0 / DOT (aTriangleNormal, myViewRayDir));
|
|
|
|
Standard_Real aTime = DOT (aTriangleNormal, anEdge);
|
|
|
|
SelectMgr_Vec3 aVec = SelectMgr_Vec3 (myViewRayDir.y() * anEdge.z() - myViewRayDir.z() * anEdge.y(),
|
|
myViewRayDir.z() * anEdge.x() - myViewRayDir.x() * anEdge.z(),
|
|
myViewRayDir.x() * anEdge.y() - myViewRayDir.y() * anEdge.x());
|
|
|
|
Standard_Real anU = DOT (aVec, aTrEdges[2]);
|
|
Standard_Real aV = DOT (aVec, aTrEdges[0]);
|
|
|
|
Standard_Boolean isInterior = (aTime >= 0.0) && (anU >= 0.0) && (aV >= 0.0) && (anU + aV <= 1.0);
|
|
|
|
if (isInterior)
|
|
{
|
|
SelectMgr_Vec3 aDetectedPnt = myNearPickedPnt + myViewRayDir * aTime;
|
|
theDepth = DISTANCE (myNearPickedPnt, aDetectedPnt);
|
|
return Standard_True;
|
|
}
|
|
|
|
gp_Pnt aPnts[3] = {thePnt1, thePnt2, thePnt3};
|
|
Standard_Real aMinDist = RealLast();
|
|
Standard_Integer aNearestEdgeIdx = -1;
|
|
SelectMgr_Vec3 aPtOnPlane = myNearPickedPnt + myViewRayDir * aTime;
|
|
for (Standard_Integer anEdgeIdx = 0; anEdgeIdx < 3; ++anEdgeIdx)
|
|
{
|
|
SelectMgr_Vec3 aW = SelectMgr_Vec3 (aPtOnPlane.x() - aPnts[anEdgeIdx].X(),
|
|
aPtOnPlane.y() - aPnts[anEdgeIdx].Y(),
|
|
aPtOnPlane.z() - aPnts[anEdgeIdx].Z());
|
|
Standard_Real aCoef = DOT (aTrEdges[anEdgeIdx], aW) / DOT (aTrEdges[anEdgeIdx], aTrEdges[anEdgeIdx]);
|
|
Standard_Real aDist = DISTANCE (aPtOnPlane, SelectMgr_Vec3 (aPnts[anEdgeIdx].X() + aCoef * aTrEdges[anEdgeIdx].x(),
|
|
aPnts[anEdgeIdx].Y() + aCoef * aTrEdges[anEdgeIdx].y(),
|
|
aPnts[anEdgeIdx].Z() + aCoef * aTrEdges[anEdgeIdx].z()));
|
|
if (aMinDist > aDist)
|
|
{
|
|
aMinDist = aDist;
|
|
aNearestEdgeIdx = anEdgeIdx;
|
|
}
|
|
}
|
|
segmentSegmentDistance (aPnts[aNearestEdgeIdx], aPnts[(aNearestEdgeIdx + 1) % 3], theDepth);
|
|
}
|
|
|
|
return Standard_True;
|
|
}
|
|
|
|
// =======================================================================
|
|
// function : DistToGeometryCenter
|
|
// purpose : Measures distance between 3d projection of user-picked
|
|
// screen point and given point theCOG
|
|
// =======================================================================
|
|
const Standard_Real SelectMgr_RectangularFrustum::DistToGeometryCenter (const gp_Pnt& theCOG)
|
|
{
|
|
const SelectMgr_Vec3& aCOG = SelectMgr_Vec3 (theCOG.X(), theCOG.Y(), theCOG.Z());
|
|
return DISTANCE (aCOG, myNearPickedPnt);
|
|
}
|
|
|
|
// =======================================================================
|
|
// function : DetectedPoint
|
|
// purpose : Calculates the point on a view ray that was detected during
|
|
// the run of selection algo by given depth
|
|
// =======================================================================
|
|
SelectMgr_Vec3 SelectMgr_RectangularFrustum::DetectedPoint (const Standard_Real theDepth) const
|
|
{
|
|
return myNearPickedPnt + myViewRayDir * theDepth;
|
|
}
|
|
|
|
// =======================================================================
|
|
// function : IsClipped
|
|
// purpose : Checks if the point of sensitive in which selection was
|
|
// detected belongs to the region defined by clipping planes
|
|
// =======================================================================
|
|
const Standard_Boolean SelectMgr_RectangularFrustum::IsClipped (const Graphic3d_SequenceOfHClipPlane& thePlanes,
|
|
const Standard_Real theDepth)
|
|
{
|
|
Graphic3d_SequenceOfHClipPlane::Iterator aPlaneIt (thePlanes);
|
|
Standard_Real aMaxDepth = DBL_MAX;
|
|
Standard_Real aMinDepth = -DBL_MAX;
|
|
Standard_Real aPlaneA, aPlaneB, aPlaneC, aPlaneD;
|
|
for ( ; aPlaneIt.More(); aPlaneIt.Next())
|
|
{
|
|
const Handle(Graphic3d_ClipPlane)& aClipPlane = aPlaneIt.Value();
|
|
if (!aClipPlane->IsOn())
|
|
continue;
|
|
|
|
gp_Pln aGeomPlane = aClipPlane->ToPlane();
|
|
|
|
aGeomPlane.Coefficients (aPlaneA, aPlaneB, aPlaneC, aPlaneD);
|
|
|
|
const gp_XYZ& aPlaneDirXYZ = aGeomPlane.Axis().Direction().XYZ();
|
|
|
|
Standard_Real aDotProduct = DOTp (myViewRayDir, aPlaneDirXYZ);
|
|
Standard_Real aDistance = - (DOTp (myNearPickedPnt, aPlaneDirXYZ) + aPlaneD);
|
|
|
|
// check whether the pick line is parallel to clip plane
|
|
if (Abs (aDotProduct) < Precision::Angular())
|
|
{
|
|
// line lies below the plane and is not clipped, skip
|
|
continue;
|
|
}
|
|
|
|
// compute distance to point of pick line intersection with the plane
|
|
Standard_Real aParam = aDistance / aDotProduct;
|
|
|
|
// check if ray intersects the plane, in case aIntDist < 0
|
|
// the plane is "behind" the ray
|
|
if (aParam < 0.0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
const SelectMgr_Vec3 anIntersectionPt = myNearPickedPnt + myViewRayDir * aParam;
|
|
const Standard_Real aDistToPln = DISTANCE (anIntersectionPt, myNearPickedPnt);
|
|
|
|
// change depth limits for case of opposite and directed planes
|
|
if (aDotProduct < 0.0)
|
|
{
|
|
aMaxDepth = Min (aDistToPln, aMaxDepth);
|
|
}
|
|
else if (aDistToPln > aMinDepth)
|
|
{
|
|
aMinDepth = Max (aDistToPln, aMinDepth);
|
|
}
|
|
}
|
|
|
|
return (theDepth <= aMinDepth || theDepth >= aMaxDepth);
|
|
}
|