Exemple #1
0
    def get_list_at_frame_2(self, fr):
        """
        Returns list of WidthPoint's at a particular frame
        """
        wplist = []
        for entry in self.get_entry_list():
            pos = entry["position"].get_value(fr)
            pos = to_Synfig_axis(pos, "real")
            width = entry["width"].get_value(fr)
            width = to_Synfig_axis(width, "real")

            side_before = entry["side_before"].get()
            side_before = int(side_before[0].attrib["value"])

            side_after = entry["side_after"].get()
            side_after = int(side_after[0].attrib["value"])

            lower_bound = entry["lower_bound"].get()
            lower_bound = float(lower_bound[0].attrib["value"])

            upper_bound = entry["upper_bound"].get()
            upper_bound = float(upper_bound[0].attrib["value"])

            wplist.append(
                common.WidthPoint.WidthPoint(pos, width, side_before,
                                             side_after, False, lower_bound,
                                             upper_bound))

        return wplist
Exemple #2
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]
Exemple #3
0
    def get_list_at_frame(self, fr):
        """
        Returns list of Dashitems at a particular frame
        Refer:  https://github.com/synfig/synfig/blob/15607089680af560ad031465d31878425af927eb/synfig-core/src/synfig/valuenodes/valuenode_dilist.cpp#L129
        """
        dilist = []
        rising = [False]

        for entry in self.get_entry_list():
            amount = entry["ActivepointList"].amount_at_time(fr, rising)
            assert (amount >= 0)
            assert (amount <= 1)

            offset = to_Synfig_axis(entry["offset"].get_value(fr), "real")
            length = to_Synfig_axis(entry["length"].get_value(fr), "real")
            side_before = entry["side_before"].get()
            side_before = int(side_before[0].attrib["value"])
            side_after = entry["side_after"].get()
            side_after = int(side_after[0].attrib["value"])
            curr = DashItem(offset, length, side_before, side_after)

            if amount > 1 - 0.0000001:  # lgtm [py/redundant-comparison]
                dilist.append(curr)

        return dilist
Exemple #4
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
Exemple #5
0
 def get_value_at_frame(self, entry, fr):
     """
     """
     pos = to_Synfig_axis(entry["point"].get_value(fr), "vector")
     pos = common.Vector.Vector(pos[0], pos[1])
     width = to_Synfig_axis(entry["width"].get_value(fr), "real")
     origin = to_Synfig_axis(entry["origin"].get_value(fr), "real")
     t1 = entry["t1"]
     t2 = entry["t2"]
     split_r = entry["split_radius"]
     split_a = entry["split_angle"]
     t1, t2 = get_tangent_at_frame(t1, t2, fr)
     # convert to synfig units
     t1 /= settings.PIX_PER_UNIT
     t2 /= settings.PIX_PER_UNIT
     split_r_val = split_r.get_value(fr)
     split_a_val = split_a.get_value(fr)
     return BlinePoint(pos, width, split_r_val, split_a_val, t1, t2, origin)
Exemple #6
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
Exemple #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
Exemple #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:   # should be dict
            val = to_Synfig_axis(og.get_value(fr), "real")
            ret += val
    ret = math.e ** ret
    return ret
Exemple #9
0
def get_outline_param_at_frame(entry, fr):
    """
    Given a entry and frame, returns the parameters of the outline layer at
    that frame

    Args:
        entry (dict) : Vertex of outline layer in Synfig format
        fr        (int)                 : frame number

    Returns:
        (common.Vector.Vector) : position of the vertex
        (float)       : width of the vertex
        (common.Vector.Vector) : Tangent 1 of the vertex
        (common.Vector.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
    """
    pos = entry["point"].get_value(fr)
    # Convert pos back to Synfig coordinates
    pos = to_Synfig_axis(pos, "vector")
    pos_ret = Vector(pos[0], pos[1])

    width = entry["width"].get_value(fr)
    width = to_Synfig_axis(width, "real")
    t1 = entry["t1"]
    t2 = entry["t2"]
    split_r = entry["split_radius"]
    split_a = entry["split_angle"]


    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 = split_r.get_value(fr)
    split_a_val = split_a.get_value(fr)

    return pos_ret, width, t1, t2, split_r_val, split_a_val
