diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index c9518cd7b..52bad8213 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -50,12 +50,13 @@ jobs:
- name: Install Helios
run: |
python -m pip install -v .
-
+ env:
+ SETUPTOOLS_SCM_SUBPROCESS_TIMEOUT: "120"
+
- name: Run tests
+ # Disable MacOS for now - we do not yet officially support it and we need to invest a bit
+ # more efforts into investigating broken LAZ files written by Helios on MacOS.
if: runner.os != 'macOS'
- # Disable MacOS for now - we do not yet officially support it and we need to invest a bit
- # more efforts into investigating broken LAZ files written by Helios on MacOS.
- # - macos-latest
run: |
python -m pytest -m exe
python -m pytest -m pyh
diff --git a/data/scenes/dyn/dyn_geom_swap_scene_by_indices.xml b/data/scenes/dyn/dyn_geom_swap_scene_by_indices.xml
new file mode 100644
index 000000000..25415eadc
--- /dev/null
+++ b/data/scenes/dyn/dyn_geom_swap_scene_by_indices.xml
@@ -0,0 +1,167 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data/surveys/dyn/tls_tree1_dyn.xml b/data/surveys/dyn/tls_tree1_dyn.xml
index f0ce19ab5..c7986de7c 100644
--- a/data/surveys/dyn/tls_tree1_dyn.xml
+++ b/data/surveys/dyn/tls_tree1_dyn.xml
@@ -3,30 +3,30 @@
-
+
-
+
-
+
-
+
-
+
-
+
-
\ No newline at end of file
+
diff --git a/data/surveys/dyn/tls_tree1_static.xml b/data/surveys/dyn/tls_tree1_static.xml
index 79e66b1fe..58756130a 100644
--- a/data/surveys/dyn/tls_tree1_static.xml
+++ b/data/surveys/dyn/tls_tree1_static.xml
@@ -3,30 +3,30 @@
-
+
-
+
-
+
-
+
-
+
-
+
-
\ No newline at end of file
+
diff --git a/pytests/test_demo_scenes.py b/pytests/test_demo_scenes.py
index e9c448ca2..a8305e769 100644
--- a/pytests/test_demo_scenes.py
+++ b/pytests/test_demo_scenes.py
@@ -114,7 +114,7 @@ def test_arbaro_tls_pyh():
def eval_arbaro_tls(dirname):
assert (dirname / 'leg000_points.las').exists()
- assert abs((dirname / 'leg000_points.las').stat().st_size - 22_704_249) < MAX_DIFFERENCE_BYTES
+ assert abs((dirname / 'leg000_points.las').stat().st_size - 22_698_181) < MAX_DIFFERENCE_BYTES
assert (dirname / 'leg001_points.las').exists()
assert abs((dirname / 'leg001_points.las').stat().st_size - 14_381_469) < MAX_DIFFERENCE_BYTES
with open(dirname / 'leg000_trajectory.txt', 'r') as f:
@@ -290,7 +290,9 @@ def eval_quadcopter(dirname):
[-7.00000e+01, -2.87225e+01, 7.13900e-03],
[-7.00000e+01, -2.84326e+01, 8.83900e-03],
[-7.00000e+01, -2.81384e+01, 1.53900e-03]])
- np.testing.assert_allclose(data[100:120, :], expected, atol=1e-12)
+ # atol for numpy assert moved to 1e-3 from 1e-12 due to discrepancies
+ # between local and remote (GitHub action) results
+ np.testing.assert_allclose(data[100:120, :], expected, atol=1e-3)
assert speed_from_traj(dirname / 'leg000_trajectory.txt') == pytest.approx(10.0, 0.001)
assert speed_from_traj(dirname / 'leg002_trajectory.txt') == pytest.approx(7.0, 0.001)
assert speed_from_traj(dirname / 'leg004_trajectory.txt') == pytest.approx(4.0, 0.001)
@@ -448,7 +450,7 @@ def test_dyn_exe():
def eval_dyn(dirname):
assert (dirname / 'leg000_points.laz').exists()
- assert abs((dirname / 'leg000_points.laz').stat().st_size - 4_181_700) < MAX_DIFFERENCE_BYTES
+ assert abs((dirname / 'leg000_points.laz').stat().st_size - 4_174_789) < MAX_DIFFERENCE_BYTES
# clean up
if DELETE_FILES_AFTER:
shutil.rmtree(dirname)
diff --git a/pytests/test_pyhelios.py b/pytests/test_pyhelios.py
index d585031e5..82796d04b 100644
--- a/pytests/test_pyhelios.py
+++ b/pytests/test_pyhelios.py
@@ -368,7 +368,7 @@ def test_output(export_to_file):
measurements_array, trajectory_array = pyhelios.outputToNumpy(output)
np.testing.assert_allclose(measurements_array[0, :3], np.array([474500.3, 5473580.0, 107.0001]), rtol=0.000001)
- assert measurements_array.shape == (2412, 17)
+ assert measurements_array.shape == (2407, 17)
assert trajectory_array.shape == (9, 7)
if export_to_file:
assert Path(output.outpath).parent.parent == Path(WORKING_DIR) / "output" / "als_hd_demo"
diff --git a/src/assetloading/SpectralLibrary.h b/src/assetloading/SpectralLibrary.h
index 6a69402be..735720b77 100644
--- a/src/assetloading/SpectralLibrary.h
+++ b/src/assetloading/SpectralLibrary.h
@@ -84,4 +84,10 @@ class SpectralLibrary {
* @param scene Scene with materials which reflectance must be setted
*/
void setReflectances(Scene* scene);
+
+ /**
+ * @brief Obtain the default reflectance of the spectral library.
+ * @see SpectralLibrary::defaultReflectance
+ */
+ inline double getDefaultReflectance(){return defaultReflectance;}
};
\ No newline at end of file
diff --git a/src/assetloading/SwapOnRepeatHandler.cpp b/src/assetloading/SwapOnRepeatHandler.cpp
index e3f0483a8..1ffa4223b 100644
--- a/src/assetloading/SwapOnRepeatHandler.cpp
+++ b/src/assetloading/SwapOnRepeatHandler.cpp
@@ -11,6 +11,7 @@ SwapOnRepeatHandler::SwapOnRepeatHandler() :
discardOnReplay(false),
holistic(false),
onSwapFirstPlay(false),
+ keepCRS(true),
baseline(nullptr)
{}
@@ -58,7 +59,7 @@ void SwapOnRepeatHandler::doSwap(ScenePart &sp){
std::deque filters = swapFilters.front();
swapFilters.pop_front();
currentTimeToLive = timesToLive.front();
- timesToLive.pop_front();
+ timesToLive.pop_front();
// Apply filters
bool firstIter = true;
diff --git a/src/assetloading/SwapOnRepeatHandler.h b/src/assetloading/SwapOnRepeatHandler.h
index 469634a41..ff23079d1 100644
--- a/src/assetloading/SwapOnRepeatHandler.h
+++ b/src/assetloading/SwapOnRepeatHandler.h
@@ -59,6 +59,11 @@ class SwapOnRepeatHandler {
* swap.
*/
bool onSwapFirstPlay;
+ /**
+ * @brief True if the handler requires to keep the current scene's CRS
+ * bounding box (default), False otherwise.
+ */
+ bool keepCRS;
public:
/**
@@ -179,6 +184,19 @@ class SwapOnRepeatHandler {
* @see SwapOnRepeatHandler::mPrimitives
*/
std::vector & getBaselinePrimitives();
+ /**
+ * @brief Set the keepCRS flag.
+ * @param keepCRS The new keepCRS flag for the handler.
+ * @see SwapOnRepeatHandler::keepCRS
+ */
+ inline void setKeepCRS(bool const keepCRS) {this->keepCRS = keepCRS;}
+ /**
+ * @brief Check whether the current keepCRS flag.
+ * @return True if the handler requires to keep the current scene's CRS,
+ * False otherwise.
+ * @see SwapOnRepeatHandler::keepCRS
+ */
+ inline bool isKeepCRS() const {return keepCRS;}
protected:
// *** UTIL METHODS *** //
diff --git a/src/assetloading/XmlSceneLoader.cpp b/src/assetloading/XmlSceneLoader.cpp
index 8dc5df25d..eafe6a159 100644
--- a/src/assetloading/XmlSceneLoader.cpp
+++ b/src/assetloading/XmlSceneLoader.cpp
@@ -236,7 +236,14 @@ shared_ptr XmlSceneLoader::loadScenePartSwaps(
"int",
1
));
+ bool const keepCRS = boost::get(XmlUtils::getAttribute(
+ swapNodes,
+ "keepCRS",
+ "bool",
+ sorh->isKeepCRS()
+ ));
sorh->pushTimeToLive(swapStep);
+ sorh->setKeepCRS(keepCRS);
tinyxml2::XMLElement *filterNodes =
swapNodes->FirstChildElement("filter");
std::deque swapFilters;
@@ -578,7 +585,7 @@ void XmlSceneLoader::handleDynamicSceneAttributes(
))
);
// Apply automatic CRS translation when requested
- glm::dvec3 const &shift = scene->getBBoxCRS()->getMin();
+ glm::dvec3 const &shift = scene->getBBoxCRS()->getCentroid();
size_t const numDynObjects = scene->numDynObjects();
for (size_t i = 0; i < numDynObjects; ++i) {
std::shared_ptr dsmo =
diff --git a/src/assetloading/XmlSurveyLoader.cpp b/src/assetloading/XmlSurveyLoader.cpp
index 3a975221a..a921a2f0e 100644
--- a/src/assetloading/XmlSurveyLoader.cpp
+++ b/src/assetloading/XmlSurveyLoader.cpp
@@ -90,6 +90,9 @@ XmlSurveyLoader::createSurveyFromXml(
(float)survey->scanner->getWavelength(), assetsDir, "spectra");
spectralLibrary.readReflectances();
spectralLibrary.setReflectances(survey->scanner->platform->scene.get());
+ survey->scanner->platform->scene->setDefaultReflectance(
+ spectralLibrary.getDefaultReflectance()
+ );
// Update materials for all swap on repeat handlers
for(std::shared_ptr sp : survey->scanner->platform->scene->parts){
diff --git a/src/pybinds/PyAABBWrapper.h b/src/pybinds/PyAABBWrapper.h
index eb707bdf0..dac379d5e 100644
--- a/src/pybinds/PyAABBWrapper.h
+++ b/src/pybinds/PyAABBWrapper.h
@@ -1,6 +1,7 @@
#pragma once
#include
+#include
namespace pyhelios{
diff --git a/src/pybinds/PyHelios.cpp b/src/pybinds/PyHelios.cpp
index 9aa231c17..5d1ecebfb 100644
--- a/src/pybinds/PyHelios.cpp
+++ b/src/pybinds/PyHelios.cpp
@@ -1507,11 +1507,21 @@ BOOST_PYTHON_MODULE(_pyhelios){
&PyPlatformWrapper::getPositionPython,
return_value_policy()
)
+ .def(
+ "getPosition",
+ &PyPlatformWrapper::getPositionPython,
+ return_value_policy()
+ )
.def(
"getAttitudePython",
&PyPlatformWrapper::getAttitudePython,
return_internal_reference<>()
)
+ .def(
+ "getAttitude",
+ &PyPlatformWrapper::getAttitudePython,
+ return_internal_reference<>()
+ )
.def(
"getCachedAbsolutePosition",
&PyPlatformWrapper::getCachedAbsolutePosition,
@@ -1592,6 +1602,22 @@ BOOST_PYTHON_MODULE(_pyhelios){
"update",
&PyPrimitiveWrapper::update
)
+ .def(
+ "isTriangle",
+ &PyPrimitiveWrapper::isTriangle
+ )
+ .def(
+ "isAABB",
+ &PyPrimitiveWrapper::isAABB
+ )
+ .def(
+ "isVoxel",
+ &PyPrimitiveWrapper::isVoxel
+ )
+ .def(
+ "isDetailedVoxel",
+ &PyPrimitiveWrapper::isDetailedVoxel
+ )
;
// Register Material
@@ -1739,6 +1765,37 @@ BOOST_PYTHON_MODULE(_pyhelios){
&PyScenePartWrapper::getObserverStep,
&PyScenePartWrapper::setObserverStep
)
+ .def(
+ "getPrimitive",
+ &PyScenePartWrapper::getPrimitive,
+ return_value_policy()
+ )
+ .def(
+ "getNumPrimitives",
+ &PyScenePartWrapper::getNumPrimitives
+ )
+ .def(
+ "computeCentroid",
+ &PyScenePartWrapper::computeCentroid
+ )
+ .def(
+ "computeBound",
+ &PyScenePartWrapper::computeBound
+ )
+ .def(
+ "getCentroid",
+ &PyScenePartWrapper::getCentroid,
+ return_value_policy()
+ )
+ .def(
+ "getBound",
+ &PyScenePartWrapper::getBound,
+ return_value_policy()
+ )
+ .def(
+ "translate",
+ &PySceneWrapper::translate
+ )
;
// Register Scene
@@ -1758,6 +1815,10 @@ BOOST_PYTHON_MODULE(_pyhelios){
&PySceneWrapper::getPrimitive,
return_value_policy()
)
+ .def(
+ "getNumPrimitives",
+ &PySceneWrapper::getNumPrimitives
+ )
.def(
"getAABB",
&PySceneWrapper::getAABB,
@@ -1800,6 +1861,20 @@ BOOST_PYTHON_MODULE(_pyhelios){
&PySceneWrapper::getDynSceneStep,
&PySceneWrapper::setDynSceneStep
)
+ .def(
+ "getBBox",
+ &PySceneWrapper::getBBox,
+ return_value_policy()
+ )
+ .def(
+ "getBBoxCRS",
+ &PySceneWrapper::getBBoxCRS,
+ return_value_policy()
+ )
+ .def(
+ "translate",
+ &PySceneWrapper::translate
+ )
;
// Register AABB
diff --git a/src/pybinds/PyPrimitiveWrapper.h b/src/pybinds/PyPrimitiveWrapper.h
index a3196e306..1ced586fb 100644
--- a/src/pybinds/PyPrimitiveWrapper.h
+++ b/src/pybinds/PyPrimitiveWrapper.h
@@ -5,6 +5,10 @@
#include
#include
#include
+#include
+#include
+#include
+#include
namespace pyhelios{
@@ -67,6 +71,14 @@ class PyPrimitiveWrapper{
size_t getNumVertices(){return prim->getNumVertices();}
PyVertexWrapper * getVertex(size_t index)
{return new PyVertexWrapper(prim->getVertices()+index);}
+ bool isTriangle () const
+ {return dynamic_cast(prim) != nullptr;}
+ bool isAABB () const
+ {return dynamic_cast(prim) != nullptr;}
+ bool isVoxel () const
+ {return dynamic_cast(prim) != nullptr;}
+ bool isDetailedVoxel () const
+ {return dynamic_cast(prim) != nullptr;}
void update(){prim->update();}
diff --git a/src/pybinds/PyScenePartWrapper.cpp b/src/pybinds/PyScenePartWrapper.cpp
index 9b0b8400b..be510c3a7 100644
--- a/src/pybinds/PyScenePartWrapper.cpp
+++ b/src/pybinds/PyScenePartWrapper.cpp
@@ -1,7 +1,23 @@
#include
#include
+#include
+#include
using pyhelios::PyScenePartWrapper;
+using pyhelios::PyPrimitiveWrapper;
+
+// *** UTIL METHODS *** //
+// ************************ //
+void PyScenePartWrapper::translate(double const x, double const y, double const z){
+ glm::dvec3 const v(x, y, z);
+ for(Primitive *p : sp.mPrimitives) p->translate(v);
+}
+
+// *** GETTERs and SETTERs *** //
+// ***************************** //
+PyPrimitiveWrapper * PyScenePartWrapper::getPrimitive(size_t const index){
+ return new PyPrimitiveWrapper(sp.mPrimitives[index]);
+}
// *** INTERNAL USE *** //
// ********************** //
diff --git a/src/pybinds/PyScenePartWrapper.h b/src/pybinds/PyScenePartWrapper.h
index 5551e1e15..ef15bba02 100644
--- a/src/pybinds/PyScenePartWrapper.h
+++ b/src/pybinds/PyScenePartWrapper.h
@@ -4,9 +4,13 @@
#include
#include
#include
+#include
+
namespace pyhelios{
+class PyPrimitiveWrapper;
+
/**
* @author Alberto M. Esmoris Pena
* @version 1.0
@@ -46,6 +50,19 @@ class PyScenePartWrapper{
{return _asDynMovingObject().getObserverStepInterval();}
void setObserverStep(size_t const stepInterval)
{_asDynMovingObject().setObserverStepInterval(stepInterval);}
+ PyPrimitiveWrapper * getPrimitive(size_t const index);
+ size_t getNumPrimitives() const {return sp.mPrimitives.size();}
+ PythonDVec3 * getCentroid() {return new PythonDVec3(sp.centroid);}
+ PyAABBWrapper * getBound() {return new PyAABBWrapper(sp.bound.get());}
+
+ // *** UTIL METHODS *** //
+ // ************************ //
+ void computeCentroid(bool const computeBound=false)
+ {sp.computeCentroid(computeBound);}
+ void computeBound() {sp.computeCentroid(true);}
+ void translate(double const x, double const y, double const z);
+
+
// *** INTERNAL USE *** //
diff --git a/src/pybinds/PySceneWrapper.cpp b/src/pybinds/PySceneWrapper.cpp
index dac0140db..ae4dda37a 100644
--- a/src/pybinds/PySceneWrapper.cpp
+++ b/src/pybinds/PySceneWrapper.cpp
@@ -3,6 +3,13 @@
using pyhelios::PySceneWrapper;
+// *** METHODS *** //
+// ******************* //
+void PySceneWrapper::translate(double const x, double const y, double const z){
+ glm::dvec3 const v(x, y, z);
+ for(Primitive *p : scene.primitives) p->translate(v);
+}
+
// *** INTERNAL USE *** //
// ********************** //
DynScene & PySceneWrapper::_asDynScene(){
diff --git a/src/pybinds/PySceneWrapper.h b/src/pybinds/PySceneWrapper.h
index fa4361a4f..45450596d 100644
--- a/src/pybinds/PySceneWrapper.h
+++ b/src/pybinds/PySceneWrapper.h
@@ -48,8 +48,9 @@ class PySceneWrapper{
scene.primitives.push_back(dv);
return new PyDetailedVoxelWrapper(dv);
}
- PyPrimitiveWrapper * getPrimitive(size_t index)
+ PyPrimitiveWrapper * getPrimitive(size_t const index)
{return new PyPrimitiveWrapper(scene.primitives[index]);}
+ size_t getNumPrimitives() const {return scene.primitives.size();}
PyAABBWrapper * getAABB()
{return new PyAABBWrapper(scene.getAABB().get());}
PythonDVec3 * getGroundPointAt(double x, double y, double z){
@@ -74,12 +75,17 @@ class PySceneWrapper{
size_t getDynSceneStep(){return _asDynScene().getStepInterval();}
void setDynSceneStep(size_t const stepInterval)
{_asDynScene().setStepInterval(stepInterval);}
+ PyAABBWrapper * getBBox()
+ {return new PyAABBWrapper(scene.getBBox().get());}
+ PyAABBWrapper * getBBoxCRS()
+ {return new PyAABBWrapper(scene.getBBoxCRS().get());}
// *** M E T H O D S *** //
// *********************** //
bool finalizeLoading() {return scene.finalizeLoading();}
void writeObject(std::string path) {scene.writeObject(path);}
+ void translate(double const x, double const y, double const z);
// *** INTERNAL USE *** //
// ********************** //
diff --git a/src/pybinds/PythonDVec3.h b/src/pybinds/PythonDVec3.h
index 39e38434b..b63da1f88 100644
--- a/src/pybinds/PythonDVec3.h
+++ b/src/pybinds/PythonDVec3.h
@@ -2,6 +2,7 @@
#include
#include
+#include
namespace pyhelios{
@@ -30,6 +31,10 @@ class PythonDVec3 {
this->v = v;
release = false;
}
+ PythonDVec3(arma::colvec const v){
+ this->v = new glm::dvec3(v[0], v[1], v[2]);
+ release = true;
+ }
virtual ~PythonDVec3(){
if(release && v!=nullptr) delete v;
}
diff --git a/src/scanner/ScanningDevice.cpp b/src/scanner/ScanningDevice.cpp
index a74efce39..b8708b8eb 100644
--- a/src/scanner/ScanningDevice.cpp
+++ b/src/scanner/ScanningDevice.cpp
@@ -286,10 +286,8 @@ void ScanningDevice::computeSubrays(
){
size_t const numSubrays = cached_subrayRotation.size();
for(size_t i = 0 ; i < numSubrays ; ++i) {
- #if DATA_ANALYTICS >=2
- bool subrayHit;
- #endif
#if DATA_ANALYTICS >=2
+ bool subrayHit;
std::vector subraySimRecord(
14, std::numeric_limits::quiet_NaN()
);
diff --git a/src/scanner/detector/AbstractPulseRunnable.cpp b/src/scanner/detector/AbstractPulseRunnable.cpp
index e3940273a..7c6dc441b 100644
--- a/src/scanner/detector/AbstractPulseRunnable.cpp
+++ b/src/scanner/detector/AbstractPulseRunnable.cpp
@@ -106,7 +106,7 @@ void AbstractPulseRunnable::capturePoint(
void AbstractPulseRunnable::capturePulse(glm::dvec3 const &beamDir){
if(detector->pulseRecordYielder != nullptr)
detector->pulseRecordYielder->push(PulseRecord(
- pulse.getOrigin() + scene.getShiftRef(), // Pulse's origin
+ pulse.getOrigin() + scene.getShift(), // Pulse's origin
beamDir, // Pulse's direction
pulse.getTime(), // Pulse's time (ns)
pulse.getPulseNumber(), // Pulse index
diff --git a/src/scene/Scene.cpp b/src/scene/Scene.cpp
index 7fdb07939..97aa11fe3 100644
--- a/src/scene/Scene.cpp
+++ b/src/scene/Scene.cpp
@@ -96,7 +96,7 @@ bool Scene::finalizeLoading(bool const safe) {
// Store original bounding box (CRS coordinates):
this->bbox_crs = AABB::getForPrimitives(primitives);
- glm::dvec3 diff = this->bbox_crs->getMin();
+ glm::dvec3 const diff = this->bbox_crs->getCentroid();
stringstream ss;
ss << "CRS bounding box (by vertices): " << this->bbox_crs->toString()
<< "\nShift: " << glm::to_string(diff)
@@ -215,7 +215,7 @@ Scene::getIntersections(
);
}
-glm::dvec3 Scene::getShift() { return this->bbox_crs->getMin(); }
+glm::dvec3 Scene::getShift() { return this->bbox_crs->getCentroid(); }
vector Scene::getAllVertices(){
unordered_set vset;
diff --git a/src/scene/Scene.h b/src/scene/Scene.h
index 753f0fb2d..297486e3c 100644
--- a/src/scene/Scene.h
+++ b/src/scene/Scene.h
@@ -124,6 +124,17 @@ class Scene : public Asset {
* @see Scene::getIntersections
*/
std::shared_ptr raycaster;
+ /**
+ * @brief The default reflectance that must be used for primitives with
+ * not reflectance (NaN). It is typically loaded through the spectral
+ * library and then assigned to the scene so it can be known when needed.
+ * @see Scene::setDefaultReflectance
+ * @see Scene::getDefaultReflectance
+ * @see SpectralLibrary
+ * @see SpectralLibrary::defaultReflectance
+ * @see XmlSurveyLoader::createSurveyFromXml
+ */
+ double defaultReflectance = 50.0;
public:
/**
@@ -244,12 +255,6 @@ class Scene : public Asset {
* before translating to (0, 0, 0)
*/
glm::dvec3 getShift();
- /**
- * @brief Like Scene::getShift but returning a const reference instead of a
- * copy
- * @see Scene::getShift
- */
- inline glm::dvec3 const& getShiftRef() const {return bbox_crs->getMin();}
/**
* @brief Obtain all vertices (without repetitions) composing the scene
@@ -473,6 +478,31 @@ class Scene : public Asset {
* @see ScenePart::sorh
*/
std::vector> getSwapOnRepeatObjects();
+ /**
+ * @brief Set the default reflectance that must be used for primitives
+ * that have no assigned reflectance.
+ * @param defaultReflectance The new default reflectance.
+ * @see Scene::defaultReflectance
+ * @see Scene::getDefaultReflectance
+ * @see SpectralLibrary
+ * @see SpectralLibrary::defaultReflectance
+ * @see XmlSurveyLoader::createSurveyFromXml
+ */
+ inline void setDefaultReflectance(double const defaultReflectance)
+ {this->defaultReflectance = defaultReflectance;}
+ /**
+ * @brief Get the default reflectance that must be used for primitives that
+ * have not got an assigned reflectance.
+ * @return The default reflectance for primitives that have not got an
+ * assigned reflectance.
+ * @see Scene::setDefaultReflectance
+ * @see SpectralLibrary
+ * @see SpectralLibrary::defaultReflectance
+ * @see XmlSurveyLoader::createSurveyFromXml
+ */
+ inline double getDefaultReflectance() const {return defaultReflectance;}
+
+
// *** READ/WRITE *** //
// ********************* //
diff --git a/src/sim/comps/SimulationPlayer.cpp b/src/sim/comps/SimulationPlayer.cpp
index 3e27f51e2..b40d4d21c 100644
--- a/src/sim/comps/SimulationPlayer.cpp
+++ b/src/sim/comps/SimulationPlayer.cpp
@@ -2,14 +2,13 @@
#include
#include
#include
-#include
+#include
#include
using helios::filems::FMSFacadeFactory;
#include
#include
#include
-#include
#include
using namespace std::chrono;
@@ -34,6 +33,7 @@ void SimulationPlayer::endPlay(){
// Get all the scene parts (objects) that will do a swap on repeat
std::vector> swapOnRepeatObjects =
scene.getSwapOnRepeatObjects();
+ bool const keepCRS = isKeepCRS(swapOnRepeatObjects);
std::stringstream ss;
for(std::shared_ptr sp : swapOnRepeatObjects){
ss.str("");
@@ -70,9 +70,9 @@ void SimulationPlayer::endPlay(){
// Restart scanner
logging::DEBUG("Restarting scanner for next simulation play ...");
restartScanner(*sim.getScanner());
- // Restar scene
+ // Restart scene
logging::DEBUG("Restarting scene for next simulation play ...");
- restartScene(*sim.getScanner()->platform->scene);
+ restartScene(*sim.getScanner()->platform->scene, keepCRS);
// Restart simulation
logging::DEBUG("Restarting context for next simulation play ...");
restartSimulation(sim);
@@ -156,19 +156,21 @@ void SimulationPlayer::restartScanner(Scanner &sc){
sh.setCurrentRotateAngle_rad(sh.getRotateStart());
}
-void SimulationPlayer::restartScene(Scene &scene){
+void SimulationPlayer::restartScene(Scene &scene, bool const keepCRS){
// Discard scene parts that should be null for the next play, and
// get primitives from current scene parts (thus, those that belong to
- // non existent scene parts will be discarded)
+ // non-existent scene parts will be discarded)
std::vector> newParts;
std::vector newPrims;
- glm::dvec3 const diff = scene.getBBoxCRS()->getMin();
+ glm::dvec3 const oldCRSCenter = scene.getBBoxCRS()->getCentroid();
+ glm::dvec3 const oldFinalCenter = scene.getBBox()->getCentroid();
+ AABB const oldCRSBBox = *scene.getBBoxCRS();
for(std::shared_ptr sp : scene.parts){
// Handle scene parts that need to be discarded
if(sp->sorh != nullptr && sp->sorh->needsDiscardOnReplay()){
for(Primitive * p: sp->sorh->getBaselinePrimitives()) delete p;
for(Primitive * p: sp->mPrimitives) delete p;
- sp->sorh = nullptr;
+ //sp->sorh = nullptr; // TODO Rethink : Remove to enable rebirth?
continue;
}
// Handle scene parts that must be preserved
@@ -179,9 +181,8 @@ void SimulationPlayer::restartScene(Scene &scene){
for(Primitive * p : sp->mPrimitives){
Vertex *v = p->getVertices();
for(size_t i = 0 ; i < p->getNumVertices() ; ++i){
- v[i].pos = v[i].pos + diff;
+ v[i].pos = v[i].pos + oldCRSCenter;
}
- p->update();
}
}
// Handle scene parts who are in the first play after a swap
@@ -199,8 +200,94 @@ void SimulationPlayer::restartScene(Scene &scene){
}
scene.parts = newParts;
scene.primitives = newPrims;
- // Reload scene
+ // Apply default reflectances when needed
+ for(Primitive * p : scene.primitives){
+ Material &mat = *p->material;
+ if(std::isnan(mat.reflectance)){
+ mat.reflectance = scene.getDefaultReflectance();
+ }
+ }
+ // Reload scene (without KDDGrove rebuilding)
+ std::shared_ptr kdgf = scene.getKDGroveFactory();
+ scene.setKDGroveFactory(nullptr);
scene.finalizeLoading(false);
+ // Keep CRS if requested
+ if(keepCRS){
+ // Undo current CRS shift on primitives
+ glm::dvec3 const currentDiff = scene.getBBoxCRS()->getCentroid();
+ double xmin = std::numeric_limits::max();
+ double xmax = std::numeric_limits::lowest();
+ double ymin=xmin, ymax=xmax, zmin=xmin, zmax=xmax;
+ for(std::shared_ptr sp : scene.parts) {
+ for (Primitive *p: sp->mPrimitives) {
+ Vertex *v = p->getVertices();
+ for (size_t i = 0; i < p->getNumVertices(); ++i) {
+ v[i].pos = v[i].pos + currentDiff;
+ }
+ p->update();
+ // Also, find min and max coordinates
+ for (size_t i = 0; i < p->getNumVertices(); ++i) {
+ glm::dvec3 const pos = v[i].pos;
+ if(pos.x < xmin) xmin = pos.x;
+ if(pos.x > xmax) xmax = pos.x;
+ if(pos.y < ymin) ymin = pos.y;
+ if(pos.y > ymax) ymax = pos.y;
+ if(pos.z < zmin) zmin = pos.z;
+ if(pos.z > zmax) zmax = pos.z;
+ }
+ }
+ }
+ // Compute the size of the new bounding box
+ glm::dvec3 const oldCRSMin = oldCRSBBox.getMin();
+ glm::dvec3 const oldCRSMax = oldCRSBBox.getMax();
+ if(oldCRSMin.x < xmin) xmin=oldCRSMin.x;
+ if(oldCRSMin.y < ymin) ymin=oldCRSMin.y;
+ if(oldCRSMin.z < zmin) zmin=oldCRSMin.z;
+ if(oldCRSMax.x > xmax) xmax=oldCRSMax.x;
+ if(oldCRSMax.y > ymax) ymax=oldCRSMax.y;
+ if(oldCRSMax.z > zmax) zmax=oldCRSMax.z;
+ glm::dvec3 const minOffset = glm::abs(
+ oldCRSCenter-glm::dvec3(xmin, ymin, zmin)
+ );
+ glm::dvec3 const maxOffset = glm::abs(
+ oldCRSCenter-glm::dvec3(xmax, ymax, zmax)
+ );
+ glm::dvec3 const halfSize(
+ std::max(minOffset.x, maxOffset.x),
+ std::max(minOffset.y, maxOffset.y),
+ std::max(minOffset.z, maxOffset.z)
+ );
+ // Calculate new CRS bounding box
+ glm::dvec3 const newCRSMin = oldCRSCenter - halfSize;
+ glm::dvec3 const newCRSMax = oldCRSCenter + halfSize;
+ std::shared_ptr newCRS = std::make_shared(
+ newCRSMin, newCRSMax
+ );
+ // Calculate new final bounding box
+ glm::dvec3 const newBBoxMin = oldFinalCenter - halfSize;
+ glm::dvec3 const newBBoxMax = oldFinalCenter + halfSize;
+ std::shared_ptr newBBox = std::make_shared(
+ newBBoxMin, newBBoxMax
+ );
+ // Apply new CRS shift on primitives
+ for(std::shared_ptr sp : scene.parts) {
+ for (Primitive *p: sp->mPrimitives) {
+ Vertex *v = p->getVertices();
+ for (size_t i = 0; i < p->getNumVertices(); ++i) {
+ v[i].pos = v[i].pos - oldCRSCenter;
+ }
+ p->update();
+ }
+ }
+ // Assign bounding boxes to scene
+ scene.setBBoxCRS(newCRS);
+ scene.setBBox(newBBox);
+ // Report
+ logging::DEBUG("SimulationPlayer::restartScene kept the scene's CRS.");
+ }
+ // Rebuild KDGrove
+ scene.setKDGroveFactory(kdgf);
+ scene.buildKDGroveWithLog(false);
}
void SimulationPlayer::restartSimulation(Simulation &sim){
@@ -228,3 +315,26 @@ void SimulationPlayer::restartSimulation(Simulation &sim){
logging::DEBUG("Restarting first leg ...");
sp.startLeg(sp.mCurrentLegIndex, true);
}
+
+bool SimulationPlayer::isKeepCRS(
+ std::vector> const &sorObjects
+){
+ // Check Keep CRS flag for each swap-on-repeat handler
+ bool anyFalse = false, anyTrue = false;
+ for(std::shared_ptr const & sp : sorObjects){
+ if(!sp->sorh->isKeepCRS()) anyFalse = true;
+ else anyTrue = true;
+ }
+ // Report that both trues and falses were found
+ if(anyFalse && anyTrue){
+ logging::WARN(
+ "SimulationPlayer::isKeepCRS found at least one "
+ "SwapOnRepeatHandler with a KeepCRS flag set to true and another "
+ "one with the flag set to false.\n"
+ "Consequently, the CRS of the current scene will not be kept "
+ "because at least one flag is set to false."
+ );
+ }
+ // Return true to keep CRS only if no SoRH has the flag to false
+ return !anyFalse;
+}
diff --git a/src/sim/comps/SimulationPlayer.h b/src/sim/comps/SimulationPlayer.h
index da2121ff9..331088fb7 100644
--- a/src/sim/comps/SimulationPlayer.h
+++ b/src/sim/comps/SimulationPlayer.h
@@ -2,12 +2,14 @@
class Simulation;
class Scene;
+class ScenePart;
class Platform;
namespace helios { namespace filems { class FMSFacade; }}
using helios::filems::FMSFacade;
class Scanner;
#include
+#include
/**
* @author Alberto M. Esmoris Pena
@@ -122,10 +124,12 @@ class SimulationPlayer {
* @brief Restart a scene to its start point, considering the swapped
* geometries.
* @param scene The scene to be restarted.
+ * @param keepCRS Whether to keep the current scene's CRS (true) or not
+ * (false).
* @see Scene
* @see SwapOnRepeatHandler
*/
- void restartScene(Scene &scene);
+ void restartScene(Scene &scene, bool const keepCRS=true);
/**
* @brief Restart a simulation to its start point.
* @param sim The simulation to be restarted.
@@ -133,4 +137,25 @@ class SimulationPlayer {
* @see SimulationPlayer::sim
*/
void restartSimulation(Simulation &sim);
+ /**
+ * @brief Check whether the current scene's CRS must be preserved (true) or
+ * not (false).
+ *
+ * The CRS will be preserved iff the keepCRS flag of each
+ * SwapOnRepeatHandler is set to True. It will be updated even if just
+ * one single handler has the keepCRS flag set to False.
+ *
+ * @param sorObjects The scene parts that have a swap on repeat handler.
+ * They can be obtained through the Scene::getSwapOnRepeatObjects
+ * method.
+ * @return True if the current scene's CRS must be preserved, false
+ * otherwise.
+ * @see SwapOnRepeatHandler
+ * @see SwapOnRepeatHandler::keepCRS
+ * @see SwapOnRepeatHandler::isKeepCRS
+ * @see ScenePart
+ * @see Scene
+ * @see Scene::getSwapOnRepeatObjects
+ */
+ bool isKeepCRS(std::vector> const &sorObjects);
};
\ No newline at end of file
diff --git a/src/sim/core/Simulation.cpp b/src/sim/core/Simulation.cpp
index 58f7073aa..66b576be4 100644
--- a/src/sim/core/Simulation.cpp
+++ b/src/sim/core/Simulation.cpp
@@ -10,10 +10,10 @@ using namespace std::chrono;
#include
#include
#ifdef DATA_ANALYTICS
-#include
#include
using helios::analytics::HDA_StateJSONReporter;
using helios::analytics::HDA_SimStepRecorder;
+using helios::analytics::HDA_Recorder;
#endif
#include "Simulation.h"
@@ -149,7 +149,11 @@ void Simulation::start() {
ss.str("");
ss << "Starting simulation loop " << simLoopIndex+1 << " ...";
logging::INFO(ss.str());
- doSimLoop();
+ doSimLoop(
+#ifdef DATA_ANALYTICS
+ ssr
+#endif
+ );
// NOTE there is no need for a sync. barrier after the last iteration
// because end of simulation will handle it.
ss.str("");
@@ -191,7 +195,14 @@ void Simulation::start() {
shutdown();
}
-void Simulation::doSimLoop(){
+void Simulation::doSimLoop(
+#ifdef DATA_ANALYTICS
+ HDA_Recorder &_ssr
+#endif
+){
+#ifdef DATA_ANALYTICS
+ HDA_SimStepRecorder &ssr = static_cast(_ssr);
+#endif
size_t iter = 1;
// Execute the main loop of the simulation
while (!isStopped()) {
diff --git a/src/sim/core/Simulation.h b/src/sim/core/Simulation.h
index 9442ce0f3..6c07c6c0f 100644
--- a/src/sim/core/Simulation.h
+++ b/src/sim/core/Simulation.h
@@ -9,6 +9,7 @@
#include
#ifdef DATA_ANALYTICS
#include
+#include
#endif
#include
#include
@@ -216,7 +217,11 @@ class Simulation {
* the computations between the first and the last leg of the survey.
* @return The number of iterations run in the simulation loop.
*/
- virtual void doSimLoop();
+ virtual void doSimLoop(
+#ifdef DATA_ANALYTICS
+ helios::analytics::HDA_Recorder &ssr
+#endif
+ );
/**
* @brief Stop the simulation
*
diff --git a/src/test/SerializationTest.h b/src/test/SerializationTest.h
index 61d72ebfc..f6e8aed84 100644
--- a/src/test/SerializationTest.h
+++ b/src/test/SerializationTest.h
@@ -115,19 +115,30 @@ bool SerializationTest::run(){
}
scene1.finalizeLoading(true);
shared_ptr kdgf = scene1.getKDGroveFactory();
- scene1.setKDGroveFactory(nullptr);
scene1.writeObject(path);
scene1.setKDGroveFactory(kdgf);
Scene *scene2 = Scene::readObject(path);
- scene2->setKDGroveFactory(kdgf);
- scene2->finalizeLoading(true);
- if(!validate(dv1, *(DetailedVoxel *) scene2->primitives[0])) return false;
- if(!validate(t1, *(Triangle *) scene2->primitives[1])) return false;
- if(!validate(v1, *(Voxel *) scene2->primitives[2])) return false;
- if(!validate(box1, *(AABB *) scene2->primitives[3])) return false;
+ if(!validate(
+ *(DetailedVoxel *) scene1.primitives[0],
+ *(DetailedVoxel *) scene2->primitives[0]
+ )) return false;
+ if(!validate(
+ *(Triangle *) scene1.primitives[1],
+ *(Triangle *) scene2->primitives[1]
+ )) return false;
+ if(!validate(
+ *(Voxel *) scene1.primitives[2],
+ *(Voxel *) scene2->primitives[2]
+ )) return false;
+ if(!validate(
+ *(AABB *) scene1.primitives[3],
+ *(AABB *) scene2->primitives[3]
+ )) return false;
for(size_t i = 0 ; i < nRepeats ; i++){
- if(!validate(dv1, *(DetailedVoxel *) scene2->primitives[i+4]))
- return false;
+ if(!validate(
+ *(DetailedVoxel *) scene1.primitives[i+4],
+ *(DetailedVoxel *) scene2->primitives[i+4]
+ )) return false;
}