예제 #1
0
def gen_value_Keyframed(lottie, animated, idx):
    """
    Generates the dictionary corresponding to properties/valueKeyframed.json in
    lottie documentation

    Args:
        lottie (dict)                  : Lottie bezier curve stored in this
        animated (lxml.etree._Element) : Synfig format animation
        idx      (int)                 : Index of animation

    Returns:
        (None)
    """
    lottie["ix"] = idx
    lottie["a"] = 1
    lottie["k"] = []
    for i in range(len(animated) - 1):
        lottie["k"].append({})
        gen_value_Keyframe(lottie["k"], animated, i)
    last_waypoint_frame = get_frame(animated[-1])
    lottie["k"].append({})
    lottie["k"][-1]["t"] = last_waypoint_frame

    if "h" in lottie["k"][-2].keys():
        lottie["k"][-1]["h"] = 1
        lottie["k"][-1]["s"] = lottie["k"][-2]["e"]

        # specific case for points when prev_points > cur_points
        if animated.attrib["type"] == "points":
            if lottie["k"][-2]["s"][0] > lottie["k"][-1]["s"][0]:
                # Adding 1 frame to the previous time
                prev_frames = get_frame(animated[-2])
                lottie["k"][-1]["t"] = prev_frames + 1

    time_adjust(lottie, animated)
예제 #2
0
def calc_pos_and_size(size_animated, pos_animated, animated_1, animated_2,
                      orig_path, i, i1):
    """
    Between two frames, this function is called if either "only point1's
    interval is constant" or "only point2's interval is constant". It calculates
    the position and size property for lottie format. It also adds a new
    waypoint just before the end of the frame if required.
    param3: Can be param1 or param2 of rectangle layer
    param4: Can be param2 or param1 of rectangle layer, but opposite of param3

    Args:
        size_animated (lxml.etree._Element): Holds the size parameter of rectangle layer in Synfig format
        pos_animated  (lxml.etree._Element): Holds the position parameter of rectangle layer in Synfig format
        animated_1    (lxml.etree._Element): Holds the param3 in Synfig format
        animated_2    (lxml.etree._Element): Holds the param4 in Synfig format
        orig_path     (dict)               : Holds the param4 in Lottie format
        i             (int)                : Iterator for animated_2
        i1            (int)                : Iterator for pos_animated and size_animated
    Returns:
        (int, int)    : Updated iterators i and i1 are returned
    """
    pos_animated[i1].attrib["after"] = animated_2[i].attrib["after"]
    size_animated[i1].attrib["after"] = animated_2[i].attrib["after"]

    copy_tcb(pos_animated[i1], animated_2[i])
    copy_tcb(size_animated[i1], animated_2[i])

    get_average(pos_animated[i1], animated_1[i], animated_2[i])
    get_difference(size_animated[i1], animated_1[i], animated_2[i])
    # Inserting a waypoint just before the nextwaypoint
    # Only if a waypoint can be inserted
    t_next = get_frame(animated_2[i + 1])
    t_present = get_frame(animated_2[i])

    ######### Need to check if t_next - t_present < 2 #####
    if abs(t_next - t_present) >= 2:
        pos = get_vector_at_frame(orig_path, t_next - 1)
        pos = to_Synfig_axis(pos, "vector")
        new_waypoint = copy.deepcopy(pos_animated[i1])
        new_waypoint.attrib["before"] = new_waypoint.attrib["after"]
        new_waypoint.attrib["time"] = str(
            (t_next - 1) / settings.lottie_format["fr"]) + "s"
        new_waypoint[0][0].text, new_waypoint[0][1].text = str(pos[0]), str(
            pos[1])

        n_size_waypoint = copy.deepcopy(new_waypoint)
        get_average(new_waypoint, new_waypoint, animated_1[i])
        get_difference(n_size_waypoint, n_size_waypoint, animated_1[i])

        pos_animated.insert(i1 + 1, new_waypoint)
        size_animated.insert(i1 + 1, n_size_waypoint)
        i1 += 1
    i, i1 = i + 1, i1 + 1
    get_average(pos_animated[i1], animated_1[i], animated_2[i])
    get_difference(size_animated[i1], animated_1[i], animated_2[i])
    return i, i1
