Beispiel #1
0
def gen_dict(lottie, offset_dict, dilation_dict, fr):
    """
    Generates the constant values for each frame

    Args:
        lottie (dict) : Bezier values will be stored in here
        offset_dict (dict) : Animation of offset in lottie format
        dilation_dict (dict) : Animation of dilation/speed in lottie format
        fr (int) : frame number

    Returns:
        (None)
    """
    lottie["i"], lottie["o"] = {}, {}
    lottie["i"]["x"], lottie["i"]["y"] = [], []
    lottie["o"]["x"], lottie["o"]["y"] = [], []
    lottie["i"]["x"].append(0.5)
    lottie["i"]["y"].append(0.5)
    lottie["o"]["x"].append(0.5)
    lottie["o"]["y"].append(0.5)
    lottie["t"] = fr

    speed_f = to_Synfig_axis(get_vector_at_frame(dilation_dict, fr), "real")
    speed_s = to_Synfig_axis(get_vector_at_frame(dilation_dict, fr + 1),
                             "real")
    first = get_vector_at_frame(
        offset_dict, fr) + (fr / settings.lottie_format["fr"]) * speed_f
    second = get_vector_at_frame(offset_dict, fr + 1) + (
        (fr + 1) / settings.lottie_format["fr"]) * speed_s
    first = min(max(get_time_bound("ip"), first), get_time_bound("op"))
    second = min(max(get_time_bound("ip"), second), get_time_bound("op"))

    lottie["s"], lottie["e"] = [first], [second]
Beispiel #2
0
def get_tangent_at_frame(t1, t2, split_r, split_a, fr):
    """
    Given a frame, returns the in-tangent and out-tangent at a bline point
    depending on whether split_radius and split_angle is "true"/"false"

    Args:
        t1      (lxml.etree._Element) : Holds Tangent 1/In Tangent
        t2      (lxml.etree._Element) : Holds Tangent 2/Out Tangent
        split_r (lxml.etree._Element) : Holds animation of split radius parameter
        split_a (lxml.etree._Element) : Holds animation of split angle parameter
        fr      (int)                 : Holds the frame value

    Returns:
        (misc.Vector, misc.Vector) : In-tangent and out-tangent at the given frame
    """

    # Get value of split_radius and split_angle at frame
    sp_r = get_bool_at_frame(split_r[0], fr)
    sp_a = get_bool_at_frame(split_a[0], fr)

    # Setting tangent 1
    for chld in t1:
        if chld.tag == "radius_path":
            dictionary = ast.literal_eval(chld.text)
            r1 = get_vector_at_frame(dictionary, fr)
        elif chld.tag == "theta_path":
            dictionary = ast.literal_eval(chld.text)
            a1 = get_vector_at_frame(dictionary, fr)
    x, y = radial_to_tangent(r1, a1)
    tangent1 = Vector(x, y)

    # Setting tangent 2
    for chld in t2:
        if chld.tag == "radius_path":
            dictionary = ast.literal_eval(chld.text)
            r2 = get_vector_at_frame(dictionary, fr)
            if not sp_r:
                # Use t1's radius
                r2 = r1
        elif chld.tag == "theta_path":
            dictionary = ast.literal_eval(chld.text)
            a2 = get_vector_at_frame(dictionary, fr)
            if not sp_a:
                # Use t1's angle
                a2 = a1
    x, y = radial_to_tangent(r2, a2)
    tangent2 = Vector(x, y)
    return tangent1, tangent2
Beispiel #3
0
def get_outline_param_at_frame(composite, fr):
    """
    Given a composite and frame, returns the parameters of the outline layer at
    that frame

    Args:
        composite (lxml.etree._Element) : Vertex of outline layer in Synfig format
        fr        (int)                 : frame number

    Returns:
        (misc.Vector) : position of the vertex
        (float)       : width of the vertex
        (misc.Vector) : Tangent 1 of the vertex
        (misc.Vector) : Tangent 2 of the vertex
        (bool)        : True if radius split is ticked at this frame
        (bool)        : True if tangent split is ticked at this frame
    """
    for child in composite:
        if child.tag == "point_path":
            dictionary = ast.literal_eval(child.text)
            pos = get_vector_at_frame(dictionary, fr)
        elif child.tag == "width_path":
            dictionary = ast.literal_eval(child.text)
            width = to_Synfig_axis(get_vector_at_frame(dictionary, fr), "real")
        elif child.tag == "t1":
            t1 = child[0]
        elif child.tag == "t2":
            t2 = child[0]
        elif child.tag == "split_radius":
            split_r = child
        elif child.tag == "split_angle":
            split_a = child

    # Convert pos back to Synfig coordinates
    pos = to_Synfig_axis(pos, "vector")
    pos_ret = Vector(pos[0], pos[1])

    t1, t2 = get_tangent_at_frame(t1, t2, split_r, split_a, fr)
    # Convert to Synfig units
    t1 /= settings.PIX_PER_UNIT
    t2 /= settings.PIX_PER_UNIT

    split_r_val = get_bool_at_frame(split_r[0], fr)
    split_a_val = get_bool_at_frame(split_a[0], fr)

    return pos_ret, width, t1, t2, split_r_val, split_a_val
