def gen_properties_offset_keyframe(curve_list, animated, i): """ Generates the dictionary corresponding to properties/offsetKeyFrame.json Args: curve_list (list) : Stores bezier curve in Lottie format animated (lxml.etree._Element) : Synfig format animation i (int) : Iterator for animation Returns: (TypeError) : If a constant interval is encountered (None) : In all other cases """ lottie = curve_list[-1] waypoint, next_waypoint = animated[i], animated[i + 1] cur_get_after, next_get_before = waypoint.attrib[ "after"], next_waypoint.attrib["before"] cur_get_before, next_get_after = waypoint.attrib[ "before"], next_waypoint.attrib["after"] # "angle" interpolations never call this function, can be removed by confirming if animated.attrib["type"] == "angle": if cur_get_after == "auto": cur_get_after = "linear" if cur_get_before == "auto": cur_get_before = "linear" if next_get_before == "auto": next_get_before = "linear" if next_get_after == "auto": next_get_after = "linear" # Synfig only supports constant interpolations for points # "points" never call this function, can be removed by confirming if animated.attrib["type"] == "points": cur_get_after = "constant" cur_get_before = "constant" next_get_after = "constant" next_get_before = "constant" # Calculate positions of waypoints cur_pos = parse_position(animated, i) next_pos = parse_position(animated, i + 1) lottie["i"] = {} # Time bezier curve, not used in synfig lottie["o"] = {} # Time bezier curve, not used in synfig lottie["i"]["x"] = 0.5 lottie["i"]["y"] = 0.5 lottie["o"]["x"] = 0.5 lottie["o"]["y"] = 0.5 if cur_get_after == "halt": # For ease out ease_out(lottie) if next_get_before == "halt": # For ease in ease_in(lottie) lottie["t"] = get_frame(waypoint) is_transform_axis = False if "transform_axis" in animated.keys(): is_transform_axis = True lottie["s"] = change_axis(cur_pos[0], cur_pos[1], is_transform_axis) lottie["e"] = change_axis(next_pos[0], next_pos[1], is_transform_axis) lottie["to"] = [] lottie["ti"] = [] # Calculating the unchanged tangent try: out_val, in_val = calc_tangent(animated, lottie, i) except Exception as excep: # This means constant interval return excep # This module is only needed for origin animation lottie["to"] = out_val.get_list() lottie["ti"] = in_val.get_list() # TCB/!TCB and list is not empty if cur_get_before == "auto" and cur_get_after != "auto" and i > 0: curve_list[-2]["ti"] = copy.deepcopy(lottie["to"]) curve_list[-2]["ti"] = [ -item / settings.TANGENT_FACTOR for item in curve_list[-2]["ti"] ] curve_list[-2]["ti"][1] = -curve_list[-2]["ti"][1] if cur_get_after == "halt": curve_list[-2]["i"]["x"] = settings.IN_TANGENT_X curve_list[-2]["i"]["y"] = settings.IN_TANGENT_Y # Lottie tangent length is larger than synfig lottie["ti"] = [item / settings.TANGENT_FACTOR for item in lottie["ti"]] lottie["to"] = [item / settings.TANGENT_FACTOR for item in lottie["to"]] # Lottie and synfig use different tangents SEE DOCUMENTATION lottie["ti"] = [-item for item in lottie["ti"]] # IMPORTANT to and ti have to be relative # The y-axis is different in lottie lottie["ti"][1] = -lottie["ti"][1] lottie["to"][1] = -lottie["to"][1] # These tangents will be used in actual calculation of points according to # Synfig lottie["synfig_to"] = [tangent for tangent in lottie["to"]] lottie["synfig_ti"] = [-tangent for tangent in lottie["ti"]] if cur_get_after == "halt": lottie["synfig_to"] = [0 for val in lottie["synfig_to"]] if next_get_before == "halt": lottie["synfig_ti"] = [0 for val in lottie["synfig_ti"]]
def gen_value_Keyframe(curve_list, animated, i): """ Generates the dictionary corresponding to properties/valueKeyframe.json in lottie documentation Args: curve_list (list) : Bezier curve in Lottie format animated (lxml.etree._Element) : Synfig format animation i (int) : Iterator for animation Returns: (TypeError) : If hold interval is encountered (None) : Otherwise """ lottie = curve_list[-1] waypoint, next_waypoint = animated[i], animated[i + 1] cur_get_after, next_get_before = waypoint.attrib[ "after"], next_waypoint.attrib["before"] cur_get_before, next_get_after = waypoint.attrib[ "before"], next_waypoint.attrib["after"] # Calculate positions of waypoints if animated.attrib["type"] in {"angle", "star_angle_new", "region_angle"}: #if animated.attrib["type"] in {"angle"}: if cur_get_after == "auto": cur_get_after = "linear" if cur_get_before == "auto": cur_get_before = "linear" if next_get_before == "auto": next_get_before = "linear" if next_get_after == "auto": next_get_after = "linear" # Synfig only supports constant interpolations for points if animated.attrib["type"] == "points": cur_get_after = "constant" cur_get_before = "constant" next_get_after = "constant" next_get_before = "constant" # After effects only supports linear,ease-in,ease-out and constant interpolations for color ##### No support for TCB and clamped interpolations in color is there yet ##### if animated.attrib["type"] == {"color", "linear_gradient"}: if cur_get_after in {"auto", "clamped"}: cur_get_after = "linear" if cur_get_before in {"auto", "clamped"}: cur_get_before = "linear" if next_get_before in {"auto", "clamped"}: next_get_before = "linear" if next_get_after in {"auto", "clamped"}: next_get_after = "linear" cur_pos = parse_position(animated, i) next_pos = parse_position(animated, i + 1) lottie["t"] = get_frame(waypoint) lottie["s"] = cur_pos.get_val() lottie["e"] = next_pos.get_val() lottie["i"] = {} lottie["o"] = {} try: out_val, in_val = calc_tangent(animated, lottie, i) except Exception as excep: # That means halt/constant interval return excep set_tangents(out_val, in_val, cur_pos, next_pos, lottie, animated) if cur_get_after == "halt": # For ease out lottie["o"]["x"][0] = settings.OUT_TANGENT_X lottie["o"]["y"][0] = settings.OUT_TANGENT_Y lottie["synfig_o"] = [0] if next_get_before == "halt": # For ease in lottie["i"]["x"][0] = settings.IN_TANGENT_X lottie["i"]["y"][0] = settings.IN_TANGENT_Y lottie["synfig_i"] = [0] # TCB/!TCB and list is not empty if cur_get_before == "auto" and cur_get_after != "auto" and i > 0: # need value for previous tangents # It may be helpful to store them somewhere prev_ov, prev_iv = calc_tangent(animated, curve_list[-2], i - 1) prev_iv = out_val set_tangents(prev_ov, prev_iv, parse_position(animated, i - 1), cur_pos, curve_list[-2], animated) if cur_get_after == "halt": curve_list[-2]["i"]["x"][0] = settings.IN_TANGENT_X curve_list[-2]["i"]["y"][0] = settings.IN_TANGENT_Y lottie["synfig_i"] = [0]
def calc_tangent(animated, lottie, i): """ Calculates the tangent, given two waypoints and there interpolation methods Args: animated (lxml.etree._Element) : Synfig format animation lottie (dict) : Lottie format animation stored here i (int) : Iterator for animation Returns: (Misc.Vector) : If waypoint's value is parsed to misc.Vector by misc.parse_position() (Misc.Color) : If waypoint's value is parsed to misc.Color ... (float) : If waypoint's value is parsed to float ... (None) : If "constant" interval is detected """ waypoint, next_waypoint = animated[i], animated[i + 1] cur_get_after, next_get_before = waypoint.attrib[ "after"], next_waypoint.attrib["before"] cur_get_before, next_get_after = waypoint.attrib[ "before"], next_waypoint.attrib["after"] if animated.attrib["type"] in {"angle", "star_angle_new", "region_angle"}: #if animated.attrib["type"] in {"angle"}: if cur_get_after == "auto": cur_get_after = "linear" if cur_get_before == "auto": cur_get_before = "linear" if next_get_before == "auto": next_get_before = "linear" if next_get_after == "auto": next_get_after = "linear" # Synfig only supports constant interpolations for points if animated.attrib["type"] == "points": cur_get_after = "constant" cur_get_before = "constant" next_get_after = "constant" next_get_before = "constant" # After effects only supports linear,ease-in,ease-out and constant interpolations for color ##### No support for TCB and clamped interpolations in color is there yet ##### if animated.attrib["type"] == "color": if cur_get_after in {"auto", "clamped"}: cur_get_after = "linear" if cur_get_before in {"auto", "clamped"}: cur_get_before = "linear" if next_get_before in {"auto", "clamped"}: next_get_before = "linear" if next_get_after in {"auto", "clamped"}: next_get_after = "linear" # Calculate positions of waypoints cur_pos = parse_position(animated, i) prev_pos = copy.deepcopy(cur_pos) next_pos = parse_position(animated, i + 1) after_next_pos = copy.deepcopy(next_pos) if i + 2 <= len(animated) - 1: after_next_pos = parse_position(animated, i + 2) if i - 1 >= 0: prev_pos = parse_position(animated, i - 1) tens, bias, cont = 0, 0, 0 # default values tens1, bias1, cont1 = 0, 0, 0 if "tension" in waypoint.keys(): tens = float(waypoint.attrib["tension"]) if "continuity" in waypoint.keys(): cont = float(waypoint.attrib["continuity"]) if "bias" in waypoint.keys(): bias = float(waypoint.attrib["bias"]) if "tension" in next_waypoint.keys(): tens1 = float(next_waypoint.attrib["tension"]) if "continuity" in next_waypoint.keys(): cont1 = float(next_waypoint.attrib["continuity"]) if "bias" in next_waypoint.keys(): bias1 = float(next_waypoint.attrib["bias"]) ### Special case for color interpolations ### if animated.attrib["type"] == "color": if cur_get_after == "linear" and next_get_before == "linear": return handle_color() # iter next # ANY/TCB ------ ANY/ANY if cur_get_after == "auto": if i >= 1: out_val = ((1 - tens) * (1 + bias) * (1 + cont) *\ (cur_pos - prev_pos))/2 +\ ((1 - tens) * (1 - bias) * (1 - cont) *\ (next_pos - cur_pos))/2 else: out_val = next_pos - cur_pos # t1 = p2 - p1 # iter next # ANY/LINEAR --- ANY/ANY # ANY/EASE --- ANY/ANY if cur_get_after in {"linear", "halt"}: out_val = next_pos - cur_pos # iter next # ANY/ANY ----- LINEAR/ANY # ANY/ANY ----- EASE/ANY if next_get_before in {"linear", "halt"}: in_val = next_pos - cur_pos # iter next # ANY/CLAMPED - ANY/ANY if cur_get_after == "clamped": if i >= 1: ease = "out" out_val = clamped_vector(prev_pos, cur_pos, next_pos, animated, i, lottie, ease) else: out_val = next_pos - cur_pos # t1 = p2 - p1 # iter next after_next # ANY/ANY ----- CLAMPED/ANY ---- ANY/ANY if next_get_before == "clamped": if i + 2 <= len(animated) - 1: ease = "in" in_val = clamped_vector(cur_pos, next_pos, after_next_pos, animated, i + 1, lottie, ease) else: in_val = next_pos - cur_pos # t2 = p2 - p1 # iter next # ANY/CONSTANT ---- ANY/ANY # ANY/ANY ---- CONSTANT/ANY if cur_get_after == "constant" or next_get_before == "constant": lottie["h"] = 1 if animated.attrib["type"] == "vector": del lottie["to"], lottie["ti"] del lottie["i"], lottie["o"] # "e" is not needed, but is still not deleted as # it is of use in the last iteration of animation # See properties/multiDimenstionalKeyframed.py for more details # del lottie["e"] # If the number of points is decresing, then hold interpolation should # have reverse effect. The value should instantly decrease and remain # same for the rest of the interval if animated.attrib["type"] == "points": if i > 0 and prev_pos[0] > cur_pos[0]: t_now = get_frame(animated[i - 1]) + 1 lottie["t"] = t_now return # iter next after_next # ANY/ANY ------ TCB/ANY ------ ANY/ANY if next_get_before == "auto": if i + 2 <= len(animated) - 1: in_val = ((1 - tens1) * (1 + bias1) * (1 - cont1) *\ (next_pos - cur_pos))/2 +\ ((1 - tens1) * (1 - bias1) * (1 + cont1) *\ (after_next_pos - next_pos))/2 else: in_val = next_pos - cur_pos # t2 = p2 - p1 return out_val, in_val