def gen_mask(lottie, invert, bline_point, idx): """ Generates a mask. This was specifically needed to support the invert parameter Args: lottie (dict) : The final mask will be stored in this dict invert (bool) : Tells if the invert parameter is set or not bline_point (lxml.etree._Element) : Synfig format shape idx (int) : Specifies the index of this mask Returns: (None) """ lottie["inv"] = invert lottie["mode"] = settings.MASK_ADDITIVE lottie["pt"] = {} lottie["o"] = {} # Opacity lottie["x"] = {} # Expression lottie["nm"] = "Mask " + str(idx) index = Count() gen_properties_shapeKeyframed(lottie["pt"], bline_point, index.inc()) gen_properties_value(lottie["o"], settings.DEFAULT_OPACITY, index.inc(), settings.DEFAULT_ANIMATED, settings.NO_INFO) gen_properties_value(lottie["x"], 0, index.inc(), settings.DEFAULT_ANIMATED, settings.NO_INFO) convert_non_loop(lottie["pt"])
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 fill_blur_dict(lottie, layer, idx, direction): """ This function will be called for each blur layer separately for the two directions Args: lottie (dict) : Lottie Dictionary for blur layers layers (common.Layer.Layer) : Synfig format layer idx (int) : Stores the index(number of) of blur layer direction (string) : Indicates the direction of blurring Returns: (None) """ index = Count() lottie["ty"] = settings.BLUR_TYPE lottie["nm"] = layer.get_description() lottie["mn"] = lottie["nm"] lottie["np"] = 5 lottie["en"] = 1 lottie["ix"] = idx lottie["ef"] = [] size = layer.get_param("size").get() temp_blurriness = {} blurriness(temp_blurriness, size, index.inc(), direction) temp_directions = {} generate_dimensions_dict(temp_directions, index.inc(), direction) temp_pixels = {} generate_dimensions_dict(temp_pixels, index.inc(), "edge_pixels") lottie["ef"].append(temp_blurriness) lottie["ef"].append(temp_directions) lottie["ef"].append(temp_pixels)
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_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_layer_shape(lottie, layer, idx): """ Generates the dictionary corresponding to layers/shape.json Args: lottie (dict) : Lottie generate shape stored here layer (common.Layer.Layer) : Synfig format shape layer idx (int) : Stores the index(number of) of shape layer Returns: (None) """ if layer.get_type() in {"linear_gradient"}: # Create dummy point1 and point2 for linear gradient to generate rectangle for it in lottie format # Will have to use add_offset() function inside after this generation gen_dummy_rectangle(layer) layer.add_offset() index = Count() lottie["ddd"] = settings.DEFAULT_3D lottie["ind"] = idx lottie["ty"] = settings.LAYER_SHAPE_TYPE lottie["nm"] = layer.get_description() lottie["sr"] = settings.LAYER_DEFAULT_STRETCH lottie["ks"] = {} # Transform properties to be filled gen_helpers_transform(lottie["ks"]) lottie["ao"] = settings.LAYER_DEFAULT_AUTO_ORIENT lottie["shapes"] = [] # Shapes to be filled yet lottie["shapes"].append({}) if layer.get_type() == "star": gen_shapes_star(lottie["shapes"][0], layer, index.inc()) elif layer.get_type() in {"circle", "simple_circle"}: gen_shapes_circle(lottie["shapes"][0], layer, index.inc()) elif layer.get_type() in {"filled_rectangle", "rectangle"}: gen_shapes_rectangle(lottie["shapes"][0], layer.get_layer(), index.inc()) elif layer.get_type() in {"linear_gradient"}: gen_shapes_shape(lottie["shapes"][0], layer, index.inc()) lottie["shapes"].append({}) # For the fill or color if layer.get_type() in {"linear_gradient"}: gen_linear_gradient(lottie["shapes"][1], layer, index.inc()) else: gen_shapes_fill(lottie["shapes"][1], layer) lottie["ip"] = settings.lottie_format["ip"] lottie["op"] = settings.lottie_format["op"] lottie["st"] = 0 # Don't know yet get_blend(lottie, layer)
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_layer_blur(lottie, layers): """ This function will be called for each canvas/composition. Main function to generate all the layers Args: lottie (dict) : Lottie Dictionary for blur layers layers (List) : Dictionary of Synfig format layers Returns: (None) """ index = Count() for layer in layers: blur_dict_x = {} fill_blur_dict(blur_dict_x, layer, index.inc(), "horizontal") blur_dict_y = {} fill_blur_dict(blur_dict_y, layer, index.inc(), "vertical") lottie.append(blur_dict_x) lottie.append(blur_dict_y)
def gen_layer_shape(lottie, layer, idx): """ Generates the dictionary corresponding to layers/shape.json Args: lottie (dict) : Lottie generate shape stored here layer (lxml.etree._Element): Synfig format shape layer idx (int) : Stores the index(number of) of shape layer Returns: (None) """ group.update_layer(layer) index = Count() lottie["ddd"] = settings.DEFAULT_3D lottie["ind"] = idx lottie["ty"] = settings.LAYER_SHAPE_TYPE set_layer_desc(layer, settings.LAYER_SHAPE_NAME + str(idx), lottie) lottie["sr"] = settings.LAYER_DEFAULT_STRETCH lottie["ks"] = {} # Transform properties to be filled gen_helpers_transform(lottie["ks"], layer) lottie["ao"] = settings.LAYER_DEFAULT_AUTO_ORIENT lottie["shapes"] = [] # Shapes to be filled yet lottie["shapes"].append({}) if layer.attrib["type"] == "star": gen_shapes_star(lottie["shapes"][0], layer, index.inc()) elif layer.attrib["type"] in {"circle", "simple_circle"}: gen_shapes_circle(lottie["shapes"][0], layer, index.inc()) elif layer.attrib["type"] in {"filled_rectangle", "rectangle"}: gen_shapes_rectangle(lottie["shapes"][0], layer, index.inc()) lottie["shapes"].append({}) # For the fill or color gen_shapes_fill(lottie["shapes"][1], layer) lottie["ip"] = settings.lottie_format["ip"] lottie["op"] = settings.lottie_format["op"] lottie["st"] = 0 # Don't know yet get_blend(lottie, layer)
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_layer_solid(lottie, layer, idx): """ Generates the dictionary corresponding to layers/solid.json Args: lottie (dict) : Lottie generated solid layer stored here layer (lxml.etree._Element): Synfig format solid layer idx (int) : Stores the index(number of) of solid layer Returns: (None) """ index = Count() lottie["ddd"] = settings.DEFAULT_3D lottie["ind"] = idx lottie["ty"] = settings.LAYER_SOLID_TYPE set_layer_desc(layer, settings.LAYER_SOLID_NAME + str(idx), lottie) lottie["sr"] = settings.LAYER_DEFAULT_STRETCH lottie["ks"] = {} # Transform properties to be filled lottie["ef"] = [] # Stores the effects pos = [ settings.lottie_format["w"] / 2 + get_additional_width() / 2, settings.lottie_format["h"] / 2 + get_additional_height() / 2 ] anchor = pos gen_helpers_transform(lottie["ks"], layer, pos, anchor) lottie["ef"].append({}) gen_effects_fill(lottie["ef"][-1], layer, index.inc()) lottie["ao"] = settings.LAYER_DEFAULT_AUTO_ORIENT lottie["sw"] = settings.lottie_format["w"] + get_additional_width( ) # Solid Width lottie["sh"] = settings.lottie_format["h"] + get_additional_height( ) # Solid Height for chld in layer: if chld.tag == "param": if chld.attrib["name"] == "color": lottie["sc"] = get_color_hex(chld[0]) # Solid Color lottie["ip"] = settings.lottie_format["ip"] lottie["op"] = settings.lottie_format["op"] lottie["st"] = 0 # Don't know yet get_blend(lottie, layer)
def gen_shapes_shape(lottie, layer, idx): """ Generates the dictionary corresponding to shapes/shape.json Args: lottie (dict) : The lottie generated shape layer will be stored in it layer (lxml.etree._Element): Synfig format shape(list of bline points) layer idx (int) : Stores the index of the shape layer Returns: (None) """ index = Count() lottie["ty"] = "sh" # Type: shape lottie["ix"] = idx # Index lottie["ks"] = {} gen_properties_shapeKeyframed(lottie["ks"], layer, index.inc())
def gen_effects_vfeather(lottie, idx): """ Generates the dictionary corresponding to effects/vertical feather Args: lottie (dict) : Lottie format effects stored in this idx (int) : Index/Count of effect Returns: (None) """ index = Count() lottie["ty"] = settings.EFFECTS_VFEATHER # Effect type lottie["nm"] = "Vertical Feather" # Name lottie["ix"] = idx # Index lottie["v"] = {} # value gen_properties_value(lottie["v"], 0, index.inc(), 0, settings.NO_INFO)
def gen_effects_fillmask(lottie, idx): """ Generates the dictionary corresponding to effects/fillmask.json Args: lottie (dict) : Lottie format effects stored in this idx (int) : Index/Count of effect Returns: (None) """ index = Count() lottie["ty"] = settings.EFFECTS_FILL_MASK # Effect type lottie["nm"] = "Fill Mask" # Name lottie["ix"] = idx # Index lottie["v"] = {} # value gen_properties_value(lottie["v"], 0, index.inc(), 0, settings.NO_INFO)
def gen_effects_hfeather(lottie, layer, idx): """ Generates the dictionary corresponding to effects/horizontal feather 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_HFEATHER # Effect type lottie["nm"] = "Horizontal Feather" # Name lottie["ix"] = idx # Index lottie["v"] = {} # value gen_properties_value(lottie["v"], 0, index.inc(), 0, settings.NO_INFO)
def gen_effects_allmask(lottie, layer, idx): """ Generates the dictionary corresponding to effects/allmask.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_ALL_MASK # Effect type lottie["nm"] = "All Masks" # Name lottie["ix"] = idx # Index lottie["v"] = {} # value gen_properties_value(lottie["v"], 0, index.inc(), 0, settings.NO_INFO)
def gen_layer_solid(lottie, layer, idx): """ Generates the dictionary corresponding to layers/solid.json Args: lottie (dict) : Lottie generated solid layer stored here layer (common.Layer.Layer) : Synfig format solid layer idx (int) : Stores the index(number of) of solid layer Returns: (None) """ index = Count() lottie["ddd"] = settings.DEFAULT_3D lottie["ind"] = idx lottie["ty"] = settings.LAYER_SOLID_TYPE lottie["nm"] = layer.get_description() lottie["sr"] = settings.LAYER_DEFAULT_STRETCH lottie["ks"] = {} # Transform properties to be filled lottie["ef"] = [] # Stores the effects pos = [ settings.lottie_format["w"] / 2 + get_additional_width() / 2, settings.lottie_format["h"] / 2 + get_additional_height() / 2 ] anchor = pos gen_helpers_transform(lottie["ks"], pos, anchor) lottie["ef"].append({}) gen_effects_fill(lottie["ef"][-1], layer, index.inc()) lottie["ao"] = settings.LAYER_DEFAULT_AUTO_ORIENT lottie["sw"] = settings.lottie_format["w"] + get_additional_width( ) # Solid Width lottie["sh"] = settings.lottie_format["h"] + get_additional_height( ) # Solid Height lottie["sc"] = get_color_hex(layer.get_param("color").get()[0]) lottie["ip"] = settings.lottie_format["ip"] lottie["op"] = settings.lottie_format["op"] lottie["st"] = 0 # Don't know yet get_blend(lottie, layer)
def gen_effects_fill(lottie, layer, idx): """ Generates the dictionary corresponding to effects/fill.json Args: lottie (dict) : Lottie format layer layer (common.Layer.Layer) : Synfig format layer idx (int) : Index/Count of effect Returns: (None) """ index = Count() lottie["ty"] = settings.EFFECTS_FILL # Effect type lottie["nm"] = "Fill" # Name lottie["ix"] = idx # Index lottie["ef"] = [] # Effect list of properties # generating the fill mask, has no use in Synfig. But a necessity for # running the .json file lottie["ef"].append({}) gen_effects_fillmask(lottie["ef"][-1], index.inc()) # generating the all mask property as required by lottie lottie["ef"].append({}) gen_effects_allmask(lottie["ef"][-1], index.inc()) # generating the color property lottie["ef"].append({}) gen_effects_color(lottie["ef"][-1], layer, index.inc()) # generating the invert property as required by lottie lottie["ef"].append({}) gen_effects_invert(lottie["ef"][-1], index.inc()) # generating the horizontal feather as required by lottie lottie["ef"].append({}) gen_effects_hfeather(lottie["ef"][-1], index.inc()) # generating the vertical feather as required by lottie lottie["ef"].append({}) gen_effects_vfeather(lottie["ef"][-1], index.inc()) # generating the opacity lottie["ef"].append({}) gen_effects_opacity(lottie["ef"][-1], layer, index.inc())
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_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, pos=settings.DEFAULT_POSITION, anchor=settings.DEFAULT_ANCHOR, scale=settings.DEFAULT_SCALE, rotation=settings.DEFAULT_ROTATION, opacity=settings.DEFAULT_OPACITY, skew=settings.DEFAULT_SKEW): """ Generates the dictionary corresponding to helpers/transform.json Args: lottie (dict) : Lottie 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 lottie["sk"] = {} # skew of the layer lottie["sa"] = {} # skew axis 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: pos.fill_path(lottie, "p") # setting the opacity if isinstance(opacity, (float, int)): gen_properties_value(lottie["o"], opacity, index.inc(), settings.DEFAULT_ANIMATED, settings.NO_INFO) else: opacity.fill_path(lottie, "o") # setting the rotation if isinstance(rotation, (float, int)): gen_properties_value(lottie["r"], rotation, index.inc(), settings.DEFAULT_ANIMATED, settings.NO_INFO) else: rotation.fill_path(lottie, "r") # setting the anchor point if isinstance(anchor, list): gen_properties_value(lottie["a"], anchor, index.inc(), settings.DEFAULT_ANIMATED, settings.NO_INFO) else: anchor.fill_path(lottie, "a") # 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: scale.fill_path(lottie, "s") # setting the skew angle if isinstance(skew, (float, int)): gen_properties_value(lottie["sk"], skew, index.inc(), settings.DEFAULT_ANIMATED, settings.NO_INFO) else: skew.fill_path(lottie, "sk") # setting the skew axis gen_properties_value(lottie["sa"], 0, 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 (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 == 2: regular_polygon["prop"] = "changing" regular_polygon["animated"] = rp[0] elif is_animate == 1: 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 == 2: # 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 == 0: 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 == 2: gen_value_Keyframed(lottie["r"], angle[0], index.inc()) else: theta = 0 # default angle for the star if is_animate == 0: 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 == 2: gen_value_Keyframed(lottie["or"], radius1[0], index.inc()) else: r_outer = 0 # default value for outer radius if is_animate == 0: 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 == 2: gen_value_Keyframed(lottie["ir"], radius2[0], index.inc()) else: r_inner = 0 # default value for inner radius if is_animate == 0: 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 == 2: gen_properties_multi_dimensional_keyframed(lottie["p"], origin[0], index.inc()) else: x_val, y_val = 0, 0 if is_animate == 0: 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_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_layer_group(lottie, layer, idx): """ Will generate a pre composition but has small differences than pre-comp layer used in layers/preComp.py This function will be used for group layer as well as switch group layer Args: lottie (dict) : Lottie format layer will be stored here layer (lxml.etree._Element) : Synfig format group/switch layer idx (int) : Index of the layer Returns: (None) """ lottie["ddd"] = settings.DEFAULT_3D lottie["ind"] = idx lottie["ty"] = settings.LAYER_PRECOMP_TYPE lottie["sr"] = settings.LAYER_DEFAULT_STRETCH lottie["ks"] = {} # Transform properties to be filled set_layer_desc(layer, settings.LAYER_PRECOMP_NAME + str(idx), lottie) index = Count() for chld in layer: if chld.tag == "param": if chld.attrib["name"] == "canvas": canvas = chld elif chld.attrib["name"] == "transformation": transform = chld[0] for child in transform: if child.tag == "scale": scale = child elif child.tag == "offset": pos = child elif child.tag == "angle": angle = child elif chld.attrib["name"] == "origin": origin = chld elif chld.attrib["name"] == "amount": opacity = chld elif chld.attrib["name"] == "outline_grow": outline_grow = chld elif chld.attrib["name"] == "time_offset": time_offset = chld elif chld.attrib["name"] == "time_dilation": time_dilation = chld outline_grow = gen_dummy_waypoint(outline_grow, "param", "real") append_path(outline_grow[0], outline_grow, "outline_grow_path") origin = gen_dummy_waypoint(origin, "param", "vector") anchor = origin group.update_pos(anchor) angle = gen_dummy_waypoint(angle, "angle", "rotate_layer_angle") pos = gen_dummy_waypoint(pos, "offset", "vector") if settings.INSIDE_PRECOMP: group.update_pos(pos) scale = gen_dummy_waypoint(scale, "scale", "group_layer_scale") # Generate the transform properties here gen_helpers_transform(lottie["ks"], layer, pos[0], anchor[0], scale[0], angle[0], opacity[0]) # Store previous states, to be recovered at the end of group layer prev_state = settings.INSIDE_PRECOMP settings.OUTLINE_GROW.append(outline_grow) settings.INSIDE_PRECOMP = True settings.lottie_format["assets"].append({}) asset = add_precomp_asset(settings.lottie_format["assets"][-1], canvas[0], len(canvas[0])) lottie["refId"] = asset lottie["w"] = settings.lottie_format[ "w"] + settings.ADDITIONAL_PRECOMP_WIDTH # Experimental increase in width and height of precomposition lottie[ "h"] = settings.lottie_format["h"] + settings.ADDITIONAL_PRECOMP_HEIGHT 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) # Time offset and speed lottie["tm"] = {} gen_time_remap(lottie["tm"], time_offset, time_dilation, index.inc()) # Change opacity of layers for switch-group layers if layer.attrib["type"] == "switch": change_opacity_switch(layer, lottie) # Change opacity of layers for group layers elif layer.attrib["type"] == "group": change_opacity_group(layer, lottie) # Return to previous state, when we go outside the group layer settings.INSIDE_PRECOMP = prev_state settings.OUTLINE_GROW.pop()
def gen_layer_shape_solid(lottie, layer, idx): """ Generates the dictionary corresponding to layers/shapes.json Args: lottie (dict) : Lottie generated solid layer stored here layer (lxml.etree._Element): Synfig format solid layer idx (int) : Stores the index(number of) of solid layer Returns: (None) """ update_layer(layer) # Setting the solid layer which will be masked index = Count() lottie["ddd"] = settings.DEFAULT_3D lottie["ind"] = idx lottie["ty"] = settings.LAYER_SOLID_TYPE set_layer_desc(layer, settings.LAYER_SOLID_NAME + str(idx), lottie) lottie["sr"] = settings.LAYER_DEFAULT_STRETCH lottie["ks"] = {} # Transform properties to be filled lottie["ef"] = [] # Stores the effects pos = [ settings.lottie_format["w"] / 2 + get_additional_width() / 2, settings.lottie_format["h"] / 2 + get_additional_height() / 2 ] anchor = pos gen_helpers_transform(lottie["ks"], layer, pos, anchor) lottie["ef"].append({}) gen_effects_fill(lottie["ef"][-1], layer, index.inc()) lottie["ao"] = settings.LAYER_DEFAULT_AUTO_ORIENT lottie["sw"] = settings.lottie_format["w"] + get_additional_width( ) # Solid Width lottie["sh"] = settings.lottie_format["h"] + get_additional_height( ) # Solid Height invert = False for chld in layer: if chld.tag == "param": if chld.attrib["name"] == "color": lottie["sc"] = get_color_hex(chld[0]) # Solid Color elif chld.attrib["name"] == "invert": is_animate = is_animated(chld[0]) if is_animate == 0: val = chld[0].attrib["value"] elif is_animate == 1: val = chld[0][0][0].attrib["value"] else: # If animated, always set invert to false val = "false" if val == "true": invert = True elif chld.attrib["name"] in {"bline", "vector_list"}: bline_point = chld[0] lottie["ip"] = settings.lottie_format["ip"] lottie["op"] = settings.lottie_format["op"] lottie["st"] = 0 # Don't know yet get_blend(lottie, layer) hasMask = True lottie["hasMask"] = hasMask lottie["masksProperties"] = [] lottie["masksProperties"].append({}) if layer.attrib["type"] in { "star", "circle", "rectangle", "filled_rectangle" }: bline_point = layer gen_mask(lottie["masksProperties"][0], invert, bline_point, index.inc())
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_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_layer_shape_solid(lottie, layer, idx): """ Generates the dictionary corresponding to layers/shapes.json Args: lottie (dict) : Lottie generated solid layer stored here layer (common.Layer.Layer) : Synfig format solid layer idx (int) : Stores the index(number of) of solid layer Returns: (None) """ layer.add_offset() # Setting the solid layer which will be masked index = Count() lottie["ddd"] = settings.DEFAULT_3D lottie["ind"] = idx lottie["ty"] = settings.LAYER_SOLID_TYPE lottie["nm"] = layer.get_description() lottie["sr"] = settings.LAYER_DEFAULT_STRETCH lottie["ks"] = {} # Transform properties to be filled lottie["ef"] = [] # Stores the effects pos = [ settings.lottie_format["w"] / 2 + get_additional_width() / 2, settings.lottie_format["h"] / 2 + get_additional_height() / 2 ] anchor = pos gen_helpers_transform(lottie["ks"], pos, anchor) lottie["ef"].append({}) gen_effects_fill(lottie["ef"][-1], layer, index.inc()) lottie["ao"] = settings.LAYER_DEFAULT_AUTO_ORIENT lottie["sw"] = settings.lottie_format["w"] + get_additional_width( ) # Solid Width lottie["sh"] = settings.lottie_format["h"] + get_additional_height( ) # Solid Height lottie["sc"] = get_color_hex(layer.get_param("color").get()[0]) invert = False Inv = layer.get_param("invert").get() flag = False #So far only 'not' convert method seems to be supported for invert param in circle, will add more subsequently. if Inv is not None: if "bool" not in str(Inv[0]) and "animated" not in str(Inv[0]): is_animate = is_animated(Inv[0][0][0]) flag = True else: is_animate = is_animated(Inv[0]) if is_animate == settings.NOT_ANIMATED: if flag: val = "false" if Inv[0][0][0].attrib["value"] == "false": val = "true" else: val = Inv[0].attrib["value"] elif is_animate == settings.SINGLE_WAYPOINT: if flag: val = "false" if Inv[0][0][0][0][0].attrib["value"] == "false": val = "true" else: val = Inv[0][0][0].attrib["value"] else: # If animated, always set invert to false val = "false" if val == "true": invert = True lottie["ip"] = settings.lottie_format["ip"] lottie["op"] = settings.lottie_format["op"] lottie["st"] = 0 # Don't know yet get_blend(lottie, layer) hasMask = True lottie["hasMask"] = hasMask lottie["masksProperties"] = [] lottie["masksProperties"].append({}) if layer.get_type() in {"star", "circle", "rectangle", "filled_rectangle"}: bline_point = layer else: bline_point = layer.get_param("bline", "vector_list") gen_mask(lottie["masksProperties"][0], invert, bline_point, index.inc())
def gen_layer_group(lottie, layer, idx): """ Will generate a pre composition but has small differences than pre-comp layer used in layers/preComp.py This function will be used for group layer as well as switch group layer Args: lottie (dict) : Lottie format layer will be stored here layer (common.Layer.Layer) : Synfig format group/switch layer idx (int) : Index of the layer Returns: (None) """ lottie["ddd"] = settings.DEFAULT_3D lottie["ind"] = idx lottie["ty"] = settings.LAYER_PRECOMP_TYPE lottie["sr"] = settings.LAYER_DEFAULT_STRETCH lottie["ks"] = {} # Transform properties to be filled lottie["nm"] = layer.get_description() index = Count() # Extract parameters canvas = Canvas(layer.get_param("canvas")) origin = layer.get_param("origin") opacity = layer.get_param("amount") outline_grow = layer.get_param("outline_grow") time_offset = layer.get_param("time_offset") time_dilation = layer.get_param("time_dilation") transformation = layer.get_param("transformation") transform = transformation[0] try_par = Param(transform, Param(transformation.get(), layer)) for child in transform: if child.tag == "scale": scale = Param(child, try_par) elif child.tag == "offset": pos = Param(child, try_par) elif child.tag == "angle": angle = Param(child, try_par) elif child.tag == "skew_angle": skew = Param(child, try_par) outline_grow.animate("real") origin.animate("vector") anchor = origin anchor.add_offset() angle.animate("rotate_layer_angle") pos.animate("vector") if settings.INSIDE_PRECOMP: pos.add_offset() scale.animate("group_layer_scale") # Generating animation for skew skew.animate("rotate_layer_angle") # Animating opacity opacity.animate("opacity") # Reset the animations after adding offset anchor.animate("vector", True) pos.animate("vector", True) # Generate the transform properties here gen_helpers_transform(lottie["ks"], pos, anchor, scale, angle, opacity, skew) # Store previous states, to be recovered at the end of group layer prev_state = settings.INSIDE_PRECOMP settings.OUTLINE_GROW.append( outline_grow ) # Storing the outline grow in settings, will be used inside child outlines settings.INSIDE_PRECOMP = True settings.lottie_format["assets"].append({}) asset = add_precomp_asset(settings.lottie_format["assets"][-1], canvas, canvas.get_num_layers()) lottie["refId"] = asset lottie["w"] = settings.lottie_format[ "w"] + settings.ADDITIONAL_PRECOMP_WIDTH # Experimental increase in width and height of precomposition lottie[ "h"] = settings.lottie_format["h"] + settings.ADDITIONAL_PRECOMP_HEIGHT 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) # Time offset and speed lottie["tm"] = {} gen_time_remap(lottie["tm"], time_offset, time_dilation, index.inc()) # Change opacity of layers for switch-group layers if layer.get_type() == "switch": change_opacity_switch(layer, lottie) # Change opacity of layers for group layers elif layer.get_type() == "group": change_opacity_group(layer, lottie) # Return to previous state, when we go outside the group layer settings.INSIDE_PRECOMP = prev_state settings.OUTLINE_GROW.pop()