• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

BlueBrain / MorphIO / 7111656197

06 Dec 2023 07:50AM UTC coverage: 76.555% (+0.5%) from 76.104%
7111656197

push

github

web-flow
Better read/write for soma types (#411)

* Be strict about soma types: ASC/H5 are contours, SWC is single point, cylinders or neuromorpho 3 point
* when writing, an incorrect soma type will throw ERROR_UNSUPPORTED_SOMA_TYPE
* removed unused ERROR_OPENING_FILE
* changed single point h5/asc files used in tests to be 4 point contours
* For compat, we will for now print a warning if the soma_type isn't set

103 of 138 new or added lines in 6 files covered. (74.64%)

6 existing lines in 3 files now uncovered.

1982 of 2589 relevant lines covered (76.55%)

882.46 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

79.45
/src/shared_utils.cpp
1
/* Copyright (c) 2013-2023, EPFL/Blue Brain Project
2
 *
3
 * SPDX-License-Identifier: Apache-2.0
4
 */
5
#include <bitset>
6
#include <cstdint>
7

8
#include "shared_utils.hpp"
9

10
#include <ghc/filesystem.hpp>
11

12
namespace morphio {
13

14
floatType _somaSurface(const SomaType type,
20✔
15
                       const range<const floatType>& diameters,
16
                       const range<const Point>& points) {
17
    size_t size = points.size();
20✔
18

19
    switch (type) {
20✔
20
    case SOMA_SINGLE_POINT: {
6✔
21
        if (diameters.size() != 1) {
6✔
22
            throw MorphioError(readers::ErrorMessages().ERROR_SOMA_INVALID_SINGLE_POINT());
2✔
23
        }
24
        floatType radius = diameters[0] / 2;
4✔
25
        return 4 * morphio::PI * radius * radius;
4✔
26
    }
27
    case SOMA_NEUROMORPHO_THREE_POINT_CYLINDERS: {
6✔
28
        if (diameters.size() != 3) {
6✔
29
            throw MorphioError(readers::ErrorMessages().ERROR_SOMA_INVALID_THREE_POINT_CYLINDER());
2✔
30
        }
31
        floatType radius = diameters[0] / 2;
4✔
32
        return 4 * morphio::PI * radius * radius;
4✔
33
    }
34
    case SOMA_CYLINDERS: {
2✔
35
        // Surface is approximated as the sum of areas of the conical frustums
36
        // defined by each segments. Does not include the endcaps areas
37
        floatType surface = 0;
2✔
38
        for (unsigned int i = 0; i < size - 1; ++i) {
6✔
39
            floatType r0 = static_cast<morphio::floatType>(diameters[i]) / 2;
4✔
40
            floatType r1 = static_cast<morphio::floatType>(diameters[i + 1]) / 2;
4✔
41
            floatType h2 = euclidean_distance(points[i], points[i + 1]);
4✔
42
            auto s = morphio::PI * (r0 + r1) * std::sqrt((r0 - r1) * (r0 - r1) + h2 * h2);
4✔
43
            surface += s;
4✔
44
        }
45
        return surface;
2✔
46
    }
47
    case SOMA_SIMPLE_CONTOUR: {
4✔
48
        throw NotImplementedError("Surface is not implemented for SOMA_SIMPLE_CONTOUR");
4✔
49
    }
50
    case SOMA_UNDEFINED:
2✔
51
    default: {
52
        morphio::readers::ErrorMessages err;
4✔
53
        throw SomaError(err.ERROR_NOT_IMPLEMENTED_UNDEFINED_SOMA("Soma::surface"));
2✔
54
    }
55
    }
56
}
57

58

59
bool is_directory(const std::string& path) {
76✔
60
    return ghc::filesystem::exists(path) &&
220✔
61
           ghc::filesystem::is_directory(ghc::filesystem::canonical(path));
220✔
62
}
63

64
bool is_regular_file(const std::string& path) {
96✔
65
    return ghc::filesystem::exists(path) &&
272✔
66
           ghc::filesystem::is_regular_file(ghc::filesystem::canonical(path));
272✔
67
}
68

69
std::string join_path(const std::string& dirname, const std::string& filename) {
66✔
70
    return (ghc::filesystem::path(dirname) / filename).string();
132✔
71
}
72
namespace details {
73
ThreePointSomaStatus checkNeuroMorphoSoma(const std::array<Point, 3>& points, floatType radius) {
12✔
74
    //  NeuroMorpho is the main provider of morphologies, but they
75
    //  with SWC as their default file format: they convert all
76
    //  uploads to SWC.  In the process of conversion, they turn all
77
    //  somas into their custom 'Three-point soma representation':
78
    //  http://neuromorpho.org/SomaFormat.html
79
    //
80
    //  If the 2nd and the 3rd point have the same x,z,d values then the only valid soma is:
81
    //  1 1 x   y   z r -1
82
    //  2 1 x (y-r) z r  1
83
    //  3 1 x (y+r) z r  1
84
    //
85
    //  However, Prof Ascoli responded that they are relaxing the format:
86
    //  "I think that any 3 point soma would do as long as it represents
87
    //  a symmetric cylinder, and all trees stem from the central one of the 3 points."
88
    //  https://github.com/BlueBrain/morph-tool/issues/117#issuecomment-1772263991
89
    //
90
    //  Which I interpret to mean that as long as the `r` offset is applied along the same
91
    //  dimension, it's fine; and the +/- can either happen on point 2 or 3:
92
    //  EX, this is ok:
93
    //  1 1 x y  z      r -1
94
    //  2 1 x y (z + r) r  1 <- have `+` first
95
    //  3 1 x y (z - r) r  1
96

97
    auto withinEpsilon = [](floatType a, floatType b) {
88✔
98
        return std::fabs(a - b) < morphio::epsilon;
88✔
99
    };
100

101
    std::bitset<3> column_mask = {};
12✔
102
    for (uint8_t i = 0; i < 3; ++i) {
48✔
103
        column_mask[i] = (withinEpsilon(points[0][i], points[1][i]) &&
58✔
104
                          withinEpsilon(points[0][i], points[2][i]));
58✔
105
    }
106

107
    if (column_mask.none()) {
12✔
NEW
108
        return ZeroColumnsAreTheSame;
×
109
    } else if (column_mask.count() == 1) {
12✔
110
        return OneColumnIsTheSame;
2✔
111
    } else if (column_mask.all()) {
10✔
NEW
112
        return ThreeColumnsAreTheSame;
×
113
    }
114

115
    const size_t col = !column_mask[0] ? 0 : !column_mask[1] ? 1 : 2;
10✔
116

117
    if (!(withinEpsilon(points[0][col], points[1][col] - radius) &&
10✔
118
          withinEpsilon(points[0][col], points[2][col] + radius)) &&
20✔
119
        !(withinEpsilon(points[0][col], points[1][col] + radius) &&
10✔
120
          withinEpsilon(points[0][col], points[2][col] - radius))) {
10✔
NEW
121
        return NotRadiusOffset;
×
122
    }
123

124
    return Conforms;
10✔
125
}
126

127
std::ostream& operator<<(std::ostream& os, ThreePointSomaStatus s) {
2✔
128
    switch (s) {
2✔
NEW
129
    case ZeroColumnsAreTheSame:
×
NEW
130
        os << "None of the columns (ie: all the X, Y or Z values) are the same.";
×
NEW
131
        break;
×
132
    case OneColumnIsTheSame:
2✔
133
        os << "Only one column has the same coordinates.";
2✔
134
        break;
2✔
NEW
135
    case ThreeColumnsAreTheSame:
×
NEW
136
        os << "All three columns have the same coordinates.";
×
NEW
137
        break;
×
NEW
138
    case NotRadiusOffset:
×
NEW
139
        os << "The non-constant columns is not offset by +/- the radius from the initial sample.";
×
NEW
140
        break;
×
NEW
141
    case Conforms:
×
NEW
142
        os << "Three point soma conforms";
×
NEW
143
        break;
×
144
    }
145
    return os;
2✔
146
}
147

148
}  // namespace details
149
}  // namespace morphio
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc