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_layer_image(lottie, layer, idx): """ Generates the dictionary corresponding to layers/image.json Args: lottie (dict) : Lottie generated image stored here layer (lxml.etree._Element): Synfig format image layer idx (int) : Stores the index(number of) of image layer Returns: (None) """ index = Count() lottie["ddd"] = settings.DEFAULT_3D lottie["ind"] = idx lottie["ty"] = settings.LAYER_IMAGE_TYPE lottie["nm"] = settings.LAYER_IMAGE_NAME + str(idx) lottie["sr"] = settings.LAYER_DEFAULT_STRETCH lottie["ks"] = {} # Transform properties to be filled settings.lottie_format["assets"].append({}) st = add_image_asset(settings.lottie_format["assets"][-1], layer) asset = settings.lottie_format["assets"][-1] # setting class (jpg, png) lottie["cl"] = asset["p"].split(".")[-1] # setting the reference id lottie["refId"] = asset["id"] pos1_animate = is_animated(st["tl"][0]) pos2_animate = is_animated(st["br"][0]) # If pos1 is not animated if pos1_animate in {0, 1}: st["tl"] = gen_dummy_waypoint(st["tl"], pos1_animate, "vector") # If pos2 is not animated if pos2_animate in {0, 1}: st["br"] = gen_dummy_waypoint(st["br"], pos2_animate, "vector") st["scale"] = gen_image_scale(st["tl"][0], st["br"][0], asset["w"], asset["h"]) anchor = [0, 0, 0] gen_helpers_transform(lottie["ks"], layer, st["tl"][0], anchor, st["scale"][0]) lottie["ao"] = settings.LAYER_DEFAULT_AUTO_ORIENT lottie["ip"] = settings.lottie_format["ip"] lottie["op"] = settings.lottie_format["op"] lottie["st"] = 0 # Don't know yet get_blend(lottie, layer) lottie["markers"] = [] # Markers to be filled yet
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 get_animated_time_list(child, time_list): """ Appends all the frames corresponding to the waypoints in the animated(child[0]) list, in time_list Args: child (lxml.etree._Element) : Parent Element of animation time_list (set) : Will store all the frames at which waypoints are present Returns: (None) """ animated = child[0] is_animate = is_animated(animated) if is_animate in {0, 1}: return for waypoint in animated: frame = get_frame(waypoint) time_list.add(frame)
def 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 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 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_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)