diff --git a/blender-addon/clever-show-addon-src/__init__.py b/blender-addon/clever-show-addon-src/__init__.py index 37413c6..2c021d5 100644 --- a/blender-addon/clever-show-addon-src/__init__.py +++ b/blender-addon/clever-show-addon-src/__init__.py @@ -50,19 +50,19 @@ class CleverShowProperties(PropertyGroup): options=set(), # not animateable ) - # takeoff_frames: IntProperty( - # name="Takeoff duration", - # description="Duration of takeoff in frames", - # default=70, - # min=1, - # ) - # - # land_frames: IntProperty( - # name="Land duration", - # description="Duration of landing in frames", - # default=100, - # min=1, - # ) + takeoff_frames: IntProperty( + name="Takeoff duration", + description="Duration of takeoff in frames", + default=70, + min=1, + ) + + land_frames: IntProperty( + name="Land duration", + description="Duration of landing in frames", + default=100, + min=1, + ) filter_obj: EnumProperty( name="Filter objects:", diff --git a/blender-addon/clever-show-addon-src/operators/export.py b/blender-addon/clever-show-addon-src/operators/export.py index fd18984..aea6959 100644 --- a/blender-addon/clever-show-addon-src/operators/export.py +++ b/blender-addon/clever-show-addon-src/operators/export.py @@ -16,8 +16,19 @@ def create_dir(folder_path): os.mkdir(folder_path) -def iter_pairs(obj): - return zip(obj[::2], obj[1::2]) +# def iter_pairs(obj): +# return zip(obj[::2], obj[1::2]) + +def neighbour_pairs(sequence): + iterable = iter(sequence) + try: + prev = next(iterable) + except StopIteration: + return () + for item in iterable: + yield prev, item + prev = item + def get_rgb(drone_obj): @@ -79,6 +90,7 @@ class ExportSwarmAnimation(Operator, ExportHelper): pass def _generate_animation(self, drone_obj, context): + # todo yield? animation = list() scene = context.scene @@ -88,14 +100,13 @@ class ExportSwarmAnimation(Operator, ExportHelper): frame_end = context.scene.frame_end # Add frame with animation parameters scene.frame_set(frame_start) - # animation.append({"set_fps": context.scene.render.fps, - # "drone_name": drone_obj.name, - # }) - if clever_show.add_takeoff: - animation.append({"takeoff": {}}) + animation.append({"set_fps": context.scene.render.fps, + "drone_name": drone_obj.name, + }) - # Add flight and + # Add flight + previous_frame = dict() for frame_num in range(frame_start, frame_end + 1): scene.frame_set(frame_start) position = [round(x, 3) for x in drone_obj.matrix_world.to_translation()] @@ -103,11 +114,14 @@ class ExportSwarmAnimation(Operator, ExportHelper): frame = dict() # check to not update position or yaw if they are same as previous frame - if animation[-1]["fly"] != position: + if previous_frame.get("fly", None) != position: frame.update({"fly": position}) - if animation[-1]["yaw"] != yaw: + if previous_frame.get("yaw", None) != yaw: frame.update({"yaw": yaw}) + if clever_show.use_armed and previous_frame.get("armed") != drone_obj.armed: + frame.update({"armed": drone_obj.armed}) + try: led_color = get_rgb(drone_obj) # TODO!!!!!! frame.update({"led_color": led_color}) @@ -115,6 +129,10 @@ class ExportSwarmAnimation(Operator, ExportHelper): pass animation.append(frame) + previous_frame = frame + + if clever_show.add_takeoff: + animation.insert(0, {"takeoff": {}}) if clever_show.add_land: animation.append({"land": {}}) @@ -124,28 +142,36 @@ class ExportSwarmAnimation(Operator, ExportHelper): return animation - def _detect_armed_states(self, drone_obj, animation, context): + @staticmethod + def find_intervals(values): + j = None + vals = itertools.chain((-1, ), values) # to ensure detection from first element + for i, items in enumerate(neighbour_pairs(vals)): + item1, item2 = items + if item2 > item1: + j = i + elif j is not None and item2 < item1: + yield (j, i) # j+1, i+1 + j = None + + if j is not None: + yield (j, j) # j+1, j+1 + + @staticmethod + def pop_func(animation, func): + state = False + for frame in animation: + current = frame.pop(func, None) + state = current if current is not None else state + yield state + + @classmethod + def _detect_states(cls, drone_obj, animation, context): # clever_show = context.scene.clever_show + intervals = cls.find_intervals(cls.pop_func(animation, "armed")) - tracks = drone_obj.animation_data - if not tracks: - return animation - - fcurve = next((fcurve for fcurve in tracks if fcurve.data_path == "drone.armed"), None) - if fcurve is None: - return animation - - keyframes = fcurve.keyframe_points - if len(keyframes) % 2 != 0: - raise RuntimeError("Incorrect armed state keyframes!") - - for frame1, frame2 in iter_pairs(keyframes): - if frame1 == frame2: # equal: wrong order - raise RuntimeError("Incorrect armed state keyframes!") - - start_frame = frame1.co[0] - duration = frame2.co[0] - start_frame - # todo height? + for start, end in intervals: + # duration = max(end-start, 1) if frame1 < frame2: # not armed -> armed: takeoff func = "takeoff" @@ -154,30 +180,47 @@ class ExportSwarmAnimation(Operator, ExportHelper): animation[start_frame].update({func: {"duration": duration}}) + @classmethod + def _detect_armed_states(cls, animation): + for i, items in enumerate(neighbour_pairs(vals)): + item1, item2 = items + if item2 > item1: + j = i + elif j is not None and item2 < item1: + yield (j, i) # j+1, i+1 + j = None + + return animation def _detect_animation_takeoff_land(self, drone_obj, animation, context): - takeoff = False - land = False + floor_level = 0 + start_frame = 0 + for i, frames in enumerate(neighbour_pairs(animation)): + frame1, frame2 = frames + if frame1 == floor_level and frame2 > frame1: # takeoff start + pass + elif frame2 == floor_level and frame1 > frame2: # land start + pass - # previous_z = 0 def find(animation): + + def get_z(index, default=float('nan')): + return animation[index].get("fly", None)[2] or default + i = 0 - previous_z = animation[i]["fly"][2] # height of the first frame + previous_z = get_z(i) # height of the first frame while i < len(animation): - z = animation[i]["fly"][2] - - - - - for frame in animation: - z = frame["fly"][2] - if previous_z == 0 and z > previous_z: - takeoff = True - #if p - + current_z = get_z(i, previous_z) + if previous_z == 0: + while current_z > previous_z: + i += 1 + previous_z = current_z + current_z = get_z(i, previous_z) + i += 1 + previous_z = current_z def _process_animation(self, animation, context): #delete unnececary flight functions while copter landed # clever_show = context.scene.clever_show diff --git a/blender-addon/old.py b/blender-addon/old.py index 40091b7..1b1844c 100644 --- a/blender-addon/old.py +++ b/blender-addon/old.py @@ -27,10 +27,7 @@ class ExportCsv(Operator, ExportHelper): filename_ext = '' use_filter_folder = True - use_namefilter: bpy.props.BoolProperty( - name="Use name filter for objects", - default=False, - ) + use_namefilter: bpy.props.BoolProperty(name="Use name filter for objects", default=False) drones_name: bpy.props.StringProperty( name="Name identifier", @@ -38,10 +35,7 @@ class ExportCsv(Operator, ExportHelper): default="clever" ) - show_warnings: bpy.props.BoolProperty( - name="Show detailed animation warnings", - default=False, - ) + show_warnings: bpy.props.BoolProperty(name="Show detailed animation warnings", default=False) speed_warning_limit: bpy.props.FloatProperty( name="Speed limit",