Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
A
arbor
Manage
Activity
Members
Code
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Deploy
Releases
Analyze
Contributor analytics
Repository analytics
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
arbor-sim
arbor
Commits
a362d9be
Commit
a362d9be
authored
9 years ago
by
Benjamin Cumming
Browse files
Options
Downloads
Plain Diff
Merge branch 'master' of github.com:eth-cscs/cell_algorithms
parents
2dc6633c
4b95bae7
No related branches found
No related tags found
No related merge requests found
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
main.cpp
+175
-0
175 additions, 0 deletions
main.cpp
makefile
+1
-1
1 addition, 1 deletion
makefile
swcio.hpp
+263
-0
263 additions, 0 deletions
swcio.hpp
with
439 additions
and
1 deletion
main.cpp
+
175
−
0
View file @
a362d9be
#include
<iostream>
#include
<fstream>
#include
<numeric>
#include
<vector>
#include
"gtest/gtest.h"
#include
"cell_tree.hpp"
#include
"swcio.hpp"
#include
"json/src/json.hpp"
using
json
=
nlohmann
::
json
;
...
...
@@ -250,6 +252,179 @@ TEST(cell_tree, json_load) {
}
}
// SWC tests
void
expect_cell_equals
(
const
neuron
::
io
::
cell_record
&
expected
,
const
neuron
::
io
::
cell_record
&
actual
)
{
EXPECT_EQ
(
expected
.
id
(),
actual
.
id
());
EXPECT_EQ
(
expected
.
type
(),
actual
.
type
());
EXPECT_FLOAT_EQ
(
expected
.
x
(),
actual
.
x
());
EXPECT_FLOAT_EQ
(
expected
.
y
(),
actual
.
y
());
EXPECT_FLOAT_EQ
(
expected
.
z
(),
actual
.
z
());
EXPECT_FLOAT_EQ
(
expected
.
radius
(),
actual
.
radius
());
EXPECT_EQ
(
expected
.
parent
(),
actual
.
parent
());
}
TEST
(
cell_record
,
construction
)
{
using
namespace
neuron
::
io
;
{
// force an invalid type
cell_record
::
kind
invalid_type
=
static_cast
<
cell_record
::
kind
>
(
100
);
EXPECT_THROW
(
cell_record
cell
(
invalid_type
,
7
,
1.
,
1.
,
1.
,
1.
,
5
),
std
::
invalid_argument
);
}
{
// invalid id
EXPECT_THROW
(
cell_record
cell
(
cell_record
::
custom
,
-
3
,
1.
,
1.
,
1.
,
1.
,
5
),
std
::
invalid_argument
);
}
{
// invalid parent id
EXPECT_THROW
(
cell_record
cell
(
cell_record
::
custom
,
0
,
1.
,
1.
,
1.
,
1.
,
-
5
),
std
::
invalid_argument
);
}
{
// invalid radius
EXPECT_THROW
(
cell_record
cell
(
cell_record
::
custom
,
0
,
1.
,
1.
,
1.
,
-
1.
,
-
1
),
std
::
invalid_argument
);
}
{
// parent_id > id
EXPECT_THROW
(
cell_record
cell
(
cell_record
::
custom
,
0
,
1.
,
1.
,
1.
,
1.
,
2
),
std
::
invalid_argument
);
}
{
// parent_id == id
EXPECT_THROW
(
cell_record
cell
(
cell_record
::
custom
,
0
,
1.
,
1.
,
1.
,
1.
,
0
),
std
::
invalid_argument
);
}
{
// check standard construction by value
cell_record
cell
(
cell_record
::
custom
,
0
,
1.
,
1.
,
1.
,
1.
,
-
1
);
EXPECT_EQ
(
cell
.
id
(),
0
);
EXPECT_EQ
(
cell
.
type
(),
cell_record
::
custom
);
EXPECT_EQ
(
cell
.
x
(),
1.
);
EXPECT_EQ
(
cell
.
y
(),
1.
);
EXPECT_EQ
(
cell
.
z
(),
1.
);
EXPECT_EQ
(
cell
.
radius
(),
1.
);
EXPECT_EQ
(
cell
.
diameter
(),
2
*
1.
);
EXPECT_EQ
(
cell
.
parent
(),
-
1
);
}
{
// check copy constructor
cell_record
cell_orig
(
cell_record
::
custom
,
0
,
1.
,
1.
,
1.
,
1.
,
-
1
);
cell_record
cell
(
cell_orig
);
expect_cell_equals
(
cell_orig
,
cell
);
}
}
TEST
(
swc_parser
,
invalid_input
)
{
using
namespace
neuron
::
io
;
{
// check incomplete lines; missing parent
std
::
istringstream
is
(
"1 1 14.566132 34.873772 7.857000 0.717830
\n
"
);
cell_record
cell
;
EXPECT_THROW
(
is
>>
cell
,
std
::
logic_error
);
}
{
// Check non-parsable values
std
::
istringstream
is
(
"1a 1 14.566132 34.873772 7.857000 0.717830 -1
\n
"
);
cell_record
cell
;
EXPECT_THROW
(
is
>>
cell
,
std
::
logic_error
);
}
{
// Check invalid cell type
std
::
istringstream
is
(
"1 10 14.566132 34.873772 7.857000 0.717830 -1
\n
"
);
cell_record
cell
;
EXPECT_THROW
(
is
>>
cell
,
std
::
invalid_argument
);
}
}
TEST
(
swc_parser
,
valid_input
)
{
using
namespace
neuron
::
io
;
{
// check empty file; no cell may be parsed
cell_record
cell
,
cell_orig
;
std
::
istringstream
is
(
""
);
EXPECT_NO_THROW
(
is
>>
cell
);
expect_cell_equals
(
cell_orig
,
cell
);
}
{
// check comment-only file not ending with a newline;
// no cell may be parsed
cell_record
cell
,
cell_orig
;
std
::
istringstream
is
(
"#comment
\n
#comment"
);
EXPECT_NO_THROW
(
is
>>
cell
);
expect_cell_equals
(
cell_orig
,
cell
);
}
{
// check last line case (no newline at the end)
std
::
istringstream
is
(
"1 1 14.566132 34.873772 7.857000 0.717830 -1"
);
cell_record
cell
;
EXPECT_NO_THROW
(
is
>>
cell
);
EXPECT_EQ
(
0
,
cell
.
id
());
// zero-based indexing
EXPECT_EQ
(
cell_record
::
soma
,
cell
.
type
());
EXPECT_FLOAT_EQ
(
14.566132
,
cell
.
x
());
EXPECT_FLOAT_EQ
(
34.873772
,
cell
.
y
());
EXPECT_FLOAT_EQ
(
7.857000
,
cell
.
z
());
EXPECT_FLOAT_EQ
(
0.717830
,
cell
.
radius
());
EXPECT_FLOAT_EQ
(
-
1
,
cell
.
parent
());
}
{
// check valid input with a series of records
std
::
vector
<
cell_record
>
cells_orig
=
{
cell_record
(
cell_record
::
soma
,
0
,
14.566132
,
34.873772
,
7.857000
,
0.717830
,
-
1
),
cell_record
(
cell_record
::
dendrite
,
1
,
14.566132
+
1
,
34.873772
+
1
,
7.857000
+
1
,
0.717830
+
1
,
-
1
)
};
std
::
stringstream
swc_input
;
swc_input
<<
"# this is a comment
\n
"
;
swc_input
<<
"# this is a comment
\n
"
;
for
(
auto
c
:
cells_orig
)
swc_input
<<
c
<<
"
\n
"
;
swc_input
<<
"# this is a final comment
\n
"
;
try
{
std
::
size_t
nr_records
=
0
;
cell_record
cell
;
while
(
!
(
swc_input
>>
cell
).
eof
())
{
ASSERT_LT
(
nr_records
,
cells_orig
.
size
());
expect_cell_equals
(
cells_orig
[
nr_records
],
cell
);
++
nr_records
;
}
}
catch
(
std
::
exception
&
e
)
{
ADD_FAILURE
();
}
}
}
int
main
(
int
argc
,
char
**
argv
)
{
::
testing
::
InitGoogleTest
(
&
argc
,
argv
);
return
RUN_ALL_TESTS
();
...
...
This diff is collapsed.
Click to expand it.
makefile
+
1
−
1
View file @
a362d9be
CC
=
clang++
FLAGS
=
-std
=
c++11
-g
FLAGS
=
-std
=
c++11
-g
-pedantic
test.exe
:
main.cpp *.hpp makefile gtest.o
$(
CC
)
$(
FLAGS
)
main.cpp
-o
test.exe gtest.o
-pthread
...
...
This diff is collapsed.
Click to expand it.
swcio.hpp
0 → 100644
+
263
−
0
View file @
a362d9be
#pragma once
#include
<exception>
#include
<iostream>
#include
<sstream>
#include
<type_traits>
namespace
neuron
{
namespace
io
{
static
bool
starts_with
(
const
std
::
string
&
str
,
const
std
::
string
&
prefix
)
{
return
(
str
.
find
(
prefix
)
==
0
);
}
class
cell_record
{
public:
// FIXME: enum's are not completely type-safe, since they can accept anything
// that can be casted to their underlying type.
//
// More on SWC files: http://research.mssm.edu/cnic/swc.html
enum
kind
{
undefined
=
0
,
soma
,
axon
,
dendrite
,
apical_dendrite
,
fork_point
,
end_point
,
custom
};
// cell records assume zero-based indexing; root's parent remains -1
cell_record
(
kind
type
,
int
id
,
float
x
,
float
y
,
float
z
,
float
r
,
int
parent_id
)
:
type_
(
type
)
,
id_
(
id
)
,
x_
(
x
)
,
y_
(
y
)
,
z_
(
z
)
,
r_
(
r
)
,
parent_id_
(
parent_id
)
{
// Check cell type as well; enum's do not offer complete type safety,
// since you can cast anything that fits to its underlying type
if
(
type_
<
0
||
type_
>
custom
)
throw
std
::
invalid_argument
(
"unknown cell type"
);
if
(
id_
<
0
)
throw
std
::
invalid_argument
(
"negative ids not allowed"
);
if
(
parent_id_
<
-
1
)
throw
std
::
invalid_argument
(
"parent_id < -1 not allowed"
);
if
(
parent_id_
>=
id_
)
throw
std
::
invalid_argument
(
"parent_id >= id is not allowed"
);
if
(
r_
<
0
)
throw
std
::
invalid_argument
(
"negative radii are not allowed"
);
}
cell_record
()
:
type_
(
cell_record
::
undefined
)
,
id_
(
0
)
,
x_
(
0
)
,
y_
(
0
)
,
z_
(
0
)
,
r_
(
0
)
,
parent_id_
(
-
1
)
{
}
cell_record
(
const
cell_record
&
other
)
=
default
;
cell_record
&
operator
=
(
const
cell_record
&
other
)
=
default
;
friend
bool
operator
==
(
const
cell_record
&
lhs
,
const
cell_record
&
rhs
)
{
return
lhs
.
id_
==
rhs
.
id_
;
}
friend
bool
operator
!=
(
const
cell_record
&
lhs
,
const
cell_record
&
rhs
)
{
return
!
(
lhs
==
rhs
);
}
friend
std
::
ostream
&
operator
<<
(
std
::
ostream
&
os
,
const
cell_record
&
cell
);
kind
type
()
const
{
return
type_
;
}
int
id
()
const
{
return
id_
;
}
int
parent
()
const
{
return
parent_id_
;
}
float
x
()
const
{
return
x_
;
}
float
y
()
const
{
return
y_
;
}
float
z
()
const
{
return
z_
;
}
float
radius
()
const
{
return
r_
;
}
float
diameter
()
const
{
return
2
*
r_
;
}
private
:
kind
type_
;
// cell type
int
id_
;
// cell id
float
x_
,
y_
,
z_
;
// cell coordinates
float
r_
;
// cell radius
int
parent_id_
;
// cell parent's id
};
class
swc_parser
{
public:
swc_parser
(
const
std
::
string
&
delim
,
std
::
string
comment_prefix
)
:
delim_
(
delim
)
,
comment_prefix_
(
comment_prefix
)
{
}
swc_parser
()
:
delim_
(
" "
)
,
comment_prefix_
(
"#"
)
{
}
std
::
istream
&
parse_record
(
std
::
istream
&
is
,
cell_record
&
cell
)
{
while
(
!
is
.
eof
()
&&
!
is
.
bad
())
{
// consume empty and comment lines first
std
::
getline
(
is
,
linebuff_
);
if
(
!
linebuff_
.
empty
()
&&
!
starts_with
(
linebuff_
,
comment_prefix_
))
break
;
}
if
(
is
.
bad
())
// let the caller check for such events
return
is
;
if
(
is
.
eof
()
&&
(
linebuff_
.
empty
()
||
starts_with
(
linebuff_
,
comment_prefix_
)))
// last line is either empty or a comment; don't parse anything
return
is
;
if
(
is
.
fail
())
throw
std
::
runtime_error
(
"too long line detected"
);
std
::
istringstream
line
(
linebuff_
);
cell
=
parse_record
(
line
);
return
is
;
}
private
:
void
check_parse_status
(
const
std
::
istream
&
is
)
{
if
(
is
.
fail
())
// If we try to read past the eof; fail bit will also be set
// FIXME: better throw a custom parse_error exception
throw
std
::
logic_error
(
"could not parse value"
);
}
template
<
typename
T
>
T
parse_value_strict
(
std
::
istream
&
is
)
{
T
val
;
check_parse_status
(
is
>>
val
);
// everything's fine
return
val
;
}
// Read the record from a string stream; will be treated like a single line
cell_record
parse_record
(
std
::
istringstream
&
is
);
std
::
string
delim_
;
std
::
string
comment_prefix_
;
std
::
string
linebuff_
;
};
// specialize parsing for cell types
template
<
>
cell_record
::
kind
swc_parser
::
parse_value_strict
(
std
::
istream
&
is
)
{
int
val
;
check_parse_status
(
is
>>
val
);
// Let cell_record's constructor check for the type validity
return
static_cast
<
cell_record
::
kind
>
(
val
);
}
cell_record
swc_parser
::
parse_record
(
std
::
istringstream
&
is
)
{
auto
id
=
parse_value_strict
<
int
>
(
is
);
auto
type
=
parse_value_strict
<
cell_record
::
kind
>
(
is
);
auto
x
=
parse_value_strict
<
float
>
(
is
);
auto
y
=
parse_value_strict
<
float
>
(
is
);
auto
z
=
parse_value_strict
<
float
>
(
is
);
auto
r
=
parse_value_strict
<
float
>
(
is
);
auto
parent_id
=
parse_value_strict
<
int
>
(
is
);
// Convert to zero-based, leaving parent_id as-is if -1
if
(
parent_id
!=
-
1
)
parent_id
--
;
return
cell_record
(
type
,
id
-
1
,
x
,
y
,
z
,
r
,
parent_id
);
}
std
::
istream
&
operator
>>
(
std
::
istream
&
is
,
cell_record
&
cell
)
{
swc_parser
parser
;
parser
.
parse_record
(
is
,
cell
);
return
is
;
}
std
::
ostream
&
operator
<<
(
std
::
ostream
&
os
,
const
cell_record
&
cell
)
{
// output in one-based indexing
os
<<
cell
.
id_
+
1
<<
" "
<<
cell
.
type_
<<
" "
<<
std
::
setprecision
(
7
)
<<
cell
.
x_
<<
" "
<<
std
::
setprecision
(
7
)
<<
cell
.
y_
<<
" "
<<
std
::
setprecision
(
7
)
<<
cell
.
z_
<<
" "
<<
std
::
setprecision
(
7
)
<<
cell
.
r_
<<
" "
<<
((
cell
.
parent_id_
==
-
1
)
?
cell
.
parent_id_
:
cell
.
parent_id_
+
1
);
return
os
;
}
}
// end of neuron::io
}
// end of neuron
This diff is collapsed.
Click to expand it.
Preview
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment