Commit 88c308a4 authored by Armin Erraji's avatar Armin Erraji

Bugfix Calculation of aperture points

parent 1fd95bb8
......@@ -38,9 +38,11 @@ namespace ITAPropagationPathSim
void ApplySensor(shared_ptr<CSensor> pSensor);
void ConstructPropagationPathsWithStopWatch(CPropagationPathList& oPaths);
void ConstructPropagationPaths(CPropagationPathList& oPaths);
size_t GetNumberPropagationPathCandidates();
private:
......@@ -48,7 +50,7 @@ namespace ITAPropagationPathSim
shared_ptr<const ITAGeo::Halfedge::CMeshModelList> m_pMeshModelList; //!< Copy of propagation model list
CShapesMap m_mvpShapeVisibilityMap; //!< Map of which shape can illuminate which ones
vector<CPropagationShapeShared> m_vpPropagationTree; //!< Vector of all propagation shapes (as well as its children) visible to the emitter
vector<CPropagationShapeShared> m_vpPropagationLists; //!< Vector of all propagation shape visible to the emitter with each one pointing to up to one child with the last one visible to the sensor
vector<CPropagationShapeShared> m_vpPropagationCandidates; //!< Vector of all propagation shape visible to the emitter with each one pointing to up to one child with the last one visible to the sensor
shared_ptr<CEmitter> m_pEmitter; //!< Const pointer to emitter
shared_ptr<CSensor> m_pSensor; //!< Const pointer to sensor
......
......@@ -97,6 +97,8 @@ namespace ITAPropagationPathSim
//!<Copy shape
void CopyFrom(const CPropagationShape& oPropagationShapeIn);
void GetBoundingBoxAxesAligned(VistaVector3D& v3Min, VistaVector3D& v3Max) const;
//!< Set barycenter and radius of the boundary sphere of the shape
bool SetBoundarySphere();
......
#ifndef INCLUDE_WATCHER_PROPAGATION_RTREE
#define INCLUDE_WATCHER_PROPAGATION_RTREE
// ITAGeo
#include <ITAGeo/Definitions.h>
#include <ITAGeo/Utils.h>
#include <ITAGeo/Base.h>
#include <ITAGeo/ModelBase.h>
#include <ITAGeo/Halfedge/MeshModel.h>
#include<ITAPropagationPathSim/CombinedModel/PropagationShapes.h>
using namespace std;
namespace ITAPropagationPathSim
{
namespace CombinedModel
{
// Typedefs
typedef OpenMesh::PolyMesh_ArrayKernelT<> CITAMesh;
//! Buildings RTree class
/**
* @todo
*
*/
class ITA_GEO_API CShapesTree
{
public:
//---Constructor and Destructor---------------------------------------------
CShapesTree();
~CShapesTree();
//---Public member functions------------------------------------------------
void Create(const vector<CPropagationShapeShared>& vpShapesIn);
//TODO
bool IsLineSegmentIntersected(const VistaVector3D& oLineStart, const VistaVector3D& oLineEnd) const;
//TODO
//void GetNonIntersectedFaces(vector<CPropagationShapeShared>& vpFacesOut, const VistaVector3D& oLineStart, const VistaVector3D& oLineStart);
/*TODO: For faster illumination tests
void GetIlluminableShapes(vector<CPropagationShapeShared>& vpShapesOut, const VistaPlane& oPlaneIn);
void GetIlluminableShapes(vector<CPropagationShapeShared>& vpShapesOut, const CPropagationFaceShared& pFace);
void GetIlluminableShapes(vector<CPropagationShapeShared>& vpShapesOut, const CPropagationEdgeShared& pEdge);
*/
//---Public member variables------------------------------------------------
private:
//---Private member functions-----------------------------------------------
void SetVertices();
void CreateBranches();
//---Private member variables-----------------------------------------------
//Minimum and maximum of bounding box as well as vertices of box
VistaVector3D m_v3TreeMin;
VistaVector3D m_v3TreeMax;
//Vector of shapes within bounding box
vector<CPropagationShapeShared> m_vpShapes;
//Branches
vector<shared_ptr<CShapesTree>> m_vpBranches;
};
}
}
#endif // INCLUDE_WATCHER_PROPAGATION_RTREE
\ No newline at end of file
......@@ -9,6 +9,7 @@ set( DirFiles
ImageConstructor.h
PropagationEngine.h
PropagationShapes.h
RTree.h
ReflectionLocator.h
)
......
......@@ -178,7 +178,7 @@ namespace ITAPropagationPathSim
map <UrbanEdgeHandle, map <UrbanEdgeHandle, double>> m_mdApertureStartDifferenceMap;
//RTree of building meshes
shared_ptr<ITAGeo::Urban::RTree::CBuildings> m_pBuildingsTree;
shared_ptr<ITAGeo::Urban::RTree::CShapesTree> m_pBuildingsTree;
//Matrix of illumination of diffraction edges
map<UrbanEdgeHandle, set<shared_ptr<DiffractionEdge>>> m_mDiffractionMatrix;
......
......@@ -175,7 +175,7 @@ namespace ITAPropagationPathSim
}
//The last direction vector(between the last edge and the (imaged) receiver) does not contain a correction vector for the receiver
vfDirectionVectorLength.push_back((*vpStartVertexDiff.back() - vfRelativeApexPosition.back() * *vpE_out.back()).GetLength());
vfDirectionVectorLength.push_back((*vpStartVertexDiff.back() + vfRelativeApexPosition.back() * *vpE_out.back()).GetLength());
// The terms can be written in tridiagonal matrix form as following:
// b[i-1] * vfRelativeApexPosition[i-1] + a[i] * vfRelativeApexPosition[i] + b[i] * vfRelativeApexPosition[i+1] = c[i]
......@@ -201,34 +201,35 @@ namespace ITAPropagationPathSim
//To get vfRelativeApexPosition, the Gaussian-elimination is used to delete
//the term b[i-1] * vfRelativeApexPosition[i-1] in each row, so that
//in the last row, only one variable is left over.
//Thereafter the searched vfRelativeApexPosition can be calculated.
//d[i] replaces a[i] and e[i] replaces c[i]
//┌ ┐ ┌ ┐ ┌ ┐
//| d[0] b[0] 0 0 ... 0 0 0 | | vfRelativeApexPosition[0] | | e[0] |
//| 0 d[1] b[1] 0 ... 0 0 0 | | vfRelativeApexPosition[1] | | e[1] |
//| 1 e[0] 0 0 ... 0 0 0 | | vfRelativeApexPosition[0] | | d[0] |
//| 0 1 e[1] 0 ... 0 0 0 | | vfRelativeApexPosition[1] | | d[1] |
//| ... ... ... ... ... ... ... |*| ... |=| ... |
//| 0 0 0 0 ... 0 d[n-2] b[n-2]| | vfRelativeApexPosition[n-2]| | e[n-2]|
//| 0 0 0 0 ... 0 0 d[n-1]| | vfRelativeApexPosition[n-1]| | e[n-1]|
//| 0 0 0 0 ... 0 1 e[n-2]| | vfRelativeApexPosition[n-2]| | d[n-2]|
//| 0 0 0 0 ... 0 0 1 | | vfRelativeApexPosition[n-1]| | d[n-1]|
//└ ┘ └ ┘ └ ┘
vector<float> d, e;
//The first row remains the same
d.push_back(a.front());
e.push_back(c.front());
d.push_back(c.front()/a.front());
if(a.size()>1)
e.push_back(b.front()/a.front());
//
for (int i = 1; i < a.size()-1; i++)
{
e.push_back(b[i] / (a[i] - b[i - 1] * e[i - 1]));
}
for (int i = 1; i < a.size(); i++)
{
d.push_back(a[i] - b[i - 1] * b[i - 1] / d[i - 1]);
e.push_back(c[i] - b[i - 1] * c[i - 1] / d[i - 1]);
d.push_back((c[i] - b[i - 1] * d[i - 1]) / (a[i] - b[i-1]*e[i - 1]));
}
//Solve the equation system beginning at the last row
vfRelativeApexPosition.back() = e.back() / d.back();
vfRelativeApexPosition.back() = d.back();
for (int i = vfRelativeApexPosition.size() - 2; i >= 0; i--)
{
vfRelativeApexPosition[i] = (e[i] - b[i] * vfRelativeApexPosition[i + 1]) / d[i];
vfRelativeApexPosition[i] = (d[i] - e[i] * vfRelativeApexPosition[i + 1]);
}
}
......@@ -251,6 +252,73 @@ namespace ITAPropagationPathSim
//v3InteractionPoint = from_vertex + vfRelativeApexPosition * (to_vertex - from_vertex)
VistaVector3D v3InteractionPoint = (1 - vfRelativeApexPosition.back()) * (*pEdge->v3StartVertex) + vfRelativeApexPosition.back()*(*pEdge->v3EndVertex);
//Interaction point must lie in valid range of parent and child of edge
if (!pEdge->pParent.expired())
{
if (pEdge->pParent.lock()->iShapeType == CPropagationShape::FACE)
{
auto pParentFace = static_pointer_cast<CPropagationFace>(pEdge->pParent.lock());
bValidAperturePoints = ITAGeoUtils::IsPointInFrontOfPlane(*pParentFace->pPlane, v3InteractionPoint);
}
else
{
auto pParentEdge = static_pointer_cast<CPropagationEdge>(pEdge->pParent.lock());
//Illumination test at from vertex position
VistaPlane oMainPlane, oOppositePlane;
oMainPlane.SetOrigin(*pParentEdge->v3StartVertex);
oOppositePlane.SetOrigin(*pParentEdge->v3StartVertex);
oMainPlane.SetNormVector(*pParentEdge->v3MainFaceNormal);
oOppositePlane.SetNormVector(*pParentEdge->v3OppositeFaceNormal);
bool bMainFaceIlluminated = ITAGeoUtils::IsPointInFrontOfPlane(oMainPlane, v3InteractionPoint);
bool bOppositeFaceIlluminated = ITAGeoUtils::IsPointInFrontOfPlane(oOppositePlane, v3InteractionPoint);
//Check whether wedge angle is >pi or <pi; TODO: export in own function
bool bIsOuterAngle = pParentEdge->v3MainFaceNormal->Cross(*pParentEdge->v3OppositeFaceNormal).Dot(*pParentEdge->v3EndVertex - *pParentEdge->v3StartVertex) > 0;
if (bIsOuterAngle)
bValidAperturePoints = bMainFaceIlluminated || bOppositeFaceIlluminated;
else
bValidAperturePoints = bMainFaceIlluminated && bOppositeFaceIlluminated;
}
}
if (pEdge->pChild != nullptr)
{
if (pEdge->pChild->iShapeType == CPropagationShape::FACE)
{
auto pChildFace = static_pointer_cast<CPropagationFace>(pEdge->pChild);
bValidAperturePoints = ITAGeoUtils::IsPointInFrontOfPlane(*pChildFace->pPlane, v3InteractionPoint);
}
else
{
auto pChildEdge = static_pointer_cast<CPropagationEdge>(pEdge->pChild);
//Illumination test at from vertex position
VistaPlane oMainPlane, oOppositePlane;
oMainPlane.SetOrigin(*pChildEdge->v3StartVertex);
oOppositePlane.SetOrigin(*pChildEdge->v3StartVertex);
oMainPlane.SetNormVector(*pChildEdge->v3MainFaceNormal);
oOppositePlane.SetNormVector(*pChildEdge->v3OppositeFaceNormal);
bool bMainFaceIlluminated = ITAGeoUtils::IsPointInFrontOfPlane(oMainPlane, v3InteractionPoint);
bool bOppositeFaceIlluminated = ITAGeoUtils::IsPointInFrontOfPlane(oOppositePlane, v3InteractionPoint);
//Check whether wedge angle is >pi or <pi; TODO: export in own function
bool bIsOuterAngle = pChildEdge->v3MainFaceNormal->Cross(*pChildEdge->v3OppositeFaceNormal).Dot(*pChildEdge->v3EndVertex - *pChildEdge->v3StartVertex) > 0;
if (bIsOuterAngle)
bValidAperturePoints = bMainFaceIlluminated || bOppositeFaceIlluminated;
else
bValidAperturePoints = bMainFaceIlluminated && bOppositeFaceIlluminated;
}
}
//Set the interaction point
pEdge->v3InteractionPoint = make_unique <VistaVector3D>(v3InteractionPoint);
......
......@@ -23,6 +23,18 @@ void CPropagationShape::CopyFrom(const CPropagationShape & oPropagationShapeIn)
pIsIlluminableBySensor = oPropagationShapeIn.pIsIlluminableBySensor;
}
void CPropagationShape::GetBoundingBoxAxesAligned(VistaVector3D& v3Min, VistaVector3D& v3Max) const
{
v3Min = *vv3Vertices[0];
v3Max = v3Min;
for (auto& v3Vertex : vv3Vertices)
{
v3Min.SetValues(fminf(v3Min[0], (*v3Vertex)[0]), fminf(v3Min[1], (*v3Vertex)[1]), fminf(v3Min[2], (*v3Vertex)[2]));
v3Max.SetValues(fmaxf(v3Max[0], (*v3Vertex)[0]), fmaxf(v3Max[1], (*v3Vertex)[1]), fmaxf(v3Max[2], (*v3Vertex)[2]));
}
}
bool CPropagationShape::SetBoundarySphere()
{
//the shape must have at least one vertex
......@@ -183,6 +195,8 @@ void CPropagationEdge::CopyFrom(const CPropagationEdge & oPropagationEdgeIn)
pHasValidImageEdge = oPropagationEdgeIn.pHasValidImageEdge;
}
bool CPropagationEdge::SetMinimumDistance(const CPropagationShape & oPropagationShape)
{
if (v3Barycenter == nullptr || oPropagationShape.v3Barycenter == nullptr)
......
#include <ITAPropagationPathSim/CombinedModel/RTree.h>
using namespace ITAGeo;
using namespace ITAPropagationPathSim::CombinedModel;
//===RTree::CBuildings class=======================================================================================
// ---Constructor & Destructor-------------------------------------------------------------------------------------
CShapesTree::CShapesTree()
{
}
CShapesTree::~CShapesTree()
{
}
// ---Create functions----------------------------------------------------------------------------------------------
void CShapesTree::Create(const vector<CPropagationShapeShared>& vpShapesIn)
{
//Set buildings vector
m_vpShapes = vpShapesIn;
//Set vertex VistaVectors
SetVertices();
//Create branches if number of shapes is more than one
if (m_vpShapes.size() > 1)
CreateBranches();
}
void CShapesTree::CreateBranches()
{
//Create 8 vectors of shape-vectors (for each region one vector)
vector<vector<CPropagationShapeShared>> vvpBranchShapes;
vvpBranchShapes.resize(8);
//Points, where branches are sub-divided into. Mid points between max and min.
float fSubDivideX = m_v3TreeMin[Vista::X] + m_v3TreeMax[Vista::X] / 2.0f;
float fSubDivideY = m_v3TreeMin[Vista::Y] + m_v3TreeMax[Vista::Y] / 2.0f;
float fSubDivideZ = m_v3TreeMin[Vista::Z] + m_v3TreeMax[Vista::Z] / 2.0f;
//Add building to branchbuildings vector according to its location
for (auto &pShape : m_vpShapes)
{
VistaVector3D v3Min, v3Max;
pShape->GetBoundingBoxAxesAligned(v3Min, v3Max);
if (v3Min[2] < fSubDivideZ)
{
if (v3Min[1] < fSubDivideY)
{
if (v3Min[0] < fSubDivideX)
{
vvpBranchShapes[0].push_back(pShape);
}
else
{
vvpBranchShapes[1].push_back(pShape);
}
}
else
{
if (v3Min[0] < fSubDivideX)
{
vvpBranchShapes[2].push_back(pShape);
}
else
{
vvpBranchShapes[3].push_back(pShape);
}
}
}
else
{
if (v3Min[1] < fSubDivideY)
{
if (v3Min[0] < fSubDivideX)
{
vvpBranchShapes[4].push_back(pShape);
}
else
{
vvpBranchShapes[5].push_back(pShape);
}
}
else
{
if (v3Min[0] < fSubDivideX)
{
vvpBranchShapes[6].push_back(pShape);
}
else
{
vvpBranchShapes[7].push_back(pShape);
}
}
}
}
//For each subdivided building vector, create a branch
for (auto &vpShapesCurrentBranch : vvpBranchShapes)
{
if (vpShapesCurrentBranch.size() > 0)
{
auto pBranch = make_shared< CShapesTree>();
pBranch->Create(vpShapesCurrentBranch);
m_vpBranches.push_back(pBranch);
}
}
}
void CShapesTree::SetVertices()
{
//Create start min and max
m_vpShapes[0]->GetBoundingBoxAxesAligned(m_v3TreeMin, m_v3TreeMax);
//Update min and max
for (auto &pShape : m_vpShapes)
{
VistaVector3D v3Min, v3Max;
pShape->GetBoundingBoxAxesAligned(v3Min, v3Max);
for (int i = 0; i < 3; i++)
{
m_v3TreeMin[i] = min(m_v3TreeMin[i], v3Min[i]);
m_v3TreeMax[i] = max(m_v3TreeMax[i], v3Max[i]);
}
}
}
// ---Test function----------------------------------------------------------------------------------------------
bool CShapesTree::IsLineSegmentIntersected(const VistaVector3D & oLineStart, const VistaVector3D & oLineEnd) const
{
//Compute intersection test with bounding box. if if intersectes, further test lower branches for intersections
//if (oLineStart.x < m_v3TreeMin.x && oLineEnd.x < m_v3TreeMin.x) return false;
//if (oLineStart.x > m_v3TreeMax.x && oLineEnd.x > m_v3TreeMax.x) return false;
//if (oLineStart.y < m_v3TreeMin.y && m_v3TreeMin.y < B1.y) return false;
//if (oLineStart.y > m_v3TreeMax.y && oLineEnd.y > m_v3TreeMax.y) return false;
//if (oLineStart.z < m_v3TreeMin.z && oLineEnd.z < m_v3TreeMin.z) return false;
//if (oLineStart.z > m_v3TreeMax.z && oLineEnd.z > m_v3TreeMax.z) return false;
return false;
}
......@@ -9,6 +9,7 @@ set( DirFiles
ImageConstructor.cpp
PropagationEngine.cpp
PropagationShapes.cpp
RTree.cpp
ReflectionLocator.cpp
)
......
......@@ -41,7 +41,7 @@ CDiffractionPath::CDiffractionPath(ITAGeo::Urban::CModel & oUrbanModel, const in
}
//Add buildings tree for faster filtering of illuminable building meshes
m_pBuildingsTree = make_shared<ITAGeo::Urban::RTree::CBuildings>();
m_pBuildingsTree = make_shared<ITAGeo::Urban::RTree::CShapesTree>();
m_pBuildingsTree->Create(pBuildings);
//Construct diffraction matrix
......
......@@ -34,20 +34,21 @@ using namespace std;
using namespace ITAGeo;
using namespace ITAPropagationPathSim;
int main( int iNumInArgs, char* pcInArgs[] )
int main(int iNumInArgs, char* pcInArgs[])
{
string sSubFolder = "SketchUpFiles/";
string sSubFolder = "M:/Sciebo/2018 MA - Armin Erraji/Tests/StreetThreeBuildings/";
//string sSubFolder = "SketchUpFiles/";
string sInFile = "ThreeBuildings.skp";
if( iNumInArgs > 1 )
sInFile = string( pcInArgs[ 1 ] );
string sInFile = "Test.skp";
if (iNumInArgs > 1)
sInFile = string(pcInArgs[1]);
//Load geo model(for later saving of the propagation paths)
SketchUp::CModel oGeoModel;
if( oGeoModel.Load(sSubFolder + sInFile ) )
if (oGeoModel.Load(sSubFolder + sInFile))
{
cout << "Successfully loaded '" << sInFile << "'" << endl;
}
......@@ -70,11 +71,20 @@ int main( int iNumInArgs, char* pcInArgs[] )
return 255;
}
//auto pSensor = make_shared< CSensor >( VistaVector3D( 3.0f, 3.3f, 1.3f ) );
//auto pEmitter = make_shared< CEmitter >( VistaVector3D( 2.1f, 2.2f, 1.7f ) );
//auto pEmitter = make_shared< CEmitter >(VistaVector3D(5.0f,-15.f, 1.0f));
//auto pEmitter = make_shared< CEmitter >(VistaVector3D(5.0f, 0.0f, 1.0f));
auto pEmitter = make_shared< CEmitter >(VistaVector3D(5.f, 10.f, 1.0f));
auto pSensor = make_shared< CSensor >(VistaVector3D(50.0f, 5.0f, 1.7f));
auto pSensor = make_shared< CSensor >(VistaVector3D(3.0f, 0, 1.4f));
auto pEmitter = make_shared< CEmitter >(VistaVector3D(-3.0f, 0, 1.4f));
/*auto pEmitter = make_shared< CEmitter >(VistaVector3D(50.0f, -5.0f, 1.7f));
auto pSensor = make_shared< CSensor >( VistaVector3D( 0.0f, 0.0f, 1.7f ) );
*/
//auto pSensor = make_shared< CSensor >(VistaVector3D(3.0f, 0, 1.4f));
//auto pEmitter = make_shared< CEmitter >(VistaVector3D(-3.0f, 0, 1.4f));
cout << "Emitter: " << pEmitter->vPos << endl;
......@@ -82,22 +92,86 @@ int main( int iNumInArgs, char* pcInArgs[] )
//Configuration of the engine
const bool bOnlyNeighbouredEdgeDiffraction = false;
const bool bDiffractionOnlyIntoShadowRegion = true;
const bool bDiffractionOnlyIntoShadowRegion = false;
const bool bFilterNotVisiblePathsBetweenEdges = false; //Intersection test between edges(expensive)
const bool bFilterNotVisiblePointToEdge = true; //Intersection test between emitter/sensor and edges
const bool bFilterNotVisiblePaths = true; //Intersection test of calculated sub paths
const float fIntersectionTestResolution = 0.001;
const int iNumIterations = 5;//!< Number of iterations for the calculation of the aperture points
const int iNumIterations = 4;//!< Number of iterations for the calculation of the aperture points
const int fMaxAccumulatedDiffractionAngle = 2*ITAConstants::PI_F;
const int iMaxDiffractionOrder = 4;
const int iMaxReflectionOrder = 3;
const int iMaxCombinedOrder = 5;
const int fMaxAccumulatedDiffractionAngle = -2 * ITAConstants::PI_F;
const float fLevelDropThreshhold = 90.f - 30.f - 11.f; //90 dB Power level car; 40 dB background noise; 11 dB initial power level drop
const float fLevelDropThreshhold = -90.f - 40.f - 11.f; //90 dB Power level car; 30 dB masking; 11 dB initial power level drop
const float fReflectionPenalty = -20 * log10f(0.7); //Level reduction of a reflection with reflection factor R = 0.7
const float fDiffractionPenalty = 2.5f; // Minimum level reduction of a diffracted signal into the shadow region for a low frequency (Taken from BA Filbert Figure 16. UTD)
//Multiple simulations
#if 0
for (int iDiffraction = 0; iDiffraction <= 3; iDiffraction++)
{
for (int iReflection = 0; iReflection <= 2; iReflection++)
{
cout << endl << "Diffraction order: " << iDiffraction << "; Reflection order: " << iReflection << endl;
const int iMaxDiffractionOrder = iDiffraction;
const int iMaxReflectionOrder = iReflection;
const int iMaxCombinedOrder = iMaxDiffractionOrder + iMaxReflectionOrder;
ITAStopWatch sw; sw.start();
auto pPathEngine = make_shared<CombinedModel::CPathEngine>();
cout << "Calculation time initialization path engine: " << timeToString(sw.stop()) << endl;
sw.start();
pPathEngine->Configure(bOnlyNeighbouredEdgeDiffraction, bDiffractionOnlyIntoShadowRegion, bFilterNotVisiblePathsBetweenEdges, bFilterNotVisiblePointToEdge, bFilterNotVisiblePointToEdge, bFilterNotVisiblePaths, iNumIterations, fIntersectionTestResolution);
pPathEngine->SetAbortionCriteria(iMaxDiffractionOrder, iMaxReflectionOrder, iMaxCombinedOrder, fLevelDropThreshhold, fReflectionPenalty, fDiffractionPenalty, fMaxAccumulatedDiffractionAngle);
cout << "Calculation time configuring filter: " << timeToString(sw.stop()) << endl;
sw.start();
pPathEngine->InitializePathEnvironment(pMeshModelList);
cout << "Calculation time initialization path environment: " << timeToString(sw.stop()) << endl;
sw.start();
pPathEngine->ApplyEmitter(pEmitter);
cout << "Calculation time applying emitter: " << timeToString(sw.stop()) << endl;
sw.start();
pPathEngine->ApplySensor(pSensor);
cout << "Calculation time applying sensor: " << timeToString(sw.stop()) << endl;
CPropagationPathList oPathListAll;
sw.start();
pPathEngine->ConstructPropagationPaths(oPathListAll);
//pPathEngine->ConstructPropagationPathsWithStopWatch(oPathListAll);
cout << "Calculation time propagation path creation: " << timeToString(sw.stop()) << endl;
cout << "Visible paths: " << to_string(oPathListAll.size()) << endl;
//Add emitter and sensor to visualization
oGeoModel.AddEmitterVisualization(*pEmitter, "Emitter A");
oGeoModel.AddSensorVisualization(*pSensor, "Sensor A");
cout << "The path with the longest length is " << oPathListAll.GetMaxLength() << " m." << endl;
}
}
return 0;
#endif
//Single simulation
#if 1
const int iMaxDiffractionOrder = 0;
const int iMaxReflectionOrder = 4;