def gen_time_remap(lottie, time_offset, time_dilation, idx): """ Time offset will be converted to time remap here Currently time remapping will be done for each frame, but this function can be intelligently written only for some particular frames,hence reducing the space Args: lottie (dict) : Time remapping in Lottie format time_offset (lxml.etree._Element) : Offset for time in Synfig format time_dilation (lxml.etree._Element) : Speed/dilation for time in Synfig format idx (itr) : Index of this property in the layer Returns: (None) """ offset_dict = {} time_offset = gen_dummy_waypoint(time_offset, "param", "time") gen_value_Keyframed(offset_dict, time_offset[0], 0) dilation_dict = {} time_dilation = gen_dummy_waypoint(time_dilation, "param", "real") gen_value_Keyframed(dilation_dict, time_dilation[0], 0) fr, lst = settings.lottie_format["ip"], settings.lottie_format["op"] lottie["a"] = 1 # Animated lottie["ix"] = idx lottie["k"] = [] while fr <= lst: lottie["k"].append({}) gen_dict(lottie["k"][-1], offset_dict, dilation_dict, fr) fr += 1
def gen_effects_opacity(lottie, layer, idx): """ Generates the dictionary corresponding to effects/opacity.json Args: lottie (dict) : Lottie format effects stored in this layer (lxml.etree._Element) : Synfig format layer idx (int) : Index/Count of effect Returns: (None) """ index = Count() lottie["ty"] = settings.EFFECTS_OPACITY # Effect type lottie["nm"] = "Opacity" # Name lottie["ix"] = idx # Index lottie["v"] = {} # Value of opacity for child in layer: if child.attrib["name"] == "amount": is_animate = is_animated(child[0]) if is_animate == 2: # Telling the function that this is for opacity child[0].attrib['type'] = 'effects_opacity' gen_value_Keyframed(lottie["v"], child[0], index.inc()) else: if is_animate == 0: val = float(child[0].attrib["value"]) else: val = float(child[0][0][0].attrib["value"]) gen_properties_value(lottie["v"], val, index.inc(), settings.DEFAULT_ANIMATED, settings.NO_INFO)
def change_opacity_switch(layer, lottie): """ Will make the opacity of underlying layers 0 according to the active layer Args: layer (lxml.etree._Element) : Synfig format layer lottie (dict) : Lottie format layer Returns: (None) """ for chld in layer: if chld.tag == "param": if chld.attrib["name"] == "layer_name": layer_name = chld elif chld.attrib["name"] == "canvas": canvas = chld layer_name = gen_dummy_waypoint(layer_name, "param", "string", "layer_name") for assets in settings.lottie_format["assets"]: if assets["id"] == lottie["refId"]: root = assets break it = 0 for c_layer in reversed(canvas[0]): active_time = set() description = root["layers"][it]["nm"] waypoint_itr = 0 while waypoint_itr < len(layer_name[0]): waypoint = layer_name[0][waypoint_itr] l_name = waypoint[0].text if (l_name == description) or (l_name is None and it == 0): update_time(active_time, layer_name[0], waypoint_itr) waypoint_itr += 1 active_time = sorted(active_time) deactive_time = sorted(flip_time(active_time)) if c_layer.attrib["type"] in set.union(settings.SHAPE_SOLID_LAYER, settings.SOLID_LAYER): anim_type = "effects_opacity" dic = root["layers"][it]["ef"][0]["ef"][-1]["v"] elif c_layer.attrib["type"] in set.union(settings.PRE_COMP_LAYER, settings.GROUP_LAYER, settings.IMAGE_LAYER): anim_type = "opacity" dic = root["layers"][it]["ks"]["o"] elif c_layer.attrib["type"] in settings.SHAPE_LAYER: anim_type = "opacity" dic = root["layers"][it]["shapes"][1]["o"] animation = gen_hold_waypoints(deactive_time, c_layer, anim_type) gen_value_Keyframed(dic, animation[0], 0) it += 1
def gen_shapes_fill(lottie, layer): """ Generates the dictionary corresponding to shapes/fill.json Args: lottie (dict) : The lottie generated fill layer will be stored in it layer (lxml.etree._Element): Synfig format fill (can be shape/solid anything, we only need color and opacity part from it) layer Returns: (None) """ index = Count() lottie["ty"] = "fl" # Type if fill lottie["c"] = {} # Color lottie["o"] = {} # Opacity of the fill layer for child in layer: if child.tag == "param": if child.attrib["name"] == "color": is_animate = is_animated(child[0]) if is_animate == 2: gen_value_Keyframed(lottie["c"], child[0], index.inc()) else: if is_animate == 0: val = child[0] else: val = child[0][0][0] red = float(val[0].text) green = float(val[1].text) blue = float(val[2].text) red, green, blue = red ** (1/settings.GAMMA), green **\ (1/settings.GAMMA), blue ** (1/ settings.GAMMA) alpha = float(val[3].text) gen_properties_value(lottie["c"], [red, green, blue, alpha], index.inc(), settings.DEFAULT_ANIMATED, settings.NO_INFO) elif child.attrib["name"] == "amount": is_animate = is_animated(child[0]) if is_animate == 2: # Telling the function that this is for opacity child[0].attrib['type'] = 'opacity' gen_value_Keyframed(lottie["o"], child[0], index.inc()) else: if is_animate == 0: val = float(child[0].attrib["value"] ) * settings.OPACITY_CONSTANT else: val = float(child[0][0][0].attrib["value"] ) * settings.OPACITY_CONSTANT gen_properties_value(lottie["o"], val, index.inc(), settings.DEFAULT_ANIMATED, settings.NO_INFO)
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_shapes_fill(lottie, layer): """ Generates the dictionary corresponding to shapes/fill.json Args: lottie (dict) : The lottie generated fill layer will be stored in it layer (common.Layer.Layer) : Synfig format fill (can be shape/solid anything, we only need color and opacity part from it) layer Returns: (None) """ index = Count() lottie["ty"] = "fl" # Type if fill lottie["c"] = {} # Color lottie["o"] = {} # Opacity of the fill layer # Color color = layer.get_param("color").get() is_animate = is_animated(color[0]) if is_animate == settings.ANIMATED: gen_value_Keyframed(lottie["c"], color[0], index.inc()) else: if is_animate == settings.NOT_ANIMATED: val = color[0] else: val = color[0][0][0] red = float(val[0].text) green = float(val[1].text) blue = float(val[2].text) red, green, blue = red**(1 / settings.GAMMA[0]), green**( 1 / settings.GAMMA[1]), blue**(1 / settings.GAMMA[2]) alpha = float(val[3].text) gen_properties_value(lottie["c"], [red, green, blue, alpha], index.inc(), settings.DEFAULT_ANIMATED, settings.NO_INFO) # Color Opacity opacity = layer.get_param("amount").get() is_animate = is_animated(opacity[0]) if is_animate == settings.ANIMATED: # Telling the function that this is for opacity opacity[0].attrib['type'] = 'opacity' gen_value_Keyframed(lottie["o"], opacity[0], index.inc()) else: if is_animate == settings.NOT_ANIMATED: val = float(opacity[0].attrib["value"]) * settings.OPACITY_CONSTANT else: val = float( opacity[0][0][0].attrib["value"]) * settings.OPACITY_CONSTANT gen_properties_value(lottie["o"], val, index.inc(), settings.DEFAULT_ANIMATED, settings.NO_INFO)
def gen_linear_gradient(lottie, layer, idx): """ Generates the dictionary corresponding to shapes/gFill.json but with linear gradient Args: """ index = Count() lottie["ty"] = "gf" lottie["r"] = 1 # Don't know it's meaning yet, but works without this also lottie["o"] = {} # Opacity of the gradient layer lottie["nm"] = layer.get_description() lottie["t"] = 1 # 1 means linear gradient layer lottie["s"] = {} # Starting point of gradient lottie["e"] = {} # Ending point of gradient lottie["g"] = {} # Gradient information is stored here # Color Opacity opacity = layer.get_param("amount").get() is_animate = is_animated(opacity[0]) if is_animate == settings.ANIMATED: # Telling the function that this is for opacity opacity[0].attrib['type'] = 'opacity' gen_value_Keyframed(lottie["o"], opacity[0], index.inc()) else: if is_animate == settings.NOT_ANIMATED: val = float(opacity[0].attrib["value"]) * settings.OPACITY_CONSTANT else: val = float( opacity[0][0][0].attrib["value"]) * settings.OPACITY_CONSTANT gen_properties_value(lottie["o"], val, index.inc(), settings.DEFAULT_ANIMATED, settings.NO_INFO) # Starting point p1 = layer.get_param("p1") p1.animate("vector") p1.fill_path(lottie, "s") # Ending point p2 = layer.get_param("p2") p2.animate("vector") p2.fill_path(lottie, "e") # Gradient colors lottie["g"]["k"] = {} lottie["g"]["ix"] = index.inc() gradient = layer.get_param("gradient") modify_gradient(gradient) gradient.animate( "gradient") # To find the lottie path of the modified gradient lottie["g"]["p"] = len(gradient.get()[0][0][0]) gradient.fill_path(lottie["g"], "k") modify_gradient_according_to_latest_version(lottie["g"]["k"])
def gen_hold_waypoints(deactive_time, layer, anim_type): """ Will only be used to modify opacity waypoints, and set zero values where the layer is deactive Args: deactive_time (set) : Range of time when the layer will be deactive layer (lxml.etree._Element) : Synfig format layer anim_type (str) : Specifies whether it is effects_opacity or opacity (it will effect a factor of 100) Returns: (lxml.etree._Element) : Modified opacity animation is returned """ for chld in layer: if chld.tag == "param" and chld.attrib["name"] == "amount": opacity = chld opacity = gen_dummy_waypoint(opacity, "param", anim_type, "amount") opacity_dict = {} gen_value_Keyframed(opacity_dict, opacity[0], 0) for it in deactive_time: # First add waypoints at both points, make it constant interval # then remove any in-between waypoints first = round(it[0] * settings.lottie_format["fr"]) second = round(it[1] * settings.lottie_format["fr"]) insert_waypoint_at_frame(opacity[0], opacity_dict, first, anim_type) insert_waypoint_at_frame(opacity[0], opacity_dict, second, anim_type) # Making it a constant interval for waypoint in opacity[0]: if approximate_equal(get_frame(waypoint), first): st_waypoint = waypoint break st_waypoint.attrib["after"] = "constant" st_waypoint[0].attrib["value"] = str(0) # removing the in between waypoints for waypoint in opacity[0]: this_frame = get_frame(waypoint) if (not approximate_equal(this_frame, first)) and \ (not approximate_equal(this_frame, second)) and \ (this_frame > first and this_frame < second): waypoint.getparent().remove(waypoint) return opacity
def gen_effects_color(lottie, layer, idx): """ Generates the dictionary corresponding to effects/color.json Args: lottie (dict) : Lottie format effects stored in this layer (lxml.etree._Element) : Synfig format layer idx (int) : Index/Count of effect Returns: (None) """ index = Count() lottie["ty"] = settings.EFFECTS_COLOR # Effect type lottie["nm"] = "Color" # Name lottie["ix"] = idx # Index lottie["v"] = {} # Value of color for child in layer: if child.tag == "param": if child.attrib["name"] == "color": is_animate = is_animated(child[0]) if is_animate == 2: gen_value_Keyframed(lottie["v"], child[0], index.inc()) else: if is_animate == 0: val = child[0] else: val = child[0][0][0] red = float(val[0].text) green = float(val[1].text) blue = float(val[2].text) red, green, blue = red ** (1/settings.GAMMA), green **\ (1/settings.GAMMA), blue ** (1/ settings.GAMMA) alpha = float(val[3].text) gen_properties_value(lottie["v"], [red, green, blue, alpha], index.inc(), settings.DEFAULT_ANIMATED, settings.NO_INFO)
def blurriness(lottie, parameter, idx, direction): """ This function will be called for blur layer direction to create the blurriness value dictionary Args: lottie (dict) : Lottie Dictionary for blurriness parameter (common.Param.Param) : Synfig format param for blur size idx (int) : Stores the index(number of) of blurriness direction (string) : Indicates the direction of blurring Returns: (None) """ lottie["ty"] = 0 lottie["nm"] = "Blurriness" lottie["mn"] = "Gaussian Blur 1" lottie["ix"] = idx lottie["v"] = {} is_animate = is_animated(parameter[0]) if is_animate == settings.ANIMATED: if direction == "horizontal": parameter[0].attrib['type'] = 'blur_anim_x' gen_value_Keyframed(lottie["v"], parameter[0], 1) else: parameter[0].attrib['type'] = 'blur_anim_y' gen_value_Keyframed(lottie["v"], parameter[0], 1) else: if is_animate == settings.NOT_ANIMATED: if direction == "horizontal": val = float(parameter[0][0].text) * 100 else: val = float(parameter[0][1].text) * 100 else: if direction == "horizontal": val = float(parameter[0][0][0].text) * 100 else: val = float(parameter[0][0][1].text) * 100 gen_properties_value(lottie["v"], val, 1, settings.DEFAULT_ANIMATED, settings.NO_INFO)
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_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_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_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 change_opacity_group(layer, lottie): """ Will make the opacity of underlying layers 0 according to the layers lying inside z range(if it is active)[z-range is non-animatable] Args: layer (lxml.etree._Element) : Synfig format layer lottie (dict) : Lottie format layer Returns: (None) """ for chld in layer: if chld.tag == "param": if chld.attrib["name"] == "z_range": z_range = chld elif chld.attrib["name"] == "z_range_position": z_range_pos = chld elif chld.attrib["name"] == "z_range_depth": z_range_depth = chld elif chld.attrib["name"] == "canvas": canvas = chld for assets in settings.lottie_format["assets"]: if assets["id"] == lottie["refId"]: root = assets break # If z-range is non-active (static value) if z_range[0].attrib["value"] == "false": return pos = gen_dummy_waypoint(z_range_pos, "param", "real", "z_range_position") depth = gen_dummy_waypoint(z_range_depth, "param", "real", "z_range_depth") pos_dict, depth_dict = {}, {} gen_value_Keyframed(pos_dict, pos[0], 0) gen_value_Keyframed(depth_dict, depth[0], 0) z_st, z_en = float('-inf'), float('-inf') active_range = [] # Stores the time and change of layers in z-range fr = settings.lottie_format["ip"] while fr <= settings.lottie_format["op"]: pos_val = to_Synfig_axis(get_vector_at_frame(pos_dict, fr), "real") depth_val = to_Synfig_axis(get_vector_at_frame(depth_dict, fr), "real") st, en = math.ceil(pos_val), math.floor(pos_val + depth_val) if st > en or en < 0: if (fr == settings.lottie_format["ip"]) or (z_st != -1 and z_en != -1): z_st, z_en = -1, -1 active_range.append([fr, z_st, z_en]) elif (st != z_st) or (en != z_en): z_st, z_en = st, en active_range.append([fr, z_st, z_en]) fr += 1 z_value = 0 for c_layer in reversed(canvas[0]): active_time = set() itr = 0 while itr < len(active_range): st, en = active_range[itr][1], active_range[itr][2] if z_value <= en and z_value >= st: now = active_range[itr][0] / settings.lottie_format["fr"] later = get_time_bound("op") if itr + 1 < len(active_range): later = active_range[itr + 1][0] / settings.lottie_format["fr"] active_time.add((now, later)) itr += 1 active_time = sorted(active_time) deactive_time = sorted(flip_time(active_time)) if c_layer.attrib["type"] in set.union(settings.SHAPE_SOLID_LAYER, settings.SOLID_LAYER): anim_type = "effects_opacity" dic = root["layers"][z_value]["ef"][0]["ef"][-1]["v"] elif c_layer.attrib["type"] in set.union(settings.PRE_COMP_LAYER, settings.GROUP_LAYER, settings.IMAGE_LAYER): anim_type = "opacity" dic = root["layers"][z_value]["ks"]["o"] elif c_layer.attrib["type"] in settings.SHAPE_LAYER: anim_type = "opacity" dic = root["layers"][z_value]["shapes"][1]["o"] animation = gen_hold_waypoints(deactive_time, c_layer, anim_type) gen_value_Keyframed(dic, animation[0], 0) z_value += 1
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_shapes_rectangle(lottie, layer, idx): """ Generates the dictionary corresponding to shapes/rect.json Args: lottie (dict) : The lottie generated rectangle layer will be stored in it layer (lxml.etree._Element): Synfig format rectangle layer idx (int) : Stores the index of the rectangle layer Returns: (None) """ index = Count() lottie["ty"] = "rc" # Type: rectangle lottie["p"] = {} # Position of rectangle lottie["d"] = settings.DEFAULT_DIRECTION lottie["s"] = {} # Size of rectangle lottie["ix"] = idx # setting the index lottie["r"] = {} # Rounded corners of rectangle points = {} bevel_found = False expand_found = False # For filled rectangle layers for child in layer: if child.tag == "param": if child.attrib["name"] == "point1": points["1"] = child # Store address of child here elif child.attrib["name"] == "point2": points["2"] = child # Store address of child here elif child.attrib["name"] == "expand": expand_found = True param_expand = child elif child.attrib["name"] == "bevel": bevel_found = True is_animate = is_animated(child[0]) if is_animate == 2: gen_value_Keyframed(lottie["r"], child[0], index.inc()) else: bevel = get_child_value(is_animate, child, "value") bevel *= settings.PIX_PER_UNIT gen_properties_value(lottie["r"], bevel, index.inc(), settings.DEFAULT_ANIMATED, settings.NO_INFO) if not bevel_found: # For rectangle layer in stable version 1.2.2 bevel = 0 gen_properties_value(lottie["r"], bevel, index.inc(), settings.DEFAULT_ANIMATED, settings.NO_INFO) if not expand_found: # Means filled rectangle layer, gen expand param st = "<param name='expand'><real value='0.0'/></param>" param_expand = etree.fromstring(st) # If expand parameter is not animated param_expand = gen_dummy_waypoint(param_expand, "param", "real") # expand parameter needs to be positive: required by Synfig make_positive_valued(param_expand) # If p1 not animated points["1"] = gen_dummy_waypoint(points["1"], "param", "vector") # If p2 not animated points["2"] = gen_dummy_waypoint(points["2"], "param", "vector") both_points_animated(points["1"], points["2"], param_expand, lottie, index)
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_radial_gradient(lottie, layer, idx): """ Generates the dictionary correspnding to shapes/gFill.json but with radial gradient Args: """ index = Count() lottie["ty"] = "gf" lottie["r"] = 1 # Don't know it's meaning yet, but works without this also lottie["o"] = {} # Opacity of the gradient layer lottie["nm"] = layer.get_description() lottie["t"] = 2 # 2 means radial gradient layer lottie["s"] = {} # Starting point of gradient lottie["e"] = {} # Ending point of gradient lottie["g"] = {} # Gradient information is stored here # Color Opacity opacity = layer.get_param("amount").get() is_animate = is_animated(opacity[0]) if is_animate == settings.ANIMATED: # Telling the function that this is for opacity opacity[0].attrib['type'] = 'opacity' gen_value_Keyframed(lottie["o"], opacity[0], index.inc()) else: if is_animate == settings.NOT_ANIMATED: val = float(opacity[0].attrib["value"]) * settings.OPACITY_CONSTANT else: val = float( opacity[0][0][0].attrib["value"]) * settings.OPACITY_CONSTANT gen_properties_value(lottie["o"], val, index.inc(), settings.DEFAULT_ANIMATED, settings.NO_INFO) # Gradient colors lottie["g"]["k"] = {} lottie["g"]["ix"] = index.inc() gradient = layer.get_param("gradient") modify_gradient(gradient) gradient.animate( "gradient") # To find the lottie path of the modified gradient lottie["g"]["p"] = len(gradient.get()[0][0][0]) gradient.fill_path(lottie["g"], "k") modify_gradient_according_to_latest_version(lottie["g"]["k"]) # Starting point and Ending points need to be retrieved from center and radius center = layer.get_param("center") center.animate("vector") center.fill_path(lottie, "s") radius = layer.get_param("radius") radius.animate("real") # Ending point will be (start[0] + radius, start[1]) # Below is just a modification of fill_path function expression = "var $bm_rt; $bm_rt = {expr}" x_expr = "sum(" + center.expression + "[0], " + radius.expression + ")" y_expr = center.expression + "[1]" expr = "[" + x_expr + ", " + y_expr + "]" expression = expression.format(expr=expr) gen_properties_value(lottie["e"], [1, 1], 0, 0, expression) if "ef" not in center.get_layer().get_lottie_layer().keys(): center.get_layer().get_lottie_layer()["ef"] = [] # If center has any expression controllers, then they would have been pushed earlier by fill_path, hence no need # center.get_layer().get_lottie_layer()["ef"].extend(center.expression_controllers) center.get_layer().get_lottie_layer()["ef"].extend( radius.expression_controllers)
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_bline_outline_constant(lottie, bline_point, layer, transformation, idx): """ """ index = Count() lottie["ty"] = "gr" lottie["nm"] = "Shape "+ str(idx) lottie["np"] = 3 lottie["cix"] = 2 lottie["bm"] = 0 lottie["ix"] = idx lottie["mn"] = "ADBE Vector Group " +str(idx) lottie["hd"] = "false" lottie["it"] = [] lottie["it"].append({}) lottie["it"].append({}) lottie["it"].append({}) bline = Bline(bline_point[0], bline_point) #Creating transformation dictionary lottie["it"][2]["ty"] = "tr" lottie["it"][2]["nm"] = "Transform" lottie["it"][2].update(transformation) #Creating stroke dictionary lottie["it"][1]["ty"] = "st" lottie["it"][1]["lc"] = 2 lottie["it"][1]["lj"] = 1 lottie["it"][1]["ml"] = 37 lottie["it"][1]["bm"] = 0 lottie["it"][1]["nm"] = "Stroke " + str(idx) lottie["it"][1]["mn"] = "ADBE Vector Graphic - Stroke " +str(idx) lottie["it"][1]["hd"] = "false" lottie["it"][1]["c"] = {} lottie["it"][1]["o"] = {} lottie["it"][1]["w"] = {} #Color color = layer.get_param("color").get() is_animate = is_animated(color[0]) if is_animate == settings.ANIMATED: gen_value_Keyframed(lottie["it"][1]["c"], color[0], index.inc()) else: if is_animate == settings.NOT_ANIMATED: val = color[0] else: val = color[0][0][0] red = float(val[0].text) green = float(val[1].text) blue = float(val[2].text) red, green, blue = red ** (1/settings.GAMMA[0]), green ** (1/settings.GAMMA[1]), blue ** (1/ settings.GAMMA[2]) alpha = float(val[3].text) gen_properties_value(lottie["it"][1]["c"], [red, green, blue, alpha], index.inc(), settings.DEFAULT_ANIMATED, settings.NO_INFO) #Opacity opacity = layer.get_param("amount") opacity.animate("opacity") opacity.fill_path(lottie["it"][1],"o") #Constant width loop = bline.get_loop() width = layer.get_param("width") width.scale_convert_link(1) width.animate("real") width.fill_path(lottie["it"][1],"w") #Creating shape dictionary lottie["it"][0]["ind"] = 0 lottie["it"][0]["ty"] = "sh" lottie["it"][0]["ix"] = 1 lottie["it"][0]["ks"] = {} lottie["it"][0]["nm"] = "Path 1" lottie["it"][0]["mn"] = "ADBE Vector Shape - Group" lottie["it"][0]["hd"] = "false" lottie["it"][0]["ks"]["a"] = 1 lottie["it"][0]["ks"]["ix"] = lottie["it"][0]["ix"] + 1 lottie["it"][0]["ks"]["k"] = [] window = {} window["first"] = sys.maxsize window["last"] = -1 for entry in bline.get_entry_list(): pos = entry["point"] width = entry["width"] t1 = entry["t1"] t2 = entry["t2"] split_r = entry["split_radius"] split_a = entry["split_angle"] pos.update_frame_window(window) # Empty the pos and fill in the new animated pos pos.animate("vector") width.update_frame_window(window) width.animate("real") split_r.update_frame_window(window) split_r.animate_without_path("bool") split_a.update_frame_window(window) split_a.animate_without_path("bool") animate_tangents(t1, window) animate_tangents(t2, window) outer_width = layer.get_param("width") sharp_cusps = layer.get_param("sharp_cusps") expand = layer.get_param("expand") r_tip0 = layer.get_param("round_tip[0]") r_tip1 = layer.get_param("round_tip[1]") homo_width = layer.get_param("homogeneous_width") origin = layer.get_param("origin") # Animating the origin origin.update_frame_window(window) origin.animate("vector") # Animating the outer width outer_width.update_frame_window(window) outer_width.animate("real") # Animating the sharp_cusps sharp_cusps.update_frame_window(window) sharp_cusps.animate_without_path("bool") # Animating the expand param expand.update_frame_window(window) expand.animate("real") # Animating the round tip 0 r_tip0.update_frame_window(window) r_tip0.animate_without_path("bool") # Animating the round tip 1 r_tip1.update_frame_window(window) r_tip1.animate_without_path("bool") # Animating the homogeneous width homo_width.update_frame_window(window) homo_width.animate_without_path("bool") # Minimizing the window size if window["first"] == sys.maxsize and window["last"] == -1: window["first"] = window["last"] = 0 frames = list(set(settings.WAYPOINTS_LIST)) length = bline.get_len() flag = False if loop: flag = True for fr in frames: if fr!=1: st_val = insert_dict_at(lottie["it"][0]["ks"]["k"], -1, fr, flag,True) cur_origin = origin.get_value(fr) for point in range(0,length): pos_ret, width, t1, t2, split_r_val, split_a_val = get_outline_param_at_frame(bline[point],fr) cubic_to(pos_ret,t1,t2,st_val,cur_origin,False,True)
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 gen_shapes_rectangle(lottie, layer, idx): """ Generates the dictionary corresponding to shapes/rect.json Args: lottie (dict) : The lottie generated rectangle layer will be stored in it layer (lxml.etree._Element): Synfig format rectangle layer idx (int) : Stores the index of the rectangle layer Returns: (None) """ index = Count() lottie["ty"] = "rc" # Type: rectangle lottie["p"] = {} # Position of rectangle lottie["d"] = settings.DEFAULT_DIRECTION lottie["s"] = {} # Size of rectangle lottie["ix"] = idx # setting the index lottie["r"] = {} # Rounded corners of rectangle points = {} for child in layer: if child.tag == "param": if child.attrib["name"] == "point1": points["1"] = child # Store address of child here elif child.attrib["name"] == "point2": points["2"] = child # Store address of child here elif child.attrib["name"] == "expand": expand_animate = is_animated(child[0]) param_expand = child elif child.attrib["name"] == "bevel": is_animate = is_animated(child[0]) if is_animate == 2: gen_value_Keyframed(lottie["r"], child[0], index.inc()) else: bevel = get_child_value(is_animate, child, "value") bevel *= settings.PIX_PER_UNIT gen_properties_value(lottie["r"], bevel, index.inc(), settings.DEFAULT_ANIMATED, settings.NO_INFO) p1_animate = is_animated(points["1"][0]) p2_animate = is_animated(points["2"][0]) # If expand parameter is not animated if expand_animate in {0, 1}: param_expand = gen_dummy_waypoint(param_expand, expand_animate, "real") # p1 not animated and p2 not animated if p1_animate in {0, 1} and p2_animate in {0, 1}: points["1"] = gen_dummy_waypoint(points["1"], p1_animate, "vector") points["2"] = gen_dummy_waypoint(points["2"], p2_animate, "vector") # p1 is animated and p2 is not animated elif p1_animate == 2 and p2_animate in {0, 1}: points["2"] = gen_dummy_waypoint(points["2"], p2_animate, "vector") # p1 is not animated and p2 is animated elif p1_animate in {0, 1} and p2_animate == 2: points["1"] = gen_dummy_waypoint(points["1"], p1_animate, "vector") both_points_animated(points["1"], points["2"], param_expand, lottie, index)
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_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