mirror of
https://git.dev.opencascade.org/repos/occt.git
synced 2025-08-09 13:22:24 +03:00
The new transform persistence mode, with flag `Graphic3d_TMF_OrthoPers`, can be combined (bitwise OR operation) with the other persistence modes (2D, Trihedron or Zoom/Rotate Persistence) to make objects be rendered with orthographic projection when it is on a view with perspective projection. If the view already uses orthographic projection, there will be no difference. This feature was implemented to fix ViewCube being distorted when view with perspective projection changes size.
1122 lines
49 KiB
C++
1122 lines
49 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 <SelectMgr_RectangularFrustum.hxx>
|
|
|
|
#include <BVH_Tools.hxx>
|
|
#include <gp_Pln.hxx>
|
|
#include <NCollection_Vector.hxx>
|
|
#include <Poly_Array1OfTriangle.hxx>
|
|
#include <SelectMgr_FrustumBuilder.hxx>
|
|
#include <SelectMgr_ViewClipRange.hxx>
|
|
|
|
// =======================================================================
|
|
// function : Constructor
|
|
// purpose :
|
|
// =======================================================================
|
|
SelectMgr_RectangularFrustum::SelectMgr_RectangularFrustum()
|
|
: myScale(1.0)
|
|
{
|
|
}
|
|
|
|
// =======================================================================
|
|
// function : segmentSegmentDistance
|
|
// purpose :
|
|
// =======================================================================
|
|
void SelectMgr_RectangularFrustum::segmentSegmentDistance (const gp_Pnt& theSegPnt1,
|
|
const gp_Pnt& theSegPnt2,
|
|
SelectBasics_PickResult& thePickResult) const
|
|
{
|
|
gp_XYZ anU = theSegPnt2.XYZ() - theSegPnt1.XYZ();
|
|
gp_XYZ aV = myFarPickedPnt.XYZ() - myNearPickedPnt.XYZ(); // use unnormalized vector instead of myViewRayDir to clip solutions behind Far plane
|
|
gp_XYZ aW = theSegPnt1.XYZ() - myNearPickedPnt.XYZ();
|
|
|
|
Standard_Real anA = anU.Dot (anU);
|
|
Standard_Real aB = anU.Dot (aV);
|
|
Standard_Real aC = aV.Dot (aV);
|
|
Standard_Real aD = anU.Dot (aW);
|
|
Standard_Real anE = aV.Dot (aW);
|
|
Standard_Real aCoef = anA * aC - aB * aB;
|
|
Standard_Real aSn = aCoef;
|
|
Standard_Real aTc, aTn, aTd = aCoef;
|
|
|
|
if (aCoef < gp::Resolution())
|
|
{
|
|
aTn = anE;
|
|
aTd = aC;
|
|
}
|
|
else
|
|
{
|
|
aSn = (aB * anE - aC * aD);
|
|
aTn = (anA * anE - aB * aD);
|
|
if (aSn < 0.0)
|
|
{
|
|
aTn = anE;
|
|
aTd = aC;
|
|
}
|
|
else if (aSn > aCoef)
|
|
{
|
|
aTn = anE + aB;
|
|
aTd = aC;
|
|
}
|
|
}
|
|
|
|
if (aTn < 0.0)
|
|
{
|
|
aTn = 0.0;
|
|
}
|
|
else if (aTn > aTd)
|
|
{
|
|
aTn = aTd;
|
|
}
|
|
aTc = (Abs (aTd) < gp::Resolution() ? 0.0 : aTn / aTd);
|
|
|
|
const gp_Pnt aClosestPnt = myNearPickedPnt.XYZ() + aV * aTc;
|
|
thePickResult.SetDepth (myNearPickedPnt.Distance (aClosestPnt) * myScale);
|
|
|
|
const gp_Vec aPickedVec = aClosestPnt.XYZ() - theSegPnt1.XYZ();
|
|
const gp_Vec aFigureVec = theSegPnt2.XYZ() - theSegPnt1.XYZ();
|
|
const Standard_Real aPickedVecMod = aPickedVec.Magnitude();
|
|
const Standard_Real aFigureVecMod = aFigureVec.Magnitude();
|
|
if (aPickedVecMod <= gp::Resolution()
|
|
|| aFigureVecMod <= gp::Resolution())
|
|
{
|
|
thePickResult.SetPickedPoint (aClosestPnt);
|
|
return;
|
|
}
|
|
|
|
const Standard_Real aCosOfAngle = aFigureVec.Dot (aPickedVec) / (aPickedVecMod * aFigureVecMod);
|
|
const Standard_Real aSegPntShift = Min(aFigureVecMod, Max(0.0, aCosOfAngle * aPickedVecMod));
|
|
thePickResult.SetPickedPoint (theSegPnt1.XYZ() + aFigureVec.XYZ() * (aSegPntShift / aFigureVecMod));
|
|
}
|
|
|
|
// =======================================================================
|
|
// function : segmentPlaneIntersection
|
|
// purpose :
|
|
// =======================================================================
|
|
bool SelectMgr_RectangularFrustum::segmentPlaneIntersection (const gp_Vec& thePlane,
|
|
const gp_Pnt& thePntOnPlane,
|
|
SelectBasics_PickResult& thePickResult) const
|
|
{
|
|
gp_XYZ anU = myFarPickedPnt.XYZ() - myNearPickedPnt.XYZ(); // use unnormalized vector instead of myViewRayDir to clip solutions behind Far plane by > 1.0 check
|
|
gp_XYZ aW = myNearPickedPnt.XYZ() - thePntOnPlane.XYZ();
|
|
Standard_Real aD = thePlane.Dot (anU);
|
|
Standard_Real aN = -thePlane.Dot (aW);
|
|
|
|
if (Abs (aD) < Precision::Confusion())
|
|
{
|
|
if (Abs (aN) < Precision::Angular())
|
|
{
|
|
thePickResult.Invalidate();
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
thePickResult.Invalidate();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
Standard_Real aParam = aN / aD;
|
|
if (aParam < 0.0 || aParam > 1.0) // > 1.0 check could be removed for an infinite ray and anU=myViewRayDir
|
|
{
|
|
thePickResult.Invalidate();
|
|
return false;
|
|
}
|
|
|
|
gp_Pnt aClosestPnt = myNearPickedPnt.XYZ() + anU * aParam;
|
|
thePickResult.SetDepth (myNearPickedPnt.Distance (aClosestPnt) * myScale);
|
|
return true;
|
|
}
|
|
|
|
namespace
|
|
{
|
|
// =======================================================================
|
|
// function : computeFrustum
|
|
// purpose : Computes base frustum data: its vertices and edge directions
|
|
// =======================================================================
|
|
void computeFrustum (const gp_Pnt2d theMinPnt, const gp_Pnt2d& theMaxPnt,
|
|
const Handle(SelectMgr_FrustumBuilder)& theBuilder,
|
|
gp_Pnt* theVertices, gp_Vec* theEdges)
|
|
{
|
|
// LeftTopNear
|
|
theVertices[0] = theBuilder->ProjectPntOnViewPlane (theMinPnt.X(),
|
|
theMaxPnt.Y(),
|
|
0.0);
|
|
// LeftTopFar
|
|
theVertices[1] = theBuilder->ProjectPntOnViewPlane (theMinPnt.X(),
|
|
theMaxPnt.Y(),
|
|
1.0);
|
|
// LeftBottomNear
|
|
theVertices[2] = theBuilder->ProjectPntOnViewPlane (theMinPnt.X(),
|
|
theMinPnt.Y(),
|
|
0.0);
|
|
// LeftBottomFar
|
|
theVertices[3] = theBuilder->ProjectPntOnViewPlane (theMinPnt.X(),
|
|
theMinPnt.Y(),
|
|
1.0);
|
|
// RightTopNear
|
|
theVertices[4] = theBuilder->ProjectPntOnViewPlane (theMaxPnt.X(),
|
|
theMaxPnt.Y(),
|
|
0.0);
|
|
// RightTopFar
|
|
theVertices[5] = theBuilder->ProjectPntOnViewPlane (theMaxPnt.X(),
|
|
theMaxPnt.Y(),
|
|
1.0);
|
|
// RightBottomNear
|
|
theVertices[6] = theBuilder->ProjectPntOnViewPlane (theMaxPnt.X(),
|
|
theMinPnt.Y(),
|
|
0.0);
|
|
// RightBottomFar
|
|
theVertices[7] = theBuilder->ProjectPntOnViewPlane (theMaxPnt.X(),
|
|
theMinPnt.Y(),
|
|
1.0);
|
|
|
|
// Horizontal
|
|
theEdges[0] = theVertices[4].XYZ() - theVertices[0].XYZ();
|
|
// Vertical
|
|
theEdges[1] = theVertices[2].XYZ() - theVertices[0].XYZ();
|
|
// LeftLower
|
|
theEdges[2] = theVertices[2].XYZ() - theVertices[3].XYZ();
|
|
// RightLower
|
|
theEdges[3] = theVertices[6].XYZ() - theVertices[7].XYZ();
|
|
// LeftUpper
|
|
theEdges[4] = theVertices[0].XYZ() - theVertices[1].XYZ();
|
|
// RightUpper
|
|
theEdges[5] = theVertices[4].XYZ() - theVertices[5].XYZ();
|
|
}
|
|
|
|
// =======================================================================
|
|
// function : computeNormals
|
|
// purpose : Computes normals to frustum faces
|
|
// =======================================================================
|
|
void computeNormals (const gp_Vec* theEdges, gp_Vec* theNormals)
|
|
{
|
|
// Top
|
|
theNormals[0] = theEdges[0].Crossed (theEdges[4]);
|
|
// Bottom
|
|
theNormals[1] = theEdges[2].Crossed (theEdges[0]);
|
|
// Left
|
|
theNormals[2] = theEdges[4].Crossed (theEdges[1]);
|
|
// Right
|
|
theNormals[3] = theEdges[1].Crossed (theEdges[5]);
|
|
// Near
|
|
theNormals[4] = theEdges[0].Crossed (theEdges[1]);
|
|
// Far
|
|
theNormals[5] = -theNormals[4];
|
|
}
|
|
}
|
|
|
|
// =======================================================================
|
|
// function : cacheVertexProjections
|
|
// purpose : Caches projection of frustum's vertices onto its plane directions
|
|
// and {i, j, k}
|
|
// =======================================================================
|
|
void SelectMgr_RectangularFrustum::cacheVertexProjections (SelectMgr_RectangularFrustum* theFrustum) const
|
|
{
|
|
if (theFrustum->Camera()->IsOrthographic())
|
|
{
|
|
// project vertices onto frustum normals
|
|
// Since orthographic view volume's faces are always a pairwise translation of
|
|
// one another, only 2 vertices that belong to opposite faces can be projected
|
|
// to simplify calculations.
|
|
Standard_Integer aVertIdxs[6] = { LeftTopNear, LeftBottomNear, // opposite planes in height direction
|
|
LeftBottomNear, RightBottomNear, // opposite planes in width direcion
|
|
LeftBottomFar, RightBottomNear }; // opposite planes in depth direction
|
|
for (Standard_Integer aPlaneIdx = 0; aPlaneIdx < 5; aPlaneIdx += 2)
|
|
{
|
|
Standard_Real aProj1 = theFrustum->myPlanes[aPlaneIdx].XYZ().Dot (theFrustum->myVertices[aVertIdxs[aPlaneIdx]].XYZ());
|
|
Standard_Real aProj2 = theFrustum->myPlanes[aPlaneIdx].XYZ().Dot (theFrustum->myVertices[aVertIdxs[aPlaneIdx + 1]].XYZ());
|
|
theFrustum->myMinVertsProjections[aPlaneIdx] = Min (aProj1, aProj2);
|
|
theFrustum->myMaxVertsProjections[aPlaneIdx] = Max (aProj1, aProj2);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// project all vertices onto frustum normals
|
|
for (Standard_Integer aPlaneIdx = 0; aPlaneIdx < 6; ++aPlaneIdx)
|
|
{
|
|
Standard_Real aMax = -DBL_MAX;
|
|
Standard_Real aMin = DBL_MAX;
|
|
const gp_XYZ& aPlane = theFrustum->myPlanes[aPlaneIdx].XYZ();
|
|
for (Standard_Integer aVertIdx = 0; aVertIdx < 8; ++aVertIdx)
|
|
{
|
|
Standard_Real aProjection = aPlane.Dot (theFrustum->myVertices[aVertIdx].XYZ());
|
|
aMin = Min (aMin, aProjection);
|
|
aMax = Max (aMax, aProjection);
|
|
}
|
|
theFrustum->myMinVertsProjections[aPlaneIdx] = aMin;
|
|
theFrustum->myMaxVertsProjections[aPlaneIdx] = aMax;
|
|
}
|
|
}
|
|
|
|
// project vertices onto {i, j, k}
|
|
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)
|
|
{
|
|
const gp_XYZ& aVert = theFrustum->myVertices[aVertIdx].XYZ();
|
|
aMax = Max (aVert.GetData()[aDim], aMax);
|
|
aMin = Min (aVert.GetData()[aDim], aMin);
|
|
}
|
|
theFrustum->myMaxOrthoVertsProjections[aDim] = aMax;
|
|
theFrustum->myMinOrthoVertsProjections[aDim] = aMin;
|
|
}
|
|
}
|
|
|
|
// =======================================================================
|
|
// function : Init
|
|
// purpose :
|
|
// =======================================================================
|
|
void SelectMgr_RectangularFrustum::Init (const gp_Pnt2d &thePoint)
|
|
{
|
|
mySelectionType = SelectMgr_SelectionType_Point;
|
|
mySelRectangle.SetMousePos (thePoint);
|
|
}
|
|
|
|
// =======================================================================
|
|
// function : Init
|
|
// purpose :
|
|
// =======================================================================
|
|
void SelectMgr_RectangularFrustum::Init (const gp_Pnt2d& theMinPnt,
|
|
const gp_Pnt2d& theMaxPnt)
|
|
{
|
|
mySelectionType = SelectMgr_SelectionType_Box;
|
|
mySelRectangle.SetMinPnt (theMinPnt);
|
|
mySelRectangle.SetMaxPnt (theMaxPnt);
|
|
}
|
|
|
|
// =======================================================================
|
|
// function : Build
|
|
// purpose :
|
|
// =======================================================================
|
|
void SelectMgr_RectangularFrustum::Build()
|
|
{
|
|
Standard_ASSERT_RAISE (mySelectionType == SelectMgr_SelectionType_Point || mySelectionType == SelectMgr_SelectionType_Box,
|
|
"Error! SelectMgr_RectangularFrustum::Build() should be called after selection frustum initialization");
|
|
gp_Pnt2d aMinPnt, aMaxPnt;
|
|
if (mySelectionType == SelectMgr_SelectionType_Point)
|
|
{
|
|
const gp_Pnt2d& aMousePos = mySelRectangle.MousePos();
|
|
myNearPickedPnt = myBuilder->ProjectPntOnViewPlane (aMousePos.X(), aMousePos.Y(), 0.0);
|
|
myFarPickedPnt = myBuilder->ProjectPntOnViewPlane (aMousePos.X(), aMousePos.Y(), 1.0);
|
|
|
|
aMinPnt.SetCoord (aMousePos.X() - myPixelTolerance * 0.5,
|
|
aMousePos.Y() - myPixelTolerance * 0.5);
|
|
aMaxPnt.SetCoord (aMousePos.X() + myPixelTolerance * 0.5,
|
|
aMousePos.Y() + myPixelTolerance * 0.5);
|
|
}
|
|
else
|
|
{
|
|
aMinPnt = mySelRectangle.MinPnt();
|
|
aMaxPnt = mySelRectangle.MaxPnt();
|
|
myNearPickedPnt = myBuilder->ProjectPntOnViewPlane ((aMinPnt.X() + aMaxPnt.X()) * 0.5,
|
|
(aMinPnt.Y() + aMaxPnt.Y()) * 0.5,
|
|
0.0);
|
|
myFarPickedPnt = myBuilder->ProjectPntOnViewPlane ((aMinPnt.X() + aMaxPnt.X()) * 0.5,
|
|
(aMinPnt.Y() + aMaxPnt.Y()) * 0.5,
|
|
1.0);
|
|
}
|
|
|
|
myViewRayDir = myFarPickedPnt.XYZ() - myNearPickedPnt.XYZ();
|
|
|
|
// calculate base frustum characteristics: vertices and edge directions
|
|
computeFrustum (aMinPnt, aMaxPnt, myBuilder, myVertices, myEdgeDirs);
|
|
|
|
// compute frustum normals
|
|
computeNormals (myEdgeDirs, myPlanes);
|
|
|
|
// compute vertices projections onto frustum normals and
|
|
// {i, j, k} vectors and store them to corresponding class fields
|
|
cacheVertexProjections (this);
|
|
|
|
myScale = 1.0;
|
|
}
|
|
|
|
// =======================================================================
|
|
// function : ScaleAndTransform
|
|
// purpose : IMPORTANT: Scaling makes sense only for frustum built on a single point!
|
|
// Note that this method does not perform any checks on type of the frustum.
|
|
// Returns a copy of the frustum resized according to the scale factor given
|
|
// and transforms it using the matrix given.
|
|
// There are no default parameters, but in case if:
|
|
// - transformation only is needed: @theScaleFactor must be initialized
|
|
// as any negative value;
|
|
// - scale only is needed: @theTrsf must be set to gp_Identity.
|
|
// =======================================================================
|
|
Handle(SelectMgr_BaseIntersector) SelectMgr_RectangularFrustum::ScaleAndTransform (const Standard_Integer theScaleFactor,
|
|
const gp_GTrsf& theTrsf,
|
|
const Handle(SelectMgr_FrustumBuilder)& theBuilder) const
|
|
{
|
|
Standard_ASSERT_RAISE (mySelectionType == SelectMgr_SelectionType_Point || mySelectionType == SelectMgr_SelectionType_Box,
|
|
"Error! SelectMgr_RectangularFrustum::ScaleAndTransform() should be called after selection frustum initialization");
|
|
|
|
Standard_ASSERT_RAISE (theScaleFactor >= 0,
|
|
"Error! Pixel tolerance for selection should not be negative");
|
|
|
|
Handle(SelectMgr_RectangularFrustum) aRes = new SelectMgr_RectangularFrustum();
|
|
const Standard_Boolean isToScale = theScaleFactor != 1;
|
|
const Standard_Boolean isToTrsf = theTrsf.Form() != gp_Identity;
|
|
|
|
if (!isToScale && !isToTrsf)
|
|
{
|
|
aRes->SetBuilder (theBuilder);
|
|
return aRes;
|
|
}
|
|
|
|
aRes->SetCamera (myCamera);
|
|
const SelectMgr_RectangularFrustum* aRef = this;
|
|
|
|
if (isToScale)
|
|
{
|
|
aRes->myNearPickedPnt = myNearPickedPnt;
|
|
aRes->myFarPickedPnt = myFarPickedPnt;
|
|
aRes->myViewRayDir = myViewRayDir;
|
|
|
|
const gp_Pnt2d& aMousePos = mySelRectangle.MousePos();
|
|
const gp_Pnt2d aMinPnt (aMousePos.X() - theScaleFactor * 0.5,
|
|
aMousePos.Y() - theScaleFactor * 0.5);
|
|
const gp_Pnt2d aMaxPnt (aMousePos.X() + theScaleFactor * 0.5,
|
|
aMousePos.Y() + theScaleFactor * 0.5);
|
|
|
|
// recompute base frustum characteristics from scratch
|
|
computeFrustum (aMinPnt, aMaxPnt, myBuilder, aRes->myVertices, aRes->myEdgeDirs);
|
|
|
|
aRef = aRes.get();
|
|
}
|
|
|
|
if (isToTrsf)
|
|
{
|
|
const Standard_Real aRefScale = aRef->myFarPickedPnt.SquareDistance (aRef->myNearPickedPnt);
|
|
|
|
gp_Pnt aPoint = aRef->myNearPickedPnt;
|
|
theTrsf.Transforms (aPoint.ChangeCoord());
|
|
aRes->myNearPickedPnt = aPoint;
|
|
|
|
aPoint.SetXYZ (aRef->myFarPickedPnt.XYZ());
|
|
theTrsf.Transforms (aPoint.ChangeCoord());
|
|
aRes->myFarPickedPnt = aPoint;
|
|
|
|
aRes->myViewRayDir = aRes->myFarPickedPnt.XYZ() - aRes->myNearPickedPnt.XYZ();
|
|
|
|
for (Standard_Integer anIt = 0; anIt < 8; anIt++)
|
|
{
|
|
aPoint = aRef->myVertices[anIt];
|
|
theTrsf.Transforms (aPoint.ChangeCoord());
|
|
aRes->myVertices[anIt] = aPoint;
|
|
}
|
|
|
|
// Horizontal
|
|
aRes->myEdgeDirs[0] = aRes->myVertices[4].XYZ() - aRes->myVertices[0].XYZ();
|
|
// Vertical
|
|
aRes->myEdgeDirs[1] = aRes->myVertices[2].XYZ() - aRes->myVertices[0].XYZ();
|
|
// LeftLower
|
|
aRes->myEdgeDirs[2] = aRes->myVertices[2].XYZ() - aRes->myVertices[3].XYZ();
|
|
// RightLower
|
|
aRes->myEdgeDirs[3] = aRes->myVertices[6].XYZ() - aRes->myVertices[7].XYZ();
|
|
// LeftUpper
|
|
aRes->myEdgeDirs[4] = aRes->myVertices[0].XYZ() - aRes->myVertices[1].XYZ();
|
|
// RightUpper
|
|
aRes->myEdgeDirs[5] = aRes->myVertices[4].XYZ() - aRes->myVertices[5].XYZ();
|
|
|
|
// Compute scale to transform depth from local coordinate system to world coordinate system
|
|
aRes->myScale = Sqrt (aRefScale / aRes->myFarPickedPnt.SquareDistance (aRes->myNearPickedPnt));
|
|
}
|
|
|
|
aRes->SetBuilder (theBuilder);
|
|
|
|
// compute frustum normals
|
|
computeNormals (aRes->myEdgeDirs, aRes->myPlanes);
|
|
|
|
cacheVertexProjections (aRes.get());
|
|
|
|
aRes->mySelectionType = mySelectionType;
|
|
aRes->mySelRectangle = mySelRectangle;
|
|
return aRes;
|
|
}
|
|
|
|
// =======================================================================
|
|
// function : CopyWithBuilder
|
|
// purpose : Returns a copy of the frustum using the given frustum builder configuration.
|
|
// Returned frustum should be re-constructed before being used.
|
|
// =======================================================================
|
|
Handle(SelectMgr_BaseIntersector) SelectMgr_RectangularFrustum::CopyWithBuilder (const Handle(SelectMgr_FrustumBuilder)& theBuilder) const
|
|
{
|
|
Standard_ASSERT_RAISE (mySelectionType == SelectMgr_SelectionType_Point || mySelectionType == SelectMgr_SelectionType_Box,
|
|
"Error! SelectMgr_RectangularFrustum::CopyWithBuilder() should be called after selection frustum initialization");
|
|
|
|
Standard_ASSERT_RAISE (!theBuilder.IsNull(),
|
|
"Error! SelectMgr_RectangularFrustum::CopyWithBuilder() should be called with valid builder");
|
|
|
|
Handle(SelectMgr_RectangularFrustum) aRes = new SelectMgr_RectangularFrustum();
|
|
aRes->mySelectionType = mySelectionType;
|
|
aRes->mySelRectangle = mySelRectangle;
|
|
aRes->myPixelTolerance = myPixelTolerance;
|
|
aRes->SetBuilder (theBuilder);
|
|
|
|
return aRes;
|
|
}
|
|
|
|
// =======================================================================
|
|
// function : IsScalable
|
|
// purpose :
|
|
// =======================================================================
|
|
Standard_Boolean SelectMgr_RectangularFrustum::IsScalable() const
|
|
{
|
|
return mySelectionType == SelectMgr_SelectionType_Point;
|
|
}
|
|
|
|
// =======================================================================
|
|
// function : OverlapsBox
|
|
// purpose : Returns true if selecting volume is overlapped by
|
|
// axis-aligned bounding box with minimum corner at point
|
|
// theMinPnt and maximum at point theMaxPnt
|
|
// =======================================================================
|
|
Standard_Boolean SelectMgr_RectangularFrustum::OverlapsBox (const SelectMgr_Vec3& theBoxMin,
|
|
const SelectMgr_Vec3& theBoxMax,
|
|
Standard_Boolean* theInside) const
|
|
{
|
|
Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point || mySelectionType == SelectMgr_SelectionType_Box,
|
|
"Error! SelectMgr_RectangularFrustum::Overlaps() should be called after selection frustum initialization");
|
|
|
|
return hasBoxOverlap (theBoxMin, theBoxMax, theInside);
|
|
}
|
|
|
|
// =======================================================================
|
|
// function : OverlapsBox
|
|
// purpose : SAT intersection test between defined volume and
|
|
// given axis-aligned box
|
|
// =======================================================================
|
|
Standard_Boolean SelectMgr_RectangularFrustum::OverlapsBox (const SelectMgr_Vec3& theBoxMin,
|
|
const SelectMgr_Vec3& theBoxMax,
|
|
const SelectMgr_ViewClipRange& theClipRange,
|
|
SelectBasics_PickResult& thePickResult) const
|
|
{
|
|
Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point || mySelectionType == SelectMgr_SelectionType_Box,
|
|
"Error! SelectMgr_RectangularFrustum::Overlaps() should be called after selection frustum initialization");
|
|
|
|
if (!hasBoxOverlap (theBoxMin, theBoxMax))
|
|
return Standard_False;
|
|
|
|
Standard_Real aDepth = 0.0;
|
|
BVH_Ray<Standard_Real, 3> aRay(SelectMgr_Vec3(myNearPickedPnt.X(), myNearPickedPnt.Y(), myNearPickedPnt.Z()),
|
|
SelectMgr_Vec3(myViewRayDir.X(), myViewRayDir.Y(), myViewRayDir.Z()));
|
|
Standard_Real aTimeEnter, aTimeLeave;
|
|
if (!BVH_Tools<Standard_Real, 3>::RayBoxIntersection (aRay, theBoxMin, theBoxMax, aTimeEnter, aTimeLeave))
|
|
{
|
|
gp_Pnt aNearestPnt (RealLast(), RealLast(), RealLast());
|
|
aNearestPnt.SetX (Max (Min (myNearPickedPnt.X(), theBoxMax.x()), theBoxMin.x()));
|
|
aNearestPnt.SetY (Max (Min (myNearPickedPnt.Y(), theBoxMax.y()), theBoxMin.y()));
|
|
aNearestPnt.SetZ (Max (Min (myNearPickedPnt.Z(), theBoxMax.z()), theBoxMin.z()));
|
|
|
|
aDepth = aNearestPnt.Distance (myNearPickedPnt);
|
|
thePickResult.SetDepth (aDepth);
|
|
return !theClipRange.IsClipped (thePickResult.Depth());
|
|
}
|
|
|
|
Bnd_Range aRange(Max (aTimeEnter, 0.0), aTimeLeave);
|
|
aRange.GetMin (aDepth);
|
|
|
|
if (!theClipRange.GetNearestDepth (aRange, aDepth))
|
|
{
|
|
return Standard_False;
|
|
}
|
|
|
|
thePickResult.SetDepth (aDepth);
|
|
|
|
return Standard_True;
|
|
}
|
|
|
|
// =======================================================================
|
|
// function : OverlapsPoint
|
|
// purpose : Intersection test between defined volume and given point
|
|
// =======================================================================
|
|
Standard_Boolean SelectMgr_RectangularFrustum::OverlapsPoint (const gp_Pnt& thePnt,
|
|
const SelectMgr_ViewClipRange& theClipRange,
|
|
SelectBasics_PickResult& thePickResult) const
|
|
{
|
|
Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point || mySelectionType == SelectMgr_SelectionType_Box,
|
|
"Error! SelectMgr_RectangularFrustum::Overlaps() should be called after selection frustum initialization");
|
|
|
|
if (!hasPointOverlap (thePnt))
|
|
return Standard_False;
|
|
|
|
gp_XYZ aV = thePnt.XYZ() - myNearPickedPnt.XYZ();
|
|
const Standard_Real aDepth = aV.Dot (myViewRayDir.XYZ());
|
|
|
|
thePickResult.SetDepth (Abs (aDepth) * myScale);
|
|
thePickResult.SetPickedPoint (thePnt);
|
|
|
|
return !theClipRange.IsClipped (thePickResult.Depth());
|
|
}
|
|
|
|
// =======================================================================
|
|
// function : OverlapsPoint
|
|
// purpose : Intersection test between defined volume and given point
|
|
// =======================================================================
|
|
Standard_Boolean SelectMgr_RectangularFrustum::OverlapsPoint (const gp_Pnt& thePnt) const
|
|
{
|
|
Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point || mySelectionType == SelectMgr_SelectionType_Box,
|
|
"Error! SelectMgr_RectangularFrustum::Overlaps() should be called after selection frustum initialization");
|
|
|
|
return hasPointOverlap (thePnt);
|
|
}
|
|
|
|
// =======================================================================
|
|
// function : OverlapsSegment
|
|
// purpose : Checks if line segment overlaps selecting frustum
|
|
// =======================================================================
|
|
Standard_Boolean SelectMgr_RectangularFrustum::OverlapsSegment (const gp_Pnt& thePnt1,
|
|
const gp_Pnt& thePnt2,
|
|
const SelectMgr_ViewClipRange& theClipRange,
|
|
SelectBasics_PickResult& thePickResult) const
|
|
{
|
|
Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point || mySelectionType == SelectMgr_SelectionType_Box,
|
|
"Error! SelectMgr_RectangularFrustum::Overlaps() should be called after selection frustum initialization");
|
|
|
|
if (!hasSegmentOverlap (thePnt1, thePnt2))
|
|
return Standard_False;
|
|
|
|
segmentSegmentDistance (thePnt1, thePnt2, thePickResult);
|
|
|
|
return !theClipRange.IsClipped (thePickResult.Depth());
|
|
}
|
|
|
|
// =======================================================================
|
|
// function : OverlapsPolygon
|
|
// 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
|
|
// =======================================================================
|
|
Standard_Boolean SelectMgr_RectangularFrustum::OverlapsPolygon (const TColgp_Array1OfPnt& theArrayOfPnts,
|
|
Select3D_TypeOfSensitivity theSensType,
|
|
const SelectMgr_ViewClipRange& theClipRange,
|
|
SelectBasics_PickResult& thePickResult) const
|
|
{
|
|
Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point || mySelectionType == SelectMgr_SelectionType_Box,
|
|
"Error! SelectMgr_RectangularFrustum::Overlaps() should be called after selection frustum initialization");
|
|
|
|
if (theSensType == Select3D_TOS_BOUNDARY)
|
|
{
|
|
Standard_Integer aMatchingSegmentsNb = -1;
|
|
SelectBasics_PickResult aPickResult;
|
|
thePickResult.Invalidate();
|
|
const Standard_Integer aLower = theArrayOfPnts.Lower();
|
|
const Standard_Integer anUpper = theArrayOfPnts.Upper();
|
|
for (Standard_Integer aPntIter = aLower; aPntIter <= anUpper; ++aPntIter)
|
|
{
|
|
const gp_Pnt& aStartPnt = theArrayOfPnts.Value (aPntIter);
|
|
const gp_Pnt& aEndPnt = theArrayOfPnts.Value (aPntIter == anUpper ? aLower : (aPntIter + 1));
|
|
if (hasSegmentOverlap (aStartPnt, aEndPnt))
|
|
{
|
|
aMatchingSegmentsNb++;
|
|
segmentSegmentDistance (aStartPnt, aEndPnt, aPickResult);
|
|
thePickResult = SelectBasics_PickResult::Min (thePickResult, aPickResult);
|
|
}
|
|
}
|
|
|
|
if (aMatchingSegmentsNb == -1)
|
|
return Standard_False;
|
|
}
|
|
else if (theSensType == Select3D_TOS_INTERIOR)
|
|
{
|
|
gp_Vec aPolyNorm (gp_XYZ (RealLast(), RealLast(), RealLast()));
|
|
if (!hasPolygonOverlap (theArrayOfPnts, aPolyNorm))
|
|
{
|
|
return Standard_False;
|
|
}
|
|
|
|
if (aPolyNorm.Magnitude() <= Precision::Confusion())
|
|
{
|
|
// treat degenerated polygon as point
|
|
return OverlapsPoint (theArrayOfPnts.First(), theClipRange, thePickResult);
|
|
}
|
|
else if (!segmentPlaneIntersection (aPolyNorm, theArrayOfPnts.First(), thePickResult))
|
|
{
|
|
return Standard_False;
|
|
}
|
|
}
|
|
|
|
return !theClipRange.IsClipped (thePickResult.Depth());
|
|
}
|
|
|
|
// =======================================================================
|
|
// function : OverlapsTriangle
|
|
// 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
|
|
// =======================================================================
|
|
Standard_Boolean SelectMgr_RectangularFrustum::OverlapsTriangle (const gp_Pnt& thePnt1,
|
|
const gp_Pnt& thePnt2,
|
|
const gp_Pnt& thePnt3,
|
|
Select3D_TypeOfSensitivity theSensType,
|
|
const SelectMgr_ViewClipRange& theClipRange,
|
|
SelectBasics_PickResult& thePickResult) const
|
|
{
|
|
Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point || mySelectionType == SelectMgr_SelectionType_Box,
|
|
"Error! SelectMgr_RectangularFrustum::Overlaps() should be called after selection frustum initialization");
|
|
|
|
if (theSensType == Select3D_TOS_BOUNDARY)
|
|
{
|
|
const gp_Pnt aPntsArrayBuf[4] = { thePnt1, thePnt2, thePnt3, thePnt1 };
|
|
const TColgp_Array1OfPnt aPntsArray (aPntsArrayBuf[0], 1, 4);
|
|
return OverlapsPolygon (aPntsArray, Select3D_TOS_BOUNDARY, theClipRange, thePickResult);
|
|
}
|
|
else if (theSensType == Select3D_TOS_INTERIOR)
|
|
{
|
|
gp_Vec aTriangleNormal (gp_XYZ (RealLast(), RealLast(), RealLast()));
|
|
if (!hasTriangleOverlap (thePnt1, thePnt2, thePnt3, aTriangleNormal))
|
|
{
|
|
return Standard_False;
|
|
}
|
|
|
|
const gp_XYZ aTrEdges[3] = { thePnt2.XYZ() - thePnt1.XYZ(),
|
|
thePnt3.XYZ() - thePnt2.XYZ(),
|
|
thePnt1.XYZ() - thePnt3.XYZ() };
|
|
if (aTriangleNormal.SquareMagnitude() < gp::Resolution())
|
|
{
|
|
// consider degenerated triangle as point or segment
|
|
return aTrEdges[0].SquareModulus() > gp::Resolution()
|
|
? OverlapsSegment (thePnt1, thePnt2, theClipRange, thePickResult)
|
|
: (aTrEdges[1].SquareModulus() > gp::Resolution()
|
|
? OverlapsSegment (thePnt2, thePnt3, theClipRange, thePickResult)
|
|
: OverlapsPoint (thePnt1, theClipRange, thePickResult));
|
|
}
|
|
|
|
const gp_Pnt aPnts[3] = {thePnt1, thePnt2, thePnt3};
|
|
const Standard_Real anAlpha = aTriangleNormal.XYZ().Dot (myViewRayDir.XYZ());
|
|
if (Abs (anAlpha) < gp::Resolution())
|
|
{
|
|
// handle the case when triangle normal and selecting frustum direction are orthogonal
|
|
SelectBasics_PickResult aPickResult;
|
|
thePickResult.Invalidate();
|
|
for (Standard_Integer anEdgeIter = 0; anEdgeIter < 3; ++anEdgeIter)
|
|
{
|
|
const gp_Pnt& aStartPnt = aPnts[anEdgeIter];
|
|
const gp_Pnt& anEndPnt = aPnts[anEdgeIter < 2 ? anEdgeIter + 1 : 0];
|
|
segmentSegmentDistance (aStartPnt, anEndPnt, aPickResult);
|
|
thePickResult = SelectBasics_PickResult::Min (thePickResult, aPickResult);
|
|
}
|
|
thePickResult.SetSurfaceNormal (aTriangleNormal);
|
|
return !theClipRange.IsClipped (thePickResult.Depth());
|
|
}
|
|
|
|
// check if intersection point belongs to triangle's interior part
|
|
const gp_XYZ anEdge = (thePnt1.XYZ() - myNearPickedPnt.XYZ()) * (1.0 / anAlpha);
|
|
|
|
const Standard_Real aTime = aTriangleNormal.Dot (anEdge);
|
|
const gp_XYZ aVec = myViewRayDir.XYZ().Crossed (anEdge);
|
|
const Standard_Real anU = aVec.Dot (aTrEdges[2]);
|
|
const Standard_Real aV = aVec.Dot (aTrEdges[0]);
|
|
|
|
const Standard_Boolean isInterior = (aTime >= 0.0) && (anU >= 0.0) && (aV >= 0.0) && (anU + aV <= 1.0);
|
|
const gp_Pnt aPtOnPlane = myNearPickedPnt.XYZ() + myViewRayDir.XYZ() * aTime;
|
|
if (isInterior)
|
|
{
|
|
thePickResult.SetDepth (myNearPickedPnt.Distance (aPtOnPlane) * myScale);
|
|
thePickResult.SetPickedPoint (aPtOnPlane);
|
|
thePickResult.SetSurfaceNormal (aTriangleNormal);
|
|
return !theClipRange.IsClipped (thePickResult.Depth());
|
|
}
|
|
|
|
Standard_Real aMinDist = RealLast();
|
|
Standard_Integer aNearestEdgeIdx1 = -1;
|
|
for (Standard_Integer anEdgeIdx = 0; anEdgeIdx < 3; ++anEdgeIdx)
|
|
{
|
|
gp_XYZ aW = aPtOnPlane.XYZ() - aPnts[anEdgeIdx].XYZ();
|
|
Standard_Real aCoef = aTrEdges[anEdgeIdx].Dot (aW) / aTrEdges[anEdgeIdx].Dot (aTrEdges[anEdgeIdx]);
|
|
Standard_Real aDist = aPtOnPlane.Distance (aPnts[anEdgeIdx].XYZ() + aCoef * aTrEdges[anEdgeIdx]);
|
|
if (aDist < aMinDist)
|
|
{
|
|
aMinDist = aDist;
|
|
aNearestEdgeIdx1 = anEdgeIdx;
|
|
}
|
|
}
|
|
Standard_Integer aNearestEdgeIdx2 = (aNearestEdgeIdx1 + 1) % 3;
|
|
const gp_Vec aVec12 (aPnts[aNearestEdgeIdx1], aPnts[aNearestEdgeIdx2]);
|
|
if (aVec12.SquareMagnitude() > gp::Resolution()
|
|
&& myViewRayDir.IsParallel (aVec12, Precision::Angular()))
|
|
{
|
|
aNearestEdgeIdx2 = aNearestEdgeIdx1 == 0 ? 2 : aNearestEdgeIdx1 - 1;
|
|
}
|
|
segmentSegmentDistance (aPnts[aNearestEdgeIdx1], aPnts[aNearestEdgeIdx2], thePickResult);
|
|
thePickResult.SetSurfaceNormal (aTriangleNormal);
|
|
}
|
|
|
|
return !theClipRange.IsClipped (thePickResult.Depth());
|
|
}
|
|
|
|
//=======================================================================
|
|
// function : OverlapsCylinder
|
|
// purpose :
|
|
//=======================================================================
|
|
Standard_Boolean SelectMgr_RectangularFrustum::OverlapsCylinder (const Standard_Real theBottomRad,
|
|
const Standard_Real theTopRad,
|
|
const Standard_Real theHeight,
|
|
const gp_Trsf& theTrsf,
|
|
const Standard_Boolean theIsHollow,
|
|
const SelectMgr_ViewClipRange& theClipRange,
|
|
SelectBasics_PickResult& thePickResult) const
|
|
{
|
|
Standard_ASSERT_RAISE (mySelectionType == SelectMgr_SelectionType_Point || mySelectionType == SelectMgr_SelectionType_Box,
|
|
"Error! SelectMgr_RectangularFrustum::Overlaps() should be called after selection frustum initialization");
|
|
Standard_Real aTimes[2] = { 0.0, 0.0 };
|
|
const gp_Trsf aTrsfInv = theTrsf.Inverted();
|
|
const gp_Pnt aLoc = myNearPickedPnt.Transformed (aTrsfInv);
|
|
const gp_Dir aRayDir = myViewRayDir .Transformed (aTrsfInv);
|
|
if (!RayCylinderIntersection (theBottomRad, theTopRad, theHeight, aLoc, aRayDir, theIsHollow, aTimes[0], aTimes[1]))
|
|
{
|
|
return Standard_False;
|
|
}
|
|
|
|
Standard_Integer aResTime = 0;
|
|
thePickResult.SetDepth (aTimes[aResTime] * myScale);
|
|
if (theClipRange.IsClipped (thePickResult.Depth()))
|
|
{
|
|
aResTime = 1;
|
|
thePickResult.SetDepth (aTimes[aResTime] * myScale);
|
|
}
|
|
|
|
const gp_Pnt aPntOnCylinder = aLoc.XYZ() + aRayDir.XYZ() * aTimes[aResTime];
|
|
if (Abs (aPntOnCylinder.Z()) < Precision::Confusion())
|
|
{
|
|
thePickResult.SetSurfaceNormal (-gp::DZ().Transformed (theTrsf));
|
|
}
|
|
else if (Abs (aPntOnCylinder.Z() - theHeight) < Precision::Confusion())
|
|
{
|
|
thePickResult.SetSurfaceNormal (gp::DZ().Transformed (theTrsf));
|
|
}
|
|
else
|
|
{
|
|
thePickResult.SetSurfaceNormal (gp_Vec (aPntOnCylinder.X(), aPntOnCylinder.Y(), 0.0).Transformed (theTrsf));
|
|
}
|
|
thePickResult.SetPickedPoint (aPntOnCylinder.Transformed (theTrsf));
|
|
return !theClipRange.IsClipped (thePickResult.Depth());
|
|
}
|
|
|
|
//=======================================================================
|
|
// function : OverlapsCircle
|
|
// purpose :
|
|
//=======================================================================
|
|
Standard_Boolean SelectMgr_RectangularFrustum::OverlapsCircle (const Standard_Real theRadius,
|
|
const gp_Trsf& theTrsf,
|
|
const Standard_Boolean theIsFilled,
|
|
const SelectMgr_ViewClipRange& theClipRange,
|
|
SelectBasics_PickResult& thePickResult) const
|
|
{
|
|
Standard_ASSERT_RAISE (mySelectionType == SelectMgr_SelectionType_Point || mySelectionType == SelectMgr_SelectionType_Box,
|
|
"Error! SelectMgr_RectangularFrustum::Overlaps() should be called after selection frustum initialization");
|
|
Standard_Real aTime = 0.0;
|
|
const gp_Trsf aTrsfInv = theTrsf.Inverted();
|
|
const gp_Pnt aLoc = myNearPickedPnt.Transformed (aTrsfInv);
|
|
const gp_Dir aRayDir = myViewRayDir.Transformed (aTrsfInv);
|
|
if (!theIsFilled)
|
|
{
|
|
if (!hasCircleOverlap (theRadius, theTrsf, theIsFilled, NULL))
|
|
{
|
|
return Standard_False;
|
|
}
|
|
if (aRayDir.Z() != 0)
|
|
{
|
|
aTime = (0 - aLoc.Z()) / aRayDir.Z();
|
|
}
|
|
}
|
|
else if (!RayCircleIntersection (theRadius, aLoc, aRayDir, theIsFilled, aTime))
|
|
{
|
|
return Standard_False;
|
|
}
|
|
|
|
thePickResult.SetDepth (aTime * myScale);
|
|
if (theClipRange.IsClipped (thePickResult.Depth()))
|
|
{
|
|
thePickResult.SetDepth (aTime * myScale);
|
|
}
|
|
|
|
const gp_Pnt aPntOnCircle = aLoc.XYZ() + aRayDir.XYZ() * aTime;
|
|
if (Abs (aPntOnCircle.Z()) < Precision::Confusion())
|
|
{
|
|
thePickResult.SetSurfaceNormal (-gp::DZ().Transformed (theTrsf));
|
|
}
|
|
else
|
|
{
|
|
thePickResult.SetSurfaceNormal (gp_Vec (aPntOnCircle.X(), aPntOnCircle.Y(), 0.0).Transformed (theTrsf));
|
|
}
|
|
thePickResult.SetPickedPoint (aPntOnCircle.Transformed (theTrsf));
|
|
return !theClipRange.IsClipped (thePickResult.Depth());
|
|
}
|
|
|
|
//=======================================================================
|
|
// function : isIntersectCircle
|
|
// purpose :
|
|
//=======================================================================
|
|
Standard_Boolean SelectMgr_RectangularFrustum::isIntersectCircle (const Standard_Real theRadius,
|
|
const gp_Pnt& theCenter,
|
|
const gp_Trsf& theTrsf,
|
|
const TColgp_Array1OfPnt& theVertices) const
|
|
{
|
|
const gp_Trsf aTrsfInv = theTrsf.Inverted();
|
|
const gp_Dir aRayDir = gp_Dir (myEdgeDirs[4 == 4 ? 4 : 0]).Transformed (aTrsfInv);
|
|
if (aRayDir.Z() == 0.0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
for (Standard_Integer anIdx = theVertices.Lower(); anIdx <= theVertices.Upper(); anIdx++)
|
|
{
|
|
const gp_Pnt aPntStart = theVertices.Value (anIdx).Transformed (aTrsfInv);
|
|
const gp_Pnt aPntFinish = anIdx == theVertices.Upper()
|
|
? theVertices.Value (theVertices.Lower()).Transformed (aTrsfInv)
|
|
: theVertices.Value (anIdx + 1).Transformed (aTrsfInv);
|
|
|
|
// Project points on the end face plane
|
|
const Standard_Real aParam1 = (theCenter.Z() - aPntStart.Z()) / aRayDir.Z();
|
|
const Standard_Real aX1 = aPntStart.X() + aRayDir.X() * aParam1;
|
|
const Standard_Real anY1 = aPntStart.Y() + aRayDir.Y() * aParam1;
|
|
|
|
const Standard_Real aParam2 = (theCenter.Z() - aPntFinish.Z()) / aRayDir.Z();
|
|
const Standard_Real aX2 = aPntFinish.X() + aRayDir.X() * aParam2;
|
|
const Standard_Real anY2 = aPntFinish.Y() + aRayDir.Y() * aParam2;
|
|
|
|
// Solving quadratic equation anA * T^2 + 2 * aK * T + aC = 0
|
|
const Standard_Real anA = (aX1 - aX2) * (aX1 - aX2) + (anY1 - anY2) * (anY1 - anY2);
|
|
const Standard_Real aK = aX1 * (aX2 - aX1) + anY1 * (anY2 - anY1);
|
|
const Standard_Real aC = aX1 * aX1 + anY1 * anY1 - theRadius * theRadius;
|
|
|
|
const Standard_Real aDiscr = aK * aK - anA * aC;
|
|
if (aDiscr >= 0.0)
|
|
{
|
|
const Standard_Real aT1 = (-aK + Sqrt (aDiscr)) / anA;
|
|
const Standard_Real aT2 = (-aK - Sqrt (aDiscr)) / anA;
|
|
if ((aT1 >= 0 && aT1 <= 1) || (aT2 >= 0 && aT2 <= 1))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//=======================================================================
|
|
// function : isSegmentsIntersect
|
|
// purpose :
|
|
//=======================================================================
|
|
Standard_Boolean SelectMgr_RectangularFrustum::isSegmentsIntersect (const gp_Pnt& thePnt1Seg1,
|
|
const gp_Pnt& thePnt2Seg1,
|
|
const gp_Pnt& thePnt1Seg2,
|
|
const gp_Pnt& thePnt2Seg2) const
|
|
{
|
|
const gp_Mat aMatPln (thePnt2Seg1.X() - thePnt1Seg1.X(), thePnt2Seg1.Y() - thePnt1Seg1.Y(), thePnt2Seg1.Z() - thePnt1Seg1.Z(),
|
|
thePnt1Seg2.X() - thePnt1Seg1.X(), thePnt1Seg2.Y() - thePnt1Seg1.Y(), thePnt1Seg2.Z() - thePnt1Seg1.Z(),
|
|
thePnt2Seg2.X() - thePnt1Seg1.X(), thePnt2Seg2.Y() - thePnt1Seg1.Y(), thePnt2Seg2.Z() - thePnt1Seg1.Z());
|
|
if (Abs (aMatPln.Determinant()) > Precision::Confusion())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
Standard_Real aFst[4] = { thePnt1Seg1.X(), thePnt2Seg1.X(), thePnt1Seg2.X(), thePnt2Seg2.X() };
|
|
Standard_Real aSnd[4] = { thePnt1Seg1.Y(), thePnt2Seg1.Y(), thePnt1Seg2.Y(), thePnt2Seg2.Y() };
|
|
if (aFst[0] == aFst[2] && aFst[1] == aFst[3])
|
|
{
|
|
aFst[0] = thePnt1Seg1.Z();
|
|
aFst[1] = thePnt2Seg1.Z();
|
|
aFst[2] = thePnt1Seg2.Z();
|
|
aFst[3] = thePnt2Seg2.Z();
|
|
}
|
|
if (aSnd[0] == aSnd[2]
|
|
&& aSnd[1] == aSnd[3])
|
|
{
|
|
aSnd[0] = thePnt1Seg1.Z();
|
|
aSnd[1] = thePnt2Seg1.Z();
|
|
aSnd[2] = thePnt1Seg2.Z();
|
|
aSnd[3] = thePnt2Seg2.Z();
|
|
}
|
|
const gp_Mat2d aMat (gp_XY (aFst[0] - aFst[1], aSnd[0] - aSnd[1]),
|
|
gp_XY (aFst[3] - aFst[2], aSnd[3] - aSnd[2]));
|
|
|
|
const gp_Mat2d aMatU (gp_XY (aFst[0] - aFst[2], aSnd[0] - aSnd[2]),
|
|
gp_XY (aFst[3] - aFst[2], aSnd[3] - aSnd[2]));
|
|
|
|
const gp_Mat2d aMatV (gp_XY (aFst[0] - aFst[1], aSnd[0] - aSnd[1]),
|
|
gp_XY (aFst[0] - aFst[2], aSnd[0] - aSnd[2]));
|
|
if (aMat.Determinant() == 0.0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const Standard_Real anU = aMatU.Determinant() / aMat.Determinant();
|
|
const Standard_Real aV = aMatV.Determinant() / aMat.Determinant();
|
|
if (anU >= 0.0 && anU <= 1.0
|
|
&& aV >= 0.0 && aV <= 1.0)
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//=======================================================================
|
|
// function : OverlapsCylinder
|
|
// purpose :
|
|
//=======================================================================
|
|
Standard_Boolean SelectMgr_RectangularFrustum::OverlapsCylinder (const Standard_Real theBottomRad,
|
|
const Standard_Real theTopRad,
|
|
const Standard_Real theHeight,
|
|
const gp_Trsf& theTrsf,
|
|
const Standard_Boolean theIsHollow,
|
|
Standard_Boolean* theInside) const
|
|
{
|
|
Standard_ASSERT_RAISE (mySelectionType == SelectMgr_SelectionType_Point || mySelectionType == SelectMgr_SelectionType_Box,
|
|
"Error! SelectMgr_RectangularFrustum::Overlaps() should be called after selection frustum initialization");
|
|
|
|
return hasCylinderOverlap (theBottomRad, theTopRad, theHeight, theTrsf, theIsHollow, theInside);
|
|
}
|
|
|
|
//=======================================================================
|
|
// function : OverlapsCircle
|
|
// purpose :
|
|
//=======================================================================
|
|
Standard_Boolean SelectMgr_RectangularFrustum::OverlapsCircle (const Standard_Real theRadius,
|
|
const gp_Trsf& theTrsf,
|
|
const Standard_Boolean theIsFilled,
|
|
Standard_Boolean* theInside) const
|
|
{
|
|
Standard_ASSERT_RAISE (mySelectionType == SelectMgr_SelectionType_Point || mySelectionType == SelectMgr_SelectionType_Box,
|
|
"Error! SelectMgr_RectangularFrustum::Overlaps() should be called after selection frustum initialization");
|
|
|
|
return hasCircleOverlap (theRadius, theTrsf, theIsFilled, theInside);
|
|
}
|
|
|
|
// =======================================================================
|
|
// function : GetMousePosition
|
|
// purpose :
|
|
// =======================================================================
|
|
const gp_Pnt2d& SelectMgr_RectangularFrustum::GetMousePosition() const
|
|
{
|
|
if (mySelectionType == SelectMgr_SelectionType_Point)
|
|
{
|
|
return mySelRectangle.MousePos();
|
|
}
|
|
return base_type::GetMousePosition();
|
|
}
|
|
|
|
// =======================================================================
|
|
// function : OverlapsSphere
|
|
// purpose :
|
|
// =======================================================================
|
|
Standard_Boolean SelectMgr_RectangularFrustum::OverlapsSphere (const gp_Pnt& theCenter,
|
|
const Standard_Real theRadius,
|
|
const SelectMgr_ViewClipRange& theClipRange,
|
|
SelectBasics_PickResult& thePickResult) const
|
|
{
|
|
Standard_ASSERT_RAISE (mySelectionType == SelectMgr_SelectionType_Point || mySelectionType == SelectMgr_SelectionType_Box,
|
|
"Error! SelectMgr_RectangularFrustum::Overlaps() should be called after selection frustum initialization");
|
|
Standard_Real aTimeEnter = 0.0, aTimeLeave = 0.0;
|
|
if (!RaySphereIntersection (theCenter, theRadius, myNearPickedPnt, myViewRayDir, aTimeEnter, aTimeLeave))
|
|
{
|
|
return Standard_False;
|
|
}
|
|
|
|
thePickResult.SetDepth (aTimeEnter * myScale);
|
|
if (theClipRange.IsClipped (thePickResult.Depth()))
|
|
{
|
|
thePickResult.SetDepth (aTimeLeave * myScale);
|
|
}
|
|
gp_Pnt aPntOnSphere (myNearPickedPnt.XYZ() + myViewRayDir.XYZ() * thePickResult.Depth() / myScale);
|
|
gp_Vec aNormal (aPntOnSphere.XYZ() - theCenter.XYZ());
|
|
thePickResult.SetPickedPoint (aPntOnSphere);
|
|
thePickResult.SetSurfaceNormal (aNormal);
|
|
return !theClipRange.IsClipped (thePickResult.Depth());
|
|
}
|
|
|
|
// =======================================================================
|
|
// function : OverlapsSphere
|
|
// purpose :
|
|
// =======================================================================
|
|
Standard_Boolean SelectMgr_RectangularFrustum::OverlapsSphere (const gp_Pnt& theCenter,
|
|
const Standard_Real theRadius,
|
|
Standard_Boolean* theInside) const
|
|
{
|
|
Standard_ASSERT_RAISE (mySelectionType == SelectMgr_SelectionType_Point || mySelectionType == SelectMgr_SelectionType_Box,
|
|
"Error! SelectMgr_RectangularFrustum::Overlaps() should be called after selection frustum initialization");
|
|
return hasSphereOverlap (theCenter, theRadius, theInside);
|
|
}
|
|
|
|
// =======================================================================
|
|
// function : DistToGeometryCenter
|
|
// purpose : Measures distance between 3d projection of user-picked
|
|
// screen point and given point theCOG
|
|
// =======================================================================
|
|
Standard_Real SelectMgr_RectangularFrustum::DistToGeometryCenter (const gp_Pnt& theCOG) const
|
|
{
|
|
Standard_ASSERT_RAISE(mySelectionType == SelectMgr_SelectionType_Point || mySelectionType == SelectMgr_SelectionType_Box,
|
|
"Error! SelectMgr_RectangularFrustum::DistToGeometryCenter() should be called after selection frustum initialization");
|
|
|
|
return theCOG.Distance (myNearPickedPnt) * myScale;
|
|
}
|
|
|
|
// =======================================================================
|
|
// function : DetectedPoint
|
|
// purpose : Calculates the point on a view ray that was detected during
|
|
// the run of selection algo by given depth
|
|
// =======================================================================
|
|
gp_Pnt SelectMgr_RectangularFrustum::DetectedPoint (const Standard_Real theDepth) const
|
|
{
|
|
Standard_ASSERT_RAISE (mySelectionType == SelectMgr_SelectionType_Point,
|
|
"SelectMgr_RectangularFrustum::DetectedPoint() should be called only for Point selection type");
|
|
return myNearPickedPnt.XYZ() + myViewRayDir.XYZ() * theDepth / myScale;
|
|
}
|
|
|
|
// =======================================================================
|
|
// function : GetPlanes
|
|
// purpose :
|
|
// =======================================================================
|
|
void SelectMgr_RectangularFrustum::GetPlanes (NCollection_Vector<SelectMgr_Vec4>& thePlaneEquations) const
|
|
{
|
|
thePlaneEquations.Clear();
|
|
|
|
SelectMgr_Vec4 anEquation;
|
|
for (Standard_Integer aPlaneIdx = 0; aPlaneIdx < 6; ++aPlaneIdx)
|
|
{
|
|
const gp_Vec& aPlaneNorm = Camera()->IsOrthographic() && aPlaneIdx % 2 == 1 ?
|
|
myPlanes[aPlaneIdx - 1].Reversed() : myPlanes[aPlaneIdx];
|
|
anEquation.x() = aPlaneNorm.X();
|
|
anEquation.y() = aPlaneNorm.Y();
|
|
anEquation.z() = aPlaneNorm.Z();
|
|
anEquation.w() = - (aPlaneNorm.XYZ().Dot (myVertices[aPlaneIdx % 2 == 0 ? aPlaneIdx : aPlaneIdx + 2].XYZ()));
|
|
thePlaneEquations.Append (anEquation);
|
|
}
|
|
}
|
|
|
|
//=======================================================================
|
|
//function : DumpJson
|
|
//purpose :
|
|
//=======================================================================
|
|
void SelectMgr_RectangularFrustum::DumpJson (Standard_OStream& theOStream, Standard_Integer theDepth) const
|
|
{
|
|
OCCT_DUMP_CLASS_BEGIN (theOStream, SelectMgr_RectangularFrustum)
|
|
OCCT_DUMP_BASE_CLASS (theOStream, theDepth, SelectMgr_Frustum)
|
|
|
|
OCCT_DUMP_FIELD_VALUES_DUMPED (theOStream, theDepth, &myNearPickedPnt)
|
|
OCCT_DUMP_FIELD_VALUES_DUMPED (theOStream, theDepth, &myFarPickedPnt)
|
|
OCCT_DUMP_FIELD_VALUES_DUMPED (theOStream, theDepth, &myViewRayDir)
|
|
OCCT_DUMP_FIELD_VALUES_DUMPED (theOStream, theDepth, &mySelRectangle.MinPnt())
|
|
OCCT_DUMP_FIELD_VALUES_DUMPED (theOStream, theDepth, &mySelRectangle.MaxPnt())
|
|
|
|
OCCT_DUMP_FIELD_VALUE_NUMERICAL (theOStream, myScale)
|
|
}
|