Skip to content

Commit

Permalink
Merge pull request #386 from favreau/master
Browse files Browse the repository at this point in the history
Added Sphere representation to morphologies and vasculature
  • Loading branch information
favreau authored Aug 29, 2024
2 parents bf6109a + a35c346 commit 99578e4
Show file tree
Hide file tree
Showing 17 changed files with 486 additions and 171 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,6 @@ void MediaMakerPlugin::_exportColorBuffer() const
auto image = frameBuffer.getImage();
ImageBuf rotatedBuf;
ImageBufAlgo::flip(rotatedBuf, image);
swapRedBlue32(rotatedBuf);

// Determine the output format
std::string format = _exportFramesToDiskPayload.format;
Expand Down
21 changes: 19 additions & 2 deletions bioexplorer/backend/science/common/SDFGeometries.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,15 +56,32 @@ Vector4fs SDFGeometries::_getProcessedSectionPoints(const MorphologyRepresentati
const Vector4fs& points)
{
Vector4fs localPoints;
if (representation == MorphologyRepresentation::bezier)
switch (representation)
{
case MorphologyRepresentation::bezier:
{
const uint64_t nbBezierPoints = std::max(static_cast<size_t>(3), points.size() / 10);
const float step = 1.f / static_cast<float>(nbBezierPoints);
for (float i = 0; i <= 1.f; i += step)
localPoints.push_back(getBezierPoint(points, i));
break;
}
else
case MorphologyRepresentation::spheres:
{
for (uint64_t i = 0; i < points.size() - 1; ++i)
{
const auto p1 = points[i];
const auto p2 = points[i + 1];
const auto spheres = fillConeWithSpheres(p1, p2);
localPoints.insert(localPoints.end(), spheres.begin(), spheres.end());
}
break;
}
default:
{
localPoints = points;
}
}
return localPoints;
}

Expand Down
23 changes: 23 additions & 0 deletions bioexplorer/backend/science/common/ThreadSafeContainer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,29 @@ uint64_t ThreadSafeContainer::addCone(const Vector3f& sourcePosition, const floa
userDataOffset});
}

void ThreadSafeContainer::addConeOfSpheres(const Vector3f& sourcePosition, const float sourceRadius,
const Vector3f& targetPosition, const float targetRadius,
const size_t materialId, const uint64_t userDataOffset,
const float constantRadius)
{
const Vector3f scale = _scale;
const Vector3f scaledSrcPosition =
getAlignmentToGrid(_alignToGrid, Vector3f(_position + _rotation * Vector3d(sourcePosition)) * scale);
const auto scaledSrcRadius = sourceRadius * scale.x;
const Vector3f scaledDstPosition =
getAlignmentToGrid(_alignToGrid, Vector3f(_position + _rotation * Vector3d(targetPosition)) * scale);
const auto scaledDstRadius = targetRadius * scale.x;

const auto spheres =
fillConeWithSpheres({scaledSrcPosition, scaledSrcRadius}, {scaledDstPosition, scaledDstRadius}, constantRadius);
for (const auto& sphere : spheres)
_addSphere(materialId, {Vector3f(sphere), sphere.w, userDataOffset});
_bounds.merge(scaledSrcPosition + scaledSrcRadius);
_bounds.merge(scaledSrcPosition - scaledSrcRadius);
_bounds.merge(scaledDstPosition + scaledDstRadius);
_bounds.merge(scaledDstPosition - scaledDstRadius);
}

uint64_t ThreadSafeContainer::addTorus(const Vector3f& position, const float outerRadius, const float innerRadius,
const size_t materialId, const uint64_t userDataOffset,
const Neighbours& neighbours, const Vector3f displacement)
Expand Down
33 changes: 25 additions & 8 deletions bioexplorer/backend/science/common/ThreadSafeContainer.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@

#include "Types.h"

#include <platform/core/common/CommonTypes.h>

namespace bioexplorer
{
namespace common
Expand Down Expand Up @@ -81,12 +83,12 @@ class ThreadSafeContainer
* @return uint64_t Index of the geometry in the model
*/
uint64_t addCutSphere(const core::Vector3f& position, const float radius, const float cutRadius,
const size_t materialId, const uint64_t userDataOffset = 0, const Neighbours& neighbours = {},
const core::Vector3f displacement = core::Vector3f());
const size_t materialId, const uint64_t userDataOffset = NO_USER_DATA,
const Neighbours& neighbours = {}, const core::Vector3f displacement = core::Vector3f());