예제 #3
0
def clamped_tangent(p1, p2, p3, animated, i):
    """
    Function corresponding to clamped function in Synfig
    It generates the tangent when clamped waypoints are used

    Args:
        p1       (float)               : First point
        p2       (float)               : Second point
        p3       (float)               : Third point
        animated (lxml.etree._Element) : Synfig format animation
        i        (int)                 : Iterator over animation

    Returns:
        (float) : Clamped tangent is returned
    """
    # pw -> prev_waypoint, w -> waypoint, nw -> next_waypoint
    pw, w, nw = animated[i - 1], animated[i], animated[i + 1]
    t1 = get_frame(pw)
    t2 = get_frame(w)
    t3 = get_frame(nw)
    bias = 0.0
    tangent = 0.0
    pm = p1 + (p3 - p1) * (t2 - t1) / (t3 - t1)
    if p3 > p1:
        if p2 >= p3 or p2 <= p1:
            tangent = tangent * 0.0
        else:
            if p2 > pm:
                bias = (pm - p2) / (p3 - pm)
            elif p2 < pm:
                bias = (pm - p2) / (pm - p1)
            else:
                bias = 0.0
            tangent = (p2 - p1) * (1.0 + bias) / 2.0 + (p3 - p2) * (1.0 -
                                                                    bias) / 2.0
    elif p1 > p2:
        if p2 >= p1 or p2 <= p3:
            tangent = tangent * 0.0
        else:
            if p2 > pm:
                bias = (pm - p2) / (pm - p1)
            elif p2 < pm:
                bias = (pm - p2) / (p3 - pm)
            else:
                bias = 0.0
            tangent = (p2 - p1) * (1.0 + bias) / 2.0 + (p3 - p2) * (1.0 -
                                                                    bias) / 2.0
    else:
        tangent = tangent * 0.0
    return tangent
예제 #4
0
def gen_properties_multi_dimensional_keyframed(lottie, animated, idx):
    """
    Generates the dictionary corresponding to
    properties/multiDimensionalKeyframed.json

    Args:
        lottie   (dict)                : Lottie generated keyframes will be stored here
        animated (lxml.etree._Element) : Synfig format animation
        idx      (int)                 : Index/Count of animation

    Returns:
        (None)
    """
    lottie["a"] = 1
    lottie["ix"] = idx
    lottie["k"] = []
    for i in range(len(animated) - 1):
        lottie["k"].append({})
        gen_properties_offset_keyframe(lottie["k"], animated, i)
    last_waypoint_frame = get_frame(animated[-1])
    lottie["k"].append({})
    lottie["k"][-1]["t"] = last_waypoint_frame

    if "h" in lottie["k"][-2].keys():
        lottie["k"][-1]["h"] = 1
        lottie["k"][-1]["s"] = lottie["k"][-2]["e"]

    # Time adjust of the curves
    time_adjust(lottie, animated)
예제 #5
0
파일: group.py 프로젝트: warlockz/synfig
def gen_hold_waypoints(inactive_time, layer, anim_type):
    """
    Will only be used to modify opacity waypoints, and set zero values where the
    layer is inactive

    Args:
        inactive_time (set) : Range of time when the layer will be inactive
        layer (common.Layer.Layer) : Synfig format layer
        anim_type (str) : Specifies whether it is effects_opacity or opacity (it
                          will effect a factor of 100)

    Returns:
        (common.Param.Param) : Modified opacity animation is returned
    """
    opacity = layer.get_param("amount")

    opacity.animate(anim_type)
    opacity_dict = {}
    opacity_dict["o"] = {}
    opacity.fill_path(opacity_dict, "o")
    opacity_dict = opacity_dict["o"]

    for it in inactive_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
예제 #6
0
파일: group.py 프로젝트: efkomik/synfig
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
예제 #7
0
파일: animation.py 프로젝트: synfig/synfig
def waypoint_at_frame(anim, frame):
    """
	Returns true if a waypoint is present at 'frame'
	"""
    for waypoint in anim:
        fr = get_frame(waypoint)
        if fr == frame:
            return True
    return False
예제 #8
0
def get_bool_at_frame(anim, frame):
    """
    Calculates the boolean value at a given frame, given a boolean animation

    Args:
        anim  (lxml.etree._Element): Boolean animation
        frame (int)                : Frame at which the value is to be calculated

    Returns:
        (bool) : True if the value is "true" at that frame
               : False otherwise
    """
    i = 0
    while i < len(anim):
        cur_fr = get_frame(anim[i])
        if frame <= cur_fr:
            break
        i += 1
    if i == 0:
        val = anim[0][0].attrib["value"]
    elif i < len(anim):
        # frame lies between i-1 and i'th value of animation
        prev = anim[i - 1][0].attrib["value"]
        cur = anim[i][0].attrib["value"]
        cur_frame = get_frame(anim[i])
        if frame == cur_frame:
            val = cur
        else:
            if prev == "true" and cur == "false":
                val = prev
            elif prev == "false" and cur == "true":
                val = cur
            elif prev == "true" and cur == "true":
                val = cur
            elif prev == "false" and cur == "false":
                val = cur
    elif i >= len(anim):
        val = anim[-1][0].attrib["value"]

    if val == "false":
        val = False
    else:
        val = True
    return val
