def calc_pos_and_size(size_animated, pos_animated, animated_1, animated_2, orig_path, i, i1): """ Between two frames, this function is called if either "only point1's interval is constant" or "only point2's interval is constant". It calculates the position and size property for lottie format. It also adds a new waypoint just before the end of the frame if required. param3: Can be param1 or param2 of rectangle layer param4: Can be param2 or param1 of rectangle layer, but opposite of param3 Args: size_animated (lxml.etree._Element): Holds the size parameter of rectangle layer in Synfig format pos_animated (lxml.etree._Element): Holds the position parameter of rectangle layer in Synfig format animated_1 (lxml.etree._Element): Holds the param3 in Synfig format animated_2 (lxml.etree._Element): Holds the param4 in Synfig format orig_path (dict) : Holds the param4 in Lottie format i (int) : Iterator for animated_2 i1 (int) : Iterator for pos_animated and size_animated Returns: (int, int) : Updated iterators i and i1 are returned """ pos_animated[i1].attrib["after"] = animated_2[i].attrib["after"] size_animated[i1].attrib["after"] = animated_2[i].attrib["after"] copy_tcb(pos_animated[i1], animated_2[i]) copy_tcb(size_animated[i1], animated_2[i]) get_average(pos_animated[i1], animated_1[i], animated_2[i]) get_difference(size_animated[i1], animated_1[i], animated_2[i]) # Inserting a waypoint just before the nextwaypoint # Only if a waypoint can be inserted t_next = get_frame(animated_2[i + 1]) t_present = get_frame(animated_2[i]) ######### Need to check if t_next - t_present < 2 ##### if abs(t_next - t_present) >= 2: pos = get_vector_at_frame(orig_path, t_next - 1) pos = to_Synfig_axis(pos, "vector") new_waypoint = copy.deepcopy(pos_animated[i1]) new_waypoint.attrib["before"] = new_waypoint.attrib["after"] new_waypoint.attrib["time"] = str( (t_next - 1) / settings.lottie_format["fr"]) + "s" new_waypoint[0][0].text, new_waypoint[0][1].text = str(pos[0]), str( pos[1]) n_size_waypoint = copy.deepcopy(new_waypoint) get_average(new_waypoint, new_waypoint, animated_1[i]) get_difference(n_size_waypoint, n_size_waypoint, animated_1[i]) pos_animated.insert(i1 + 1, new_waypoint) size_animated.insert(i1 + 1, n_size_waypoint) i1 += 1 i, i1 = i + 1, i1 + 1 get_average(pos_animated[i1], animated_1[i], animated_2[i]) get_difference(size_animated[i1], animated_1[i], animated_2[i]) return i, i1
def gen_dummy_waypoint(non_animated, is_animate, anim_type): """ Makes a non animated parameter to animated parameter by creating a new dummy waypoint with constant animation Args: non_animated (lxml.etree._Element): Holds the non-animated parameter in Synfig xml format is_animate (int) : Decides if a waypoint is animated anim_type (str) : Decides the animation type Returns: (lxml.etree._Element) : Updated non-animated parameter, which is now animated """ if is_animate == 0: st = '<param name="anything"><animated type="{anim_type}"><waypoint time="0s" before="constant" after="constant"></waypoint></animated></param>' st = st.format(anim_type=anim_type) root = etree.fromstring(st) root[0][0].append(copy.deepcopy(non_animated[0])) non_animated = root elif is_animate == 1: non_animated[0][0].attrib["before"] = non_animated[0][0].attrib[ "after"] = "constant" new_waypoint = copy.deepcopy(non_animated[0][0]) frame = get_frame(non_animated[0][0]) frame += 1 time = frame / settings.lottie_format["fr"] time = str(time) + "s" new_waypoint.attrib["time"] = time non_animated[0].insert(1, new_waypoint) return non_animated
def get_cross_list(animation_1, animation_2, orig_path_1, orig_path_2): """ This function will return a list('set' technically) at which the point1 and point2 of rectangle will cross each other. This set might contain frames at which waypoints are already present, hence this need to be taken care of Args: animation_1 (lxml.etree._Element): Stores the animation of `point1` parameter in Synfig format animation_2 (lxml.etree._Element): Stores the animation of `point2` parameter in Synfig format orig_path_1 (dict) : Stores the animation of `point1` parameter in Lottie format orig_path_2 (dict) : Stores the animation of `point2` parameter in Lottie format Returns: (set) : Contains the frames at which point1 and point2 cross each other """ en_fr = max(get_frame(animation_1[-1]), get_frame(animation_2[-1])) # Function to determine the sign of a variable sign = lambda a: (1, -1)[a < 0] prev_1 = float(animation_1[0][0][0].text), float(animation_1[0][0][1].text) prev_2 = float(animation_2[0][0][0].text), float(animation_2[0][0][1].text) # The list to be returned ret_list = set() frame = 1 # Loop for all the frames while frame <= en_fr: now_1 = get_vector_at_frame(orig_path_1, frame) now_1 = to_Synfig_axis(now_1, "vector") now_2 = get_vector_at_frame(orig_path_2, frame) now_2 = to_Synfig_axis(now_2, "vector") is_needed = False if sign(prev_1[0] - prev_2[0]) != sign(now_1[0] - now_2[0]): is_needed = True elif sign(prev_1[1] - prev_2[1]) != sign(now_1[1] - now_2[1]): is_needed = True if is_needed: ret_list.add(frame - 1) ret_list.add(frame) prev_1, prev_2 = now_1, now_2 frame += 1 return ret_list
def gen_image_scale(animated_1, animated_2, width, height): """ In Synfig, no scale parameter is available for image layer, so it will be created here for Lottie conversion Args: animated_1 (lxml.etree._Element): point1 animation in Synfig format animated_2 (lxml.etree._Element): point2 animation in Synfig format width (int) : Width of the original image height (int) : Height of the original image Returns: (lxml.etree._Element) : Scale parameter in Synfig format """ st = '<param name="image_scale"><real value="0.0000000000"/></param>' root = etree.fromstring(st) is_animate = is_animated(root) root = gen_dummy_waypoint(root, is_animate, "image_scale") anim1_path, anim2_path = {}, {} gen_properties_multi_dimensional_keyframed(anim1_path, animated_1, 0) gen_properties_multi_dimensional_keyframed(anim2_path, animated_2, 0) # Filling the first 2 frames with there original scale values fill_image_scale_at_frame(root[0], anim1_path, anim2_path, width, height, 0) fill_image_scale_at_frame(root[0], anim1_path, anim2_path, width, height, 1) mx_fr = max(get_frame(animated_1[-1]), get_frame(animated_2[-1])) fr = 2 while fr <= mx_fr: new_waypoint = copy.deepcopy(root[0][0]) time = fr / settings.lottie_format["fr"] time = str(time) + "s" new_waypoint.attrib["time"] = time root[0].append(new_waypoint) fill_image_scale_at_frame(root[0], anim1_path, anim2_path, width, height, fr) fr += 1 return root
def insert_waypoint_at_frame(animated, orig_path, frame, animated_name): """ This function will only insert a waypoint at 'frame' if no waypoint is present at that 'frame' already Args: animated (lxml.etree._Element): Holds the animation in Synfig xml format orig_path (dict) : Holds the animation in Lottie format frame (int) : The frame at which the waypoint is to be inserted animated_name (str) : The name/type of animation Returns: (None) """ i = 0 while i < len(animated): at_frame = get_frame(animated[i]) if frame == at_frame: return elif frame < at_frame: break i += 1 pos = get_vector_at_frame(orig_path, frame) pos = to_Synfig_axis(pos, animated_name) if i == len(animated): new_waypoint = copy.deepcopy(animated[i - 1]) else: new_waypoint = copy.deepcopy(animated[i]) if animated_name == "vector": new_waypoint[0][0].text = str(pos[0]) new_waypoint[0][1].text = str(pos[1]) else: new_waypoint[0].attrib["value"] = str(pos) new_waypoint.attrib["time"] = str( frame / settings.lottie_format["fr"]) + "s" if i == 0 or i == len(animated): # No need of tcb value copy as halt interpolation need to be copied here new_waypoint.attrib["before"] = new_waypoint.attrib[ "after"] = "constant" else: copy_tcb_average(new_waypoint, animated[i], animated[i - 1]) new_waypoint.attrib["before"] = animated[i - 1].attrib["after"] new_waypoint.attrib["after"] = animated[i].attrib["before"] # If the interval was constant before, then the whole interval should # remain constant now also if new_waypoint.attrib["before"] == "constant" or new_waypoint.attrib[ "after"] == "constant": new_waypoint.attrib["before"] = new_waypoint.attrib[ "after"] = "constant" animated.insert(i, new_waypoint)
def get_animated_time_list(child, time_list): """ Appends all the frames corresponding to the waypoints in the animated(child[0]) list, in time_list Args: child (lxml.etree._Element) : Parent Element of animation time_list (set) : Will store all the frames at which waypoints are present Returns: (None) """ animated = child[0] is_animate = is_animated(animated) if is_animate in {0, 1}: return for waypoint in animated: frame = get_frame(waypoint) time_list.add(frame)
def both_points_animated(animated_1, animated_2, param_expand, lottie, index): """ This function generates the lottie dictionary for position and size property of lottie(point1 and point2 are used from Synfig), when both point1 and point2 are animated Args: animated_1 (lxml.etree._Element): Holds the parameter `point1`'s animation in Synfig xml format animated_2 (lxml.etree._Element): Holds the parameter `point2`'s animation in Synfig xml format param_expand (lxml.etree._Element): Holds the parameter `expand`'s animation in Synfig xml format lottie (dict) : Lottie format rectangle layer will be store in this index (int) : Stores the index of parameters in rectangle layer Returns: (None) """ animated_1, animated_2 = animated_1[0], animated_2[0] orig_path_1, orig_path_2 = {}, {} expand_path = {} gen_value_Keyframed(expand_path, param_expand[0], 0) gen_properties_multi_dimensional_keyframed(orig_path_1, animated_1, 0) gen_properties_multi_dimensional_keyframed(orig_path_2, animated_2, 0) #################### SECTION 1 ########################### # Insert waypoints in the point1 and point2 parameter at the place where # expand parameter is animated time_list = set() get_animated_time_list(param_expand, time_list) for frame in time_list: insert_waypoint_at_frame(animated_1, orig_path_1, frame, "vector") insert_waypoint_at_frame(animated_2, orig_path_2, frame, "vector") #################### END OF SECTION 1 #################### ### SECTION TRY ### # Every frames value is precomputed in order to achieve maximum similarity # to that of Synfig en_fr = max(get_frame(animated_1[-1]), get_frame(animated_2[-1])) fra = 1 while fra <= en_fr: insert_waypoint_at_frame(animated_1, orig_path_1, fra, "vector") insert_waypoint_at_frame(animated_2, orig_path_2, fra, "vector") fra += 1 ### END SECTION ### ######################### SECTION 2 ########################## # Insert the waypoints at corresponding positions where point1 is animated # and point2 is animated for waypoint in animated_1: frame = get_frame(waypoint) insert_waypoint_at_frame(animated_2, orig_path_2, frame, "vector") for waypoint in animated_2: frame = get_frame(waypoint) insert_waypoint_at_frame(animated_1, orig_path_1, frame, "vector") ##################### END OF SECTION 2 ####################### ##################### SECTION 3 ############################## # Add the impact of expand parameter amount towards point1 and point2 # parameter assert len(animated_1) == len(animated_2) for waypoint1, waypoint2 in zip(animated_1, animated_2): frame = get_frame(waypoint1) assert frame == get_frame(waypoint2) expand_amount = get_vector_at_frame(expand_path, frame) expand_amount = to_Synfig_axis(expand_amount, "real") pos1, pos2 = get_vector(waypoint1), get_vector(waypoint2) # Comparing the x-coordinates if pos1.val1 > pos2.val1: pos1.val1 += expand_amount pos2.val1 -= expand_amount else: pos1.val1 -= expand_amount pos2.val1 += expand_amount # Comparing the y-coordinates if pos1.val2 > pos2.val2: pos1.val2 += expand_amount pos2.val2 -= expand_amount else: pos1.val2 -= expand_amount pos2.val2 += expand_amount set_vector(waypoint1, pos1) set_vector(waypoint2, pos2) ##################### END OF SECTION 3 ####################### #################### SECTION 4 ############################# # Place waypoints at which the x and y cross each other/cross the extremas cross_list = get_cross_list(animated_1, animated_2, orig_path_1, orig_path_2) for frame in cross_list: insert_waypoint_at_frame(animated_1, orig_path_1, frame, "vector") insert_waypoint_at_frame(animated_2, orig_path_2, frame, "vector") #################### END SECTION 4 ######################### ################## SECTION 5 ################################################ # Store the position of rectangle according to the waypoints in pos_animated # Store the size of rectangle according to the waypoints in size_animated pos_animated = copy.deepcopy(animated_1) size_animated = copy.deepcopy(animated_1) size_animated.attrib["type"] = "rectangle_size" i, i1 = 0, 0 while i < len(animated_1) - 1: cur_get_after_1, cur_get_after_2 = animated_1[i].attrib[ "after"], animated_2[i].attrib["after"] next_get_before_1, next_get_before_2 = animated_1[ i + 1].attrib["before"], animated_2[i + 1].attrib["before"] dic_1 = {"linear", "auto", "clamped", "halt"} dic_2 = {"constant"} constant_interval_1 = cur_get_after_1 in dic_2 or next_get_before_1 in dic_2 constant_interval_2 = cur_get_after_2 in dic_2 or next_get_before_2 in dic_2 # Case 1 no "constant" interval is present if (cur_get_after_1 in dic_1) and (cur_get_after_2 in dic_1) and ( next_get_before_1 in dic_1) and (next_get_before_2 in dic_1): get_average(pos_animated[i1], animated_1[i], animated_2[i]) copy_tcb_average(pos_animated[i1], animated_1[i], animated_2[i]) get_difference(size_animated[i1], animated_1[i], animated_2[i]) copy_tcb(size_animated[i1], pos_animated[i1]) i, i1 = i + 1, i1 + 1 get_average(pos_animated[i1], animated_1[i], animated_2[i]) copy_tcb_average(pos_animated[i1], animated_1[i], animated_2[i]) get_difference(size_animated[i1], animated_1[i], animated_2[i]) copy_tcb(size_animated[i1], pos_animated[i1]) # Case 2 only one "constant" interval: could mean two "constant"'s are present elif (constant_interval_1 and not constant_interval_2) or (not constant_interval_1 and constant_interval_2): if constant_interval_1: i, i1 = calc_pos_and_size(size_animated, pos_animated, animated_1, animated_2, orig_path_2, i, i1) elif constant_interval_2: i, i1 = calc_pos_and_size(size_animated, pos_animated, animated_2, animated_1, orig_path_1, i, i1) # Case 3 both are constant elif constant_interval_1 and constant_interval_2: # No need to copy tcb, as it's pos should be "constant" get_average(pos_animated[i1], animated_1[i], animated_2[i]) get_difference(size_animated[i1], animated_1[i], animated_2[i]) i, i1 = i + 1, i1 + 1 get_difference(size_animated[i1], animated_1[i], animated_2[i]) get_average(pos_animated[i1], animated_1[i], animated_2[i]) ######################### SECTION 5 END ############################## ######################### SECTION 6 ################################## # Generate the position and size for lottie format gen_properties_multi_dimensional_keyframed(lottie["p"], pos_animated, index.inc()) gen_value_Keyframed(lottie["s"], size_animated, index.inc())