/**
* @brief Add a cone to the thread safe model. If both radii are identical
* and signed-distance field technique is not used, a cylinder is add
* and signed-distance field technique is not used, a cylinder is added
* instead of a cone
*
* @param sourcePosition Base position of the cone
Expand All @@ -104,9 +106,24 @@ class ThreadSafeContainer
*/
uint64_t addCone(const core::Vector3f& sourcePosition, const float sourceRadius,
const core::Vector3f& targetPosition, const float targetRadius, const size_t materialId,
const bool useSdf, const uint64_t userDataOffset = 0, const Neighbours& neighbours = {},
const bool useSdf, const uint64_t userDataOffset = NO_USER_DATA, const Neighbours& neighbours = {},
const core::Vector3f displacement = core::Vector3f());

/**
* @brief Add a cone of spheres to the thread safe model.
*
* @param sourcePosition Base position of the cone
* @param sourceRadius Base radius of the cone
* @param targetPosition Top position of the cone
* @param targetRadius Top radius of the cone
* @param materialId Material identifier
* @param userDataOffset User data to attach to the sphere
* @param constantRadius Use contant sphere radius if true
*/
void addConeOfSpheres(const core::Vector3f& sourcePosition, const float sourceRadius,
const core::Vector3f& targetPosition, const float targetRadius, const size_t materialId,
const uint64_t userDataOffset = NO_USER_DATA, const float constantRadius = 0.f);

/**
* @brief Add a torus to the thread safe model
*
Expand All @@ -121,8 +138,8 @@ class ThreadSafeContainer
* @return uint64_t Index of the geometry in the model
*/
uint64_t addTorus(const core::Vector3f& position, const float outerRadius, const float innerRadius,
const size_t materialId, const uint64_t userDataOffset = 0, const Neighbours& neighbours = {},
const core::Vector3f displacement = core::Vector3f());
const size_t materialId, const uint64_t userDataOffset = NO_USER_DATA,
const Neighbours& neighbours = {}, const core::Vector3f displacement = core::Vector3f());

/**
* @brief Add a vesica to the thread safe model
Expand All @@ -138,8 +155,8 @@ class ThreadSafeContainer
* @return uint64_t Index of the geometry in the model
*/
uint64_t addVesica(const core::Vector3f& sourcePosition, const core::Vector3f& targetPosition, const float radius,
const size_t materialId, const uint64_t userDataOffset = 0, const Neighbours& neighbours = {},
const core::Vector3f displacement = core::Vector3f());
const size_t materialId, const uint64_t userDataOffset = NO_USER_DATA,
const Neighbours& neighbours = {}, const core::Vector3f displacement = core::Vector3f());

