def gen_path(self, anim_type="real", idx=0): """ Generates the path for this parameter over time depending on the animation type of this parameter """ self.path = {} if anim_type in {"real", "bool"}: gen_value_Keyframed(self.path, self.param[0], idx) else: gen_properties_multi_dimensional_keyframed(self.path, self.param[0], idx)
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 append_path(element, parent, element_name, typ="real"): """ Generates a dictionary corresponding to the path followed by that element and appends it at the parent which will be needed later Args: element (lxml.etree._Element) : Synfig format element/parameter parent (lxml.etree._Element) : Parent of the element/parameter element_name (str) : Tag of the dictionary to be stored in parent typ (:obj: str, optional) : Specifies the type of dictionary to be created Returns: (None) """ # Generating the path and store in the lxml element element_dict = {} if typ == "real": gen_value_Keyframed(element_dict, element, 0) else: gen_properties_multi_dimensional_keyframed(element_dict, element, 0) # Store in lxml element element_lxml = etree.Element(element_name) element_lxml.text = str(element_dict) parent.append(element_lxml)
def gen_helpers_transform(lottie, layer, pos=settings.DEFAULT_POSITION, anchor=settings.DEFAULT_ANCHOR, scale=settings.DEFAULT_SCALE, rotation=settings.DEFAULT_ROTATION, opacity=settings.DEFAULT_OPACITY): """ Generates the dictionary corresponding to helpers/transform.json Args: lottie (dict) : Lottie format layer layer (lxml.etree._Element) : Synfig format layer pos (:obj: `list | lxml.etree._Element`, optional) : position of layer anchor (:obj: `list | lxml.etree._Element`, optional) : anchor point of layer scale (:obj: `list | lxml.etree._Element`, optional) : scale of layer rotation (:obj: `float | lxml.etree._Element`, optional) : rotation of layer opacity (:obj: `float | lxml.etree._Element`, optional) : Opacity of layer Returns: (None) """ index = Count() lottie["o"] = {} # opacity/Amount lottie["r"] = {} # Rotation of the layer lottie["p"] = {} # Position of the layer lottie["a"] = {} # Anchor point of the layer lottie["s"] = {} # Scale of the layer # setting the position if isinstance(pos, list): gen_properties_value(lottie["p"], pos, index.inc(), settings.DEFAULT_ANIMATED, settings.NO_INFO) else: is_animate = is_animated(pos) if is_animate == 2: gen_properties_multi_dimensional_keyframed(lottie["p"], pos, index.inc()) else: x_val, y_val = 0, 0 if is_animate == 0: x_val = float(pos[0].text) * settings.PIX_PER_UNIT y_val = float(pos[1].text) * settings.PIX_PER_UNIT else: x_val = float(pos[0][0][0].text) * settings.PIX_PER_UNIT y_val = float(pos[0][0][1].text) * settings.PIX_PER_UNIT gen_properties_value(lottie["p"], change_axis(x_val, y_val, True), index.inc(), settings.DEFAULT_ANIMATED, settings.NO_INFO) # setting the opacity if isinstance(opacity, (float, int)): gen_properties_value(lottie["o"], opacity, index.inc(), settings.DEFAULT_ANIMATED, settings.NO_INFO) else: is_animate = is_animated(opacity) if is_animate == 2: opacity.attrib["type"] = "opacity" gen_value_Keyframed(lottie["o"], opacity, index.inc()) else: if is_animate == 0: val = float( opacity.attrib["value"]) * settings.OPACITY_CONSTANT else: val = float( opacity[0][0].attrib["value"]) * settings.OPACITY_CONSTANT gen_properties_value(lottie["o"], val, index.inc(), settings.DEFAULT_ANIMATED, settings.NO_INFO) # setting the rotation if isinstance(rotation, (float, int)): gen_properties_value(lottie["r"], rotation, index.inc(), settings.DEFAULT_ANIMATED, settings.NO_INFO) else: is_animate = is_animated(rotation) if is_animate == 2: gen_value_Keyframed(lottie["r"], rotation, index.inc()) else: theta = 0 # default rotation if is_animate == 0: theta = (float(rotation.attrib["value"])) else: theta = (float(rotation[0][0].attrib["value"])) gen_properties_value(lottie["r"], theta, index.inc(), settings.DEFAULT_ANIMATED, settings.NO_INFO) # setting the anchor point if isinstance(anchor, list): gen_properties_value(lottie["a"], anchor, index.inc(), settings.DEFAULT_ANIMATED, settings.NO_INFO) else: is_animate = is_animated(anchor) if is_animate == 2: gen_properties_multi_dimensional_keyframed(lottie["a"], anchor, index.inc()) else: x_val, y_val = 0, 0 if is_animate == 0: x_val = float(anchor[0].text) * settings.PIX_PER_UNIT y_val = float(anchor[1].text) * settings.PIX_PER_UNIT else: x_val = float(anchor[0][0][0].text) * settings.PIX_PER_UNIT y_val = float(anchor[0][0][1].text) * settings.PIX_PER_UNIT gen_properties_value(lottie["a"], change_axis(x_val, y_val, True), index.inc(), settings.DEFAULT_ANIMATED, settings.NO_INFO) # setting the scale if isinstance(scale, list): gen_properties_value(lottie["s"], scale, index.inc(), settings.DEFAULT_ANIMATED, settings.NO_INFO) # This means scale parameter is animated else: is_animate = is_animated(scale) if is_animate == 2: gen_value_Keyframed(lottie["s"], scale, index.inc()) else: zoom = 0 if is_animate == 0: zoom = float(scale.attrib["value"]) else: zoom = float(scale[0][0].attrib["value"]) zoom = (math.e**zoom) * 100 gen_properties_value(lottie["s"], [zoom, zoom], index.inc(), settings.DEFAULT_ANIMATED, settings.NO_INFO)
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 st_fr = min(get_frame(animated_1[0]), get_frame(animated_2[0])) en_fr = max(get_frame(animated_1[-1]), get_frame(animated_2[-1])) fra = st_fr 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[0] > pos2[0]: pos1[0] += expand_amount pos2[0] -= expand_amount else: pos1[0] -= expand_amount pos2[0] += expand_amount # Comparing the y-coordinates if pos1[1] > pos2[1]: pos1[1] += expand_amount pos2[1] -= expand_amount else: pos1[1] -= expand_amount pos2[1] += 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())
def gen_shapes_circle(lottie, layer, idx): """ Generates the dictionary corresponding to shapes/ellipse.json where ellipse will always be considered as circle Args: lottie (dict) : The lottie generated circle layer will be stored in it layer (lxml.etree._Element): Synfig format circle layer idx (int) : Stores the index of the circle layer Returns: (None) """ index = Count() lottie["ty"] = "el" # Type: circle lottie["p"] = {} # Position of circle lottie["d"] = settings.DEFAULT_DIRECTION lottie["s"] = {} # Size of circle lottie["ix"] = idx # setting the index for child in layer: if child.tag == "param": if child.attrib["name"] in {"origin", "center"}: is_animate = is_animated(child[0]) if is_animate == 2: gen_properties_multi_dimensional_keyframed( lottie["p"], child[0], index.inc()) else: x_val, y_val = 0, 0 if is_animate == 0: x_val = float(child[0][0].text) * settings.PIX_PER_UNIT y_val = float(child[0][1].text) * settings.PIX_PER_UNIT else: x_val = float( child[0][0][0][0].text) * settings.PIX_PER_UNIT y_val = float( child[0][0][0][1].text) * settings.PIX_PER_UNIT gen_properties_value(lottie["p"], change_axis(x_val, y_val), index.inc(), settings.DEFAULT_ANIMATED, settings.NO_INFO) # This will be exported as size of ellipse in lottie format elif child.attrib["name"] == "radius": is_animate = is_animated(child[0]) if is_animate == 2: child[0].attrib['type'] = "circle_radius" gen_value_Keyframed(lottie["s"], child[0], index.inc()) else: radius = 0 # default value for radius if is_animate == 0: radius = float(child[0].attrib["value"]) else: radius = float(child[0][0][0].attrib["value"]) radius_pix = int(settings.PIX_PER_UNIT) * radius diam = radius_pix * 2 gen_properties_value(lottie["s"], [diam, diam], index.inc(), settings.DEFAULT_ANIMATED, settings.NO_INFO)
def gen_shapes_star(lottie, layer, idx): """ Generates the dictionary corresponding to shapes/star.json Args: lottie (dict) : The lottie generated star layer will be stored in it layer (lxml.etree._Element): Synfig format star layer idx (int) : Stores the index of the star layer Returns: (None) """ index = Count() lottie["ty"] = "sr" # Type: star lottie["pt"] = {} # Number of points on the star lottie["p"] = {} # Position of star lottie["r"] = {} # Angle / Star's rotation lottie["ir"] = {} # Inner radius lottie["or"] = {} # Outer radius lottie["is"] = {} # Inner roundness of the star lottie["os"] = {} # Outer roundness of the star regular_polygon = {"prop": "false"} for child in layer: if child.tag == "param": if child.attrib["name"] == "regular_polygon": is_animate = is_animated(child[0]) if is_animate == 2: regular_polygon["prop"] = "changing" # Copy the child address to dictionary regular_polygon["animated"] = child[0] elif is_animate == 1: regular_polygon["prop"] = child[0][0][0].attrib["value"] else: regular_polygon["prop"] = child[0].attrib["value"] regular_polygon["animate"] = is_animate elif child.attrib["name"] == "points": is_animate = is_animated(child[0]) if is_animate == 2: # To uniquely identify the points, attribute type is changed child[0].attrib['type'] = 'points' gen_value_Keyframed(lottie["pt"], child[0], index.inc()) else: num_points = 3 # default number of points if is_animate == 0: num_points = int(child[0].attrib["value"]) else: num_points = int(child[0][0][0].attrib["value"]) gen_properties_value(lottie["pt"], num_points, index.inc(), settings.DEFAULT_ANIMATED, settings.NO_INFO) elif child.attrib["name"] == "angle": is_animate = is_animated(child[0]) if is_animate == 2: gen_value_Keyframed(lottie["r"], child[0], index.inc()) else: theta = 0 # default angle for the star if is_animate == 0: theta = get_angle(float(child[0].attrib["value"])) else: theta = get_angle(float( child[0][0][0].attrib["value"])) gen_properties_value(lottie["r"], theta, index.inc(), settings.DEFAULT_ANIMATED, settings.NO_INFO) elif child.attrib["name"] == "radius1": is_animate = is_animated(child[0]) if is_animate == 2: gen_value_Keyframed(lottie["or"], child[0], index.inc()) else: r_outer = 0 # default value for outer radius if is_animate == 0: r_outer = float(child[0].attrib["value"]) else: r_outer = float(child[0][0][0].attrib["value"]) gen_properties_value(lottie["or"], int(settings.PIX_PER_UNIT * r_outer), index.inc(), settings.DEFAULT_ANIMATED, settings.NO_INFO) elif child.attrib["name"] == "radius2": is_animate = is_animated(child[0]) if is_animate == 2: gen_value_Keyframed(lottie["ir"], child[0], index.inc()) else: r_inner = 0 # default value for inner radius if is_animate == 0: r_inner = float(child[0].attrib["value"]) else: r_inner = float(child[0][0][0].attrib["value"]) gen_properties_value(lottie["ir"], int(settings.PIX_PER_UNIT * r_inner), index.inc(), settings.DEFAULT_ANIMATED, settings.NO_INFO) elif child.attrib["name"] == "origin": is_animate = is_animated(child[0]) if is_animate == 2: gen_properties_multi_dimensional_keyframed( lottie["p"], child[0], index.inc()) else: x_val, y_val = 0, 0 if is_animate == 0: x_val = float(child[0][0].text) * settings.PIX_PER_UNIT y_val = float(child[0][1].text) * settings.PIX_PER_UNIT else: x_val = float( child[0][0][0][0].text) * settings.PIX_PER_UNIT y_val = float( child[0][0][0][1].text) * settings.PIX_PER_UNIT gen_properties_value(lottie["p"], change_axis(x_val, y_val), index.inc(), settings.DEFAULT_ANIMATED, settings.NO_INFO) # If not animated, then go to if, else if regular_polygon["animate"] in {0, 1}: if regular_polygon["prop"] == "false": lottie["sy"] = 1 # Star Type # inner property is only needed if type is star gen_properties_value(lottie["is"], 0, index.inc(), settings.DEFAULT_ANIMATED, settings.NO_INFO) else: lottie["sy"] = 2 # Polygon Type # for polygon type, "ir" and "is" must not be present del lottie["ir"] # If animated, it will always be of type star else: polygon_correction(lottie, regular_polygon["animated"]) lottie["sy"] = 1 gen_properties_value(lottie["is"], 0, index.inc(), settings.DEFAULT_ANIMATED, settings.NO_INFO) gen_properties_value(lottie["os"], 0, index.inc(), settings.DEFAULT_ANIMATED, settings.NO_INFO) lottie["ix"] = idx
def gen_dynamic_list_polygon(lottie, dynamic_list): """ Generates the bline corresponding to polygon layer Args: lottie (dict) : Lottie format polygon layer will be stored here dynamic_list (lxml.etree._Element) : Synfig format points of polygon Returns: (None) """ ################## SECTION 1 ################ # Inserting the waypoints if not animated, finding the first and last frame # Calculating the path after this window = {} window["first"] = sys.maxsize window["last"] = -1 count = 0 for entry in dynamic_list: pos = entry update_frame_window(pos[0], window) new_pos = gen_dummy_waypoint(pos, "entry", "vector") pos.getparent().remove(pos) dynamic_list.insert(count, new_pos) append_path(new_pos[0], dynamic_list[count], "pos_path", "vector") count += 1 layer = dynamic_list.getparent().getparent() for chld in layer: if chld.tag == "param": if chld.attrib["name"] == "origin": origin = chld # Animating the origin update_frame_window(origin[0], window) origin_parent = origin.getparent() origin = gen_dummy_waypoint(origin, "param", "vector", "origin") update_child_at_parent(origin_parent, origin, "param", "origin") # Generate path for the origin component origin_dict = {} origin[0].attrib["transform_axis"] = "true" gen_properties_multi_dimensional_keyframed(origin_dict, origin[0], 0) if window["first"] == sys.maxsize and window["last"] == -1: window["first"] = window["last"] = 0 ################ END OF SECTION 1 ############## ################ SECTION 2 ##################### # Generating values for all the frames in the window fr = window["first"] while fr <= window["last"]: st_val, en_val = insert_dict_at(lottie, -1, fr, False) for entry in dynamic_list: # Only two childs, one should be animated, other one is path for child in entry: if child.tag == "pos_path": dictionary = ast.literal_eval(child.text) pos_cur = get_vector_at_frame(dictionary, fr) pos_next = get_vector_at_frame(dictionary, fr + 1) tangent1_cur, tangent2_cur = Vector(0, 0), Vector(0, 0) tangent1_next, tangent2_next = Vector(0, 0), Vector(0, 0) # Adding origin to each vertex origin_cur = get_vector_at_frame(origin_dict, fr) origin_next = get_vector_at_frame(origin_dict, fr + 1) for i in range(len(pos_cur)): pos_cur[i] += origin_cur[i] for i in range(len(pos_next)): pos_next[i] += origin_next[i] # Store values in dictionary st_val["i"].append(tangent1_cur.get_list()) st_val["o"].append(tangent2_cur.get_list()) st_val["v"].append(pos_cur) en_val["i"].append(tangent1_next.get_list()) en_val["o"].append(tangent2_next.get_list()) en_val["v"].append(pos_next) fr += 1 # Setting the final time lottie.append({}) lottie[-1]["t"] = fr
def gen_bline_outline(lottie, bline_point): """ Generates the bline corresponding to outline layer by adding some vertices to bline and converting it to region layer Some parts are common with gen_bline_shapePropKeyframe(), which will be placed in a common function latter Args: lottie (dict) : Lottie format outline layer will be stored in this bline_point (lxml.etree._Element) : Synfig format bline points Returns: (None) """ ################### SECTION 1 ######################### # Inserting waypoints if not animated and finding the first and last frame # AFter that, there path will be calculated in lottie format which can # latter be used in get_vector_at_frame() function window = {} window["first"] = sys.maxsize window["last"] = -1 for entry in bline_point: composite = entry[0] for child in composite: if child.tag == "point": pos = child elif child.tag == "width": width = child elif child.tag == "t1": t1 = child elif child.tag == "t2": t2 = child elif child.tag == "split_radius": split_r = child elif child.tag == "split_angle": split_a = child # Necassary to update this before inserting new waypoints, as new # waypoints might include there on time: 0 seconds update_frame_window(pos[0], window) # Empty the pos and fill in the new animated pos pos = gen_dummy_waypoint(pos, "point", "vector") update_child_at_parent(composite, pos, "point") # Empty the width and fill in the new animated width update_frame_window(width[0], window) width = gen_dummy_waypoint(width, "width", "real") update_child_at_parent(composite, width, "width") update_frame_window(split_r[0], window) split_r = gen_dummy_waypoint(split_r, "split_radius", "bool") update_child_at_parent(composite, split_r, "split_radius") update_frame_window(split_a[0], window) split_a = gen_dummy_waypoint(split_a, "split_angle", "bool") update_child_at_parent(composite, split_a, "split_angle") append_path(pos[0], composite, "point_path", "vector") append_path(width[0], composite, "width_path") animate_radial_composite(t1[0], window) animate_radial_composite(t2[0], window) layer = bline_point.getparent().getparent() for chld in layer: if chld.tag == "param": if chld.attrib["name"] == "width": outer_width = chld elif chld.attrib["name"] == "sharp_cusps": sharp_cusps = chld elif chld.attrib["name"] == "expand": expand = chld elif chld.attrib["name"] == "round_tip[0]": r_tip0 = chld elif chld.attrib["name"] == "round_tip[1]": r_tip1 = chld elif chld.attrib["name"] == "homogeneous_width": homo_width = chld elif chld.attrib["name"] == "origin": origin = chld # Animating the origin update_frame_window(origin[0], window) origin_parent = origin.getparent() origin = gen_dummy_waypoint(origin, "param", "vector", "origin") update_child_at_parent(origin_parent, origin, "param", "origin") # Generate path for the origin component origin_dict = {} origin[0].attrib["transform_axis"] = "true" gen_properties_multi_dimensional_keyframed(origin_dict, origin[0], 0) update_frame_window(outer_width[0], window) outer_width = gen_dummy_waypoint(outer_width, "param", "real", "width") # Update the layer with this animated outline width update_child_at_parent(layer, outer_width, "param", "width") # Generate outline width for Lottie format # No need to store this dictionary in lxml element, as it will be used in this function and will not be rewritten outer_width_dict = {} gen_value_Keyframed(outer_width_dict, outer_width[0], 0) # Animating the sharp_cusps update_frame_window(sharp_cusps[0], window) sharp_cusps = gen_dummy_waypoint(sharp_cusps, "param", "bool", "sharp_cusps") # Update the layer with this animated outline sharp cusps update_child_at_parent(layer, sharp_cusps, "param", "sharp_cusps") update_frame_window(expand[0], window) expand = gen_dummy_waypoint(expand, "param", "real", "expand") update_child_at_parent(layer, expand, "param", "expand") expand_dict = {} gen_value_Keyframed(expand_dict, expand[0], 0) update_frame_window(r_tip0[0], window) r_tip0 = gen_dummy_waypoint(r_tip0, "param", "bool", "round_tip[0]") update_child_at_parent(layer, r_tip0, "param", "round_tip[0]") update_frame_window(r_tip1[0], window) r_tip1 = gen_dummy_waypoint(r_tip1, "param", "bool", "round_tip[1]") update_child_at_parent(layer, r_tip1, "param", "round_tip[1]") update_frame_window(homo_width[0], window) homo_width = gen_dummy_waypoint(homo_width, "param", "bool", "homogeneous_width") update_child_at_parent(layer, homo_width, "param", "homogeneous_width") # Minimizing the window size if window["first"] == sys.maxsize and window["last"] == -1: window["first"] = window["last"] = 0 ################# END OF SECTION 1 ################### ################ SECTION 2 ########################### # Generating values for all the frames in the window fr = window["first"] while fr <= window["last"]: st_val, en_val = insert_dict_at( lottie, -1, fr, False) # This loop needs to be considered somewhere down synfig_outline(bline_point, st_val, origin_dict, outer_width_dict, sharp_cusps, expand_dict, r_tip0, r_tip1, homo_width, fr) synfig_outline(bline_point, en_val, origin_dict, outer_width_dict, sharp_cusps, expand_dict, r_tip0, r_tip1, homo_width, fr + 1) fr += 1 # Setting the final time lottie.append({}) lottie[-1]["t"] = fr
def gen_bline_region(lottie, bline_point): """ Generates the dictionary corresponding to properties/shapePropKeyframe.json, given a bline/spline Args: lottie (dict) : Lottie generated keyframes will be stored here for shape/path bline_path (lxml.etree._Element) : shape/path store in Synfig format Returns: (None) """ ################### SECTION 1 ######################### # Inserting waypoints if not animated and finding the first and last frame # AFter that, there path will be calculated in lottie format which can # latter be used in get_vector_at_frame() function window = {} window["first"] = sys.maxsize window["last"] = -1 loop = False if "loop" in bline_point.keys(): val = bline_point.attrib["loop"] if val == "false": loop = False else: loop = True for entry in bline_point: composite = entry[0] for child in composite: if child.tag == "point": pos = child elif child.tag == "t1": t1 = child elif child.tag == "t2": t2 = child elif child.tag == "split_radius": split_r = child elif child.tag == "split_angle": split_a = child # Necassary to update this before inserting new waypoints, as new # waypoints might include there on time: 0 seconds update_frame_window(pos[0], window) # Empty the pos and fill in the new animated pos pos = gen_dummy_waypoint(pos, "point", "vector") update_child_at_parent(composite, pos, "point") update_frame_window(split_r[0], window) split_r = gen_dummy_waypoint(split_r, "split_radius", "bool") update_child_at_parent(composite, split_r, "split_radius") update_frame_window(split_a[0], window) split_a = gen_dummy_waypoint(split_a, "split_angle", "bool") update_child_at_parent(composite, split_a, "split_angle") append_path(pos[0], composite, "point_path", "vector") animate_radial_composite(t1[0], window) animate_radial_composite(t2[0], window) layer = bline_point.getparent().getparent() for chld in layer: if chld.tag == "param" and chld.attrib["name"] == "origin": origin = chld # Animating the origin update_frame_window(origin[0], window) origin_parent = origin.getparent() origin = gen_dummy_waypoint(origin, "param", "vector", "origin") update_child_at_parent(origin_parent, origin, "param", "origin") # Generate path for the origin component origin_dict = {} origin[0].attrib["transform_axis"] = "true" gen_properties_multi_dimensional_keyframed(origin_dict, origin[0], 0) # Minimizing the window size if window["first"] == sys.maxsize and window["last"] == -1: window["first"] = window["last"] = 0 ################# END OF SECTION 1 ################### ################ SECTION 2 ########################### # Generating values for all the frames in the window fr = window["first"] while fr <= window["last"]: st_val, en_val = insert_dict_at(lottie, -1, fr, loop) for entry in bline_point: composite = entry[0] for child in composite: if child.tag == "point_path": dictionary = ast.literal_eval(child.text) pos_cur = get_vector_at_frame(dictionary, fr) pos_next = get_vector_at_frame(dictionary, fr + 1) elif child.tag == "t1": t1 = child[0] elif child.tag == "t2": t2 = child[0] elif child.tag == "split_radius": split_r = child elif child.tag == "split_angle": split_a = child tangent1_cur, tangent2_cur = get_tangent_at_frame( t1, t2, split_r, split_a, fr) tangent1_next, tangent2_next = get_tangent_at_frame( t1, t2, split_r, split_a, fr) tangent1_cur, tangent2_cur = convert_tangent_to_lottie( tangent1_cur, tangent2_cur) tangent1_next, tangent2_next = convert_tangent_to_lottie( tangent1_next, tangent2_next) # Adding origin to each vertex origin_cur = get_vector_at_frame(origin_dict, fr) origin_next = get_vector_at_frame(origin_dict, fr + 1) for i in range(len(pos_cur)): pos_cur[i] += origin_cur[i] for i in range(len(pos_next)): pos_next[i] += origin_next[i] # Store values in dictionary st_val["i"].append(tangent1_cur.get_list()) st_val["o"].append(tangent2_cur.get_list()) st_val["v"].append(pos_cur) en_val["i"].append(tangent1_next.get_list()) en_val["o"].append(tangent2_next.get_list()) en_val["v"].append(pos_next) fr += 1 # Setting final time lottie.append({}) lottie[-1]["t"] = fr
def gen_shapes_star(lottie, layer, idx): """ Generates the dictionary corresponding to shapes/star.json Args: lottie (dict) : The lottie generated star layer will be stored in it layer (common.Layer.Layer) : Synfig format star layer idx (int) : Stores the index of the star layer Returns: (None) """ index = Count() lottie["ty"] = "sr" # Type: star lottie["pt"] = {} # Number of points on the star lottie["p"] = {} # Position of star lottie["r"] = {} # Angle / Star's rotation lottie["ir"] = {} # Inner radius lottie["or"] = {} # Outer radius lottie["is"] = {} # Inner roundness of the star lottie["os"] = {} # Outer roundness of the star regular_polygon = {"prop": "false"} # Regular polygon rp = layer.get_param("regular_polygon").get() is_animate = is_animated(rp[0]) if is_animate == settings.ANIMATED: regular_polygon["prop"] = "changing" regular_polygon["animated"] = rp[0] elif is_animate == settings.SINGLE_WAYPOINT: regular_polygon["prop"] = rp[0][0][0].attrib["value"] else: regular_polygon["prop"] = rp[0].attrib["value"] regular_polygon["animate"] = is_animate # Points points = layer.get_param("points").get() is_animate = is_animated(points[0]) if is_animate == settings.ANIMATED: # To uniquely identify the points, attribute type is changed points[0].attrib['type'] = 'points' gen_value_Keyframed(lottie["pt"], points[0], index.inc()) else: num_points = 3 # default number of points if is_animate == settings.NOT_ANIMATED: num_points = int(points[0].attrib["value"]) else: num_points = int(points[0][0][0].attrib["value"]) gen_properties_value(lottie["pt"], num_points, index.inc(), settings.DEFAULT_ANIMATED, settings.NO_INFO) # Angle angle = layer.get_param("angle").get() is_animate = is_animated(angle[0]) if is_animate == settings.ANIMATED: gen_value_Keyframed(lottie["r"], angle[0], index.inc()) else: theta = 0 # default angle for the star if is_animate == settings.NOT_ANIMATED: theta = get_angle(float(angle[0].attrib["value"])) else: theta = get_angle(float(angle[0][0][0].attrib["value"])) gen_properties_value(lottie["r"], theta, index.inc(), settings.DEFAULT_ANIMATED, settings.NO_INFO) # Radius1 radius1 = layer.get_param("radius1").get() is_animate = is_animated(radius1[0]) if is_animate == settings.ANIMATED: gen_value_Keyframed(lottie["or"], radius1[0], index.inc()) else: r_outer = 0 # default value for outer radius if is_animate == settings.NOT_ANIMATED: r_outer = float(radius1[0].attrib["value"]) else: r_outer = float(radius1[0][0][0].attrib["value"]) gen_properties_value(lottie["or"], int(settings.PIX_PER_UNIT * r_outer), index.inc(), settings.DEFAULT_ANIMATED, settings.NO_INFO) # Radius2 radius2 = layer.get_param("radius2").get() is_animate = is_animated(radius2[0]) if is_animate == settings.ANIMATED: gen_value_Keyframed(lottie["ir"], radius2[0], index.inc()) else: r_inner = 0 # default value for inner radius if is_animate == settings.NOT_ANIMATED: r_inner = float(radius2[0].attrib["value"]) else: r_inner = float(radius2[0][0][0].attrib["value"]) gen_properties_value(lottie["ir"], int(settings.PIX_PER_UNIT * r_inner), index.inc(), settings.DEFAULT_ANIMATED, settings.NO_INFO) # Origin origin = layer.get_param("origin").get() is_animate = is_animated(origin[0]) if is_animate == settings.ANIMATED: gen_properties_multi_dimensional_keyframed(lottie["p"], origin[0], index.inc()) else: x_val, y_val = 0, 0 if is_animate == settings.NOT_ANIMATED: x_val = float(origin[0][0].text) * settings.PIX_PER_UNIT y_val = float(origin[0][1].text) * settings.PIX_PER_UNIT else: x_val = float(origin[0][0][0][0].text) * settings.PIX_PER_UNIT y_val = float(origin[0][0][0][1].text) * settings.PIX_PER_UNIT gen_properties_value(lottie["p"], change_axis(x_val, y_val), index.inc(), settings.DEFAULT_ANIMATED, settings.NO_INFO) # If not animated, then go to if, else if regular_polygon["animate"] in {0, 1}: if regular_polygon["prop"] == "false": lottie["sy"] = 1 # Star Type # inner property is only needed if type is star gen_properties_value(lottie["is"], 0, index.inc(), settings.DEFAULT_ANIMATED, settings.NO_INFO) else: lottie["sy"] = 2 # Polygon Type # for polygon type, "ir" and "is" must not be present del lottie["ir"] # If animated, it will always be of type star else: lottie["sy"] = 1 gen_properties_value(lottie["is"], 0, index.inc(), settings.DEFAULT_ANIMATED, settings.NO_INFO) gen_properties_value(lottie["os"], 0, index.inc(), settings.DEFAULT_ANIMATED, settings.NO_INFO) lottie["ix"] = idx
def gen_list_circle(lottie, layer): """ Generates a shape layer corresponding to circle layer by manipulating the origin and radius of the circle Args: lottie (dict) : Lottie format circle layer will be stored in this layer (lxml.etree._Element) : Synfig format circle layer Returns: (None) """ ################### SECTION 1 ######################### # Inserting waypoints if not animated and finding the first and last frame # AFter that, there path will be calculated in lottie format which can # latter be used in get_vector_at_frame() function window = {} window["first"] = sys.maxsize window["last"] = -1 for chld in layer: if chld.tag == "param": if chld.attrib["name"] == "origin": origin = chld elif chld.attrib["name"] == "radius": radius = chld # Animating the origin update_frame_window(origin[0], window) origin = gen_dummy_waypoint(origin, "param", "vector", "origin") update_child_at_parent(layer, origin, "param", "origin") # Generate path for the origin component origin_dict = {} origin[0].attrib["transform_axis"] = "true" gen_properties_multi_dimensional_keyframed(origin_dict, origin[0], 0) update_frame_window(radius[0], window) radius = gen_dummy_waypoint(radius, "param", "real", "radius") update_child_at_parent(layer, radius, "param", "width") # Generate radius for Lottie format radius_dict = {} gen_value_Keyframed(radius_dict, radius[0], 0) # Minimizing the window size if window["first"] == sys.maxsize and window["last"] == -1: window["first"] = window["last"] = 0 ################# END OF SECTION 1 ################### ################ SECTION 2 ########################### # Generating values for all the frames in the window fr = window["first"] while fr <= window["last"]: st_val, en_val = insert_dict_at( lottie, -1, fr, False) # This loop needs to be considered somewhere down synfig_circle(st_val, origin_dict, radius_dict, fr) synfig_circle(en_val, origin_dict, radius_dict, fr + 1) fr += 1 # Setting the final time lottie.append({}) lottie[-1]["t"] = fr
def gen_list_rectangle(lottie, layer): """ Generates a shape layer corresponding to rectangle layer by manipulating the parameters of the rectangle Args: lottie (dict) : Lottie format rectangle layer will be stored in this layer (lxml.etree._Element) : Synfig format rectangle layer Returns: (None) """ ################### SECTION 1 ######################### # Inserting waypoints if not animated and finding the first and last frame # AFter that, there path will be calculated in lottie format which can # latter be used in get_vector_at_frame() function window = {} window["first"] = sys.maxsize window["last"] = -1 bevel_found = False expand_found = False for chld in layer: if chld.tag == "param": if chld.attrib["name"] == "point1": point1 = chld elif chld.attrib["name"] == "point2": point2 = chld elif chld.attrib["name"] == "expand": expand_found = True expand = chld elif chld.attrib["name"] == "bevel": bevel_found = True bevel = chld elif chld.attrib["name"] == "bevCircle": bevCircle = chld if not expand_found: # Means filled rectangle layer st = "<param name='expand'><real value='0.0'/></param>" expand = etree.fromstring(st) if not bevel_found: # For rectangle layer in stable version 1.2.2 st = "<param name='bevel'><real value='0.0'/></param>" bevel = etree.fromstring(st) st = "<param name='bevCircle'><bool value='false'/></param>" bevCircle = etree.fromstring(st) # Animating point1 update_frame_window(point1[0], window) point1 = gen_dummy_waypoint(point1, "param", "vector", "point1") update_child_at_parent(layer, point1, "param", "point1") # Generate path for the point1 component p1_dict = {} #point1[0].attrib["transform_axis"] = "true" gen_properties_multi_dimensional_keyframed(p1_dict, point1[0], 0) # Animating point2 update_frame_window(point2[0], window) point2 = gen_dummy_waypoint(point2, "param", "vector", "point2") update_child_at_parent(layer, point2, "param", "point2") # Generate path for the point2 component p2_dict = {} gen_properties_multi_dimensional_keyframed(p2_dict, point2[0], 0) # Animating expand update_frame_window(expand[0], window) expand = gen_dummy_waypoint(expand, "param", "real", "expand") update_child_at_parent(layer, expand, "param", "expand") # Generate expand param for Lottie format expand_dict = {} gen_value_Keyframed(expand_dict, expand[0], 0) # Animating bevel update_frame_window(bevel[0], window) bevel = gen_dummy_waypoint(bevel, "param", "real", "bevel") update_child_at_parent(layer, bevel, "param", "bevel") # Generate bevel param for Lottie format bevel_dict = {} gen_value_Keyframed(bevel_dict, bevel[0], 0) # Animating bevCircle update_frame_window(bevCircle[0], window) bevCircle = gen_dummy_waypoint(bevCircle, "param", "bool", "bevCircle") update_child_at_parent(layer, bevCircle, "param", "bevCircle") # Minimizing the window size if window["first"] == sys.maxsize and window["last"] == -1: window["first"] = window["last"] = 0 ################# END OF SECTION 1 ################### ################ SECTION 2 ########################### # Generating values for all the frames in the window fr = window["first"] while fr <= window["last"]: st_val, en_val = insert_dict_at(lottie, -1, fr, False) synfig_rectangle(st_val, p1_dict, p2_dict, expand_dict, bevel_dict, bevCircle, fr) synfig_rectangle(en_val, p1_dict, p2_dict, expand_dict, bevel_dict, bevCircle, fr + 1) fr += 1 # Setting the final time lottie.append({}) lottie[-1]["t"] = fr
def gen_helpers_transform(lottie, layer, pos=[0, 0], anchor=[0, 0, 0], scale=[100, 100, 100]): """ Generates the dictionary corresponding to helpers/transform.json Args: lottie (dict) : Lottie format layer layer (lxml.etree._Element) : Synfig format layer pos (:obj: `list | lxml.etree._Element`, optional) : position of layer anchor (:obj: `list`) : anchor point of layer scale (:obj: `list | lxml.etree._Element`, optional) : scale of layer Returns: (None) """ index = Count() lottie["o"] = {} # opacity/Amount lottie["r"] = {} # Rotation of the layer lottie["p"] = {} # Position of the layer lottie["a"] = {} # Anchor point of the layer lottie["s"] = {} # Scale of the layer # setting the default location if isinstance(pos, list): gen_properties_value(lottie["p"], pos, index.inc(), settings.DEFAULT_ANIMATED, settings.NO_INFO) else: gen_properties_multi_dimensional_keyframed(lottie["p"], pos, index.inc()) # setting the default opacity i.e. 100 gen_properties_value(lottie["o"], settings.DEFAULT_OPACITY, index.inc(), settings.DEFAULT_ANIMATED, settings.NO_INFO) # setting the rotation gen_properties_value(lottie["r"], settings.DEFAULT_ROTATION, index.inc(), settings.DEFAULT_ANIMATED, settings.NO_INFO) # setting the anchor point gen_properties_value(lottie["a"], anchor, index.inc(), settings.DEFAULT_ANIMATED, settings.NO_INFO) # setting the scale if isinstance(scale, list): gen_properties_value(lottie["s"], scale, index.inc(), settings.DEFAULT_ANIMATED, settings.NO_INFO) # This means scale parameter is animated else: gen_value_Keyframed(lottie["s"], scale, index.inc())
def gen_list_star(lottie, layer): """ Generates a shape layer corresponding to star layer by manipulating the parameters of the star Args: lottie (dict) : Lottie format rectangle layer will be stored in this layer (lxml.etree._Element) : Synfig format rectangle layer Returns: (None) """ ################### SECTION 1 ######################### # Inserting waypoints if not animated and finding the first and last frame # AFter that, there path will be calculated in lottie format which can # latter be used in get_vector_at_frame() function window = {} window["first"] = sys.maxsize window["last"] = -1 for chld in layer: if chld.tag == "param": if chld.attrib["name"] == "origin": origin = chld elif chld.attrib["name"] == "radius1": radius1 = chld elif chld.attrib["name"] == "radius2": radius2 = chld elif chld.attrib["name"] == "angle": angle = chld elif chld.attrib["name"] == "points": points = chld elif chld.attrib["name"] == "regular_polygon": regular_polygon = chld # Animating origin update_frame_window(origin[0], window) origin = gen_dummy_waypoint(origin, "param", "vector", "origin") update_child_at_parent(layer, origin, "param", "origin") # Generate path for the origin component origin_dict = {} origin[0].attrib["transform_axis"] = "true" gen_properties_multi_dimensional_keyframed(origin_dict, origin[0], 0) # Animating radius1 update_frame_window(radius1[0], window) radius1 = gen_dummy_waypoint(radius1, "param", "real", "radius1") update_child_at_parent(layer, radius1, "param", "radius1") # Generate expand param for Lottie format radius1_dict = {} gen_value_Keyframed(radius1_dict, radius1[0], 0) # Animating radius2 update_frame_window(radius2[0], window) radius2 = gen_dummy_waypoint(radius2, "param", "real", "radius2") update_child_at_parent(layer, radius2, "param", "radius2") # Generate expand param for Lottie format radius2_dict = {} gen_value_Keyframed(radius2_dict, radius2[0], 0) # Animating angle update_frame_window(angle[0], window) angle = gen_dummy_waypoint(angle, "param", "star_angle_new", "angle") update_child_at_parent(layer, angle, "param", "angle") # Generate expand param for Lottie format angle_dict = {} gen_value_Keyframed(angle_dict, angle[0], 0) # Animating points update_frame_window(points[0], window) points = gen_dummy_waypoint(points, "param", "real", "points") update_child_at_parent(layer, points, "param", "points") # Generate expand param for Lottie format points_dict = {} gen_value_Keyframed(points_dict, points[0], 0) mx_points = get_max_points(points) # Animating regular_polygon update_frame_window(regular_polygon[0], window) regular_polygon = gen_dummy_waypoint(regular_polygon, "param", "bool", "regular_polygon") update_child_at_parent(layer, regular_polygon, "param", "regular_polygon") # Minimizing the window size if window["first"] == sys.maxsize and window["last"] == -1: window["first"] = window["last"] = 0 ################# END OF SECTION 1 ################### ################ SECTION 2 ########################### # Generating values for all the frames in the window fr = window["first"] while fr <= window["last"]: st_val, en_val = insert_dict_at(lottie, -1, fr, False) synfig_star(st_val, mx_points, origin_dict, radius1_dict, radius2_dict, angle_dict, points_dict, regular_polygon, fr) synfig_star(en_val, mx_points, origin_dict, radius1_dict, radius2_dict, angle_dict, points_dict, regular_polygon, fr + 1) fr += 1 # Setting the final time lottie.append({}) lottie[-1]["t"] = fr