From c7421aacabe45718e0270f48340a22a324fb9384 Mon Sep 17 00:00:00 2001 From: Cyrille Pierre Henri Favreau Date: Thu, 12 Oct 2023 18:02:34 +0200 Subject: [PATCH] Added surface and contour representation to neurons --- CMakeLists.txt | 19 +++- bioexplorer/backend/science/common/Types.h | 4 +- .../backend/science/meshing/SurfaceMesher.cpp | 22 ++--- .../backend/science/meshing/SurfaceMesher.h | 8 +- .../backend/science/morphologies/Neurons.cpp | 86 ++++++++++++++++++- .../backend/science/morphologies/Neurons.h | 5 ++ .../pythonsdk/bioexplorer/bio_explorer.py | 2 + 7 files changed, 123 insertions(+), 23 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4a28d6192..e33cdf1b4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -48,6 +48,7 @@ find_package(CUDA) find_package(assimp) find_package(LibJpegTurbo) find_package(Rockets) +find_package(CGAL) set(BIOEXPLORER_SOURCE_DIRS ${PROJECT_SOURCE_DIR} ${CMAKE_BINARY_DIR}/generated) @@ -89,13 +90,23 @@ if(OptiX7_FOUND AND CUDA_FOUND) if(PLATFORM_OPTIX7_ENABLED) set(OPTIX7_FOUND 1) message(STATUS "[Core] OptiX 7 module enabled") + else() + message(STATUS "[Core] Optix 7 module disabled") endif() +else() + message(STATUS "[Core] Optix 7 not found") endif() -set(CGAL_FOUND 0) -if(${${NAME}_USE_CGAL}) - set(CGAL_DATA_DIR ".") - find_package(CGAL REQUIRED) +if(CGAL_FOUND) + if(${${NAME}_USE_CGAL}) + message(STATUS "[Core] CGAL module enabled") + set(CGAL_DATA_DIR ".") + find_package(CGAL REQUIRED) + else() + message(STATUS "[Core] CGAL module disabled") + endif() +else() + message(STATUS "[Core] CGAL module not found") endif() set(ASSIMP_FOUND 0) diff --git a/bioexplorer/backend/science/common/Types.h b/bioexplorer/backend/science/common/Types.h index 1d10fa500..94187fa46 100644 --- a/bioexplorer/backend/science/common/Types.h +++ b/bioexplorer/backend/science/common/Types.h @@ -438,7 +438,9 @@ enum class MorphologyRepresentation section = 1, segment = 2, orientation = 3, - bezier = 4 + bezier = 4, + contour = 5, + surface = 6 }; enum class MorphologyRealismLevel diff --git a/bioexplorer/backend/science/meshing/SurfaceMesher.cpp b/bioexplorer/backend/science/meshing/SurfaceMesher.cpp index d94448248..c6f5f9103 100644 --- a/bioexplorer/backend/science/meshing/SurfaceMesher.cpp +++ b/bioexplorer/backend/science/meshing/SurfaceMesher.cpp @@ -74,13 +74,13 @@ SurfaceMesher::SurfaceMesher(const uint32_t uuid) { } -ModelDescriptorPtr SurfaceMesher::generateSurface(core::Scene& scene, const std::string& pdbId, const Vector4ds& atoms, +ModelDescriptorPtr SurfaceMesher::generateSurface(core::Scene& scene, const std::string& name, const Vector4ds& points, const double shrinkfactor) { #ifdef USE_CGAL ModelDescriptorPtr modelDescriptor{nullptr}; MeshLoader meshLoader(scene); - const std::string filename = GeneralSettings::getInstance()->getMeshFolder() + pdbId + ".off"; + const std::string filename = GeneralSettings::getInstance()->getMeshFolder() + name + ".off"; try { PLUGIN_INFO(3, "Trying to load surface from cache " << filename); @@ -94,10 +94,10 @@ ModelDescriptorPtr SurfaceMesher::generateSurface(core::Scene& scene, const std: } std::list l; - for (const auto& atom : atoms) - l.push_front(Weighted_point(Point_3(atom.x, atom.y, atom.z), atom.w)); + for (const auto& point : points) + l.push_front(Weighted_point(Point_3(point.x, point.y, point.z), point.w)); - PLUGIN_INFO(3, "Constructing skin surface from " << l.size() << " atoms"); + PLUGIN_INFO(3, "Constructing skin surface from " << l.size() << " point"); Polyhedron polyhedron; Skin_surface_3 skinSurface(l.begin(), l.end(), shrinkfactor); @@ -115,17 +115,17 @@ ModelDescriptorPtr SurfaceMesher::generateSurface(core::Scene& scene, const std: #endif } -ModelDescriptorPtr SurfaceMesher::generateUnionOfBalls(core::Scene& scene, const std::string& pdbId, - const Vector4ds& atoms) +ModelDescriptorPtr SurfaceMesher::generateUnionOfBalls(core::Scene& scene, const std::string& name, + const Vector4ds& points) { #ifdef USE_CGAL std::list l; - for (const auto& atom : atoms) - l.push_front(Weighted_point(Point_3(atom.x, atom.y, atom.z), atom.w)); + for (const auto& point : points) + l.push_front(Weighted_point(Point_3(point.x, point.y, point.z), point.w)); ModelDescriptorPtr modelDescriptor{nullptr}; MeshLoader meshLoader(scene); - const std::string filename = GeneralSettings::getInstance()->getMeshFolder() + pdbId + ".off"; + const std::string filename = GeneralSettings::getInstance()->getMeshFolder() + name + ".off"; try { PLUGIN_INFO(3, "Trying to load union of balls from cache " << filename); @@ -138,7 +138,7 @@ ModelDescriptorPtr SurfaceMesher::generateUnionOfBalls(core::Scene& scene, const PLUGIN_INFO(3, "Failed to load union of balls from cache (" << e.what() << "), constructing it..."); } - PLUGIN_INFO(3, "Constructing union of balls from " << l.size() << " atoms"); + PLUGIN_INFO(3, "Constructing union of balls from " << l.size() << " points"); Polyhedron polyhedron; Union_of_balls_3 union_of_balls(l.begin(), l.end()); diff --git a/bioexplorer/backend/science/meshing/SurfaceMesher.h b/bioexplorer/backend/science/meshing/SurfaceMesher.h index b01ef8182..c45676ebc 100644 --- a/bioexplorer/backend/science/meshing/SurfaceMesher.h +++ b/bioexplorer/backend/science/meshing/SurfaceMesher.h @@ -36,18 +36,18 @@ class SurfaceMesher /** Generates a triangle based mesh model * - * @param atoms atoms used to generate the mesh + * @param points points used to generate the mesh * @param triangles Generated triangles */ - core::ModelDescriptorPtr generateSurface(core::Scene& scene, const std::string& pdbId, const Vector4ds& atoms, + core::ModelDescriptorPtr generateSurface(core::Scene& scene, const std::string& name, const Vector4ds& points, const double shrinkfactor = 0.5); /** Generates a triangle based mesh model * - * @param atoms atoms used to generate the mesh + * @param points points used to generate the mesh * @param triangles Generated triangles */ - core::ModelDescriptorPtr generateUnionOfBalls(core::Scene& scene, const std::string& pdbId, const Vector4ds& atoms); + core::ModelDescriptorPtr generateUnionOfBalls(core::Scene& scene, const std::string& name, const Vector4ds& points); private: uint32_t _uuid; diff --git a/bioexplorer/backend/science/morphologies/Neurons.cpp b/bioexplorer/backend/science/morphologies/Neurons.cpp index 8daa33a85..f87757cdd 100644 --- a/bioexplorer/backend/science/morphologies/Neurons.cpp +++ b/bioexplorer/backend/science/morphologies/Neurons.cpp @@ -37,6 +37,11 @@ #include #include +#ifdef USE_CGAL +#include +#include +#endif + #include using namespace core; @@ -47,6 +52,10 @@ using namespace details; using namespace common; using namespace io; using namespace db; +#ifdef USE_CGAL +using namespace meshing; +#endif + namespace morphology { const uint64_t NB_MYELIN_FREE_SEGMENTS = 4; @@ -133,6 +142,56 @@ void Neurons::_logRealismParams() PLUGIN_INFO(1, "----------------------------------------------------"); } +void Neurons::_buildContours(ThreadSafeContainer& container, const NeuronSomaMap& somas, const size_t baseMaterialId) +{ +#ifdef USE_CGAL + uint64_t progress = 0; + PointCloud pointCloud; + + for (const auto soma : somas) + { + PLUGIN_PROGRESS("Loading somas", progress, somas.size()); + ++progress; + + const Vector3d position = soma.second.position; + pointCloud[baseMaterialId].push_back({position.x, position.y, position.z, _details.radiusMultiplier}); + } + + PointCloudMesher pcm; + pcm.toConvexHull(container, pointCloud); +#else + PLUGIN_THROW("The BioExplorer was not compiled with the CGAL library") +#endif +} + +void Neurons::_buildSurface(const NeuronSomaMap& somas) +{ +#ifdef USE_CGAL + uint64_t progress = 0; + PointCloud pointCloud; + size_t materialId = 0; + + for (const auto soma : somas) + { + PLUGIN_PROGRESS("Loading somas", progress, somas.size()); + ++progress; + + const Vector3d position = soma.second.position; + pointCloud[materialId].push_back({position.x, position.y, position.z, _details.radiusMultiplier}); + } + + SurfaceMesher sm(_uuid); + _modelDescriptor = sm.generateSurface(_scene, _details.assemblyName, pointCloud[materialId]); + if (_modelDescriptor) + _scene.addModel(_modelDescriptor); + else + PLUGIN_THROW("Failed to generate surface") + +#else + PLUGIN_THROW("The BioExplorer was not compiled with the CGAL library") +#endif +} + void Neurons::_buildNeurons() { const auto& connector = DBConnector::getInstance(); @@ -164,13 +223,34 @@ void Neurons::_buildNeurons() _details.loadSomas && !_details.loadAxon && !_details.loadApicalDendrites && !_details.loadBasalDendrites; ThreadSafeContainers containers; - if (somasOnly || _details.morphologyRepresentation == MorphologyRepresentation::orientation) + if (somasOnly || _details.morphologyRepresentation == MorphologyRepresentation::orientation || + _details.morphologyRepresentation == MorphologyRepresentation::contour) { ThreadSafeContainer container(*model, _alignToGrid, _position, _rotation, _scale); - if (_details.morphologyRepresentation == MorphologyRepresentation::orientation) + switch (_details.morphologyRepresentation) + { + case MorphologyRepresentation::orientation: + { _buildOrientations(container, somas, baseMaterialId); - else + break; + } + case MorphologyRepresentation::contour: + { + _buildContours(container, somas, baseMaterialId); + break; + } + case MorphologyRepresentation::surface: + { + _buildSurface(somas); + return; + break; + } + default: + { _buildSomasOnly(container, somas, baseMaterialId); + break; + } + } containers.push_back(container); } else diff --git a/bioexplorer/backend/science/morphologies/Neurons.h b/bioexplorer/backend/science/morphologies/Neurons.h index 738afe5da..33df83521 100644 --- a/bioexplorer/backend/science/morphologies/Neurons.h +++ b/bioexplorer/backend/science/morphologies/Neurons.h @@ -74,6 +74,11 @@ class Neurons : public Morphologies void _buildNeurons(); + void _buildContours(common::ThreadSafeContainer& container, const NeuronSomaMap& somas, + const size_t baseMaterialId); + + void _buildSurface(const NeuronSomaMap& somas); + void _buildSomasOnly(common::ThreadSafeContainer& container, const NeuronSomaMap& somas, const size_t baseMaterialId); diff --git a/bioexplorer/pythonsdk/bioexplorer/bio_explorer.py b/bioexplorer/pythonsdk/bioexplorer/bio_explorer.py index bde62a24e..15c430ea8 100644 --- a/bioexplorer/pythonsdk/bioexplorer/bio_explorer.py +++ b/bioexplorer/pythonsdk/bioexplorer/bio_explorer.py @@ -659,6 +659,8 @@ class BioExplorer: MORPHOLOGY_REPRESENTATION_SEGMENT = 2 MORPHOLOGY_REPRESENTATION_ORIENTATION = 3 MORPHOLOGY_REPRESENTATION_BEZIER = 4 + MORPHOLOGY_REPRESENTATION_CONTOUR = 5 + MORPHOLOGY_REPRESENTATION_SURFACE = 6 MICRO_DOMAIN_REPRESENTATION_MESH = 0 MICRO_DOMAIN_REPRESENTATION_CONVEX_HULL = 1