def strides(
        foot_positions: limb.Property,
        times: dict
) -> typing.Dict[str, list]:

    out = dict()

    for key, positions in foot_positions.items():
        distances = []
        out[key + '_strides'] = distances
        last_fixed = None

        for index, time in enumerate(times['cycles']):
            p = positions[index]

            if last_fixed is None:
                # Skip entries in motion until the first fixed position is
                # found
                if p.annotation == 'F':
                    last_fixed = p

                continue

            if p.annotation == 'F' and not last_fixed.compare(p):
                distances.append(events.Event(
                    time=time,
                    index=index,
                    value=last_fixed.distance_between(p)
                ))
                last_fixed = p

    return out
def write_data(
        path: str,
        settings: dict,
        trackway_definition: trackway.TrackwayDefinition,
        foot_positions: limb.Property,
        times: dict,
        coupling_data: dict,
        advancement_data: dict,
        tangent_data: dict
):
    """
    Writes a JSON serialized data file containing the results of the trial for
    later analysis

    :param path:
    :param settings:
    :param trackway_definition:
    :param foot_positions:
    :param times:
    :param coupling_data:
    :param advancement_data:
    :param tangent_data:
    :return:
    """

    position_data = dict()
    for limb_id, positions in foot_positions.items():
        position_data[limb_id] = [x.to_dict() for x in positions]

    track_data = dict()
    for limb_id, positions in trackway_definition.limb_positions.items():
        track_data[limb_id] = [x.to_dict() for x in positions]

    reporting.write_json_results(path, dict(
        settings=settings,
        times=times,
        foot_positions=position_data,
        track_positions=track_data,
        couplings=coupling.serialize(coupling_data),
        advancement=advancement.serialize(advancement_data),
        tangent=tangent.serialize(tangent_data)
    ))
def unused_foot_prints(
        print_positions: limb.Property,
        foot_positions: limb.Property
):
    """
    Trims the print positions lists to include only those positions found in
    the foot positions lists and the positions just before and after to provide
    context

    :param print_positions:
    :param foot_positions:
    :return:
    """

    was_pruned = False

    def is_in(uid: str, items: list) -> bool:
        for item in items:
            if item.uid == uid:
                return True
        return False

    for limb_key, foot_prints in print_positions.items():

        index = len(foot_prints) - 1
        while index > 0:
            index -= 1
            if is_in(foot_prints[index].uid, foot_positions.get(limb_key)):
                break
            foot_prints.pop()
            was_pruned = True

        while len(foot_prints) > 1:
            if is_in(foot_prints[1].uid, foot_positions.get(limb_key)):
                break
            foot_prints.pop(0)
            was_pruned = True

    return was_pruned
def trackway_positions(
        limb_positions: limb.Property,
        drawer,
        positions=None
):
    """

    :param limb_positions:
    :param drawer:
    :param positions:
    :return:
    """

    limb_positions = create_positions(limb_positions, positions)

    bounds = [1e12, 1e12, -1e12, -1e12]
    for positions in limb_positions.values():
        for pos in positions:
            bounds[0] = min(bounds[0], pos.x.raw, pos.x.value)
            bounds[1] = min(bounds[1], pos.y.raw, pos.y.value)
            bounds[2] = max(bounds[2], pos.x.raw, pos.x.value)
            bounds[3] = max(bounds[3], pos.y.raw, pos.y.value)

    scale = 2048.0 / max(
        abs(bounds[2] - bounds[0]),
        abs(bounds[3] - bounds[1])
    )

    drawer.add_style_definition('.left_pes', {
        'fill': svg.SvgWriter.LIMB_COLORS.left_pes,
        'opacity': '0.5'
    })

    drawer.add_style_definition('.right_pes', {
        'fill': svg.SvgWriter.LIMB_COLORS.right_pes,
        'opacity': '0.5'
    })

    drawer.add_style_definition('.left_manus', {
        'fill': svg.SvgWriter.LIMB_COLORS.left_manus,
        'opacity': '0.5'
    })

    drawer.add_style_definition('.right_manus', {
        'fill': svg.SvgWriter.LIMB_COLORS.right_manus,
        'opacity': '0.5'
    })

    for key, positions in limb_positions.items():
        is_pes = key in [limb.LEFT_PES, limb.RIGHT_PES]

        for pos in positions:
            classes = [key, 'track-pos']

            html_data = {
                'x': '{}'.format(pos.x.value),
                'x-unc': '{}'.format(pos.x.uncertainty),
                'y': '{}'.format(pos.y.value),
                'y-unc': '{}'.format(pos.y.uncertainty),
                'color': svg.SvgWriter.LIMB_COLORS.get(key)
            }

            if pos.annotation:
                html_data['annotation'] = pos.annotation
            if pos.uid:
                html_data['uid'] = pos.uid
            if pos.name:
                html_data['name'] = pos.name
            if pos.assumed:
                html_data['assumed'] = '1'
                classes.append('assumed')

            drawer.draw_circle(
                x=scale*pos.x.raw,
                y=-scale*pos.y.raw,
                radius=PES_RADIUS if is_pes else RADIUS,
                classes=classes,
                data=html_data
            )

        color = svg.SvgWriter.LIMB_COLORS.get(key)

        print_style_name = '.{}'.format(key)
        print_style = {
            'fill': color,
            'opacity': '0.5',
            'stroke-width': '{}px'.format(STROKE),
            'stroke': color
        }

        assumed_style_name = '.{}.assumed'.format(key)
        assumed_style = {
            'fill': 'white',
            'opacity': '0.33'
        }

        marker_style_name = '.{}-marker'.format(key)
        marker_style = {
            'fill': 'transparent',
            'stroke-width': '{}px'.format(STROKE),
            'stroke': color
        }

        if not is_pes:
            dash_array = '{},{}'.format(4, 4)
            marker_style['stroke-dasharray'] = '{},{}'.format(8,4)
            print_style['stroke-dasharray'] = dash_array
            assumed_style['stroke-dasharray'] = dash_array

        drawer.add_style_definition(print_style_name, print_style)
        drawer.add_style_definition(assumed_style_name, assumed_style)
        drawer.add_style_definition(marker_style_name, marker_style)

        p0 = positions[0]
        drawer.draw_circle(
            x=scale*p0.x.raw,
            y=-scale*p0.y.raw,
            radius=(PES_RADIUS if is_pes else RADIUS) + 0.5 * STROKE,
            classes=marker_style_name[1:],
            name=key
        )

    return {
        'scale': scale,
        'offset': (0, 0)
    }