Skip to content

Commit

Permalink
Merge pull request #316 from favreau/master
Browse files Browse the repository at this point in the history
Added surface and contour representation to neurons
  • Loading branch information
favreau authored Oct 12, 2023
2 parents 34a7b49 + c7421aa commit fadb78b
Show file tree
Hide file tree
Showing 7 changed files with 123 additions and 23 deletions.
19 changes: 15 additions & 4 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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)
Expand Down
4 changes: 3 additions & 1 deletion bioexplorer/backend/science/common/Types.h
Original file line number Diff line number Diff line change
Expand Up @@ -438,7 +438,9 @@ enum class MorphologyRepresentation
section = 1,
segment = 2,
orientation = 3,
bezier = 4
bezier = 4,
contour = 5,
surface = 6
};

enum class MorphologyRealismLevel
Expand Down
22 changes: 11 additions & 11 deletions bioexplorer/backend/science/meshing/SurfaceMesher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -94,10 +94,10 @@ ModelDescriptorPtr SurfaceMesher::generateSurface(core::Scene& scene, const std:
}

std::list<Weighted_point> 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);
Expand All @@ -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<Weighted_point> 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);
Expand All @@ -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());
Expand Down
8 changes: 4 additions & 4 deletions bioexplorer/backend/science/meshing/SurfaceMesher.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
86 changes: 83 additions & 3 deletions bioexplorer/backend/science/morphologies/Neurons.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@
#include <platform/core/engineapi/Model.h>
#include <platform/core/engineapi/Scene.h>

#ifdef USE_CGAL
#include <science/meshing/PointCloudMesher.h>
#include <science/meshing/SurfaceMesher.h>
#endif

#include <omp.h>

using namespace core;
Expand All @@ -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;
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions bioexplorer/backend/science/morphologies/Neurons.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
2 changes: 2 additions & 0 deletions bioexplorer/pythonsdk/bioexplorer/bio_explorer.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit fadb78b

Please sign in to comment.