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

Update from origin #2

Merged
merged 11 commits into from
Oct 24, 2019
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,22 @@ The first way is to actually code new features for EZC3D. The easiest way to do

The second way is to provide me with non-working C3D files (See the C3D Softwares section below for more details). There is another repository for test files in the pyomeca (https://github.com/pyomeca/ezc3d_c3dTestFiles). You can fork this project, add your C3D in according to the recommendations and pull request it. This will be greatly appreciated by me and the biomechanics community!

## Using the test suite
EZC3D is tested with the test suite from google `gtest` (https://github.com/google/googletest).

If you want to add or change some tests, you are very welcome to do so (actually it makes me very happy!). You should compile EZC3D with the `BUILD_TESTS` options turned on. The google test suite should download itself automatically.

Afterwards, you can create a new test with the following function declaration
```c++
TEST(NameOfTestStructure, NameOfTest) {
// Your test here...
}
```

You are invited to write tests for true positive, false positive, true negative and false negative using different combinations of `EXPECT_EQ` (or `EXPECT_FLOAT_EQ` if you compare float-precision numbers), `EXPECT_NE`, `ASSERT_TRUE`, `EXPECT_THROW` and `EXPECT_NO_THROW`. For a complete explaination of the google test suite, please refer to one of the numerous tutorial on the web.

I also implemented some useful function such as `compareHeader(myFirstC3d, mySecondC3d)` and `compareData(myFirstC3d, mySecondC3d)` which strickly compares header and data respectively. If you expect differences though, these function are for no use and you should copy-paste the content of them in your test (and change whatever is expected to be different). It is also possible to create a fully filled structure using the `fillC3D(c3dTestStruct& c3dStruc, bool withPoints, bool withAnalogs)` function and it can be tested with the `defaultHeaderTest` and `defaultParametersTest` function. Again, if you expect differences with the default setting, you should not use these default testing functions, but copy the relevant part in you extra test.

# Supported generated C3D
The software companies have loosely implemented the C3D standard proposed by http://C3D.org. Hence, there are some workaround that must be incorporated to the code to be able to read the C3D created using third-party softwares. So far, C3D from three different companies were tested. Vicon (https://www.vicon.com/), Qualisys (https://www.qualisys.com/) and Optotrak (https://www.ndigital.com/msci/products/optotrak-certus/). But I am sure there is plenty of other obscure companies or simply cases that were not tested from these companies (simply because I don't have C3D to test). If you find yourself with a bug when trying to read a C3D that should work, please open an issue and provide me with the corresponding C3D (see How to contribute).

Expand Down
2 changes: 1 addition & 1 deletion binding/python3/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,4 @@ INSTALL(TARGETS _${PROJECT_NAME}
DESTINATION ${PYTHON_INSTALL_DESTINATION}
)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/_version.py
DESTINATION ${PYTHON_INSTALL_DESTINATION})
DESTINATION ${PYTHON_INSTALL_DESTINATION}/${PROJECT_NAME})
14 changes: 7 additions & 7 deletions src/Parameter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,11 @@ void ezc3d::ParametersNS::GroupNS::Parameter::write(
// Assusimng dimension[0] is the number of characters
// and dimension[1] is the number of string
dimension[0] = longestElement();

// Remove unecessary dimension
if (dimension.size() == 2 && dimension[1] == 1) {
dimension = {dimension[0]};
}
}

