Пример #1
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)
Пример #2
0
    def get_list_at_frame(self, fr):
        """
        Returns the Bline list at a particular frame
        Refer: https://github.com/synfig/synfig/blob/15607089680af560ad031465d31878425af927eb/synfig-core/src/synfig/valuenodes/valuenode_bline.cpp
        """
        EPSILON = 0.0000001
        ret_list = []
        first_flag = True
        rising = [False]
        next_scale = 1.0

        first = BlinePoint(Vector(0, 0), 1, True, False, Vector(0, 0),
                           Vector(0, 0))
        first.set_origin(100)

        iterr = 0
        while iterr != self.get_len():
            entry = self.get_entry_list()[iterr]
            amount = entry["ActivepointList"].amount_at_time(fr, rising)
            assert (amount >= 0.0)
            assert (amount <= 1.0)

            # It's fully on
            if amount > 1.0 - EPSILON:
                if first_flag:
                    first_iter = iterr
                    first = prev = self.get_blinepoint(iterr, fr)
                    first_flag = False
                    ret_list.append(copy.deepcopy(first))
                    iterr += 1
                    continue
                curr = self.get_blinepoint(iterr, fr)

                if next_scale != 1.0:
                    ret_list[-1].set_split_tangent_both(True)
                    ret_list[-1].set_tangent2(prev.get_tangent2() * next_scale)
                    ret_list.append(copy.deepcopy(curr))
                    ret_list[-1].set_split_tangent_both(True)
                    ret_list[-1].set_tangent2(curr.get_tangent2())
                    ret_list[-1].set_tangent1(curr.get_tangent1() * next_scale)
                    next_scale = 1.0
                else:
                    ret_list.append(copy.deepcopy(curr))
                prev = curr

            # It's partly on
            elif amount > 0.0:
                blp_here_off = BlinePoint(Vector(0, 0), 1, True, False,
                                          Vector(0, 0), Vector(0, 0))
                blp_here_now = BlinePoint(Vector(0, 0), 1, True, False,
                                          Vector(0, 0), Vector(0, 0))
                blp_prev_off = BlinePoint(Vector(0, 0), 1, True, False,
                                          Vector(0, 0), Vector(0, 0))
                dist_from_begin = 0
                dist_from_end = 0

                if not rising[0]:
                    try:
                        on_time = self.get_entry_list(
                        )[iterr]["ActivepointList"].find_prev(fr).get_time()
                    except Exception as e:
                        on_time = settings.SOT
                    try:
                        off_time = self.get_entry_list(
                        )[iterr]["ActivepointList"].find_next(fr).get_time()
                    except Exception as e:
                        off_time = settings.EOT
                else:
                    try:
                        off_time = self.get_entry_list(
                        )[iterr]["ActivepointList"].find_prev(fr).get_time()
                    except Exception as e:
                        off_time = settings.SOT
                    try:
                        on_time = self.get_entry_list(
                        )[iterr]["ActivepointList"].find_next(fr).get_time()
                    except Exception as e:
                        on_time = settings.EOT

                blp_here_on = self.get_blinepoint(iterr, on_time)
                end_iter = iterr

                end_iter += 1
                while end_iter != self.get_len():
                    if self.get_entry_list(
                    )[end_iter]["ActivepointList"].amount_at_time(fr) > amount:
                        break
                    end_iter += 1

                if end_iter == self.get_len():
                    if self.get_loop() and (not first_flag):
                        end_iter = first_iter
                    else:
                        end_iter = self.get_len() - 1

                blp_next_off = self.get_blinepoint(end_iter, off_time)

                begin_iter = iterr
                blp_prev_off.set_origin(100)

                while True:
                    if begin_iter == 0:
                        if self.get_loop():
                            begin_iter = self.get_len()
                        else:
                            break
                    begin_iter -= 1
                    dist_from_begin += 1

                    if begin_iter == iterr:
                        break

                    if self.get_entry_list()[begin_iter][
                            "ActivepointList"].amount_at_time(fr) > amount:
                        blp_prev_off = self.get_blinepoint(
                            begin_iter, off_time)
                        break

                if blp_prev_off.get_origin() == 100:
                    if first_flag:
                        begin_iter = 0
                    else:
                        begin_iter = first_iter
                    blp_prev_off = self.get_blinepoint(begin_iter, off_time)

                curve = Hermite(blp_prev_off.get_vertex(),
                                blp_next_off.get_vertex(),
                                blp_prev_off.get_tangent2(),
                                blp_next_off.get_tangent1())

                blp_here_off.set_vertex(curve.value(blp_here_on.get_origin()))
                blp_here_off.set_width(
                    (blp_next_off.get_width() - blp_prev_off.get_width()) *
                    blp_here_on.get_origin() + blp_prev_off.get_width())
                blp_here_off.set_tangent1(
                    curve.derivative(blp_here_on.get_origin()))
                blp_here_off.set_tangent2(
                    curve.derivative(blp_here_on.get_origin()))

                if begin_iter == (iterr - 1) or dist_from_begin == 1:
                    prev_tangent_scalar = self.linear_interpolation(
                        blp_here_on.get_origin(), 1.0, amount)
                else:
                    prev_tangent_scalar = self.linear_interpolation(
                        blp_here_on.get_origin() - prev.get_origin(), 1.0,
                        amount)

                if end_iter == (iterr + 1) or dist_from_end == 1:
                    next_tangent_scalar = self.linear_interpolation(
                        1.0 - blp_here_on.get_origin(), 1.0, amount)
                elif self.get_len() != (iterr + 1):
                    nextt = self.get_blinepoint(iterr + 1, fr)
                    next_tangent_scalar = self.linear_interpolation(
                        nextt.get_origin() - blp_here_on.get_origin(), 1.0,
                        amount)
                else:
                    next_tangent_scalar = self.linear_interpolation(
                        blp_next_off.get_origin() - blp_here_on.get_origin(),
                        1.0, amount)
                next_scale = next_tangent_scalar

                # My second try
                off_coord_sys = []
                on_coord_sys = []
                curr_coord_sys = []
                end_pos_at_off_time = self.get_blinepoint(
                    end_iter, off_time).get_vertex()
                begin_pos_at_off_time = self.get_blinepoint(
                    begin_iter, off_time).get_vertex()
                off_coord_origin = (begin_pos_at_off_time +
                                    end_pos_at_off_time) / 2
                off_coord_sys.append(
                    (begin_pos_at_off_time - end_pos_at_off_time).norm())
                off_coord_sys.append(off_coord_sys[0].perp())

                end_pos_at_on_time = self.get_blinepoint(end_iter,
                                                         on_time).get_vertex()
                begin_pos_at_on_time = self.get_blinepoint(
                    begin_iter, on_time).get_vertex()
                on_coord_origin = (begin_pos_at_on_time +
                                   end_pos_at_on_time) / 2
                on_coord_sys.append(
                    (begin_pos_at_on_time - end_pos_at_on_time).norm())
                on_coord_sys.append(on_coord_sys[0].perp())

                end_pos_at_current_time = self.get_blinepoint(end_iter,
                                                              fr).get_vertex()
                begin_pos_at_current_time = self.get_blinepoint(
                    begin_iter, fr).get_vertex()
                curr_coord_origin = (begin_pos_at_current_time +
                                     end_pos_at_current_time) / 2
                curr_coord_sys.append((begin_pos_at_current_time -
                                       end_pos_at_current_time).norm())
                curr_coord_sys.append(curr_coord_sys[0].perp())

                # swapping
                temp = curr_coord_sys[0][1]
                curr_coord_sys[0][1] = curr_coord_sys[1][0]
                curr_coord_sys[1][0] = temp

                trans_on_point = Vector(0, 0)
                trans_off_point = Vector(0, 0)
                trans_on_t1 = Vector(0, 0)
                trans_off_t1 = Vector(0, 0)
                trans_on_t2 = Vector(0, 0)
                trans_off_t2 = Vector(0, 0)

                trans_on_point = self.transform_coords(
                    blp_here_on.get_vertex(), trans_on_point, on_coord_origin,
                    on_coord_sys)
                trans_off_point = self.transform_coords(
                    blp_here_off.get_vertex(), trans_off_point,
                    off_coord_origin, off_coord_sys)

                trans_on_t1 = self.transform_coords(blp_here_on.get_tangent1(),
                                                    trans_on_t1, Vector(0, 0),
                                                    on_coord_sys)
                trans_off_t1 = self.transform_coords(
                    blp_here_off.get_tangent1(), trans_off_t1, Vector(0, 0),
                    off_coord_sys)

                if blp_here_on.get_split_tangent_both():
                    trans_on_t2 = self.transform_coords(
                        blp_here_on.get_tangent2(), trans_on_t2, Vector(0, 0),
                        on_coord_sys)
                    trans_off_t2 = self.transform_coords(
                        blp_here_off.get_tangent2(), trans_off_t2,
                        Vector(0, 0), off_coord_sys)

                tmp = Vector(0, 0)
                tmp = self.untransform_coords(
                    self.linear_interpolation(trans_off_point, trans_on_point,
                                              amount), tmp, curr_coord_origin,
                    curr_coord_sys)
                blp_here_now.set_vertex(tmp)

                tmp = Vector(0, 0)
                tmp = self.untransform_coords(
                    self.radial_interpolation(trans_off_t1,
                                              trans_on_t1, amount), tmp,
                    Vector(0, 0), curr_coord_sys)
                blp_here_now.set_tangent1(tmp)

                # blp_here_now.set_tangent1(self.radial_interpolation(blp_here_off.get_tangent1(), blp_here_on.get_tangent1(), amount))

                if blp_here_on.get_split_tangent_both():
                    blp_here_now.set_split_tangent_both(True)
                    tmp = Vector(0, 0)
                    tmp = self.untransform_coords(
                        self.radial_interpolation(trans_off_t2,
                                                  trans_on_t2, amount), tmp,
                        Vector(0, 0), curr_coord_sys)
                    blp_here_now.set_tangent2(tmp)
                else:
                    blp_here_now.set_split_tangent_both(False)

                blp_here_now.set_origin(blp_here_on.get_origin())
                blp_here_now.set_width(
                    self.linear_interpolation(blp_here_off.get_width(),
                                              blp_here_on.get_width(), amount))

                if first_flag:
                    blp_here_now.set_tangent1(blp_here_now.get_tangent1() *
                                              prev_tangent_scalar)
                    first_iter = iterr
                    first = prev = blp_here_now
                    first_flag = False
                    ret_list.append(copy.deepcopy(blp_here_now))
                    iterr += 1
                    continue

                ret_list[-1].set_split_tangent_both(True)
                ret_list[-1].set_tangent2(prev.get_tangent2() *
                                          prev_tangent_scalar)

                ret_list.append(copy.deepcopy(blp_here_now))
                ret_list[-1].set_split_tangent_both(True)
                ret_list[-1].set_tangent1(blp_here_now.get_tangent1() *
                                          prev_tangent_scalar)
                prev = blp_here_now

            iterr += 1

        if next_scale != 1:
            ret_list[-1].set_split_tangent_both(True)
            ret_list[-1].set_tangent2(prev.get_tangent2() * next_scale)

        return ret_list