Blob Blame Raw
# pylint: disable=line-too-long
"""
Will store all the functions and modules for generation of star layer
in Lottie format
"""

import sys
import math
from common.Vector import Vector
from common.Layer import Layer
from synfig.animation import to_Synfig_axis
from properties.shapePropKeyframe.helper import add, insert_dict_at, update_child_at_parent
sys.path.append("../../")


def gen_list_star(lottie, layer):
    """
    Generates a shape layer corresponding to star layer by manipulating the
    parameters of the star

    Args:
        lottie (dict) : Lottie format rectangle layer will be stored in this
        layer  (common.Layer.Layer) : Synfig format rectangle layer

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

    origin = layer.get_param("origin")
    radius1 = layer.get_param("radius1")
    radius2 = layer.get_param("radius2")
    angle = layer.get_param("angle")
    points = layer.get_param("points")
    regular_polygon = layer.get_param("regular_polygon")

    # Animating origin
    origin.update_frame_window(window)
    # Keeping the transform true here
    #origin.animate("vector", True)
    origin.animate("vector")

    # Animating radius1
    radius1.update_frame_window(window)
    radius1.animate("real")

    # Animating radius2
    radius2.update_frame_window(window)
    radius2.animate("real")

    # Animating angle
    angle.update_frame_window(window)
    angle.animate("star_angle_new")

    # Animating points
    points.update_frame_window(window)
    points.animate("real")

    mx_points = get_max_points(points)

    # Animating regular_polygon
    regular_polygon.update_frame_window(window)
    regular_polygon.animate_without_path("bool")

    # 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, False)
        synfig_star(st_val, mx_points, origin, radius1, radius2, angle, points, regular_polygon, fr)
        synfig_star(en_val, mx_points, origin, radius1, radius2, angle, points, regular_polygon, fr + 1)

        fr += 1
    # Setting the final time
    lottie.append({})
    lottie[-1]["t"] = fr


def get_max_points(points):
    """
    As a shape layer needs to have same number of vertices at each frame, we
    will draw a shape with max number of points. When number of points at a
    frame is less than mx, redundant vertices on the shape will be added

    NOTE/IMPORTANT: There might come a case when somewhere between two
    waypoints, the number of points is greater than both those 2 waypoints(very
    rare). This case is not handled here

    Args:
        points (lxml.etree._Element) : Animation of points param in Synfig format

    Returns:
        mx (int) : Maximum number of points ever in the animation
    """
    mx = -1
    for waypoint in points[0]:
        val = float(waypoint[0].attrib["value"])
        mx = max(mx, val)
    return mx


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)