// Write the parameter values
Expand Down Expand Up @@ -161,7 +166,7 @@ size_t ezc3d::ParametersNS::GroupNS::Parameter::writeImbricatedParameter(
static_cast<int>(_data_type));
else if (_data_type == DATA_TYPE::CHAR){
std::string toWrite(_param_data_string[cmp]);
toWrite.resize(dim[0]); // Pad with \0
toWrite.resize(dim[0], ' '); // Pad with x20
f.write(toWrite.c_str(),
static_cast<int>(dim[0] * DATA_TYPE::BYTE));
}
Expand Down Expand Up @@ -298,13 +303,8 @@ size_t ezc3d::ParametersNS::GroupNS::Parameter::longestElement() const{
if (_dimension.size() == 1)
return _param_data_string[0].size();
else {
if (_dimension.size() != 2) {
throw std::runtime_error(
"longestElement is only implemented for 1d or 2d CHAR matrix. "
"Please report this error for help improving ezc3d.");
}
size_t longestSoFar(0);
for (size_t i = 0; i<_dimension[1]; ++i){
for (size_t i = 0; i<_param_data_string.size(); ++i){
if (_param_data_string[i].size() > longestSoFar)
longestSoFar = _param_data_string[i].size();
}
Expand Down
3 changes: 2 additions & 1 deletion src/Point.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,11 @@ void ezc3d::DataNS::Points3dNS::Point::write(std::fstream &f) const {
}
else {
float zero(0);
float minusOne(-1);
f.write(reinterpret_cast<const char*>(&zero), ezc3d::DATA_TYPE::FLOAT);
f.write(reinterpret_cast<const char*>(&zero), ezc3d::DATA_TYPE::FLOAT);
f.write(reinterpret_cast<const char*>(&zero), ezc3d::DATA_TYPE::FLOAT);
f.write(reinterpret_cast<const char*>(&_data[3]), ezc3d::DATA_TYPE::FLOAT);
f.write(reinterpret_cast<const char*>(&minusOne), ezc3d::DATA_TYPE::FLOAT);
}
}

Expand Down
40 changes: 25 additions & 15 deletions test/test_ezc3d.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -194,14 +194,27 @@ void compareHeader(const ezc3d::c3d& c3d1, const ezc3d::c3d& c3d2){
}
}

void compareData(const ezc3d::c3d& c3d1, const ezc3d::c3d& c3d2){
void compareData(const ezc3d::c3d& c3d1, const ezc3d::c3d& c3d2
, bool skipResidual = false){
// All the data should be the same
for (size_t f=0; f<c3d1.header().nbFrames(); ++f){
for (size_t p=0; p<c3d1.header().nb3dPoints(); ++p){
EXPECT_FLOAT_EQ(c3d1.data().frame(f).points().point(p).x(), c3d2.data().frame(f).points().point(p).x());
EXPECT_FLOAT_EQ(c3d1.data().frame(f).points().point(p).y(), c3d2.data().frame(f).points().point(p).y());
EXPECT_FLOAT_EQ(c3d1.data().frame(f).points().point(p).z(), c3d2.data().frame(f).points().point(p).z());
EXPECT_FLOAT_EQ(c3d1.data().frame(f).points().point(p).residual(), c3d2.data().frame(f).points().point(p).residual());
if (c3d1.data().frame(f).points().point(p).residual() < 0) {
ASSERT_TRUE(std::isnan(c3d1.data().frame(f).points().point(p).x()));
ASSERT_TRUE(std::isnan(c3d1.data().frame(f).points().point(p).y()));
ASSERT_TRUE(std::isnan(c3d1.data().frame(f).points().point(p).z()));

ASSERT_TRUE(std::isnan(c3d2.data().frame(f).points().point(p).x()));
ASSERT_TRUE(std::isnan(c3d2.data().frame(f).points().point(p).y()));
ASSERT_TRUE(std::isnan(c3d2.data().frame(f).points().point(p).z()));
}
else {
EXPECT_FLOAT_EQ(c3d1.data().frame(f).points().point(p).x(), c3d2.data().frame(f).points().point(p).x());
EXPECT_FLOAT_EQ(c3d1.data().frame(f).points().point(p).y(), c3d2.data().frame(f).points().point(p).y());
EXPECT_FLOAT_EQ(c3d1.data().frame(f).points().point(p).z(), c3d2.data().frame(f).points().point(p).z());
}
if (!skipResidual)
EXPECT_FLOAT_EQ(c3d1.data().frame(f).points().point(p).residual(), c3d2.data().frame(f).points().point(p).residual());
}
for (size_t sf=0; sf<c3d1.data().frame(f).analogs().nbSubframes(); ++sf){
for (size_t c=0; c<c3d1.header().nbAnalogByFrame(); ++c){
Expand Down Expand Up @@ -1260,9 +1273,6 @@ TEST(c3dFileIO, CreateWriteAndReadBackWithNan){
c3dTestStruct ref_c3d;
fillC3D(ref_c3d, true, true);

// Lock Point parameter
ref_c3d.c3d.lockGroup("POINT");

// Change some values for Nan
size_t idxFrame(1);
size_t idxSubframe(2);
Expand All @@ -1274,7 +1284,7 @@ TEST(c3dFileIO, CreateWriteAndReadBackWithNan){
frame.points().point(idxPoint).x(NAN);
frame.points().point(idxPoint).y(NAN);
frame.points().point(idxPoint).z(NAN);
frame.points().point(idxPoint).residual(NAN);
frame.points().point(idxPoint).residual(-1);
frame.analogs().subframe(idxSubframe).channel(idxChannel).data(NAN);

// Write the c3d on the disk
Expand All @@ -1290,7 +1300,7 @@ TEST(c3dFileIO, CreateWriteAndReadBackWithNan){
EXPECT_TRUE(std::isnan(point.x()));
EXPECT_TRUE(std::isnan(point.y()));
EXPECT_TRUE(std::isnan(point.z()));
EXPECT_EQ(point.residual(), 0);
EXPECT_EQ(point.residual(), -1);

ezc3d::DataNS::AnalogsNS::Channel channel(
read_c3d.data().frame(idxFrame).analogs().subframe(idxSubframe)
Expand Down Expand Up @@ -1749,9 +1759,9 @@ TEST(c3dFileIO, comparedIdenticalFilesSample1){
compareHeader(c3d_pr, c3d_vi);

// All the data should be the same
compareData(c3d_pr, c3d_pi);
compareData(c3d_pr, c3d_vr);
compareData(c3d_pr, c3d_vi);
compareData(c3d_pr, c3d_pi, true);
compareData(c3d_pr, c3d_vr, true);
compareData(c3d_pr, c3d_vi, true);
}

TEST(c3dFileIO, comparedIdenticalFilesSample2){
Expand All @@ -1769,8 +1779,8 @@ TEST(c3dFileIO, comparedIdenticalFilesSample2){

// All the data should be the same
// compareData(c3d_pr, c3d_pi); // Data are actually sligthly different
compareData(c3d_pr, c3d_vr);
compareData(c3d_pr, c3d_vi);
compareData(c3d_pr, c3d_vr, true);
compareData(c3d_pr, c3d_vi, true);
}

TEST(c3dFileIO, parseAndBuildSameFileBTS){
Expand Down