Skip to content

Commit 6d70fb9

Browse files
add capacitor and refactor units definitions
1 parent 9c858e0 commit 6d70fb9

File tree

4 files changed

+95
-64
lines changed

4 files changed

+95
-64
lines changed

schemascii/components/__init__.py

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,34 @@
1+
from __future__ import annotations
12
import typing
23
from dataclasses import dataclass
34

45
import schemascii.component as _c
56
import schemascii.errors as _errors
7+
import schemascii.utils as _utils
8+
9+
10+
class SimpleComponent:
11+
"""Component mixin class that simplifies the formatting
12+
of the various values and their units into the id_text.
13+
"""
14+
15+
value_format: typing.ClassVar[list[tuple[str, str]
16+
| tuple[str, str, bool]
17+
| tuple[str, str, bool, bool]]]
18+
19+
def format_id_text(self: _c.Component | SimpleComponent,
20+
textpoint: complex, **options):
21+
val_fmt = []
22+
for valsch in self.value_format:
23+
val_fmt.append((options[valsch[0]], *valsch[1:]))
24+
try:
25+
id_text = _utils.id_text(self.rd.name, self.terminals,
26+
val_fmt, textpoint, **options)
27+
except ValueError as e:
28+
raise _errors.BOMError(
29+
f"{self.rd.name}: Range of values not allowed "
30+
"on fixed-value component") from e
31+
return id_text
632

733

834
@dataclass
@@ -21,8 +47,10 @@ def __post_init__(self):
2147

2248

2349
class TwoTerminalComponent(NTerminalComponent):
24-
"""Shortcut to define a component with two terminals."""
25-
n_terminals: typing.Final = 2
50+
"""Shortcut to define a component with two terminals, and one primary
51+
value, that may or may not be variable."""
52+
n_terminals: typing.Final[int] = 2
53+
is_variable: typing.ClassVar[bool] = False
2654

2755

2856
@dataclass
@@ -44,3 +72,7 @@ def __post_init__(self):
4472
if self.terminals[1].flag == "+":
4573
# swap first and last
4674
self.terminals.insert(0, self.terminals.pop(-1))
75+
76+
@property
77+
def is_polarized(self) -> bool:
78+
return any(t.flag == "+" for t in self.terminals)

schemascii/components/capacitor.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
from cmath import phase, rect
2+
3+
import schemascii.components as _c
4+
import schemascii.data_consumer as _dc
5+
import schemascii.utils as _utils
6+
7+
8+
class Capacitor(_c.PolarizedTwoTerminalComponent, _c.SimpleComponent,
9+
ids=("C",), namespaces=(":capacitor",)):
10+
options = [
11+
"inherit",
12+
_dc.Option("value", str, "Capacitance in farads"),
13+
_dc.Option("voltage", str, "Maximum voltage tolerance in volts", None)
14+
]
15+
16+
@property
17+
def value_format(self):
18+
return [("value", "F", True, self.is_variable),
19+
("voltage", "V", False)]
20+
21+
def render(self, **options) -> str:
22+
t1, t2 = self.terminals[0].pt, self.terminals[1].pt
23+
mid = (t1 + t2) / 2
24+
angle = phase(t1 - t2)
25+
lines = [
26+
(t1, mid + rect(1/4, angle)),
27+
(t2, mid + rect(-1/4, angle)),
28+
*_utils.deep_transform([
29+
(complex(2/5, 1/4), complex(-2/5, 1/4)),
30+
(complex(2/5, -1/4), complex(-2/5, -1/4)),
31+
], mid, angle)
32+
]
33+
return (_utils.bunch_o_lines(lines, **options)
34+
+ (_utils.make_plus(self.terminals, mid, angle, **options)
35+
if self.is_polarized else "")
36+
+ self.format_id_text(
37+
_utils.make_text_point(t1, t2, **options), **options))
38+
39+
40+
class VariableCapacitor(Capacitor, ids=("VC", "CV")):
41+
is_variable = True
42+
43+
def render(self, **options):
44+
t1, t2 = self.terminals[0].pt, self.terminals[1].pt
45+
return (super().render(**options)
46+
+ _utils.make_variable(
47+
(t1 + t2) / 2, phase(t1 - t2), **options))

schemascii/components/resistor.py

Lines changed: 11 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,8 @@
33
import schemascii.components as _c
44
import schemascii.data_consumer as _dc
55
import schemascii.utils as _utils
6-
import schemascii.errors as _errors
76

8-
# TODO: IEC rectangular symbol
7+
# TODO: IEC rectangular symbol, other variable markings?
98
# see here: https://eepower.com/resistor-guide/resistor-standards-and-codes/resistor-symbols/ # noqa: E501
109