Beispiel #4
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
Beispiel #5
0
    def get_single_value(self, frame):
        """
        Returns the value of some parameter which is not a convert method
        """
        if self.param[0].attrib["type"] == "bool":  # No need of lottie format path here
            return get_bool_at_frame(self.param[0], frame)

        if not self.path:   # Empty dictionary
            raise KeyError("Please calculate the path of this parameter before getting value at a frame")
        return get_vector_at_frame(self.path, frame)
Beispiel #6
0
def synfig_circle(st_val, origin_dict, radius_dict, fr):
    """
    Calculates the points for the circle layer as in Synfig:
    https://github.com/synfig/synfig/blob/678cc3a7b1208fcca18c8b54a29a20576c499927/synfig-core/src/modules/mod_geometry/circle.cpp

    Args:
        st_val (dict) : Lottie format circle is stored in this
        origin_dict (dict) : Lottie format origin of circle
        radius_dict (dict) : Lottie format radius of circle
        fr (int) : Frame number

    Returns:
        (None)
    """

    num_splines = 8
    angle = 180.0 / num_splines
    angle *= ((math.pi * 2) / 360)
    k = 1.0 / math.cos(angle)

    radius = abs(to_Synfig_axis(get_vector_at_frame(radius_dict, fr), "real"))
    origin = get_vector_at_frame(origin_dict, fr)

    matrix = Matrix2()
    matrix.set_rotate(angle)

    p0, p1, p2 = Vector(), Vector(), Vector(radius, 0)

    # Setup chunk list
    chunk_list = []
    chunk_list.append([p2, Vector(), Vector()])
    i = 0
    while i < num_splines:
        p0 = p2
        p1 = matrix.get_transformed(p0)
        p2 = matrix.get_transformed(p1)
        cp1, cp2 = quadratic_to_cubic(p2, k * p1, p0)
        cur_tan = p2 - cp1
        chunk_list[-1][2] = cp2 - p0
        chunk_list.append([p2, cur_tan, Vector()])
        i += 1

    add(chunk_list, st_val, origin)
Beispiel #7
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
Beispiel #8
0
def get_outline_grow(fr):
    """
    Gives the value of outline grow parameter at a particular frame
    """
    ret = 0
    for og in settings.OUTLINE_GROW:
        if isinstance(og, (float, int)):
            ret += og
        else:
            for chld in og:
                if chld.tag == "outline_grow_path":
                    dictionary = ast.literal_eval(chld.text)
                    val = to_Synfig_axis(get_vector_at_frame(dictionary, fr),
                                         "real")
            ret += val
    ret = math.e**ret
    return ret
