Example #1
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
Example #2
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
Example #3
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)
Example #4
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)
Example #5
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])
Example #6
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)