1110

@@ -22,30 +21,26 @@ def _ansi_resistor_squiggle(t1: complex, t2: complex) -> list[complex]:
2221
return points
2322

2423

25-
class Resistor(_c.TwoTerminalComponent, ids=("R",), namespaces=(":resistor",)):
24+
class Resistor(_c.TwoTerminalComponent, _c.SimpleComponent,
25+
ids=("R",), namespaces=(":resistor",)):
2626
options = [
2727
"inherit",
2828
_dc.Option("value", str, "Resistance in ohms"),
2929
_dc.Option("power", str, "Maximum power dissipation in watts "
3030
"(i.e. size of the resistor)", None)
3131
]
3232

33-
is_variable = False
33+
@property
34+
def value_format(self):
35+
return [("value", "Ω", False, self.is_variable),
36+
("power", "W", False)]
3437

35-
def render(self, value: str, power: str, **options) -> str:
38+
def render(self, **options) -> str:
3639
t1, t2 = self.terminals[0].pt, self.terminals[1].pt
3740
points = _ansi_resistor_squiggle(t1, t2)
38-
try:
39-
id_text = _utils.id_text(self.rd.name, self.terminals,
40-
((value, "Ω", False, self.is_variable),
41-
(power, "W", False)),
42-
_utils.make_text_point(t1, t2, **options),
43-
**options)
44-
except ValueError as e:
45-
raise _errors.BOMError(
46-
f"{self.rd.name}: Range of values not allowed "
47-
"on fixed resistor") from e
48-
return _utils.polylinegon(points, **options) + id_text
41+
return (_utils.polylinegon(points, **options)
42+
+ self.format_id_text(
43+
_utils.make_text_point(t1, t2, **options), **options))
4944

5045

5146
class VariableResistor(Resistor, ids=("VR", "RV")):
@@ -58,7 +53,3 @@ def render(self, **options):
5853
(t1 + t2) / 2, phase(t1 - t2), **options))
5954

6055
# TODO: potentiometers
61-
62-
63-
if __name__ == "__main__":
64-
print(Resistor.all_components)

schemascii/components_render.py

Lines changed: 3 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,7 @@ def n_check(
5454
if len(terminals) != n_terminals:
5555
raise TerminalsError(
5656
f"{box.type}{box.id} component can only "
57-
f"have {n_terminals} terminals"
58-
)
57+
f"have {n_terminals} terminals")
5958
return func(box, terminals, bom_data, **options)
6059

6160
return n_check
@@ -90,54 +89,16 @@ def sort_terminals(
9089
bom_data: list[BOMData], **options):
9190
if len(terminals) != 2:
9291
raise TerminalsError(
93-
f"{box.type}{box.id} component can only " f"have 2 terminals"
94-
)
92+
f"{box.type}{box.id} component can only have 2 terminals")
9593
if terminals[1].flag == "+":
9694
terminals[0], terminals[1] = terminals[1], terminals[0]
9795
return func(box, terminals, bom_data, **options)
9896

99-
sort_terminals.__doc__ = func.__doc__
10097
return sort_terminals
10198

10299

103-
@component("C", "CV", "VC")
104-
@n_terminal(2)
105-
@no_ambiguous
106-
def capacitor(box: Cbox, terminals: list[Terminal],
107-
bom_data: BOMData, **options):
108-
"""Draw a capacitor, variable capacitor, etc.
109-
bom:farads[,volts]
110-
flags:+=positive"""
111-
t1, t2 = terminals[0].pt, terminals[1].pt
112-
mid = (t1 + t2) / 2
113-
angle = phase(t1 - t2)
114-
lines = [
115-
(t1, mid + rect(0.25, angle)),
116-
(t2, mid + rect(-0.25, angle)),
117-
] + deep_transform(
118-
[
119-
(complex(0.4, 0.25), complex(-0.4, 0.25)),
120-
(complex(0.4, -0.25), complex(-0.4, -0.25)),
121-
],
122-
mid,
123-
angle,
124-
)
125-
return (
126-
bunch_o_lines(lines, **options)
127-
+ make_plus(terminals, mid, angle, **options)
128-
+ make_variable(mid, angle, "V" in box.type, **options)
129-
+ id_text(
130-
box,
131-
bom_data,
132-
terminals,
133-
(("F", True), ("V", False)),
134-
make_text_point(t1, t2, **options),
135-
**options,
136-
)
137-
)
138-
139-
140100
@component("L", "VL", "LV")
101+
@n_terminal(2)
141102
@no_ambiguous
142103
def inductor(box: Cbox, terminals: list[Terminal],
143104
bom_data: BOMData, **options):

0 commit comments

Comments
 (0)