Beispiel #9
0
def change_opacity_group(layer, lottie):
    """
    Will make the opacity of underlying layers 0 according to the layers lying
    inside z range(if it is active)[z-range is non-animatable]

    Args:
        layer (lxml.etree._Element) : Synfig format layer
        lottie (dict)               : Lottie format layer

    Returns:
        (None)
    """
    for chld in layer:
        if chld.tag == "param":
            if chld.attrib["name"] == "z_range":
                z_range = chld
            elif chld.attrib["name"] == "z_range_position":
                z_range_pos = chld
            elif chld.attrib["name"] == "z_range_depth":
                z_range_depth = chld
            elif chld.attrib["name"] == "canvas":
                canvas = chld

    for assets in settings.lottie_format["assets"]:
        if assets["id"] == lottie["refId"]:
            root = assets
            break

    # If z-range is non-active (static value)
    if z_range[0].attrib["value"] == "false":
        return

    pos = gen_dummy_waypoint(z_range_pos, "param", "real", "z_range_position")
    depth = gen_dummy_waypoint(z_range_depth, "param", "real", "z_range_depth")
    pos_dict, depth_dict = {}, {}
    gen_value_Keyframed(pos_dict, pos[0], 0)
    gen_value_Keyframed(depth_dict, depth[0], 0)

    z_st, z_en = float('-inf'), float('-inf')
    active_range = []  # Stores the time and change of layers in z-range
    fr = settings.lottie_format["ip"]
    while fr <= settings.lottie_format["op"]:
        pos_val = to_Synfig_axis(get_vector_at_frame(pos_dict, fr), "real")
        depth_val = to_Synfig_axis(get_vector_at_frame(depth_dict, fr), "real")
        st, en = math.ceil(pos_val), math.floor(pos_val + depth_val)
        if st > en or en < 0:
            if (fr == settings.lottie_format["ip"]) or (z_st != -1
                                                        and z_en != -1):
                z_st, z_en = -1, -1
                active_range.append([fr, z_st, z_en])
        elif (st != z_st) or (en != z_en):
            z_st, z_en = st, en
            active_range.append([fr, z_st, z_en])
        fr += 1

    z_value = 0
    for c_layer in reversed(canvas[0]):
        active_time = set()
        itr = 0
        while itr < len(active_range):
            st, en = active_range[itr][1], active_range[itr][2]
            if z_value <= en and z_value >= st:
                now = active_range[itr][0] / settings.lottie_format["fr"]
                later = get_time_bound("op")
                if itr + 1 < len(active_range):
                    later = active_range[itr +
                                         1][0] / settings.lottie_format["fr"]
                active_time.add((now, later))
            itr += 1
        active_time = sorted(active_time)
        deactive_time = sorted(flip_time(active_time))

        if c_layer.attrib["type"] in set.union(settings.SHAPE_SOLID_LAYER,
                                               settings.SOLID_LAYER):
            anim_type = "effects_opacity"
            dic = root["layers"][z_value]["ef"][0]["ef"][-1]["v"]
        elif c_layer.attrib["type"] in set.union(settings.PRE_COMP_LAYER,
                                                 settings.GROUP_LAYER,
                                                 settings.IMAGE_LAYER):
            anim_type = "opacity"
            dic = root["layers"][z_value]["ks"]["o"]
        elif c_layer.attrib["type"] in settings.SHAPE_LAYER:
            anim_type = "opacity"
            dic = root["layers"][z_value]["shapes"][1]["o"]

        animation = gen_hold_waypoints(deactive_time, c_layer, anim_type)
        gen_value_Keyframed(dic, animation[0], 0)
        z_value += 1
Beispiel #10
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())
Beispiel #11
0
def gen_dynamic_list_polygon(lottie, dynamic_list):
    """
    Generates the bline corresponding to polygon layer

    Args:
        lottie (dict) : Lottie format polygon layer will be stored here
        dynamic_list (lxml.etree._Element) : Synfig format points of polygon

    Returns:
        (None)
    """
    ################## SECTION 1 ################
    # Inserting the waypoints if not animated, finding the first and last frame
    # Calculating the path after this
    window = {}
    window["first"] = sys.maxsize
    window["last"] = -1
    count = 0

    for entry in dynamic_list:
        pos = entry
        update_frame_window(pos[0], window)

        new_pos = gen_dummy_waypoint(pos, "entry", "vector")
        pos.getparent().remove(pos)
        dynamic_list.insert(count, new_pos)

        append_path(new_pos[0], dynamic_list[count], "pos_path", "vector")

        count += 1

    layer = dynamic_list.getparent().getparent()
    for chld in layer:
        if chld.tag == "param":
            if chld.attrib["name"] == "origin":
                origin = chld

    # Animating the origin
    update_frame_window(origin[0], window)
    origin_parent = origin.getparent()
    origin = gen_dummy_waypoint(origin, "param", "vector", "origin")
    update_child_at_parent(origin_parent, origin, "param", "origin")

    # Generate path for the origin component
    origin_dict = {}
    origin[0].attrib["transform_axis"] = "true"
    gen_properties_multi_dimensional_keyframed(origin_dict, origin[0], 0)

    if window["first"] == sys.maxsize and window["last"] == -1:
        window["first"] = window["last"] = 0
    ################ END OF SECTION 1 ##############

    ################ SECTION 2 #####################
    # Generating values for all the frames in the window
    fr = window["first"]
    while fr <= window["last"]:
        st_val, en_val = insert_dict_at(lottie, -1, fr, False)

        for entry in dynamic_list:
            # Only two childs, one should be animated, other one is path
            for child in entry:
                if child.tag == "pos_path":
                    dictionary = ast.literal_eval(child.text)
                    pos_cur = get_vector_at_frame(dictionary, fr)
                    pos_next = get_vector_at_frame(dictionary, fr + 1)

            tangent1_cur, tangent2_cur = Vector(0, 0), Vector(0, 0)
            tangent1_next, tangent2_next = Vector(0, 0), Vector(0, 0)

            # Adding origin to each vertex
            origin_cur = get_vector_at_frame(origin_dict, fr)
            origin_next = get_vector_at_frame(origin_dict, fr + 1)
            for i in range(len(pos_cur)):
                pos_cur[i] += origin_cur[i]
            for i in range(len(pos_next)):
                pos_next[i] += origin_next[i]

            # Store values in dictionary
            st_val["i"].append(tangent1_cur.get_list())
            st_val["o"].append(tangent2_cur.get_list())
            st_val["v"].append(pos_cur)
            en_val["i"].append(tangent1_next.get_list())
            en_val["o"].append(tangent2_next.get_list())
            en_val["v"].append(pos_next)
        fr += 1
    # Setting the final time
    lottie.append({})
    lottie[-1]["t"] = fr