예제 #9
0
def get_cross_list(animation_1, animation_2, orig_path_1, orig_path_2):
    """
    This function will return a list('set' technically) at which the point1 and point2 of rectangle
    will cross each other.
    This set might contain frames at which waypoints are already present, hence
    this need to be taken care of

    Args:
        animation_1 (lxml.etree._Element): Stores the animation of `point1` parameter in Synfig format
        animation_2 (lxml.etree._Element): Stores the animation of `point2` parameter in Synfig format
        orig_path_1 (dict)               : Stores the animation of `point1` parameter in Lottie format
        orig_path_2 (dict)               : Stores the animation of `point2` parameter in Lottie format

    Returns:
        (set) : Contains the frames at which point1 and point2 cross each other
    """
    en_fr = max(get_frame(animation_1[-1]), get_frame(animation_2[-1]))
    # Function to determine the sign of a variable
    sign = lambda a: (1, -1)[a < 0]
    prev_1 = float(animation_1[0][0][0].text), float(animation_1[0][0][1].text)
    prev_2 = float(animation_2[0][0][0].text), float(animation_2[0][0][1].text)

    # The list to be returned
    ret_list = set()
    frame = 1
    # Loop for all the frames
    while frame <= en_fr:
        now_1 = get_vector_at_frame(orig_path_1, frame)
        now_1 = to_Synfig_axis(now_1, "vector")
        now_2 = get_vector_at_frame(orig_path_2, frame)
        now_2 = to_Synfig_axis(now_2, "vector")
        is_needed = False
        if sign(prev_1[0] - prev_2[0]) != sign(now_1[0] - now_2[0]):
            is_needed = True
        elif sign(prev_1[1] - prev_2[1]) != sign(now_1[1] - now_2[1]):
            is_needed = True
        if is_needed:
            ret_list.add(frame - 1)
            ret_list.add(frame)
        prev_1, prev_2 = now_1, now_2
        frame += 1
    return ret_list
