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

Fixed computation of number of frames of Rotations in Python #317

Merged
merged 4 commits into from
Mar 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 30 additions & 11 deletions binding/matlab/ezc3dRead.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ void mexFunction(int nlhs,mxArray *plhs[],int nrhs,const mxArray *prhs[])
}

// Preparer the first layer of the output structure
const char *globalFieldsNames[] = {"header", "parameters","data"};
const char *globalFieldsNames[] = {"header", "parameters", "data"};
int headerIdx = 0;
int parametersIdx = 1;
int dataIdx = 2;
Expand All @@ -70,7 +70,7 @@ void mexFunction(int nlhs,mxArray *plhs[],int nrhs,const mxArray *prhs[])

// Fill the header
{
const char *headerFieldsNames[] = {"points", "analogs", "events"};
const char *headerFieldsNames[] = {"points", "analogs", "rotations", "events"};
mwSize headerFieldsDims[2] = {1, 1};
mxArray * headerStruct = mxCreateStructArray(
2, headerFieldsDims,
Expand All @@ -79,8 +79,8 @@ void mexFunction(int nlhs,mxArray *plhs[],int nrhs,const mxArray *prhs[])
mxSetFieldByNumber(plhs[0], 0, headerIdx, headerStruct);
// fill points
{
const char *pointsFieldsNames[] = {"size", "frameRate",
"firstFrame", "lastFrame"};
const char *pointsFieldsNames[] = {
"size", "frameRate", "firstFrame", "lastFrame"};
mwSize pointFieldsDims[2] = {1, 1};
mxArray * pointsStruct = mxCreateStructArray(
2, pointFieldsDims,
Expand All @@ -97,8 +97,8 @@ void mexFunction(int nlhs,mxArray *plhs[],int nrhs,const mxArray *prhs[])
}
// fill analogs
{
const char *analogsFieldsNames[] = {"size", "frameRate",
"firstFrame", "lastFrame"};
const char *analogsFieldsNames[] = {
"size", "frameRate", "firstFrame", "lastFrame"};
mwSize analogsFieldsDims[2] = {1, 1};
mxArray * analogsStruct = mxCreateStructArray(
2, analogsFieldsDims,
Expand All @@ -118,20 +118,39 @@ void mexFunction(int nlhs,mxArray *plhs[],int nrhs,const mxArray *prhs[])
c3d->header().nbAnalogByFrame()
* (c3d->header().lastFrame()+1));
}
// fill rotations
{
const char *rotationsFieldsNames[] = {
"size", "frameRate", "firstFrame", "lastFrame"};
mwSize rotationsFieldsDims[2] = {1, 1};
mxArray * rotationsStruct = mxCreateStructArray(
2, rotationsFieldsDims,
sizeof(rotationsFieldsNames) / sizeof(*rotationsFieldsNames),
rotationsFieldsNames);
mxSetFieldByNumber(headerStruct, 0, 2, rotationsStruct);

ezc3d::DataNS::RotationNS::Info rotationsInfo(*c3d);
fillMatlabField(rotationsStruct, 0, rotationsInfo.used());
fillMatlabField(rotationsStruct, 1,
static_cast<mxDouble>(rotationsInfo.ratio() * c3d->header().frameRate()));
fillMatlabField(rotationsStruct, 2,
rotationsInfo.ratio() * c3d->header().firstFrame()+1);
fillMatlabField(rotationsStruct, 3,
rotationsInfo.ratio() * (c3d->header().lastFrame()+1));
}

// fill events
{
const char *eventsFieldsNames[] = {"size", "eventsTime",
"eventsLabel"};
const char *eventsFieldsNames[] = {
"size", "eventsTime", "eventsLabel"};
mwSize eventsFieldsDims[2] = {1, 1};
mxArray * eventsStruct = mxCreateStructArray(
2, eventsFieldsDims,
sizeof(eventsFieldsNames) / sizeof(*eventsFieldsNames),
eventsFieldsNames);
mxSetFieldByNumber(headerStruct, 0, 2, eventsStruct);
mxSetFieldByNumber(headerStruct, 0, 3, eventsStruct);

fillMatlabField(eventsStruct, 0, static_cast<int>(
c3d->header().eventsTime().size()));
fillMatlabField(eventsStruct, 0, static_cast<int>(c3d->header().eventsTime().size()));
fillMatlabField(eventsStruct, 1, c3d->header().eventsTime());
fillMatlabField(eventsStruct, 2, c3d->header().eventsLabel());
}
Expand Down
41 changes: 30 additions & 11 deletions binding/octave/ezc3dRead.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ void mexFunction(int nlhs,mxArray *plhs[],int nrhs,const mxArray *prhs[])
}

// Preparer the first layer of the output structure
const char *globalFieldsNames[] = {"header", "parameters","data"};
const char *globalFieldsNames[] = {"header", "parameters", "data"};
int headerIdx = 0;
int parametersIdx = 1;
int dataIdx = 2;
Expand All @@ -70,7 +70,7 @@ void mexFunction(int nlhs,mxArray *plhs[],int nrhs,const mxArray *prhs[])

// Fill the header
{
const char *headerFieldsNames[] = {"points", "analogs", "events"};
const char *headerFieldsNames[] = {"points", "analogs", "rotations", "events"};
mwSize headerFieldsDims[2] = {1, 1};
mxArray * headerStruct = mxCreateStructArray(
2, headerFieldsDims,
Expand All @@ -79,8 +79,8 @@ void mexFunction(int nlhs,mxArray *plhs[],int nrhs,const mxArray *prhs[])
mxSetFieldByNumber(plhs[0], 0, headerIdx, headerStruct);
// fill points
{
const char *pointsFieldsNames[] = {"size", "frameRate",
"firstFrame", "lastFrame"};
const char *pointsFieldsNames[] = {
"size", "frameRate", "firstFrame", "lastFrame"};
mwSize pointFieldsDims[2] = {1, 1};
mxArray * pointsStruct = mxCreateStructArray(
2, pointFieldsDims,
Expand All @@ -97,8 +97,8 @@ void mexFunction(int nlhs,mxArray *plhs[],int nrhs,const mxArray *prhs[])
}
// fill analogs
{
const char *analogsFieldsNames[] = {"size", "frameRate",
"firstFrame", "lastFrame"};
const char *analogsFieldsNames[] = {
"size", "frameRate", "firstFrame", "lastFrame"};
mwSize analogsFieldsDims[2] = {1, 1};
mxArray * analogsStruct = mxCreateStructArray(
2, analogsFieldsDims,
Expand All @@ -118,20 +118,39 @@ void mexFunction(int nlhs,mxArray *plhs[],int nrhs,const mxArray *prhs[])
c3d->header().nbAnalogByFrame()
* (c3d->header().lastFrame()+1));
}
// fill rotations
{
const char *rotationsFieldsNames[] = {
"size", "frameRate", "firstFrame", "lastFrame"};
mwSize rotationsFieldsDims[2] = {1, 1};
mxArray * rotationsStruct = mxCreateStructArray(
2, rotationsFieldsDims,
sizeof(rotationsFieldsNames) / sizeof(*rotationsFieldsNames),
rotationsFieldsNames);
mxSetFieldByNumber(headerStruct, 0, 2, rotationsStruct);

ezc3d::DataNS::RotationNS::Info rotationsInfo(*c3d);
fillMatlabField(rotationsStruct, 0, rotationsInfo.used());
fillMatlabField(rotationsStruct, 1,
static_cast<mxDouble>(rotationsInfo.ratio() * c3d->header().frameRate()));
fillMatlabField(rotationsStruct, 2,
rotationsInfo.ratio() * c3d->header().firstFrame()+1);
fillMatlabField(rotationsStruct, 3,
rotationsInfo.ratio() * (c3d->header().lastFrame()+1));
}

// fill events
{
const char *eventsFieldsNames[] = {"size", "eventsTime",
"eventsLabel"};
const char *eventsFieldsNames[] = {
"size", "eventsTime", "eventsLabel"};
mwSize eventsFieldsDims[2] = {1, 1};
mxArray * eventsStruct = mxCreateStructArray(
2, eventsFieldsDims,
sizeof(eventsFieldsNames) / sizeof(*eventsFieldsNames),
eventsFieldsNames);
mxSetFieldByNumber(headerStruct, 0, 2, eventsStruct);
mxSetFieldByNumber(headerStruct, 0, 3, eventsStruct);

fillMatlabField(eventsStruct, 0, static_cast<int>(
c3d->header().eventsTime().size()));
fillMatlabField(eventsStruct, 0, static_cast<int>(c3d->header().eventsTime().size()));
fillMatlabField(eventsStruct, 1, c3d->header().eventsTime());
fillMatlabField(eventsStruct, 2, c3d->header().eventsLabel());
}
Expand Down
66 changes: 52 additions & 14 deletions binding/python3/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@
from ._version import __version__


# This is a dummy class that is used as an interface for the group of the parameters
class _GroupParameter:
def __init__(self, data):
self.__dict__ = data


class C3dMapper(Mapping):
def __init__(self, *args, **kw):
self._storage = dict(*args, **kw)
Expand Down Expand Up @@ -132,8 +138,20 @@ def __init__(self, path="", extract_forceplat_data=False, ignore_bad_formatting=
self.extract_forceplat_data = extract_forceplat_data
self._storage["header"] = c3d.Header(self.c3d_swig.header(), rotations_info)
self._storage["parameters"] = c3d.Parameter(self.c3d_swig.parameters())
self._storage["data"] = c3d.Data(self.c3d_swig, rotations_info, self.extract_forceplat_data)
self._storage["data"] = c3d.Data(self.c3d_swig, self.extract_forceplat_data)
return

@property
def header(self):
return self._storage["header"]

@property
def parameters(self):
return self._storage["parameters"]

@property
def data(self):
return self._storage["data"]

def __deepcopy__(self, memodict=None):
if memodict is None:
Expand All @@ -145,7 +163,7 @@ def __deepcopy__(self, memodict=None):

new._storage["header"] = c3d.Header(new.c3d_swig.header(), rotations_info)
new._storage["parameters"] = c3d.Parameter(new.c3d_swig.parameters())
new._storage["data"] = c3d.Data(new.c3d_swig, rotations_info, new.extract_forceplat_data)
new._storage["data"] = c3d.Data(new.c3d_swig, new.extract_forceplat_data)

# Update the structure with a copy of all data
for header_key in self["header"]:
Expand Down Expand Up @@ -177,14 +195,12 @@ def __init__(self, swig_header, rotation_info):
"first_frame": self.header.nbAnalogByFrame() * self.header.firstFrame(),
"last_frame": self.header.nbAnalogByFrame() * (self.header.lastFrame() + 1) - 1,
}
if rotation_info.hasGroup():
rotation_frame_rate = self.header.frameRate() * rotation_info.ratio()
self._storage["rotations"] = {
"size": rotation_info.used(),
"frame_rate": rotation_frame_rate,
"first_frame": rotation_frame_rate * self.header.firstFrame(),
"last_frame": rotation_frame_rate * (self.header.lastFrame() + 1) - 1,
}
self._storage["rotations"] = {
"size": rotation_info.used(),
"frame_rate": self.header.frameRate() * rotation_info.ratio(),
"first_frame": rotation_info.ratio() * self.header.firstFrame(),
"last_frame": rotation_info.ratio() * (self.header.lastFrame() + 1) - 1,
}
self._storage["events"] = {
"size": len(self.header.eventsTime()),
"events_time": self.header.eventsTime(),
Expand All @@ -205,8 +221,15 @@ def __init__(self, swig_param):
self.create_group_if_needed(group_name)
self._storage[group_name]["__METADATA__"]["DESCRIPTION"] = group.description()
self._storage[group_name]["__METADATA__"]["IS_LOCKED"] = group.isLocked()

# Add easy accessor to the group
setattr(self, group_name, _GroupParameter(self._storage[group_name]))

for parameter in group.parameters():
self.add_parameter(group_name, parameter)

# There is no need to add an easy accessor to the parameter as it is implicit by the fact that it is added to the KEYS

return

def create_group_if_needed(self, group_name):
Expand Down Expand Up @@ -275,7 +298,7 @@ def __init__(self, swig_pf):
self._storage["Tz"][:, i] = Tz[i].to_array()[:, 0]

class Data(C3dMutableMapper):
def __init__(self, swig_c3d, rotations_info, extract_forceplat_data):
def __init__(self, swig_c3d, extract_forceplat_data):
super().__init__()

# Interface to swig pointers
Expand All @@ -288,8 +311,7 @@ def __init__(self, swig_c3d, rotations_info, extract_forceplat_data):
}
self._storage["analogs"] = swig_c3d.get_analogs()

if rotations_info.hasGroup():
self._storage["rotations"] = swig_c3d.get_rotations()
self._storage["rotations"] = swig_c3d.get_rotations()

# Add the platform filer if required
if extract_forceplat_data:
Expand All @@ -298,6 +320,22 @@ def __init__(self, swig_c3d, rotations_info, extract_forceplat_data):
all_pf.append(c3d.PlatForm(pf))
self._storage["platform"] = all_pf
return

@property
def points(self):
return self._storage["points"]

@property
def meta_points(self):
return self._storage["meta_points"]

@property
def analogs(self):
return self._storage["analogs"]

@property
def rotations(self):
return self._storage["rotations"]

def add_parameter(
self,
Expand Down Expand Up @@ -329,7 +367,7 @@ def add_parameter(

def add_event(
self,
time: (list, tuple),
time: list | tuple,
context: str = "",
label: str = "",
description: str = "",
Expand Down
2 changes: 1 addition & 1 deletion src/RotationsInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ ezc3d::DataNS::RotationNS::Info::Info(
_hasGroup(false),
_dataStart(-1),
_used(0),
_ratio(1)
_ratio(0)
{
if (!c3d.parameters().isGroup("ROTATION")){
return;
Expand Down
27 changes: 25 additions & 2 deletions test/python3/test_binder_python.py
Original file line number Diff line number Diff line change
Expand Up @@ -525,9 +525,32 @@ def test_parse_and_rebuild_header(c3d_build_rebuild_all):


def test_parse_and_rebuild_parameters(c3d_build_rebuild_reduced):
# UNITS2 is not in the original file (Label2), but is required. Therefore, the parameters won't match
orig, rebuilt = c3d_build_rebuild_reduced
assert orig["parameters"] == rebuilt["parameters"]
for group_key in orig.parameters._storage:
for param_key in orig.parameters[group_key]:
if not isinstance(orig.parameters[group_key][param_key], dict):
# Only test the values that are actual parameters
continue
if "type" not in orig.parameters[group_key][param_key]:
# Only test the values that are actual parameters
continue

if param_key == "DATA_START":
# Skip DATA_START as it is an internal value
continue

try:
assert orig.parameters[group_key][param_key]['type'] == rebuilt.parameters[group_key][param_key]['type']
except:
# Type may differ for empty values
if not orig.parameters[group_key][param_key]['value'] and not rebuilt.parameters[group_key][param_key]['value']:
pass
else:
assert orig.parameters[group_key][param_key]['type'] == rebuilt.parameters[group_key][param_key]['type']
assert orig.parameters[group_key][param_key]['description'] == rebuilt.parameters[group_key][param_key]['description']
assert orig.parameters[group_key][param_key]['is_locked'] == rebuilt.parameters[group_key][param_key]['is_locked']
assert np.all(orig.parameters[group_key][param_key]['value'] == rebuilt.parameters[group_key][param_key]['value'])



def test_parse_and_rebuild_data(c3d_build_rebuild_all):
Expand Down
Loading