Beispiel #12
0
def synfig_outline(bline_point, st_val, origin_dict, outer_width_dict,
                   sharp_cusps_anim, expand_dict, r_tip0_anim, r_tip1_anim,
                   homo_width_anim, fr):
    """
    Calculates the points for the outline layer as in Synfig:
    https://github.com/synfig/synfig/blob/678cc3a7b1208fcca18c8b54a29a20576c499927/synfig-core/src/modules/mod_geometry/outline.cpp

    Args:
        bline_point (lxml.etree._Element) : Synfig format bline points of outline layer
        st_val (dict) : Lottie format outline stored in this
        origin_dict (dict) : Lottie format origin of outline layer
        outer_width_dict (dict) : Lottie format outer width
        sharp_cusps_anim (lxml.etree._Element) : sharp cusps in Synfig format
        expand_dict (dict) : Lottie format expand parameter
        r_tip0_anim (lxml.etree._Element) : Round tip[0] in Synfig format
        r_tip1_anim (lxml.etree._Element) : Round tip[1] in Synfig format
        homo_width_anim (lxml.etree._Element) : Homogeneous width in Synfig format
        fr (int) : Frame number

    Returns:
        (None)
    """

    EPSILON = 0.000000001
    SAMPLES = 50
    CUSP_TANGENT_ADJUST = 0.025
    CUSP_THRESHOLD = 0.40
    SPIKE_AMOUNT = 4
    ROUND_END_FACTOR = 4

    outer_width = to_Synfig_axis(get_vector_at_frame(outer_width_dict, fr),
                                 "real")
    expand = to_Synfig_axis(get_vector_at_frame(expand_dict, fr), "real")
    sharp_cusps = get_bool_at_frame(sharp_cusps_anim[0], fr)
    r_tip0 = get_bool_at_frame(r_tip0_anim[0], fr)
    r_tip1 = get_bool_at_frame(r_tip1_anim[0], fr)
    homo_width = get_bool_at_frame(homo_width_anim[0], fr)

    gv = get_outline_grow(fr)

    # Setup chunk list
    side_a, side_b = [], []

    # Check if looped
    loop = False
    if "loop" in bline_point.keys() and bline_point.attrib["loop"] == "true":
        loop = True

    # Iterators
    end_it = len(bline_point)
    next_it = 0
    if loop:
        iter_it = end_it - 1
    else:
        iter_it = next_it
        next_it += 1

    first_point = bline_point[iter_it][0]
    first_tangent = get_outline_param_at_frame(bline_point[0][0], fr)[3]
    last_tangent = get_outline_param_at_frame(first_point, fr)[2]

    # If we are looped and drawing sharp cusps, we 'll need a value
    # for the incoming tangent. This code fails if we have a "degraded" spline
    # with just one vertex, so we avoid such case.
    if loop and sharp_cusps and last_tangent.is_equal_to(Vector(
            0, 0)) and len(bline_point) > 1:
        prev_it = iter_it
        prev_it -= 1
        prev_it %= len(bline_point)
        prev_point = bline_point[prev_it][0]
        curve = Hermite(
            get_outline_param_at_frame(prev_point, fr)[0],
            get_outline_param_at_frame(first_point, fr)[0],
            get_outline_param_at_frame(prev_point, fr)[3],
            get_outline_param_at_frame(first_point, fr)[2])
        last_tangent = curve.derivative(1.0 - CUSP_TANGENT_ADJUST)

    first = not loop
    while next_it != end_it:
        bp1 = bline_point[iter_it][0]
        bp2 = bline_point[next_it][0]

        # Calculate current vertex and next vertex parameters
        pos_c, width_c, t1_c, t2_c, split_r_c, split_a_c = get_outline_param_at_frame(
            bp1, fr)
        pos_n, width_n, t1_n, t2_n, split_r_n, split_a_n = get_outline_param_at_frame(
            bp2, fr)

        # Setup tangents
        prev_t = t1_c
        iter_t = t2_c
        next_t = t1_n

        split_flag = split_a_c or split_r_c

        # If iter_it.t2 == 0 and next.t1 == 0, this is a straight line
        if iter_t.is_equal_to(Vector(0, 0)) and next_t.is_equal_to(Vector(
                0, 0)):
            iter_t = next_t = pos_n - pos_c

            # If the 2 points are on top of each other, ignore this segment
            # leave 'first' true if was before
            if iter_t.is_equal_to(Vector(0, 0)):
                iter_it = next_it
                iter_it %= len(bline_point)
                next_it += 1
                continue

        # Setup the curve
        curve = Hermite(pos_c, pos_n, iter_t, next_t)

        # Setup width's
        iter_w = gv * (width_c * outer_width * 0.5 + expand)
        next_w = gv * (width_n * outer_width * 0.5 + expand)

        if first:
            first_tangent = curve.derivative(CUSP_TANGENT_ADJUST)

        # Make cusps as necassary
        if not first and \
           sharp_cusps and \
           split_flag and \
           ((not prev_t.is_equal_to(iter_t)) or (iter_t.is_equal_to(Vector(0, 0)))) and \
           (not last_tangent.is_equal_to(Vector(0, 0))):

            curr_tangent = curve.derivative(CUSP_TANGENT_ADJUST)
            t1 = last_tangent.perp().norm()
            t2 = curr_tangent.perp().norm()

            cross = t1 * t2.perp()
            perp = (t1 - t2).mag()
            if cross > CUSP_THRESHOLD:
                p1 = pos_c + t1 * iter_w
                p2 = pos_c + t2 * iter_w
                side_a.append([
                    line_intersection(p1, last_tangent, p2, curr_tangent),
                    Vector(0, 0),
                    Vector(0, 0)
                ])
            elif cross < -CUSP_THRESHOLD:
                p1 = pos_c - t1 * iter_w
                p2 = pos_c - t2 * iter_w
                side_b.append([
                    line_intersection(p1, last_tangent, p2, curr_tangent),
                    Vector(0, 0),
                    Vector(0, 0)
                ])
            elif cross > 0.0 and perp > 1.0:
                amount = max(
                    0.0, cross / CUSP_THRESHOLD) * (SPIKE_AMOUNT - 1.0) + 1.0
                side_a.append([
                    pos_c + (t1 + t2).norm() * iter_w * amount,
                    Vector(0, 0),
                    Vector(0, 0)
                ])
            elif cross < 0 and perp > 1:
                amount = max(
                    0.0, -cross / CUSP_THRESHOLD) * (SPIKE_AMOUNT - 1.0) + 1.0
                side_b.append([
                    pos_c - (t1 + t2).norm() * iter_w * amount,
                    Vector(0, 0),
                    Vector(0, 0)
                ])

        # Precalculate positions and coefficients
        length = 0.0
        points = []
        dists = []
        n = 0.0
        itr = 0
        while n < 1.000001:
            points.append(curve.value(n))
            if n:
                length += (points[itr] - points[itr - 1]).mag()
            dists.append(length)

            n += 1.0 / SAMPLES
            itr += 1
        length += (curve.value(1) - points[itr - 1]).mag()

        div_length = 1
        if length > EPSILON:
            div_length = 1.0 / length

        # Might not need /3 for the tangents genereated finally - VERY IMPORTANT
        # Make the outline
        pt = curve.derivative(CUSP_TANGENT_ADJUST) / 3
        n = 0.0
        itr = 0
        while n < 1.000001:
            t = curve.derivative(
                min(max(n, CUSP_TANGENT_ADJUST),
                    1.0 - CUSP_TANGENT_ADJUST)) / 3
            d = t.perp().norm()
            k = dists[itr] * div_length
            if not homo_width:
                k = n
            w = (next_w - iter_w) * k + iter_w
            if False and n:
                # create curve
                a = points[itr - 1] + d * w
                b = points[itr] + d * w
                tk = (b - a).mag() * div_length
                side_a.append([b, pt * tk, -t * tk])

                a = points[itr - 1] - d * w
                b = points[itr] - d * w
                tk = (b - a).mag() * div_length
                side_b.append([b, pt * tk, -t * tk])
            else:
                side_a.append(
                    [points[itr] + d * w,
                     Vector(0, 0),
                     Vector(0, 0)])
                side_b.append(
                    [points[itr] - d * w,
                     Vector(0, 0),
                     Vector(0, 0)])
            pt = t
            itr += 1
            n += 1.0 / SAMPLES

        last_tangent = curve.derivative(1.0 - CUSP_TANGENT_ADJUST)
        side_a.append([
            curve.value(1.0) + last_tangent.perp().norm() * next_w,
            Vector(0, 0),
            Vector(0, 0)
        ])
        side_b.append([
            curve.value(1.0) - last_tangent.perp().norm() * next_w,
            Vector(0, 0),
            Vector(0, 0)
        ])
        first = False

        iter_it = next_it
        iter_it %= len(bline_point)
        next_it += 1

    if len(side_a) < 2 or len(side_b) < 2:
        return

    origin_cur = get_vector_at_frame(origin_dict, fr)
    move_to(side_a[0][0], st_val, origin_cur)

    if loop:
        add(side_a, st_val, origin_cur)
        add_reverse(side_b, st_val, origin_cur)
    else:
        # Insert code for adding end tip
        if r_tip1:
            bp = bline_point[-1][0]
            vertex = get_outline_param_at_frame(bp, fr)[0]
            tangent = last_tangent.norm()
            w = gv * (get_outline_param_at_frame(bp, fr)[1] * outer_width * 0.5
                      + expand)

            a = vertex + tangent.perp() * w
            b = vertex - tangent.perp() * w
            p1 = a + tangent * w * (ROUND_END_FACTOR / 3.0)
            p2 = b + tangent * w * (ROUND_END_FACTOR / 3.0)
            tan = tangent * w * (ROUND_END_FACTOR / 3.0)

            # replace the last point
            side_a[-1] = [a, Vector(0, 0), tan]
            add(side_a, st_val, origin_cur)
            add([[b, -tan, Vector(0, 0)]], st_val, origin_cur)
        else:
            add(side_a, st_val, origin_cur)

        # Insert code for adding beginning tip
        if r_tip0:
            bp = bline_point[0][0]
            vertex = get_outline_param_at_frame(bp, fr)[0]
            tangent = first_tangent.norm()
            w = gv * (get_outline_param_at_frame(bp, fr)[1] * outer_width * 0.5
                      + expand)

            a = vertex - tangent.perp() * w
            b = vertex + tangent.perp() * w
            p1 = a - tangent * w * (ROUND_END_FACTOR / 3.0)
            p2 = b - tangent * w * (ROUND_END_FACTOR / 3.0)
            tan = -tangent * w * (ROUND_END_FACTOR / 3.0)

            # replace the first point
            side_b[0] = [a, Vector(0, 0), tan]
            add_reverse(side_b, st_val, origin_cur)
            add([[b, -tan, Vector(0, 0)]], st_val, origin_cur)
        else:
            add_reverse(side_b, st_val, origin_cur)
