Created
January 5, 2025 02:26
-
-
Save migurski/53212c0f4505cb41987c319f334f3995 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
import itertools | |
import typing | |
import math | |
import cairo | |
import shapely.affinity | |
import shapely.geometry | |
DISTANCE_CUTOFF = 2 # max jump length to bridge with a curve | |
OUTLINE_DISTANCE = .5 # ideally half the line width | |
INSET_DISTANCE = .9 # distance from outside edge to begin hatching | |
HATCH_OVERLAP = .3 # amount to overlap hatch lines to account for slop; 0 = no overlap | |
CURVE_JOIN_DISTANCE = .25 | |
BACK_AND_FORTH = True | |
class Segment (typing.NamedTuple): | |
''' | |
''' | |
row: int | |
x1: float | |
y1: float | |
x2: float | |
y2: float | |
def distance(self, other): | |
''' Distance from self end to other beginning | |
''' | |
return math.hypot(self.x2 - other.x1, self.y2 - other.y1) | |
class Fragment (typing.NamedTuple): | |
''' | |
''' | |
p1: int | |
p2: int | |
p: int | |
class MultiPath (typing.NamedTuple): | |
''' | |
''' | |
points: shapely.geometry.MultiPoint | |
fragments: typing.List[typing.List[Fragment]] | |
def get_hatch_segment_xs(xmin, xmax): | |
''' | |
''' | |
if not BACK_AND_FORTH: | |
# All parallel lines point in the same direction and ends do not connect | |
return itertools.cycle([(xmax, xmin)]) | |
return itertools.cycle([(xmin, xmax), (xmax, xmin)]) | |
def fill_hatch_segments(polygon): | |
''' | |
''' | |
assert polygon.type == 'Polygon' | |
xmin, y, xmax, ymax = polygon.bounds | |
rows, xs = itertools.count(), get_hatch_segment_xs(xmin, xmax) | |
segments = [] | |
for (row, (x1, x2)) in zip(rows, xs): | |
line = shapely.geometry.LineString([(x1, y), (x2, y)]) | |
if y > ymax: | |
break | |
elif not line.intersects(polygon): | |
continue | |
else: | |
y += 1 | |
scan = line.intersection(polygon) | |
segments.extend([ | |
Segment( | |
row, | |
part.coords[0][0], | |
part.coords[0][1], | |
part.coords[-1][0], | |
part.coords[-1][1], | |
) | |
for part in getattr(scan, 'geoms', [scan]) | |
if part.type == 'LineString' | |
]) | |
return segments | |
def fill_hatch_sequences(polygon): | |
''' | |
''' | |
assert polygon.type == 'Polygon' | |
distance_cutoff = DISTANCE_CUTOFF if BACK_AND_FORTH else 0 | |
segments = fill_hatch_segments(polygon) | |
sequences = [] | |
while segments: | |
segment = segments.pop(0) | |
sequence = [(segment.x1, segment.y1), (segment.x2, segment.y2)] | |
while True: | |
others = [ | |
(i, other) | |
for (i, other) in enumerate(segments) | |
if abs(other.row - segment.row) == 1 | |
and segment.distance(other) < distance_cutoff | |
] | |
if not others: | |
sequences.append(sequence) | |
break | |
others.sort(key=lambda o: segment.distance(o[1])) | |
segment = segments.pop(others[0][0]) | |
prior_length = abs(sequence[-1][-2] - sequence[-2][-2]) | |
follow_length = abs(segment.x2 - segment.x1) | |
prior_diff = min(CURVE_JOIN_DISTANCE, prior_length) | |
follow_diff = min(CURVE_JOIN_DISTANCE, follow_length) | |
if segment.row % 2 == 0: | |
# pointing right | |
prior_diff, follow_diff = -prior_diff, -follow_diff | |
sequence[-1] = tuple( | |
list(sequence[-1][:-2]) + [sequence[-1][-2] - prior_diff, sequence[-1][-1]] | |
) | |
x3 = segment.x1 - follow_diff | |
y3 = segment.y1 | |
x1, y1 = sequence[-1][-2] + 2*prior_diff, sequence[-1][-1] | |
x2, y2 = x3 + 2*follow_diff, y3 | |
sequence.extend([ | |
(x1, y1, x2, y2, x3, y3), | |
(segment.x2, segment.y2), | |
]) | |
return sequences | |
def inscribed_hatch_multipath(polygon, scale, angle=None): | |
''' | |
''' | |
assert polygon.type == 'Polygon' | |
# Add a bit of overlap to the hatched lines when the Axidraw is sloppy | |
_scale = scale / (1.0 + HATCH_OVERLAP) | |
# Track the best option of the angle deltas we try | |
_count, multipath = math.inf, None | |
# Try several candidate angle deltas? | |
angles = [angle] if angle is not None else (0, 26, 52, 77, 103, 129, 154) | |
for _angle in angles: | |
transformed_polygon = shapely.affinity.rotate( | |
shapely.affinity.scale(polygon, 1/_scale, 1/_scale, origin=(0, 0)), | |
_angle, | |
origin=(0, 0), | |
) | |
transformed_multipath = native_hatch_multipath(transformed_polygon) | |
untransformed_points = shapely.affinity.scale( | |
shapely.affinity.rotate(transformed_multipath.points, -_angle, origin=(0, 0)), | |
_scale, | |
_scale, | |
origin=(0, 0), | |
) | |
if len(transformed_multipath.fragments) < _count: | |
# Choose a new, better multipath candidate | |
_count = len(transformed_multipath.fragments) | |
multipath = MultiPath(untransformed_points, transformed_multipath.fragments) | |
if _count <= 3: | |
# Eh, good enough | |
break | |
return multipath | |
def native_hatch_multipath(polygon): | |
''' | |
''' | |
assert polygon.type == 'Polygon' | |
points, fragments = [], [] | |
outline = polygon.buffer(-OUTLINE_DISTANCE).boundary | |
for line in getattr(outline, 'geoms', [outline]): | |
if line.type == 'LineString' and True: | |
fragments.append([ | |
Fragment(None, None, i) | |
for i in range(len(points), len(points) + len(line.coords)) | |
]) | |
points.extend(line.coords) | |
inset_multipolygon = polygon.buffer(-INSET_DISTANCE) | |
if inset_multipolygon.type not in ('Polygon', 'MultiPolygon') or inset_multipolygon.area == 0: | |
return MultiPath(shapely.geometry.MultiPoint(points), fragments) | |
for inset_polygon in getattr(inset_multipolygon, 'geoms', [inset_multipolygon]): | |
for sequence in fill_hatch_sequences(inset_polygon): | |
head, tail = sequence[0], sequence[1:] | |
assert len(head) == 2 | |
fragments.append([Fragment(None, None, len(points))]) | |
points.append(head[-2:]) | |
for part in tail: | |
assert len(part) in (2, 6) | |
if len(part) == 2: | |
fragments[-1].append(Fragment(None, None, len(points))) | |
points.append(part) | |
else: | |
fragments[-1].append(Fragment(len(points), len(points)+1, len(points)+2)) | |
points.extend([part[:2], part[2:4], part[4:6]]) | |
return MultiPath(shapely.geometry.MultiPoint(points), fragments) | |
def draw_shapely_line_geom(ctx, geom): | |
if getattr(geom, 'geoms', False): | |
parts = [part for part in geom.geoms] | |
else: | |
parts = [geom] | |
for part in parts: | |
try: | |
coords = part.coords | |
except NotImplementedError: | |
continue | |
if not coords: | |
continue | |
ctx.move_to(*coords[0]) | |
for coord in coords[1:]: | |
ctx.line_to(*coord) | |
def draw_multipath_strokes(ctx, multipath): | |
''' | |
''' | |
for fragments in multipath.fragments: | |
head, tail = fragments[0], fragments[1:] | |
p = multipath.points.geoms[head.p] | |
ctx.move_to(p.x, p.y) | |
for fragment in tail: | |
if fragment.p1 is None and fragment.p2 is None: | |
p = multipath.points.geoms[fragment.p] | |
ctx.line_to(p.x, p.y) | |
else: | |
p1 = multipath.points.geoms[fragment.p1] | |
p2 = multipath.points.geoms[fragment.p2] | |
p = multipath.points.geoms[fragment.p] | |
ctx.curve_to(p1.x, p1.y, p2.x, p2.y, p.x, p.y) | |
ctx.stroke() | |
def calculate_hatch_angle(shape): | |
''' | |
''' | |
rectangle = shape.minimum_rotated_rectangle.exterior | |
(x1, y1), (x2, y2), (x3, y3) = rectangle.coords[:3] | |
if math.hypot(x3 - x2, y3 - y2) < math.hypot(x2 - x1, y2 - y1): | |
return 180 * math.atan2(y2 - y3, x3 - x2) / math.pi | |
else: | |
return 180 * math.atan2(y1 - y2, x2 - x1) / math.pi | |
if __name__ == '__main__': | |
font_svg = __import__('font-svg') | |
color_cube = __import__('color-cube') | |
FONT = font_svg.load_font('fonts/EMSReadability.svg') | |
with cairo.SVGSurface('out.svg', 8.5*72, 5.5*72) as surface: | |
context = cairo.Context(surface) | |
hotdog1 = shapely.geometry.LineString([(72, 72), (144, 144)]).buffer(36, 5) | |
draw_multipath_strokes( | |
context, | |
inscribed_hatch_multipath(hotdog1, 9, None), | |
) | |
hotdog2 = shapely.geometry.LineString([(216, 72), (252, 144)]).buffer(36, 5) | |
draw_multipath_strokes( | |
context, | |
inscribed_hatch_multipath(hotdog2, 6, None), | |
) | |
hotdog3 = shapely.geometry.LineString([(288, 72), (360, 108)]).buffer(36, 5) | |
draw_multipath_strokes( | |
context, | |
inscribed_hatch_multipath(hotdog3, 3, None), | |
) | |
context.stroke() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="UTF-8"?> | |
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="612pt" height="396pt" viewBox="0 0 612 396" version="1.1"> | |
<g id="surface1"> | |
<path style="fill:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 120.800781 166.816406 L 129.246094 172.953125 L 138.917969 176.09375 L 149.082031 176.09375 L 158.753906 172.953125 L 166.976562 166.976562 L 172.953125 158.753906 L 176.09375 149.082031 L 176.09375 138.917969 L 172.953125 129.246094 L 166.816406 120.800781 L 95.199219 49.183594 L 86.753906 43.046875 L 77.082031 39.90625 L 66.917969 39.90625 L 57.246094 43.046875 L 49.023438 49.023438 L 43.046875 57.246094 L 39.90625 66.917969 L 39.90625 77.082031 L 43.046875 86.753906 L 49.183594 95.199219 L 120.800781 166.816406 "/> | |
<path style="fill:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 91.058594 49.597656 L 54.671875 49.597656 C 51.210938 49.597656 45.265625 56.519531 48.730469 56.519531 L 96.890625 56.519531 C 100.351562 56.519531 107.273438 63.441406 103.8125 63.441406 L 45.675781 63.441406 C 42.214844 63.441406 40.941406 70.367188 44.40625 70.367188 L 110.734375 70.367188 C 114.195312 70.367188 121.121094 77.289062 117.660156 77.289062 L 44.613281 77.289062 C 41.152344 77.289062 43.402344 84.210938 46.863281 84.210938 L 124.582031 84.210938 C 128.042969 84.210938 134.964844 91.136719 131.503906 91.136719 L 51.382812 91.136719 C 47.921875 91.136719 54.226562 98.058594 57.6875 98.058594 L 138.429688 98.058594 C 141.890625 98.058594 148.8125 104.980469 145.351562 104.980469 L 64.613281 104.980469 C 61.152344 104.980469 68.074219 111.90625 71.535156 111.90625 L 152.273438 111.90625 C 155.734375 111.90625 162.660156 118.828125 159.195312 118.828125 L 78.457031 118.828125 C 74.996094 118.828125 81.921875 125.75 85.382812 125.75 L 165.257812 125.75 C 168.722656 125.75 172.886719 132.675781 169.425781 132.675781 L 92.304688 132.675781 C 88.84375 132.675781 95.765625 139.597656 99.226562 139.597656 L 171.59375 139.597656 C 175.058594 139.597656 175.058594 146.519531 171.59375 146.519531 L 106.152344 146.519531 C 102.6875 146.519531 109.613281 153.441406 113.074219 153.441406 L 170.035156 153.441406 C 173.496094 153.441406 170.089844 160.367188 166.628906 160.367188 L 119.996094 160.367188 C 116.535156 160.367188 124.433594 167.289062 127.894531 167.289062 L 161.835938 167.289062 "/> | |
<path style="fill:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 221.785156 158.90625 L 228.015625 167.621094 L 236.488281 173.878906 L 246.480469 177.207031 L 257.011719 177.289062 L 267.054688 174.109375 L 275.621094 167.984375 L 281.878906 159.511719 L 285.207031 149.519531 L 285.289062 138.988281 L 282.054688 128.769531 L 246.214844 57.09375 L 239.984375 48.378906 L 231.511719 42.121094 L 221.519531 38.792969 L 210.988281 38.710938 L 200.945312 41.890625 L 192.378906 48.015625 L 186.121094 56.488281 L 182.792969 66.480469 L 182.710938 77.011719 L 185.945312 87.230469 L 221.785156 158.90625 "/> | |
<path style="fill:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 232.539062 45.175781 L 200.679688 45.175781 C 198.371094 45.175781 192.207031 49.789062 194.515625 49.789062 L 237.570312 49.789062 C 239.878906 49.789062 243.179688 54.40625 240.871094 54.40625 L 191.109375 54.40625 C 188.800781 54.40625 186.070312 59.023438 188.378906 59.023438 L 243.960938 59.023438 C 246.269531 59.023438 248.578125 63.636719 246.269531 63.636719 L 186.839844 63.636719 C 184.53125 63.636719 183.472656 68.253906 185.777344 68.253906 L 248.578125 68.253906 C 250.886719 68.253906 253.191406 72.867188 250.886719 72.867188 L 185.742188 72.867188 C 183.4375 72.867188 183.644531 77.484375 185.949219 77.484375 L 253.191406 77.484375 C 255.5 77.484375 257.808594 82.097656 255.5 82.097656 L 187.410156 82.097656 C 185.105469 82.097656 186.597656 86.714844 188.90625 86.714844 L 257.808594 86.714844 C 260.117188 86.714844 262.421875 91.328125 260.117188 91.328125 L 191.214844 91.328125 C 188.90625 91.328125 191.214844 95.945312 193.519531 95.945312 L 262.421875 95.945312 C 264.730469 95.945312 267.039062 100.558594 264.730469 100.558594 L 195.828125 100.558594 C 193.519531 100.558594 195.828125 105.175781 198.136719 105.175781 L 267.039062 105.175781 C 269.347656 105.175781 271.65625 109.789062 269.347656 109.789062 L 200.445312 109.789062 C 198.136719 109.789062 200.445312 114.40625 202.75 114.40625 L 271.65625 114.40625 C 273.960938 114.40625 276.269531 119.023438 273.960938 119.023438 L 205.058594 119.023438 C 202.75 119.023438 205.058594 123.636719 207.367188 123.636719 L 276.269531 123.636719 C 278.578125 123.636719 280.886719 128.253906 278.578125 128.253906 L 209.675781 128.253906 C 207.367188 128.253906 209.675781 132.867188 211.984375 132.867188 L 280.261719 132.867188 C 282.570312 132.867188 284.03125 137.484375 281.722656 137.484375 L 214.289062 137.484375 C 211.984375 137.484375 214.289062 142.097656 216.597656 142.097656 L 282.265625 142.097656 C 284.570312 142.097656 284.535156 146.714844 282.230469 146.714844 L 218.90625 146.714844 C 216.597656 146.714844 218.90625 151.328125 221.214844 151.328125 L 281.503906 151.328125 C 283.8125 151.328125 282.273438 155.945312 279.964844 155.945312 L 223.519531 155.945312 C 221.214844 155.945312 224.082031 160.558594 226.390625 160.558594 L 277.652344 160.558594 C 279.960938 160.558594 276.554688 165.175781 274.246094 165.175781 L 229.691406 165.175781 C 227.382812 165.175781 232.910156 169.789062 235.214844 169.789062 L 269.921875 169.789062 "/> | |
<path style="fill:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 260 174.40625 L 243.914062 174.40625 "/> | |
<path style="fill:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 344.335938 139.125 L 354.8125 142.441406 L 365.710938 142.359375 L 376.046875 138.914062 L 384.816406 132.441406 L 391.15625 123.578125 L 394.441406 113.1875 L 394.359375 102.289062 L 390.914062 91.953125 L 384.441406 83.183594 L 375.503906 76.792969 L 303.664062 40.875 L 293.1875 37.558594 L 282.289062 37.640625 L 271.953125 41.085938 L 263.183594 47.558594 L 256.84375 56.421875 L 253.558594 66.8125 L 253.640625 77.710938 L 257.085938 88.046875 L 263.558594 96.816406 L 272.496094 103.207031 L 344.335938 139.125 "/> | |
<path style="fill:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 270.769531 43.105469 L 256.078125 61.910156 "/> | |
<path style="fill:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 254.488281 67.695312 L 275.0625 41.359375 C 275.773438 40.449219 279.734375 39.128906 279.023438 40.039062 L 254.871094 70.953125 C 254.160156 71.863281 254.1875 75.574219 254.898438 74.664062 L 282.753906 39.011719 C 283.464844 38.101562 286.410156 38.078125 285.699219 38.988281 L 255.21875 78.003906 C 254.507812 78.914062 255.382812 81.539062 256.09375 80.632812 L 288.644531 38.964844 C 289.355469 38.058594 292.300781 38.035156 291.589844 38.945312 L 256.96875 83.257812 C 256.257812 84.167969 257.132812 86.796875 257.84375 85.886719 L 294.164062 39.402344 C 294.875 38.492188 297.222656 39.234375 296.511719 40.144531 L 259 88.152344 C 258.292969 89.0625 259.714844 90.992188 260.425781 90.082031 L 298.859375 40.886719 C 299.570312 39.976562 301.917969 40.722656 301.207031 41.628906 L 261.847656 92.007812 C 261.136719 92.917969 262.558594 94.84375 263.269531 93.9375 L 303.492188 42.453125 C 304.203125 41.542969 306.308594 42.59375 305.597656 43.503906 L 264.730469 95.8125 C 264.023438 96.722656 265.902344 98.066406 266.609375 97.15625 L 307.707031 44.558594 C 308.414062 43.648438 310.523438 44.703125 309.8125 45.609375 L 268.488281 98.5 C 267.78125 99.410156 269.660156 100.75 270.367188 99.84375 L 311.917969 46.664062 C 312.628906 45.753906 314.734375 46.808594 314.023438 47.714844 L 272.246094 101.1875 C 271.539062 102.09375 273.511719 103.316406 274.222656 102.40625 L 316.128906 48.769531 C 316.839844 47.859375 318.945312 48.914062 318.234375 49.824219 L 276.328125 103.460938 C 275.617188 104.367188 277.722656 105.421875 278.433594 104.511719 L 320.339844 50.875 C 321.050781 49.964844 323.15625 51.019531 322.445312 51.929688 L 280.539062 105.566406 C 279.832031 106.472656 281.9375 107.527344 282.644531 106.617188 L 324.550781 52.980469 C 325.261719 52.070312 327.367188 53.125 326.65625 54.035156 L 284.753906 107.671875 C 284.042969 108.582031 286.148438 109.632812 286.859375 108.722656 L 328.765625 55.085938 C 329.472656 54.179688 331.582031 55.230469 330.871094 56.140625 L 288.964844 109.777344 C 288.253906 110.6875 290.359375 111.738281 291.070312 110.828125 L 332.976562 57.191406 C 333.6875 56.285156 335.792969 57.335938 335.082031 58.246094 L 293.175781 111.882812 C 292.464844 112.792969 294.570312 113.84375 295.28125 112.9375 L 337.1875 59.296875 C 337.898438 58.390625 340.003906 59.441406 339.292969 60.351562 L 297.386719 113.988281 C 296.675781 114.898438 298.78125 115.949219 299.492188 115.042969 L 341.398438 61.40625 C 342.109375 60.496094 344.214844 61.546875 343.503906 62.457031 L 301.597656 116.09375 C 300.890625 117.003906 302.996094 118.058594 303.703125 117.148438 L 345.609375 63.511719 C 346.320312 62.601562 348.425781 63.652344 347.714844 64.5625 L 305.8125 118.199219 C 305.101562 119.109375 307.207031 120.164062 307.917969 119.253906 L 349.824219 65.617188 C 350.53125 64.707031 352.640625 65.761719 351.929688 66.667969 L 310.023438 120.304688 C 309.3125 121.214844 311.417969 122.269531 312.128906 121.359375 L 354.035156 67.722656 C 354.746094 66.8125 356.851562 67.867188 356.140625 68.773438 L 314.234375 122.410156 C 313.523438 123.320312 315.628906 124.375 316.339844 123.464844 L 358.246094 69.828125 C 358.957031 68.917969 361.0625 69.972656 360.351562 70.882812 L 318.445312 124.519531 C 317.734375 125.425781 319.839844 126.480469 320.550781 125.570312 L 362.457031 71.933594 C 363.167969 71.023438 365.273438 72.078125 364.5625 72.988281 L 322.65625 126.625 C 321.949219 127.53125 324.054688 128.585938 324.761719 127.675781 L 366.667969 74.039062 C 367.378906 73.128906 369.484375 74.183594 368.773438 75.09375 L 326.871094 128.730469 C 326.160156 129.640625 328.265625 130.691406 328.976562 129.78125 L 370.882812 76.144531 C 371.589844 75.238281 373.699219 76.289062 372.988281 77.199219 L 331.082031 130.835938 C 330.371094 131.746094 332.476562 132.796875 333.1875 131.886719 L 375.046875 78.308594 C 375.757812 77.402344 377.636719 78.742188 376.925781 79.652344 L 335.292969 132.941406 C 334.582031 133.851562 336.6875 134.902344 337.398438 133.996094 L 378.804688 80.996094 C 379.515625 80.085938 381.394531 81.429688 380.683594 82.339844 L 339.503906 135.046875 C 338.792969 135.957031 340.898438 137.007812 341.609375 136.101562 L 382.5625 83.683594 C 383.273438 82.773438 384.90625 84.433594 384.195312 85.339844 L 343.714844 137.152344 C 343.007812 138.0625 345.203125 139 345.914062 138.089844 L 385.617188 87.269531 C 386.328125 86.359375 387.753906 88.285156 387.042969 89.195312 L 348.261719 138.832031 C 347.550781 139.742188 349.898438 140.488281 350.609375 139.578125 L 388.464844 91.121094 C 389.175781 90.214844 390.539062 92.21875 389.828125 93.128906 L 352.957031 140.320312 C 352.246094 141.230469 354.59375 141.972656 355.304688 141.0625 L 390.703125 95.753906 C 391.414062 94.847656 392.289062 97.472656 391.578125 98.382812 L 358.25 141.042969 C 357.539062 141.953125 360.484375 141.929688 361.195312 141.019531 L 392.453125 101.011719 C 393.164062 100.101562 393.800781 103.035156 393.089844 103.941406 L 364.140625 140.996094 C 363.429688 141.90625 366.78125 141.367188 367.492188 140.457031 L 393.121094 107.65625 C 393.828125 106.746094 393.859375 110.457031 393.148438 111.367188 L 371.449219 139.136719 C 370.742188 140.046875 374.699219 138.726562 375.410156 137.816406 L 392.671875 115.722656 "/> | |
<path style="fill:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 390.675781 122.027344 L 381.554688 133.703125 "/> | |
</g> | |
</svg> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment