1
- import cmath
1
+ import collections as _collections
2
2
3
- s = """
3
+ strings = [
4
+ """
4
5
#
5
6
###
6
7
#####
7
8
#######
8
9
#####
9
10
###
10
11
#
11
- """
12
- pts = [complex (c , r )
13
- for (r , rt ) in enumerate (s .splitlines ())
14
- for (c , ch ) in enumerate (rt )
15
- if ch == "#" ]
16
-
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
+ #""" ]
17
55
18
- def centroid (pts : list [complex ]) -> complex :
19
- return sum (pts ) / len (pts )
20
56
57
+ def sinker (pts : list [complex ]) -> list [complex ]:
58
+ last = None
59
+ out = []
60
+ for p in pts :
61
+ if not last or last .real != p .real :
62
+ out .append (p )
63
+ last = p
64
+ out .append (p )
65
+ return out
21
66
22
- def sort_counterclockwise (pts : list [complex ],
23
- center : complex | None = None ) -> list [complex ]:
24
- if center is None :
25
- center = centroid (pts )
26
- return sorted (pts , key = lambda p : cmath .phase (p - center ))
27
67
68
+ def get_outline_points (pts : list [complex ]) -> list [complex ]:
69
+ by_y = _collections .defaultdict (list )
70
+ for p in pts :
71
+ by_y [p .imag ].append (p .real )
72
+ left_side = sinker ([complex (min (row ), y )
73
+ for y , row in sorted (by_y .items ())])
74
+ right_side = sinker ([complex (max (row ), y )
75
+ for y , row in sorted (by_y .items (), reverse = True )])
28
76
29
- def perimeter (pts : list [complex ]) -> list [complex ]:
30
- out = []
31
- for pt in pts :
32
- for d in (- 1 , 1 , - 1j , 1j , - 1 + 1j , 1 + 1j , - 1 - 1j , 1 - 1j ):
33
- xp = pt + d
34
- if xp not in pts :
35
- out .append (pt )
36
- break
37
- return sort_counterclockwise (out , centroid (pts ))
77
+ return left_side + right_side
38
78
39
79
40
80
def example (all_points : list [complex ], scale : float = 20 ) -> str :
41
- p = perimeter (all_points )
42
- p .append (p [0 ])
43
- vbx = max (map (lambda x : x .real , p )) + 1
44
- vby = max (map (lambda x : x .imag , p )) + 1
81
+ p = get_outline_points (all_points )
82
+ vbx = max (map (lambda x : x .real , p ))
83
+ vby = max (map (lambda x : x .imag , p ))
45
84
return f"""<svg
46
- viewBox="-1 -1 { vbx } { vby } "
85
+ viewBox="-1 -1 { vbx + 1 } { vby + 1 } "
47
86
width="{ vbx * scale } "
48
- height="{ vbx * scale } ">
87
+ height="{ vby * scale } ">
49
88
<polyline
50
89
fill="none"
51
90
stroke="black"
@@ -54,4 +93,9 @@ def example(all_points: list[complex], scale: float = 20) -> str:
54
93
</polyline></svg>"""
55
94
56
95
57
- print (example (pts ))
96
+ for s in strings :
97
+ pts = [complex (c , r )
98
+ for (r , rt ) in enumerate (s .splitlines ())
99
+ for (c , ch ) in enumerate (rt )
100
+ if ch == "#" ]
101
+ print (example (pts ))
0 commit comments