summaryrefslogtreecommitdiff
path: root/lib/utils/smoothing.py
blob: 9c16bb0f46dafcc1fcf7e0ac421ba192c793cb16 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
import numpy as np

from ..stitches.running_stitch import stitch_curve_evenly
from .geometry import Point, coordinate_list_to_point_list


def _remove_duplicate_coordinates(coords_array):
    """Remove consecutive duplicate points from an array.

    Arguments:
        coords_array -- numpy.array

    Returns:
        a numpy.array of coordinates, minus consecutive duplicates
    """

    differences = np.diff(coords_array, axis=0)
    zero_differences = np.isclose(differences, 0)
    keepers = np.r_[True, np.any(zero_differences == False, axis=1)]  # noqa: E712

    return coords_array[keepers]


def smooth_path(path, smoothness=1.0, iterations=5):
    """Smooth a path of coordinates.

    Arguments:
        path -- an iterable of coordinate tuples or Points
        smoothness -- float, how much smoothing to apply.  Bigger numbers
            smooth more.

    Returns:
        A list of Points.
    """
    points = coordinate_list_to_point_list(path)
    if smoothness == 0:
        return points

    # Smoothing seems to look nicer if the line segments in the path are mostly
    # similar in length.  If we have some especially long segments, then the
    # smoothed path sometimes diverges more from the original path as the
    # spline curve struggles to fit the path.  This can be especially bad at
    # the start and end.
    #
    # Fortunately, we can convert the path to segments that are mostly the same
    # length by using the running stitch algorithm.
    points = stitch_curve_evenly(points, smoothness * 5, smoothness * 2)
    points = np.array(points)
    for _ in range(iterations):
        ll = points.repeat(2, axis=0)
        r = np.empty_like(ll)
        if len(r) == 0:
            continue
        r[0] = ll[0]
        r[2::2] = ll[1:-1:2]
        r[1:-1:2] = ll[2::2]
        r[-1] = ll[-1]
        points = ll * 0.75 + r * 0.25

    return [Point(*coord) for coord in points]