def make_cycle_data( foot_positions: limb.Property, times: dict ) -> limb.Property: """ Trial report pages contain a duty-cycle diagram, which is created using the data generated in this method. The returned limb.Property contains a list for each limb of the duty cycle regions for that limb where a region is a list with elements: - 0: The time at which this cycle ends - 1: The enumerated annotation for this cycle :param foot_positions: Simulation results :param times: Time step information """ gait_cycles = limb.Property().assign([], [], [], []) for i in range(times['count']): for key in limb.KEYS: cycles = gait_cycles.get(key) pos = foot_positions.get(key)[i] if not cycles or cycles[-1][1] != pos.annotation: cycles.append([1, pos.annotation]) else: cycles[-1][0] += 1 return gait_cycles
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 create_tangents(foot_positions: limb.Property) -> limb.Property: """ :param foot_positions: :return: """ def find_next_position(_positions, _i): reference_pos = _positions[_i] for pos in _positions[(i + 1) :]: identical = ( mstats.value.equivalent(pos.x.raw, reference_pos.x.raw, 0.1), mstats.value.equivalent(pos.y.raw, reference_pos.y.raw, 0.1), ) if not identical[0] or not identical[1]: return pos return None tangents = limb.Property().assign([], [], [], []) for key in limb.KEYS: positions = foot_positions.get(key) for i in range(len(positions)): p = positions[i] next_pos = find_next_position(positions, i) if not next_pos or i >= (len(positions) - 1): try: tan = tangents.get(key)[-1] except IndexError as err: print("Index:", i) print("Positions:", len(positions)) print("Next:", next_pos) raise err else: tan = geometry.LineSegment2D(p, next_pos) tan.post_extend_line(4) tan.pre_extend_line(4) tangents.get(key).append(tan) return tangents
def save_positions_file(trackway_positions: limb.Property, path): """ Saves a limb positions property object to the specified path as a CSV file with columns: lp_x, lp_dx, lp_y, lp_dy, [lp_assumed], [lp_name], [lp_uid] rp_x, rp_dx, rp_y, rp_dy, [rp_assumed], [rp_name], [rp_uid] lm_x, lm_dx, lm_y, lm_dy, [lm_assumed], [lm_name], [lm_uid] rm_x, rm_dx, rm_y, rm_dy, [rm_assumed], [rm_name], [rm_uid] :param trackway_positions: The trackway positions to be saved :param path: The path to the positions file to be saved """ df = [] for prefix, limb_key in limb.LIMB_KEY_LOOKUP.items(): for index, position in enumerate(trackway_positions.get(limb_key)): while index >= len(df): df.append(dict()) row = df[index] row['{}_x'.format(prefix)] = position.x.value row['{}_dx'.format(prefix)] = position.x.uncertainty row['{}_y'.format(prefix)] = position.y.value row['{}_dy'.format(prefix)] = position.y.uncertainty row['{}_name'.format(prefix)] = position.name row['{}_uid'.format(prefix)] = position.uid row['{}_assumed'.format(prefix)] = 'x' if position.assumed else None df = pd.DataFrame(df) df.to_csv(path) return df
def make_animation_frame_data( foot_positions: limb.Property, times: dict, coupling_data: dict, tangent_data: dict ) -> typing.List[dict]: """ Creates a list of animation frame data from the results, which is used by the JavaScript report to animate the feet within the trackway. Each frame in the returned list contains: - time: The simulation time for the frame - positions: An ordered list of position dictionaries for each limb, where the order is defined by the limb.KEYS order. Each dictionary contains: - x: A list where x[0] is the position and x[1] is the uncertainty - y: A list where y[0] is the position and y[1] is the uncertainty - f: The enumerated annotation for the position :param coupling_data: :param tangent_data: :param foot_positions: The simulation results :param times: Time step information """ frames = [] for i in range(times['count']): positions = [] tangents = [] for key in limb.KEYS: pos = foot_positions.get(key)[i] tan = tangent_data['tangents'].get(key)[i] positions.append({ 'x': [pos.x.value, pos.x.uncertainty, pos.x.raw], 'y': [pos.y.value, pos.y.uncertainty, pos.y.raw], 'f': pos.annotation, 'tx0': [tan.start.x.raw, tan.start.x.uncertainty], 'ty0': [tan.start.y.raw, tan.start.y.uncertainty], 'tx1': [tan.end.x.raw, tan.end.x.uncertainty], 'ty1': [tan.end.y.raw, tan.end.y.uncertainty], }) rear = coupling_data['rear'][i] forward = coupling_data['forward'][i] midpoint = coupling_data['midpoints'][i] rear_box = tangent_data['rear_boxes'][i] forward_box = tangent_data['forward_boxes'][i] frames.append(dict( time=times['cycles'][i], support_time=times['support_cycles'][i], positions=positions, rear_coupler={ 'x': [rear.x.value, rear.x.uncertainty, rear.x.raw], 'y': [rear.y.value, rear.y.uncertainty, rear.y.raw] }, forward_coupler={ 'x': [forward.x.value, forward.x.uncertainty, forward.x.raw], 'y': [forward.y.value, forward.y.uncertainty, forward.y.raw] }, midpoint={ 'x': [midpoint.x.value, midpoint.x.uncertainty, midpoint.x.raw], 'y': [midpoint.y.value, midpoint.y.uncertainty, midpoint.y.raw] }, rear_support_box=[ { 'x': rear_box[0].x.raw, 'y': rear_box[0].y.raw }, { 'x': rear_box[1].x.raw, 'y': rear_box[1].y.raw }, { 'x': rear_box[2].x.raw, 'y': rear_box[2].y.raw }, { 'x': rear_box[3].x.raw, 'y': rear_box[3].y.raw } ], forward_support_box=[ { 'x': forward_box[0].x.raw, 'y': forward_box[0].y.raw }, { 'x': forward_box[1].x.raw, 'y': forward_box[1].y.raw }, { 'x': forward_box[2].x.raw, 'y': forward_box[2].y.raw }, { 'x': forward_box[3].x.raw, 'y': forward_box[3].y.raw } ] )) return frames