Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make Soma a single class used by both mutable and immutable #366

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
20 changes: 19 additions & 1 deletion src/soma.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#include <limits>

#include <morphio/section.h>
#include <morphio/soma.h>
#include <morphio/vector_types.h>
Expand All @@ -7,6 +9,7 @@

namespace morphio {

const floatType NaN = std::numeric_limits<floatType>::quiet_NaN();

Soma::Soma(const Property::Properties& properties)
: soma_type_(properties._cellLevel._somaType)
Expand All @@ -18,11 +21,20 @@ Soma::Soma(const Property::PointLevel& point_properties)


Point Soma::center() const {

if (points().empty()) {
return {NaN, NaN, NaN};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do people check for NaN? If I use Python, I would just expect this to return None, and by now, in compiled languages, expect std::optional (or language equivalent)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, I removed them after thinking twice and also realizing that the volume/surface functions already raise exceptions.

}
return centerOfGravity(points());
}


floatType Soma::volume() const {

if (diameters().empty()) {
return NaN;
}

switch (soma_type_) {
case SOMA_NEUROMORPHO_THREE_POINT_CYLINDERS: {
floatType radius = diameters()[0] / 2;
Expand All @@ -40,11 +52,17 @@ floatType Soma::volume() const {


floatType Soma::surface() const {
if (points().empty()) {
return NaN;
}
return _somaSurface(type(), diameters(), points());
}


floatType Soma::maxDistance() const {
return maxDistanceToCenterOfGravity(properties_._points);
if (points().empty()) {
return NaN;
}
return maxDistanceToCenterOfGravity(points());
}
} // namespace morphio
1 change: 1 addition & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
set(TESTS_SRC
main.cpp
test_soma.cpp
test_immutable_morphology.cpp
test_mitochondria.cpp
test_morphology_readers.cpp
Expand Down
139 changes: 139 additions & 0 deletions tests/test_soma.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@

#include <catch2/catch.hpp>
#include <cmath>

#include <morphio/enums.h>
#include <morphio/soma.h>

#include <morphio/morphology.h>
#include <morphio/mut/morphology.h>


TEST_CASE("soma-default-constructor", "[soma]") {
const morphio::Soma soma;

REQUIRE(soma.type() == morphio::enums::SomaType::SOMA_UNDEFINED);
REQUIRE(soma.points().empty());
REQUIRE(soma.diameters().empty());

REQUIRE(soma.properties()._points.empty());
REQUIRE(soma.properties()._diameters.empty());
REQUIRE(soma.properties()._perimeters.empty());

morphio::Point center = soma.center();

REQUIRE(std::isnan(center[0]));
REQUIRE(std::isnan(center[1]));
REQUIRE(std::isnan(center[2]));
REQUIRE(std::isnan(soma.surface()));
REQUIRE(std::isnan(soma.volume()));
REQUIRE(std::isnan(soma.maxDistance()));
}


TEST_CASE("soma-point-properties-constructor", "[soma]") {
auto properties = morphio::Property::PointLevel();

properties._points = {{0., 1., 2.}, {3., 4., 5.}};
properties._diameters = {0.2, 0.3};

morphio::Soma soma(properties);

REQUIRE(soma.points() == properties._points);
REQUIRE(soma.diameters() == properties._diameters);
REQUIRE(soma.type() == morphio::enums::SomaType::SOMA_UNDEFINED);

REQUIRE(soma.center() == morphio::Point({1.5, 2.5, 3.5}));
REQUIRE_THAT(soma.maxDistance(), Catch::WithinAbs(2.598076, 1e-6));
}


TEST_CASE("soma-properties-constructor", "[soma]") {
auto properties = morphio::Property::Properties();

std::vector<morphio::Point> expected_points = {{0., 1., 2.}, {3., 4., 5.}, {6., 7., 8.}};
std::vector<morphio::floatType> expected_diameters = {0.2, 0.3, 0.4};
morphio::enums::SomaType expected_soma_type = morphio::enums::SomaType::SOMA_SIMPLE_CONTOUR;

properties._somaLevel._points = expected_points;
properties._somaLevel._diameters = expected_diameters;
properties._cellLevel._somaType = expected_soma_type;

morphio::Soma soma(properties);

REQUIRE(soma.points() == expected_points);
REQUIRE(soma.diameters() == expected_diameters);
REQUIRE(soma.type() == expected_soma_type);
}


TEST_CASE("soma-copy-constructor", "[soma]") {
auto properties = morphio::Property::PointLevel();

properties._points = {{0., 1., 2.}, {3., 4., 5.}};
properties._diameters = {0.2, 0.3};

// allocate a soma in the heap to delete it afterwards
// and check if the copy maintains its data
morphio::Soma* soma = new morphio::Soma(properties);
morphio::Soma soma_copy(*soma);

auto expected_points = soma->points();
auto expected_diameters = soma->diameters();
auto expected_type = soma->type();

REQUIRE(expected_points == soma_copy.points());
REQUIRE(expected_diameters == soma_copy.diameters());
REQUIRE(expected_type == soma_copy.type());

delete soma;

REQUIRE(expected_points == soma_copy.points());
REQUIRE(expected_diameters == soma_copy.diameters());
REQUIRE(expected_type == soma_copy.type());
}


TEST_CASE("soma-immutable-morphology-constructor", "[soma]") {
const auto soma = morphio::Morphology("data/h5/v1/Neuron.h5").soma();

REQUIRE(soma.type() == morphio::SomaType::SOMA_SIMPLE_CONTOUR);

std::vector<morphio::Point> expected_soma_points = {{0.0f, 0.0f, 0.0f},
{0.0f, 0.2f, 0.0f},
{0.1f, 0.1f, 0.0f}};
REQUIRE(soma.points() == expected_soma_points);

std::vector<morphio::floatType> expected_soma_diameters = {0.2f, 0.2f, 0.2f};
REQUIRE(soma.diameters() == expected_soma_diameters);
}


TEST_CASE("soma-mutable-morphology-constructor", "[soma]") {
auto morph = morphio::mut::Morphology("data/h5/v1/Neuron.h5");

const auto soma = *morph.soma();

REQUIRE(soma.type() == morphio::SomaType::SOMA_SIMPLE_CONTOUR);

std::vector<morphio::Point> expected_soma_points = {{0.0f, 0.0f, 0.0f},
{0.0f, 0.2f, 0.0f},
{0.1f, 0.1f, 0.0f}};
REQUIRE(soma.points() == expected_soma_points);

std::vector<morphio::floatType> expected_soma_diameters = {0.2f, 0.2f, 0.2f};
REQUIRE(soma.diameters() == expected_soma_diameters);
}


TEST_CASE("soma-mutable-morphology-mutation", "[soma]") {
auto morph = morphio::mut::Morphology("data/h5/v1/Neuron.h5");

std::vector<morphio::Point> expected_soma_points = {{0.1f, 0.1f, 0.0f}};
morph.soma()->points() = expected_soma_points;
REQUIRE(morph.soma()->points() == expected_soma_points);

std::vector<morphio::floatType> expected_soma_diameters = {3.0f};
morph.soma()->diameters() = expected_soma_diameters;
REQUIRE(morph.soma()->diameters() == expected_soma_diameters);
}