From 9a85ef1a4d369217dea37e9b5e6b68fe8646fc0d Mon Sep 17 00:00:00 2001 From: Benoit Coste Date: Thu, 11 Feb 2021 10:08:26 +0100 Subject: [PATCH] Allow markers to have no diameters (#233) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Markers may have only `(X Y Z)` specified instead of the more common `(X Y Z D)`. In this case, diameters are set to 0. Also: Strings in the middle of the list of points are now skipped. ℹ️ The example file added in this commit is from C280999A-I4.asc The following illustrates both use cases: ```lisp ( (Color Red) ( -0.97 -141.17 84.77) "<--4 boutons on a PC soma" ) ; End of text ``` --- README.md | 2 ++ src/readers/morphologyASC.cpp | 26 ++++++++++++++++++-------- tests/data/marker-with-string.asc | 12 ++++++++++++ tests/test_2_neurolucida.py | 5 +++++ 4 files changed, 37 insertions(+), 8 deletions(-) create mode 100644 tests/data/marker-with-string.asc diff --git a/README.md b/README.md index b25b0d2e1..9ca7f9503 100644 --- a/README.md +++ b/README.md @@ -573,6 +573,8 @@ The following s-expressions are parsed: ) ; End of markers ``` +ℹ️ Markers can may have only `(X Y Z)` specified instead of the more common `(X Y Z D)`. In this case, diameters are set to 0. + ##### Usage ```python diff --git a/src/readers/morphologyASC.cpp b/src/readers/morphologyASC.cpp index b3f7ca0b3..04a7d72ed 100644 --- a/src/readers/morphologyASC.cpp +++ b/src/readers/morphologyASC.cpp @@ -76,23 +76,30 @@ class NeurolucidaParser } private: - std::tuple parse_point(NeurolucidaLexer& lex) { + std::tuple parse_point(NeurolucidaLexer& lex, bool is_marker) { lex.expect(Token::LPAREN, "Point should start in LPAREN"); - std::array point{}; // X,Y,Z,R - for (auto& p : point) { + std::array point{}; // X,Y,Z,D + for (unsigned int i = 0; i < 4; i++) { try { #ifdef MORPHIO_USE_DOUBLE - p = std::stod(lex.consume()->str()); + point[i] = std::stod(lex.consume()->str()); #else - p = std::stof(lex.consume()->str()); + point[i] = std::stof(lex.consume()->str()); #endif } catch (const std::invalid_argument&) { throw RawDataError(err_.ERROR_PARSING_POINT(lex.line_num(), lex.current()->str())); } + + // Markers can have an s-exp (X Y Z) without diameter + if (is_marker && i == 2 && (lex_.peek()->str() == ")")) { + point[3] = 0; + break; + } } lex.consume(); + // case where the s-exp is (X Y Z R WORD). For example: (1 1 0 1 S1) if (lex.current()->id == +Token::WORD) { lex.consume(Token::WORD); } @@ -231,13 +238,14 @@ class NeurolucidaParser } else if (id == Token::RPAREN) { return header; } else if (id == Token::LPAREN) { + const auto next_token = static_cast(peek_id); if (skip_sexp(peek_id)) { // skip words/strings/markers lex_.consume_until_balanced_paren(); if (peek_id == +Token::FONT) lex_.consume_until_balanced_paren(); - } else if (is_neurite_type(static_cast(peek_id))) { - header.token = static_cast(peek_id); + } else if (is_neurite_type(next_token)) { + header.token = next_token; lex_.consume(); // Advance to NeuriteType lex_.consume(); lex_.consume(Token::RPAREN, "New Neurite should end in RPAREN"); @@ -286,7 +294,7 @@ class NeurolucidaParser } else if (peek_id == +Token::NUMBER) { Point point; floatType radius; - std::tie(point, radius) = parse_point(lex_); + std::tie(point, radius) = parse_point(lex_, (header.token == Token::STRING)); points.push_back(point); diameters.push_back(radius); } else if (peek_id == +Token::LPAREN) { @@ -300,6 +308,8 @@ class NeurolucidaParser throw RawDataError( err_.ERROR_UNKNOWN_TOKEN(lex_.line_num(), lex_.peek()->str())); } + } else if (id == Token::STRING) { + lex_.consume(); } else { throw RawDataError(err_.ERROR_UNKNOWN_TOKEN(lex_.line_num(), lex_.peek()->str())); } diff --git a/tests/data/marker-with-string.asc b/tests/data/marker-with-string.asc new file mode 100644 index 000000000..5a2d529c7 --- /dev/null +++ b/tests/data/marker-with-string.asc @@ -0,0 +1,12 @@ +; The following annotation has been found in C280999A-I4.asc +; It is a top-level annotation with a quote inside +( (Color Red) + ( -0.97 -141.17 84.77) + "<--4 boutons on a PC soma" +) ; End of text + +("CellBody" + (Color Red) + (CellBody) + (0 0 0 2) + ) \ No newline at end of file diff --git a/tests/test_2_neurolucida.py b/tests/test_2_neurolucida.py index 0e76bd346..cf8814518 100644 --- a/tests/test_2_neurolucida.py +++ b/tests/test_2_neurolucida.py @@ -700,3 +700,8 @@ def test_skip_imagecoord(): def test_Sections_block(): assert_array_equal(Morphology(DATA_DIR / 'sections-block.asc').points, Morphology(DATA_DIR / 'simple.asc').points) + +def test_marker_with_string(): + m = Morphology(DATA_DIR / 'marker-with-string.asc') + assert_array_equal(m.markers[0].points, np.array([[ -0.97 , -141.169998, 84.769997]], + dtype=np.float32))