예제 #10
0
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)
    root = gen_dummy_waypoint(root, "param", "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
예제 #11
0
파일: animation.py 프로젝트: synfig/synfig
def insert_waypoint_at_frame(animated, orig_path, frame, animated_name):
    """
	This function will only insert a waypoint at 'frame' if no waypoint is
	present at that 'frame' already

	Args:
		animated      (lxml.etree._Element): Holds the animation in Synfig xml format
		orig_path     (dict)               : Holds the animation in Lottie format
		frame         (int)                : The frame at which the waypoint is to be inserted
		animated_name (str)                : The name/type of animation

	Returns:
		(None)
	"""
    i = 0
    while i < len(animated):
        at_frame = get_frame(animated[i])
        if approximate_equal(frame, at_frame):
            return
        elif frame < at_frame:
            break
        i += 1
    pos = get_vector_at_frame(orig_path, frame)
    pos = to_Synfig_axis(pos, animated_name)

    if i == len(animated):
        new_waypoint = copy.deepcopy(animated[i - 1])
    else:
        new_waypoint = copy.deepcopy(animated[i])
    if animated_name == "vector":
        new_waypoint[0][0].text = str(pos[0])
        new_waypoint[0][1].text = str(pos[1])
    else:
        new_waypoint[0].attrib["value"] = str(pos)

    new_waypoint.attrib["time"] = str(
        frame / settings.lottie_format["fr"]) + "s"
    if i == 0 or i == len(animated):
        # No need of tcb value copy as halt interpolation need to be copied here
        new_waypoint.attrib["before"] = new_waypoint.attrib[
            "after"] = "constant"
    else:
        copy_tcb_average(new_waypoint, animated[i], animated[i - 1])
        new_waypoint.attrib["before"] = animated[i - 1].attrib["after"]
        new_waypoint.attrib["after"] = animated[i].attrib["before"]
        # If the interval was constant before, then the whole interval should
        # remain constant now also
        if new_waypoint.attrib["before"] == "constant" or new_waypoint.attrib[
                "after"] == "constant":
            new_waypoint.attrib["before"] = new_waypoint.attrib[
                "after"] = "constant"
    animated.insert(i, new_waypoint)
예제 #12
0
파일: animation.py 프로젝트: synfig/synfig
def gen_dummy_waypoint(non_animated,
                       animated_tag,
                       anim_type,
                       animated_name="anything"):
    """
	Makes a non animated parameter to animated parameter by creating a new dummy
	waypoint with constant animation

	Args:
		non_animated (lxml.etree._Element): Holds the non-animated parameter in Synfig xml format
		animated_tag(str)                : Decides the tag of the animation
		anim_type    (str)                : Decides the animation type

	Returns:
		(lxml.etree._Element) : Updated non-animated parameter, which is now animated
	"""
    is_animate = is_animated(non_animated[0])
    if is_animate == settings.ANIMATED:
        # If already animated, no need to add waypoints
        # Forcibly set it's animation type to the given anim_type :needed in:->
        # properties/shapePropKeyframe.py #31
        non_animated[0].attrib["type"] = anim_type
        return non_animated
    elif is_animate == settings.NOT_ANIMATED:
        st = '<{animated_tag} name="{animated_name}"><animated type="{anim_type}"><waypoint time="0s" before="constant" after="constant"></waypoint></animated></{animated_tag}>'
        st = st.format(anim_type=anim_type,
                       animated_name=animated_name,
                       animated_tag=animated_tag)
        root = etree.fromstring(st)
        root[0][0].append(copy.deepcopy(non_animated[0]))
        non_animated = root
    elif is_animate == settings.SINGLE_WAYPOINT:
        # Forcibly set it's animation type to the given anim_type
        non_animated[0].attrib["type"] = anim_type

        non_animated[0][0].attrib["before"] = non_animated[0][0].attrib[
            "after"] = "constant"

    new_waypoint = copy.deepcopy(non_animated[0][0])
    frame = get_frame(non_animated[0][0])
    frame += 1
    time = frame / settings.lottie_format["fr"]
    time = str(time) + "s"
    new_waypoint.attrib["time"] = time
    non_animated[0].insert(1, new_waypoint)
    return non_animated
예제 #13
0
파일: helper.py 프로젝트: efkomik/synfig
def update_frame_window(node, window):
    """
    Given an animation, finds the minimum and maximum frame at which the
    waypoints are located

    Args:
        node    (lxml.etree._Element) : Animation to be searched in
        window  (dict)                : max and min frame will be stored in this

    Returns:
        (None)
    """
    if is_animated(node) == 2:
        for waypoint in node:
            fr = get_frame(waypoint)
            if fr > window["last"]:
                window["last"] = fr
            if fr < window["first"]:
                window["first"] = fr
예제 #14
0
파일: animation.py 프로젝트: synfig/synfig
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 {settings.NOT_ANIMATED, settings.SINGLE_WAYPOINT}:
        return
    for waypoint in animated:
        frame = get_frame(waypoint)
        time_list.add(frame)
예제 #15
0
파일: animation.py 프로젝트: synfig/synfig
def modify_bool_animation(anim):
    """
	Inserts waypoints at such frames so that the animation is similar to that in
	lottie
	"""
    i = 0
    while i < len(anim):
        cur_fr = get_frame(anim[i])
        val_now = get_bool_at_frame(anim, cur_fr)
        # Check at one frame less and one frame more: only cases
        if cur_fr - 1 >= settings.lottie_format[
                "ip"] and not waypoint_at_frame(anim, cur_fr - 1):
            val_before = get_bool_at_frame(anim, cur_fr - 1)
            if val_now != val_before:
                new = copy.deepcopy(anim[i])
                new.attrib["time"] = str(
                    (cur_fr - 1) / settings.lottie_format["fr"]) + "s"
                if val_before:
                    new[0].attrib["value"] = "true"
                else:
                    new[0].attrib["value"] = "false"
                anim.insert(i, new)
                i += 1
        if cur_fr + 1 <= settings.lottie_format[
                "op"] and not waypoint_at_frame(anim, cur_fr + 1):
            val_after = get_bool_at_frame(anim, cur_fr + 1)
            if val_after != val_now:
                new = copy.deepcopy(anim[i])
                new.attrib["time"] = str(
                    (cur_fr + 1) / settings.lottie_format["fr"]) + "s"
                if val_after:
                    new[0].attrib["value"] = "true"
                else:
                    new[0].attrib["value"] = "false"
                anim.insert(i + 1, new)
                i += 1

        i += 1

    # Make the animation constant
    for waypoint in anim:
        waypoint.attrib["before"] = waypoint.attrib["after"] = "constant"
예제 #16
0
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())
예제 #17
0
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]
예제 #18
0
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"]]
예제 #19
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