summaryrefslogtreecommitdiff
path: root/lib/elements/gradient_fill.py
blob: 5ac49c4e208be649fb0157b87c250dd3f2f7f75a (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
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
from math import pi

from inkex import DirectedLineSegment, Transform
from shapely import geometry as shgeo
from shapely.affinity import affine_transform, rotate
from shapely.ops import split

from ..svg import get_correction_transform


def gradient_shapes_and_attributes(element, shape):
    # e.g. url(#linearGradient872) -> linearGradient872
    color = element.color[5:-1]
    xpath = f'.//svg:defs/svg:linearGradient[@id="{color}"]'
    gradient = element.node.getroottree().getroot().findone(xpath)
    gradient.apply_transform()
    point1 = (float(gradient.get('x1')), float(gradient.get('y1')))
    point2 = (float(gradient.get('x2')), float(gradient.get('y2')))
    # get 90° angle to calculate the splitting angle
    line = DirectedLineSegment(point1, point2)
    angle = line.angle - (pi / 2)
    # Ink/Stitch somehow turns the stitch angle
    stitch_angle = angle * -1
    # create bbox polygon to calculate the length necessary to make sure that
    # the gradient splitter lines will cut the entire design
    bbox = element.node.bounding_box()
    bbox_polygon = shgeo.Polygon([(bbox.left, bbox.top), (bbox.right, bbox.top),
                                  (bbox.right, bbox.bottom), (bbox.left, bbox.bottom)])
    # gradient stops
    offsets = gradient.stop_offsets
    stop_styles = gradient.stop_styles
    # now split the shape according to the gradient stops
    polygons = []
    colors = []
    attributes = []
    previous_color = None
    end_row_spacing = None
    for i, offset in enumerate(offsets):
        shape_rest = []
        split_point = shgeo.Point(line.point_at_ratio(float(offset)))
        length = split_point.hausdorff_distance(bbox_polygon)
        split_line = shgeo.LineString([(split_point.x - length - 2, split_point.y),
                                       (split_point.x + length + 2, split_point.y)])
        split_line = rotate(split_line, angle, origin=split_point, use_radians=True)
        transform = -Transform(get_correction_transform(element.node))
        transform = list(transform.to_hexad())
        split_line = affine_transform(split_line, transform)
        offset_line = split_line.parallel_offset(1, 'right')
        polygon = split(shape, split_line)
        color = stop_styles[i]['stop-color']
        # does this gradient line split the shape
        offset_outside_shape = len(polygon.geoms) == 1
        for poly in polygon.geoms:
            if isinstance(poly, shgeo.Polygon) and element.shape_is_valid(poly):
                if poly.intersects(offset_line):
                    if previous_color:
                        polygons.append(poly)
                        colors.append(previous_color)
                        attributes.append({'angle': stitch_angle, 'end_row_spacing': end_row_spacing, 'color': previous_color})
                    polygons.append(poly)
                    attributes.append({'angle': stitch_angle + pi, 'end_row_spacing': end_row_spacing, 'color': color})
                else:
                    shape_rest.append(poly)
        shape = shgeo.MultiPolygon(shape_rest)
        previous_color = color
        end_row_spacing = element.row_spacing * 2
    # add left over shape(s)
    if shape:
        if offset_outside_shape:
            for s in shape.geoms:
                polygons.append(s)
                attributes.append({'color': stop_styles[-2]['stop-color'], 'angle': stitch_angle, 'end_row_spacing': end_row_spacing})
            stitch_angle += pi
        else:
            end_row_spacing = None
        for s in shape.geoms:
            polygons.append(s)
            attributes.append({'color': stop_styles[-1]['stop-color'], 'angle': stitch_angle, 'end_row_spacing': end_row_spacing})
    return polygons, attributes