Exemple #10
0
    def get_value_at_frame(self, entry, fr):
        pos = entry["position"].get_value(fr)
        pos = to_Synfig_axis(pos, "real")
        width = entry["width"].get_value(fr)
        width = to_Synfig_axis(width, "real")

        side_before = entry["side_before"].get()
        side_before = int(side_before[0].attrib["value"])

        side_after = entry["side_after"].get()
        side_after = int(side_after[0].attrib["value"])

        lower_bound = entry["lower_bound"].get()
        lower_bound = float(lower_bound[0].attrib["value"])

        upper_bound = entry["upper_bound"].get()
        upper_bound = float(upper_bound[0].attrib["value"])

        curr = common.WidthPoint.WidthPoint(pos, width, side_before,
                                            side_after, False, lower_bound,
                                            upper_bound)
        return curr
Exemple #11
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
Exemple #12
0
def synfig_circle(st_val, origin_param, radius_param, 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 (common.Param.Param) : Lottie format origin of circle
        radius (common.Param.Param) : 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(radius_param.get_value(fr), "real"))
    origin = origin_param.get_value(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)
Exemple #13
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
Exemple #14
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())
Exemple #15
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 (common.Layer.Layer) : Synfig format layer
        lottie (dict)      : Lottie format layer

    Returns:
        (None)
    """
    z_range = layer.get_param("z_range")
    z_range_pos = layer.get_param("z_range_position")
    z_range_depth = layer.get_param("z_range_depth")
    canvas = Canvas(layer.get_param("canvas"))

    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

    z_range_pos.animate("real")

    z_range_depth.animate("real")

    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(z_range_pos.get_value(fr), "real")
        depth_val = to_Synfig_axis(z_range_depth.get_value(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.get_layer_list()):
        if not c_layer.is_active() or not c_layer.to_render():
            continue
        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)
        inactive_time = sorted(flip_time(active_time))

        if c_layer.get_type() in set.union(settings.SHAPE_SOLID_LAYER,
                                           settings.SOLID_LAYER):
            anim_type = "effects_opacity"
            sw = 1
        elif c_layer.get_type() in set.union(settings.PRE_COMP_LAYER,
                                             settings.GROUP_LAYER,
                                             settings.IMAGE_LAYER):
            anim_type = "opacity"
            sw = 2
        elif c_layer.get_type() in settings.SHAPE_LAYER:
            anim_type = "opacity"
            sw = 3
            dic = root["layers"][z_value]["shapes"][1]["o"]

        animation = gen_hold_waypoints(inactive_time, c_layer, anim_type)
        animation.animate(anim_type)

        if sw == 1:
            animation.fill_path(root["layers"][z_value]["ef"][0]["ef"][-1],
                                "v")
            # See effects/fill.py: Opacity is the last property, and hence we are using [-1].
            # We should actually search for "opacity", but due to multiple elements with same
            # "ty"(which should not happen), we are using [-1]. "ty" here means type which uniquely
            # identifies the effect in Lottie, but 'horizontal feather', 'vertical feather' and
            # 'opacity' in Lottie have the same type and hence we can not search for "opacity"
            # uniquely
        elif sw == 2:
            animation.fill_path(root["layers"][z_value]["ks"], "o")
        elif sw == 3:
            animation.fill_path(root["layers"][z_value]["shapes"][1], "o")

        z_value += 1
Exemple #16
0
def synfig_rectangle(st_val, point1_p, point2_p, expand_p, bevel_p, 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
        point1_p (self.Param.Param) : Lottie format point1 animation
        point2_p (self.Param.Param) : Lottie format point2 animation
        expand_p (self.Param.Param) : Lottie format expand parameter animation
        bevel_p (self.Param.Param) : 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(expand_p.get_value(fr), "real"))
    bevel = abs(to_Synfig_axis(bevel_p.get_value(fr), "real"))
    p0 = to_Synfig_axis(point1_p.get_value(fr), "vector")
    p0 = Vector(p0[0], p0[1])
    p1 = to_Synfig_axis(point2_p.get_value(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 = bevCircle.get_value(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], True)
Exemple #17
0
    def __get_value(self, frame):
        """
        Returns the value of the parameter at a given frame
        """
        if self.param.tag in settings.BONES or self.param[0].tag in settings.CONVERT_METHODS:
            if self.param.tag == "bone":
                cur_origin = self.subparams["origin"].__get_value(frame)

                # Now adding the parent's effects in this bone
                guid = self.subparams["parent"][0].attrib["guid"]
                canvas = self.get_canvas()
                bone = canvas.get_bone(guid)
                shifted_origin, shifted_angle, lls, rls = bone.__get_value(frame)
                a1, a2 = math.radians(shifted_angle), math.radians(shifted_angle+90)

                # Calculating this bones angle with respect to parent bone's
                # angle
                if "angle" in self.subparams.keys():
                    angle = to_Synfig_axis(self.subparams["angle"].__get_value(frame), "angle")
                else:
                    angle = 0

                # Calculating the local length scale
                local_length_scale = self.subparams["scalelx"].__get_value(frame)

                # Calculating the recursive length scale
                this_rls = self.subparams["scalex"].__get_value(frame)    # In current angle's direction
                absolute_angle = shifted_angle+angle
                aa1 = math.radians(absolute_angle)
                this_rls = [this_rls * math.cos(aa1), this_rls * math.sin(aa1)]

                # Calculate returning recursive length
                ret_rls = [this_rls[0]*rls[0], this_rls[1]*rls[1]]
                ##### REMOVE AFTER DEBUGGING
                ret_rls = [1, 1]

                # Multiplying the current bone origin with the scale
                cur_origin = [i*lls for i in cur_origin]

                ret = shifted_origin
                # Adding effect of x component
                ret[0] = ret[0] + (cur_origin[0] * math.cos(a1) + cur_origin[1] * math.cos(a2)) * rls[0]
                ret[1] = ret[1] + (cur_origin[0] * math.sin(a1) + cur_origin[1] * math.sin(a2)) * rls[1]

                return ret, absolute_angle, local_length_scale, ret_rls

            elif self.param.tag == "bone_root":
                origin = [0, 0]
                angle = 0
                local_length_scale = 1
                recursive_length_scale = [1, 1] # x and y axis
                return origin, angle, local_length_scale, recursive_length_scale

            elif self.param[0].tag == "add":
                ret = self.subparams["add"].subparams["lhs"].__get_value(frame)
                ret2 = self.subparams["add"].subparams["rhs"].__get_value(frame)
                mul = self.subparams["add"].subparams["scalar"].__get_value(frame)
                if isinstance(ret, list):
                    ret[0] += ret2[0]
                    ret[1] += ret2[1]
                    ret = [it*mul for it in ret]
                else:
                    ret += ret2
                    ret *= mul

            elif self.param[0].tag == "exp":
                exp = self.subparams["exp"].subparams["exp"].__get_value(frame)
                scale = self.subparams["exp"].subparams["scale"].__get_value(frame)
                ret = scale * math.exp(exp)

            elif self.param[0].tag == "average":
                lst = self.subparams["average"].subparams["entry"]
                if not isinstance(lst, list):
                    lst = [lst]

                ret = [0, 0]
                if not isinstance(lst[0].__get_value(frame), list):
                    ret = 0
                for it in lst:
                    val = it.__get_value(frame)
                    if isinstance(val, list):
                        ret[0], ret[1] = ret[0] + val[0], ret[1] + val[1]
                    else:
                        ret += val
                if isinstance(ret, list):
                    ret[0], ret[1] = ret[0] / len(lst), ret[1] / len(lst)
                else:
                    ret /= float(len(lst))

            elif self.param[0].tag == "weighted_average":
                self.subparams["weighted_average"].extract_subparams()
                lst = self.subparams["weighted_average"].subparams["entry"]
                if not isinstance(lst, list):   # When only one entry is present
                    lst = [lst]

                ret = [0, 0]
                den = 0
                if not isinstance(lst[0].subparams["weighted_vector"].subparams["value"].__get_value(frame), list):
                    ret = 0
                for it in lst:
                    weight = it.subparams["weighted_vector"].subparams["weight"].__get_value(frame)
                    value = it.subparams["weighted_vector"].subparams["value"].__get_value(frame)
                    den += weight
                    if isinstance(value, list):
                        ret[0], ret[1] = ret[0] + value[0]*weight, ret[1] + value[1]*weight
                    else:
                        ret += value*weight
                if isinstance(ret, list):
                    ret[0], ret[1] = ret[0] / den, ret[1] / den
                else:
                    ret /= float(den)

            elif self.param[0].tag == "composite":  # Only available for vectors
                x = self.subparams["composite"].subparams["x"].__get_value(frame)
                y = self.subparams["composite"].subparams["y"].__get_value(frame)
                ret = [x, y]

            elif self.param[0].tag == "linear":
                slope = self.subparams["linear"].subparams["slope"].__get_value(frame)
                offset = self.subparams["linear"].subparams["offset"].__get_value(frame)
                if isinstance(slope, list):
                    ret = [0, 0]
                    ret[0] = offset[0] + slope[0]*(frame/settings.lottie_format["fr"])
                    ret[1] = offset[1] + slope[1]*(frame/settings.lottie_format["fr"])
                else:
                    ret = offset + slope*(frame/settings.lottie_format["fr"])

            elif self.param[0].tag == "radial_composite":   # Only for vectors
                rad = self.subparams["radial_composite"].subparams["radius"].__get_value(frame)
                angle = to_Synfig_axis(self.subparams["radial_composite"].subparams["theta"].__get_value(frame), "angle")
                angle = math.radians(angle)
                x = rad * math.cos(angle)
                y = rad * math.sin(angle)
                ret = [x, y]

            elif self.param[0].tag == "scale":
                link = self.subparams["scale"].subparams["link"].__get_value(frame)
                scalar = self.subparams["scale"].subparams["scalar"].__get_value(frame)
                if isinstance(link, list):
                    link[0] *= scalar
                    link[1] *= scalar
                else:
                    link *= scalar
                ret = link

            elif self.param[0].tag == "subtract":
                lhs = self.subparams["subtract"].subparams["lhs"].__get_value(frame)
                rhs = self.subparams["subtract"].subparams["rhs"].__get_value(frame)
                scalar = self.subparams["subtract"].subparams["scalar"].__get_value(frame)
                if isinstance(lhs, list):
                    ret = [0, 0]
                    ret[0] = (lhs[0] - rhs[0]) * scalar
                    ret[1] = (lhs[1] - rhs[1]) * scalar
                else:
                    ret = (lhs - rhs) * scalar

            elif self.param[0].tag == "switch":
                link_off = self.subparams["switch"].subparams["link_off"].__get_value(frame)
                link_on = self.subparams["switch"].subparams["link_on"].__get_value(frame)
                switch = self.subparams["switch"].subparams["switch"].__get_value(frame)
                if isinstance(link_on, list):
                    ret = [0, 0]
                    ret[0] = link_on[0] * switch + link_off[0] * (1 - switch)
                    ret[1] = link_on[1] * switch + link_off[1] * (1 - switch)
                else:
                    ret = link_on * switch + link_off * (1 - switch)

            elif self.param[0].tag == "bone_link":
                guid = self.subparams["bone_link"].subparams["bone"][0].attrib["guid"]
                bone = self.get_bone_from_canvas(guid)
                ret_origin, ret_angle, lls, rls = bone.__get_value(frame)

                # Adding the base value effect here
                base_value = self.subparams["bone_link"].subparams["base_value"].__get_value(frame)
                a1, a2 = math.radians(ret_angle), math.radians(ret_angle+90)
                ret = ret_origin

                # base_value to be arranged according to the local scale
                base_value = [lls*i for i in base_value]

                ret[0] = ret[0] + (base_value[0] * math.cos(a1) - base_value[1] * math.cos(a2)) * rls[0]
                ret[1] = ret[1] + (base_value[0] * math.sin(a1) - base_value[1] * math.sin(a2)) * rls[1]

                ret = [ret[0], ret[1]]

            elif self.param[0].tag == "sine":
                angle = self.subparams["sine"].subparams["angle"].__get_value(frame)
                amp = self.subparams["sine"].subparams["amp"].__get_value(frame)
                angle = math.radians(angle)
                
                if isinstance(amp, list):
                    ret = [0, 0]

                    ret[0] = math.sin(angle) * amp[0]
                    ret[1] = math.sin(angle) * amp[1]
                else:
                    ret = math.sin(angle)*amp

            elif self.param[0].tag == "cos":
                angle = self.subparams["cos"].subparams["angle"].__get_value(frame)
                amp = self.subparams["cos"].subparams["amp"].__get_value(frame)
                angle = math.radians(angle)
                if isinstance(amp, list):
                    ret = [0, 0]
                    ret[0] = math.cos(angle) * amp[0]
                    ret[1] = math.cos(angle) * amp[1]
                else:
                    ret = math.cos(angle)*amp

            elif self.param[0].tag == "fromint":
                link = self.subparams["fromint"].subparams["link"].__get_value(frame)
                if isinstance(link, list):
                    ret = [0, 0]
                    ret[0] = round(link[0])*settings.PIX_PER_UNIT
                    ret[1] = round(link[1])*settings.PIX_PER_UNIT
                else:
                    ret = round(link)*settings.PIX_PER_UNIT

            elif self.param[0].tag == "atan2":
                y = self.subparams["atan2"].subparams["y"].__get_value(frame)
                x = self.subparams["atan2"].subparams["x"].__get_value(frame)
                rad = math.pi/180
                ret = math.atan2(y,x)/rad

            elif self.param[0].tag == "vectorangle":
                vector = self.subparams["vectorangle"].subparams["vector"].__get_value(frame)
                rad = math.pi/180
                ret = math.atan2(vector[1],vector[0])/rad

        else:
            ret = self.get_single_value(frame)
            if isinstance(ret, list):
                # Need to change the calculation inside get_single_value, this
                # is just a hack
                ret = [ret[0], -ret[1]]
        return ret
Exemple #18
0
def synfig_outline(bline, st_val, origin_p, outer_width_p, sharp_cusps_p, expand_p, r_tip0_p, r_tip1_p, homo_width_p, 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 (common.Bline.Bline) : Synfig format bline points of outline layer
        st_val (dict) : Lottie format outline stored in this
        origin_p (common.Param.Param) : Lottie format origin of outline layer
        outer_width_p (common.Param.Param) : Lottie format outer width
        sharp_cusps_p (common.Param.Param) : sharp cusps in Synfig format
        expand_p (common.Param.Param) : Lottie format expand parameter
        r_tip0_p (common.Param.Param) : Round tip[0] in Synfig format
        r_tip1_p (common.Param.Param) : Round tip[1] in Synfig format
        homo_width_p (common.Param.Param) : 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

    bline_list = bline.get_list_at_frame(fr)
    outer_width = to_Synfig_axis(outer_width_p.get_value(fr), "real")
    expand = to_Synfig_axis(expand_p.get_value(fr), "real")
    sharp_cusps = sharp_cusps_p.get_value(fr)
    r_tip0 = r_tip0_p.get_value(fr)
    r_tip1 = r_tip1_p.get_value(fr)
    homo_width = homo_width_p.get_value(fr)

    gv = get_outline_grow(fr)

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

    # Check if looped
    loop = bline.get_loop()

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

    first_point = bline_list[iter_it]
    first_tangent = bline_list[0].get_tangent2()
    last_tangent = first_point.get_tangent1()

    # 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_list) > 1:
        prev_it = iter_it
        prev_it -= 1
        prev_it %= len(bline_list)
        prev_point = bline_list[prev_it]
        curve = Hermite(prev_point.get_vertex(),
                        first_point.get_vertex(),
                        prev_point.get_tangent2(),
                        first_point.get_tangent1())
        last_tangent = curve.derivative(1.0 - CUSP_TANGENT_ADJUST)

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

        # Setup tangents
        prev_t = bp1.get_tangent1()
        iter_t = bp1.get_tangent2()
        next_t = bp2.get_tangent1()

        split_flag = bp1.get_split_tangent_angle() or bp1.get_split_tangent_radius()

        # 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 = bp2.get_vertex() - bp1.get_vertex()

            # 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_list)
                next_it += 1
                continue

        # Setup the curve
        curve = Hermite(bp1.get_vertex(),
                        bp2.get_vertex(),
                        iter_t,
                        next_t)

        # Setup width's
        iter_w = gv*(bp1.get_width() * outer_width * 0.5 + expand)
        next_w = gv*(bp2.get_width() * 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 = bp1.get_vertex() + t1*iter_w
                p2 = bp1.get_vertex() + 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 = bp1.get_vertex() - t1*iter_w
                p2 = bp1.get_vertex() - 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([bp1.get_vertex() + (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([bp1.get_vertex() - (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 generated 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_list)
        next_it += 1

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

    origin_cur = origin_p.get_value(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_list[-1]
            vertex = bp.get_vertex()
            tangent = last_tangent.norm()
            w = gv*(bp.get_width() * 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_list[0]
            vertex = bp.get_vertex()
            tangent = first_tangent.norm()
            w = gv*(bp.get_width() * 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)
Exemple #19
0
def synfig_star(st_val, mx_points, origin_p, radius1_p, radius2_p, angle_p, points_p, regular_polygon_p, 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_p (common.Param.Param) : Lottie format radius1 animation
        radius2_p (common.Param.Param) : Lottie format radius2 animation
        angle_p   (common.Param.Param) : Lottie format angle animation
        points_p  (common.Param.Param) : Lottie format points animation
        regular_polygon_p (common.Param.Param) : Synfig format regularPolygon animation
        fr (int) : Frame number

    Returns:
        (None)
    """

    angle = angle_p.get_value(fr)
    points = int(to_Synfig_axis(points_p.get_value(fr), "real"))
    radius1 = to_Synfig_axis(radius1_p.get_value(fr), "real")
    radius2 = to_Synfig_axis(radius2_p.get_value(fr), "real")
    regular_polygon = regular_polygon_p.get_value(fr)
    origin_cur = origin_p.get_value(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)