Beispiel #13
0
def gen_bline_region(lottie, bline_point):
    """
    Generates the dictionary corresponding to properties/shapePropKeyframe.json,
    given a bline/spline

    Args:
        lottie     (dict) : Lottie generated keyframes will be stored here for shape/path
        bline_path (lxml.etree._Element) : shape/path store in Synfig format

    Returns:
        (None)
    """
    ################### SECTION 1 #########################
    # Inserting waypoints if not animated and finding the first and last frame
    # AFter that, there path will be calculated in lottie format which can
    # latter be used in get_vector_at_frame() function
    window = {}
    window["first"] = sys.maxsize
    window["last"] = -1

    loop = False
    if "loop" in bline_point.keys():
        val = bline_point.attrib["loop"]
        if val == "false":
            loop = False
        else:
            loop = True

    for entry in bline_point:
        composite = entry[0]
        for child in composite:
            if child.tag == "point":
                pos = child
            elif child.tag == "t1":
                t1 = child
            elif child.tag == "t2":
                t2 = child
            elif child.tag == "split_radius":
                split_r = child
            elif child.tag == "split_angle":
                split_a = child

        # Necassary to update this before inserting new waypoints, as new
        # waypoints might include there on time: 0 seconds
        update_frame_window(pos[0], window)

        # Empty the pos and fill in the new animated pos
        pos = gen_dummy_waypoint(pos, "point", "vector")
        update_child_at_parent(composite, pos, "point")

        update_frame_window(split_r[0], window)
        split_r = gen_dummy_waypoint(split_r, "split_radius", "bool")
        update_child_at_parent(composite, split_r, "split_radius")

        update_frame_window(split_a[0], window)
        split_a = gen_dummy_waypoint(split_a, "split_angle", "bool")
        update_child_at_parent(composite, split_a, "split_angle")

        append_path(pos[0], composite, "point_path", "vector")

        animate_radial_composite(t1[0], window)
        animate_radial_composite(t2[0], window)

    layer = bline_point.getparent().getparent()
    for chld in layer:
        if chld.tag == "param" and chld.attrib["name"] == "origin":
            origin = chld

    # Animating the origin
    update_frame_window(origin[0], window)
    origin_parent = origin.getparent()
    origin = gen_dummy_waypoint(origin, "param", "vector", "origin")
    update_child_at_parent(origin_parent, origin, "param", "origin")

    # Generate path for the origin component
    origin_dict = {}
    origin[0].attrib["transform_axis"] = "true"
    gen_properties_multi_dimensional_keyframed(origin_dict, origin[0], 0)

    # Minimizing the window size
    if window["first"] == sys.maxsize and window["last"] == -1:
        window["first"] = window["last"] = 0
    ################# END OF SECTION 1 ###################

    ################ SECTION 2 ###########################
    # Generating values for all the frames in the window
    fr = window["first"]
    while fr <= window["last"]:
        st_val, en_val = insert_dict_at(lottie, -1, fr, loop)

        for entry in bline_point:
            composite = entry[0]
            for child in composite:
                if child.tag == "point_path":
                    dictionary = ast.literal_eval(child.text)
                    pos_cur = get_vector_at_frame(dictionary, fr)
                    pos_next = get_vector_at_frame(dictionary, fr + 1)
                elif child.tag == "t1":
                    t1 = child[0]
                elif child.tag == "t2":
                    t2 = child[0]
                elif child.tag == "split_radius":
                    split_r = child
                elif child.tag == "split_angle":
                    split_a = child

            tangent1_cur, tangent2_cur = get_tangent_at_frame(
                t1, t2, split_r, split_a, fr)
            tangent1_next, tangent2_next = get_tangent_at_frame(
                t1, t2, split_r, split_a, fr)

            tangent1_cur, tangent2_cur = convert_tangent_to_lottie(
                tangent1_cur, tangent2_cur)
            tangent1_next, tangent2_next = convert_tangent_to_lottie(
                tangent1_next, tangent2_next)

            # Adding origin to each vertex
            origin_cur = get_vector_at_frame(origin_dict, fr)
            origin_next = get_vector_at_frame(origin_dict, fr + 1)
            for i in range(len(pos_cur)):
                pos_cur[i] += origin_cur[i]
            for i in range(len(pos_next)):
                pos_next[i] += origin_next[i]

            # Store values in dictionary
            st_val["i"].append(tangent1_cur.get_list())
            st_val["o"].append(tangent2_cur.get_list())
            st_val["v"].append(pos_cur)
            en_val["i"].append(tangent1_next.get_list())
            en_val["o"].append(tangent2_next.get_list())
            en_val["v"].append(pos_next)
        fr += 1
    # Setting final time
    lottie.append({})
    lottie[-1]["t"] = fr
