33from enum import IntEnum
44from math import pi
55from cmath import phase , rect
6- from types import GeneratorType
76from typing import Callable
87from .metric import format_metric_unit
8+ import re
99
1010Cbox = namedtuple ('Cbox' , 'p1 p2 type id' )
1111BOMData = namedtuple ('BOMData' , 'type id data' )
@@ -69,7 +69,7 @@ def merge_colinear(points: list[tuple[complex, complex]]) -> list[tuple[complex,
6969 return out
7070
7171
72- def iterate_line (p1 : complex , p2 : complex , step : float = 1. ) -> GeneratorType :
72+ def iterate_line (p1 : complex , p2 : complex , step : float = 1. ):
7373 "Yields complex points along a line."
7474 vec = p2 - p1
7575 point = p1
@@ -96,19 +96,33 @@ def deep_transform(data, origin: complex, theta: float):
9696 "bad type to deep_transform(): " + type (data ).__name__ ) from err
9797
9898
99+ def fix_number (n : float ) -> str :
100+ """If n is an integer, remove the trailing ".0".
101+ Otherwise round it to 2 digits."""
102+ if n .is_integer ():
103+ return str (int (n ))
104+ return str (round (n , 4 ))
105+
106+
99107class XMLClass :
100108 def __getattr__ (self , tag ) -> Callable :
101109 def mk_tag (* contents , ** attrs ) -> str :
102110 out = f'<{ tag } '
103111 for k , v in attrs .items ():
104- if v is not False :
105- out += f'{ k .removesuffix ("_" ).replace ("__" , "-" )} ="{ v } " '
112+ if v is False :
113+ continue
114+ if isinstance (v , float ):
115+ v = fix_number (v )
116+ elif isinstance (v , str ):
117+ v = re .sub (r"\d+(\.\d+)" , lambda m : fix_number (float (m .group ())), v )
118+ out += f'{ k .removesuffix ("_" ).replace ("__" , "-" )} ="{ v } " '
106119 out = out .rstrip () + '>' + '' .join (contents )
107120 return out + f'</{ tag } >'
108121 return mk_tag
109122
110123
111124XML = XMLClass ()
125+ del XMLClass
112126
113127
114128def polylinegon (points : list [complex ], is_polygon : bool = False , ** options ):
@@ -224,6 +238,16 @@ def make_plus(
224238 class_ = "plus" )
225239
226240
241+ def arrow_points (p1 : complex , p2 : complex ) -> list [tuple [complex , complex ]]:
242+ "Return points to make an arrow from p1 pointing to p2."
243+ angle = phase (p2 - p1 )
244+ tick_len = min (0.25 , abs (p2 - p1 ))
245+ return [
246+ (p2 , p1 ),
247+ (p2 , p2 - rect (tick_len , angle + pi / 5 )),
248+ (p2 , p2 - rect (tick_len , angle - pi / 5 ))]
249+
250+
227251def make_variable (
228252 center : complex ,
229253 theta : float ,
@@ -232,28 +256,33 @@ def make_variable(
232256 "Draw a 'variable' arrow across the component."
233257 if not is_variable :
234258 return ""
235- theta = theta % pi
236- return bunch_o_lines (deep_transform ([
237- (- .6 + .5j , .75 - .5j ),
238- (.75 - .5j , .5 - .55j ),
239- (.75 - .5j , .7 - .25j ),
240- ], center , theta ), ** options )
241-
242-
243- # def arrows(
244- # center: complex,
245- # theta: float,
246- # out: bool,
247- # **options):
248- # """Draw arrows towards or away from the component
249- # (i.e.light-emitting or light-dependent)."""
250- # theta = theta % pi
251- # if out:
252- # return bunch_o_lines(deep_transform([
253- # (-.5-.6j, .5-1.2j),
254- # (.375-1.2j, .5-1.2j),
255- # (.5-1.)
256- # ], center, theta), **options)
259+ return bunch_o_lines (
260+ deep_transform (
261+ arrow_points (- 1 , 1 ),
262+ center ,
263+ (theta % pi ) + pi / 4 ),
264+ ** options )
265+
266+
267+ def light_arrows (
268+ center : complex ,
269+ theta : float ,
270+ out : bool ,
271+ ** options ):
272+ """Draw arrows towards or away from the component
273+ (i.e. light-emitting or light-dependent)."""
274+ a , b = 1j , .3 + .3j
275+ if out :
276+ a , b = b , a
277+ return bunch_o_lines (
278+ deep_transform (
279+ arrow_points (a , b ),
280+ center , theta - pi / 2 ),
281+ ** options ) + bunch_o_lines (
282+ deep_transform (
283+ arrow_points (a - .5 , b - .5 ),
284+ center , theta - pi / 2 ),
285+ ** options )
257286
258287
259288def sort_counterclockwise (terminals : list [Terminal ]) -> list [Terminal ]:
0 commit comments