/**
* @brief Add a vesica to the thread safe model
Expand Down
16 changes: 14 additions & 2 deletions bioexplorer/backend/science/common/Types.h
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,14 @@ enum class XYZFileFormat
xyzr_rgba_ascii = 7
};
const std::string ASCII_FILE_SEPARATOR = ",";

typedef struct
{
bool enabled{false};
bool uniform{false};
double radius{1.0};

} SpheresRepresentation;
} // namespace common

namespace molecularsystems
Expand Down Expand Up @@ -440,7 +448,9 @@ enum class MorphologyRepresentation
orientation = 3,
bezier = 4,
contour = 5,
surface = 6
surface = 6,
spheres = 7,
uniform_spheres = 8
};

enum class MorphologyRealismLevel
Expand Down Expand Up @@ -1403,7 +1413,9 @@ enum class VasculatureRepresentation
section = 1,
segment = 2,
optimized_segment = 3,
bezier = 4
bezier = 4,
spheres = 5,
uniform_spheres = 6
};

typedef struct
Expand Down
35 changes: 29 additions & 6 deletions bioexplorer/backend/science/common/Utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -357,10 +357,10 @@ Transformation combineTransformations(const Transformations& transformations)
finalMatrix *= matrix;
}

glm::vec3 scale;
Vector3f scale;
glm::quat rotation;
glm::vec3 translation;
glm::vec3 skew;
Vector3f translation;
Vector3f skew;
glm::vec4 perspective;
glm::decompose(finalMatrix, scale, rotation, translation, skew, perspective);

Expand Down Expand Up @@ -449,10 +449,10 @@ double capsuleVolume(const double height, const double radius)

Vector3f transformVector3f(const Vector3f& v, const Matrix4f& transformation)
{
glm::vec3 scale;
Vector3f scale;
glm::quat rotation;
glm::vec3 translation;
glm::vec3 skew;
Vector3f translation;
Vector3f skew;
glm::vec4 perspective;
glm::decompose(transformation, scale, rotation, translation, skew, perspective);
return translation + rotation * v;
Expand Down Expand Up @@ -613,5 +613,28 @@ Vector3d getAlignmentToGrid(const double gridSize, const Vector3d& position)
return gridSize > 0.0 ? Vector3d(Vector3i(position / gridSize) * static_cast<int>(gridSize)) : position;
}

Vector4fs fillConeWithSpheres(const Vector4f& center, const Vector4f& apex, const float constantRadius)
{
const Vector3f p1 = Vector3f(center);
const Vector3f p2 = Vector3f(apex);
const float height = length(p2 - p1);
const Vector3f direction = (p2 - p1) / height;

Vector4fs spheres;
float t = 0.f;
while (t <= 1.f)
{
Vector3f c = lerp(p1, p2, t);
float radius = constantRadius;
if (radius == 0.f)
radius = center.w + t * (apex.w - center.w);
if (radius > 0.f)
spheres.push_back({c, radius});
t += (radius * 0.5f / height); // Overlapping by half of their radius
}

return spheres;
}

} // namespace common
} // namespace bioexplorer
14 changes: 14 additions & 0 deletions bioexplorer/backend/science/common/Utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -302,5 +302,19 @@ double valueFromDoubles(const doubles& array, const size_t index, const double d
*/
core::Vector3d getAlignmentToGrid(const double gridSize, const core::Vector3d& position);

/**
* @brief Fills a cone with spheres that progressively decrease in radius.
*
* This function generates spheres that fit perfectly within the profile of a cone defined by two points and two radii.
* The spheres overlap by half of their radius for an optimal fit.
*
* @param center Center of the base of the cone.
* @param apex Apex of the cone
* @param constantRadius Uses constant sphere radius if different from 0
* @return A vector of spheres, each defined by its center and radius.
*/
core::Vector4fs fillConeWithSpheres(const core::Vector4f& center, const core::Vector4f& apex,
const float constantRadius = 0.f);

} // namespace common
} // namespace bioexplorer
10 changes: 6 additions & 4 deletions bioexplorer/backend/science/io/CacheLoader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -818,6 +818,8 @@ void CacheLoader::exportToXYZ(const std::string& filename, const XYZFileFormat f
const auto& spheresMap = model.getSpheres();
for (const auto& spheres : spheresMap)
{
if (spheres.first == BOUNDINGBOX_MATERIAL_ID || spheres.first == SECONDARY_MODEL_MATERIAL_ID)
continue;
const auto material = model.getMaterial(spheres.first);

for (const auto& sphere : spheres.second)
Expand Down Expand Up @@ -861,10 +863,10 @@ void CacheLoader::exportToXYZ(const std::string& filename, const XYZFileFormat f
const auto& color = material->getDiffuseColor();
const auto opacity = material->getOpacity();

file << ASCII_FILE_SEPARATOR << static_cast<size_t>(256.f * color.x) << ASCII_FILE_SEPARATOR
<< static_cast<size_t>(256.f * color.y) << ASCII_FILE_SEPARATOR
<< static_cast<size_t>(256.f * color.z) << ASCII_FILE_SEPARATOR
<< static_cast<size_t>(256.f * opacity);
file << ASCII_FILE_SEPARATOR << static_cast<size_t>(255.f * color.x) << ASCII_FILE_SEPARATOR
<< static_cast<size_t>(255.f * color.y) << ASCII_FILE_SEPARATOR
<< static_cast<size_t>(255.f * color.z) << ASCII_FILE_SEPARATOR
<< static_cast<size_t>(255.f * opacity);
}
file << std::endl;
break;
Expand Down
Loading

0 comments on commit 99578e4

Please sign in to comment.