Beispiel #14
0
def synfig_rectangle(st_val, p1_dict, p2_dict, expand_dict, bevel_dict,
                     bevCircle, fr):
    """
    Calculates the points for the rectangle layer as in Synfig:
    https://github.com/synfig/synfig/blob/678cc3a7b1208fcca18c8b54a29a20576c499927/synfig-core/src/modules/mod_geometry/rectangle.cpp

    Args:
        st_val (dict) : Lottie format rectangle will be stored in this
        p1_dict (dict) : Lottie format point1 animation
        p2_dict (dict) : Lottie format point2 animation
        expand_dict (dict) : Lottie format expand parameter animation
        bevel_dict (dict) : Lottie format bevel parameter animation
        bevCircle (lxml.etree._Element) : Animation of bevCircle in Synfig format
        fr (int) : Frame Number

    Returns:
        (None)
    """

    expand = abs(to_Synfig_axis(get_vector_at_frame(expand_dict, fr), "real"))
    bevel = abs(to_Synfig_axis(get_vector_at_frame(bevel_dict, fr), "real"))
    p0 = to_Synfig_axis(get_vector_at_frame(p1_dict, fr), "vector")
    p0 = Vector(p0[0], p0[1])
    p1 = to_Synfig_axis(get_vector_at_frame(p2_dict, fr), "vector")
    p1 = Vector(p1[0], p1[1])
    if p1[0] < p0[0]:
        p0[0], p1[0] = p1[0], p0[0]
    if p1[1] < p0[1]:
        p0[1], p1[1] = p1[1], p0[1]

    bev_circle = get_bool_at_frame(bevCircle[0], fr)

    w = p1[0] - p0[0] + 2 * expand
    h = p1[1] - p0[1] + 2 * expand
    bev = bevel
    if bevel > 1:
        bev = 1
    if bev_circle:
        bevx = min(w * bev / 2.0, h * bev / 2.0)
        bevy = min(w * bev / 2.0, h * bev / 2.0)
    else:
        bevx = w * bev / 2.0
        bevy = h * bev / 2.0

    # Setup chunk list
    chunk_list = []

    if approximate_equal(bevel, 0.0):
        chunk_list.append(
            [Vector(p0[0] - expand, p0[1] - expand),
             Vector(),
             Vector()])
        chunk_list.append(
            [Vector(p1[0] + expand, p0[1] - expand),
             Vector(),
             Vector()])
        chunk_list.append(
            [Vector(p1[0] + expand, p1[1] + expand),
             Vector(),
             Vector()])
        chunk_list.append(
            [Vector(p0[0] - expand, p1[1] + expand),
             Vector(),
             Vector()])
    else:
        cur = Vector(p1[0] + expand - bevx, p0[1] - expand)
        chunk_list.append([cur, Vector(), Vector()])
        prev = cur

        cur = Vector(p1[0] + expand, p0[1] - expand + bevy)
        cp1, cp2 = quadratic_to_cubic(cur,
                                      Vector(p1[0] + expand, p0[1] - expand),
                                      prev)
        chunk_list[-1][2] = cp2 - prev
        chunk_list.append([cur, cur - cp1, Vector()])
        prev = cur

        cur = Vector(p1[0] + expand, p1[1] + expand - bevy)
        chunk_list.append([cur, Vector(), Vector()])
        prev = cur

        cur = Vector(p1[0] + expand - bevx, p1[1] + expand)
        cp1, cp2 = quadratic_to_cubic(cur,
                                      Vector(p1[0] + expand, p1[1] + expand),
                                      prev)
        chunk_list[-1][2] = cp2 - prev
        chunk_list.append([cur, cur - cp1, Vector()])
        prev = cur

        cur = Vector(p0[0] - expand + bevx, p1[1] + expand)
        chunk_list.append([cur, Vector(), Vector()])
        prev = cur

        cur = Vector(p0[0] - expand, p1[1] + expand - bevy)
        cp1, cp2 = quadratic_to_cubic(cur,
                                      Vector(p0[0] - expand, p1[1] + expand),
                                      prev)
        chunk_list[-1][2] = cp2 - prev
        chunk_list.append([cur, cur - cp1, Vector()])
        prev = cur

        cur = Vector(p0[0] - expand, p0[1] - expand + bevy)
        chunk_list.append([cur, Vector(), Vector()])
        prev = cur

        cur = Vector(p0[0] - expand + bevx, p0[1] - expand)
        cp1, cp2 = quadratic_to_cubic(cur,
                                      Vector(p0[0] - expand, p0[1] - expand),
                                      prev)
        chunk_list[-1][2] = cp2 - prev
        chunk_list.append([cur, cur - cp1, Vector()])
        prev = cur

    add(chunk_list, st_val, [0, 0])
