@@ -22,7 +22,7 @@ class Side(IntEnum):
22
22
BOTTOM = 3
23
23
24
24
25
- def colinear (points : list [ complex ] ) -> bool :
25
+ def colinear (* points : complex ) -> bool :
26
26
"Returns true if all the points are in the same line."
27
27
return len (set (phase (p - points [0 ]) for p in points [1 :])) == 1
28
28
@@ -49,27 +49,45 @@ def intersecting(a, b, p, q):
49
49
return sort_a <= sort_p <= sort_b or sort_p <= sort_b <= sort_q
50
50
51
51
52
- # UNUSED as of yet
53
- def merge_colinear (
54
- points : list [tuple [complex , complex ]]
55
- ) -> list [tuple [complex , complex ]]:
56
- "Merges line segments that are colinear."
57
- points = list (set (points ))
58
- out = []
59
- a , b = points [0 ]
60
- while points :
61
- print (points )
62
- for pq in points [1 :]:
63
- p , q = pq
64
- if not (colinear ((a , b , p , q )) and intersecting (a , b , p , q )):
52
+ def take_next_group (links : list [tuple [complex , complex ]]) -> list [tuple [complex , complex ]]:
53
+ """Pops the longes possible link off of the `links` list and returns it,
54
+ mutating the input list."""
55
+ best = [links .pop ()]
56
+ while True :
57
+ for pair in links :
58
+ if best [0 ][0 ] == pair [1 ]:
59
+ best .insert (0 , pair )
60
+ links .remove (pair )
61
+ elif best [0 ][0 ] == pair [0 ]:
62
+ best .insert (0 , (pair [1 ], pair [0 ]))
63
+ links .remove (pair )
64
+ elif best [- 1 ][1 ] == pair [0 ]:
65
+ best .append (pair )
66
+ links .remove (pair )
67
+ elif best [- 1 ][1 ] == pair [1 ]:
68
+ best .append ((pair [1 ], pair [0 ]))
69
+ links .remove (pair )
70
+ else :
65
71
continue
66
- points .remove (pq )
67
- a , b = sorted ((a , b , p , q ), key = lambda x : x .real )[::3 ]
68
72
break
69
73
else :
70
- out .append ((a , b ))
71
- (a , b ), points = points [0 ], points [1 :]
72
- return out
74
+ break
75
+ return best
76
+
77
+
78
+ def merge_colinear (links : list [tuple [complex , complex ]]):
79
+ "Merges line segments that are colinear. Mutates the input list."
80
+ i = 1
81
+ while True :
82
+ if i == len (links ):
83
+ break
84
+ elif links [i ][0 ] == links [i ][1 ]:
85
+ links .remove (links [i ])
86
+ elif links [i - 1 ][1 ] == links [i ][0 ] and colinear (links [i - 1 ][0 ], links [i ][0 ], links [i ][1 ]):
87
+ links [i - 1 ] = (links [i - 1 ][0 ], links [i ][1 ])
88
+ links .remove (links [i ])
89
+ else :
90
+ i += 1
73
91
74
92
75
93
def iterate_line (p1 : complex , p2 : complex , step : float = 1.0 ):
@@ -93,7 +111,8 @@ def deep_transform(data, origin: complex, theta: float):
93
111
try :
94
112
return deep_transform (complex (data ), origin , theta )
95
113
except TypeError as err :
96
- raise TypeError ("bad type to deep_transform(): " + type (data ).__name__ ) from err
114
+ raise TypeError ("bad type to deep_transform(): " +
115
+ type (data ).__name__ ) from err
97
116
98
117
99
118
def fix_number (n : float ) -> str :
@@ -114,7 +133,8 @@ def mk_tag(*contents: str, **attrs: str) -> str:
114
133
if isinstance (v , float ):
115
134
v = fix_number (v )
116
135
elif isinstance (v , str ):
117
- v = re .sub (r"\d+(\.\d+)" , lambda m : fix_number (float (m .group ())), v )
136
+ v = re .sub (r"\d+(\.\d+)" ,
137
+ lambda m : fix_number (float (m .group ())), v )
118
138
out += f'{ k .removesuffix ("_" ).replace ("__" , "-" )} ="{ v } " '
119
139
out = out .rstrip () + ">" + "" .join (contents )
120
140
return out + f"</{ tag } >"
@@ -126,7 +146,7 @@ def mk_tag(*contents: str, **attrs: str) -> str:
126
146
del XMLClass
127
147
128
148
129
- def polylinegon (points : list [complex ], is_polygon : bool = False , ** options ):
149
+ def polylinegon (points : list [complex ], is_polygon : bool = False , ** options ) -> str :
130
150
"Turn the list of points into a <polyline> or <polygon>."
131
151
scale = options ["scale" ]
132
152
w = options ["stroke_width" ]
@@ -155,20 +175,23 @@ def find_dots(points: list[tuple[complex, complex]]) -> list[complex]:
155
175
return [pt for pt , count in seen .items () if count > 3 ]
156
176
157
177
158
- def bunch_o_lines (points : list [tuple [complex , complex ]], ** options ):
159
- "Return a <polyline> for each pair of points ."
178
+ def bunch_o_lines (pairs : list [tuple [complex , complex ]], ** options ) -> str :
179
+ "Collapse the pairs of points and return the smallest number of <polyline>s ."
160
180
out = ""
161
181
scale = options ["scale" ]
162
182
w = options ["stroke_width" ]
163
183
c = options ["stroke" ]
164
- for p1 , p2 in points :
165
- if abs (p1 - p2 ) == 0 :
166
- continue
184
+ lines = []
185
+ while pairs :
186
+ group = take_next_group (pairs )
187
+ merge_colinear (group )
188
+ # make it a polyline
189
+ pts = [group [0 ][0 ]] + [p [1 ] for p in group ]
190
+ lines .append (pts )
191
+ for line in lines :
167
192
out += XML .polyline (
168
- points = f"{ p1 .real * scale } ,"
169
- f"{ p1 .imag * scale } "
170
- f"{ p2 .real * scale } ,"
171
- f"{ p2 .imag * scale } " ,
193
+ points = " " .join (
194
+ f"{ p .real * scale } ,{ p .imag * scale } " for p in line ),
172
195
stroke = c ,
173
196
stroke__width = w ,
174
197
)
@@ -182,7 +205,7 @@ def id_text(
182
205
unit : str | list [str ] | None ,
183
206
point : complex | None = None ,
184
207
** options ,
185
- ):
208
+ ) -> str :
186
209
"Format the component ID and value around the point."
187
210
if options ["nolabels" ]:
188
211
return ""
@@ -215,9 +238,11 @@ def id_text(
215
238
else "middle"
216
239
)
217
240
else :
218
- textach = "middle" if terminals [0 ].side in (Side .TOP , Side .BOTTOM ) else "start"
241
+ textach = "middle" if terminals [0 ].side in (
242
+ Side .TOP , Side .BOTTOM ) else "start"
219
243
return XML .text (
220
- (XML .tspan (f"{ box .type } { box .id } " , class_ = "cmp-id" ) * bool ("L" in label_style )),
244
+ (XML .tspan (f"{ box .type } { box .id } " , class_ = "cmp-id" )
245
+ * bool ("L" in label_style )),
221
246
" " * (bool (data ) and "L" in label_style ),
222
247
data * bool ("V" in label_style ),
223
248
x = point .real ,
@@ -238,9 +263,7 @@ def make_text_point(t1: complex, t2: complex, **options) -> complex:
238
263
return text_pt
239
264
240
265
241
- def make_plus (
242
- terminals : list [Terminal ], center : complex , theta : float , ** options
243
- ) -> str :
266
+ def make_plus (terminals : list [Terminal ], center : complex , theta : float , ** options ) -> str :
244
267
"Make a + sign if the terminals indicate the component is polarized."
245
268
if all (t .flag != "+" for t in terminals ):
246
269
return ""
@@ -268,9 +291,7 @@ def arrow_points(p1: complex, p2: complex) -> list[tuple[complex, complex]]:
268
291
]
269
292
270
293
271
- def make_variable (
272
- center : complex , theta : float , is_variable : bool = True , ** options
273
- ) -> str :
294
+ def make_variable (center : complex , theta : float , is_variable : bool = True , ** options ) -> str :
274
295
"Draw a 'variable' arrow across the component."
275
296
if not is_variable :
276
297
return ""
@@ -319,9 +340,7 @@ def is_clockwise(terminals: list[Terminal]) -> bool:
319
340
return False
320
341
321
342
322
- def sort_for_flags (
323
- terminals : list [Terminal ], box : Cbox , * flags : list [str ]
324
- ) -> list [Terminal ]:
343
+ def sort_for_flags (terminals : list [Terminal ], box : Cbox , * flags : list [str ]) -> list [Terminal ]:
325
344
"""Sorts out the terminals in the specified order using the flags.
326
345
Raises and error if the flags are absent."""
327
346
out = ()
0 commit comments