Beispiel #15
0
def synfig_star(st_val, mx_points, origin_dict, radius1_dict, radius2_dict,
                angle_dict, points_dict, regular_polygon_anim, fr):
    """
    Calculates the points for the rectangle layer as in Synfig:
    https://github.com/synfig/synfig/blob/678cc3a7b1208fcca18c8b54a29a20576c499927/synfig-core/src/modules/mod_geometry/star.cpp

    Args:
        st_val (dict) : Lottie format star will be stored here
        mx_points (int) : Maximum points ever in star animation
        radius1_dict (dict) : Lottie format radius1 animation
        radius2_dict (dict) : Lottie format radius2 animation
        angle_dict   (dict) : Lottie format angle animation
        points_dict  (dict) : Lottie format points animation
        regular_polygon_anim (lxml.etree._Element) : Synfig format regularPolygon animation
        fr (int) : Frame number

    Returns:
        (None)
    """

    angle = get_vector_at_frame(angle_dict, fr)
    points = int(to_Synfig_axis(get_vector_at_frame(points_dict, fr), "real"))
    radius1 = to_Synfig_axis(get_vector_at_frame(radius1_dict, fr), "real")
    radius2 = to_Synfig_axis(get_vector_at_frame(radius2_dict, fr), "real")
    regular_polygon = get_bool_at_frame(regular_polygon_anim[0], fr)
    origin_cur = get_vector_at_frame(origin_dict, fr)

    angle = math.radians(angle)
    dist_between_points = (math.pi * 2) / float(points)
    vector_list = []

    ####
    tot_points = 2 * mx_points

    i = 0
    while i < points:
        dist1 = dist_between_points * i + angle
        dist2 = dist_between_points * i + dist_between_points / 2 + angle
        vector_list.append(
            Vector(math.cos(dist1) * radius1,
                   math.sin(dist1) * radius1))
        if not regular_polygon:
            vector_list.append(
                Vector(math.cos(dist2) * radius2,
                       math.sin(dist2) * radius2))
        else:
            # This condition is needed because in lottie a shape must have equal
            # number of vertices at each frame
            vector_list.append(
                Vector(math.cos(dist1) * radius1,
                       math.sin(dist1) * radius1))

        tot_points -= 2
        i += 1

    if len(vector_list) < 3:
        # Should throw error
        return

    while tot_points > 0:
        vector_list.append(vector_list[-1])
        tot_points -= 1

    # Setup chunk list
    chunk_list = []
    chunk_list.append([vector_list[0], Vector(), Vector()])
    i = 1
    while i < len(vector_list):
        if math.isnan(vector_list[i][0]) or math.isnan(vector_list[i][1]):
            break
        chunk_list.append([vector_list[i], Vector(), Vector()])
        i += 1

    add(chunk_list, st_val, origin_cur)