Skip to content

Commit 85a3ac5

Browse files
AlexboiboiBoisselet Alexandre (IFAT DC ATV SC D TE2)OrtnerMichael
authoredAug 23, 2023
Add sizemode property for Sensor, pixel and Dipole (magpylib#658)
* fix zoom on extra backend traces * fix str text labels * refactor default style description * implement sizemodes * fix multi trimesh line merges * move tight layout to figure kwarg to avoid warning --------- Co-authored-by: Boisselet Alexandre (IFAT DC ATV SC D TE2) <[email protected]> Co-authored-by: mortner <[email protected]>
1 parent b9c8be1 commit 85a3ac5

File tree

5 files changed

+118
-24
lines changed

5 files changed

+118
-24
lines changed
 

‎magpylib/_src/defaults/defaults_values.py

+17-3
Original file line numberDiff line numberDiff line change
@@ -89,18 +89,32 @@
8989
},
9090
"sensor": {
9191
"size": 1,
92-
"pixel": {"size": 1, "color": None, "symbol": "o"},
92+
"sizemode": "scaled",
93+
"pixel": {
94+
"size": 1,
95+
"sizemode": "scaled",
96+
"color": None,
97+
"symbol": "o",
98+
},
9399
"arrows": {
94100
"x": {"color": "red"},
95101
"y": {"color": "green"},
96102
"z": {"color": "blue"},
97103
},
98104
},
99-
"dipole": {"size": 1, "pivot": "middle"},
105+
"dipole": {"size": 1, "sizemode": "scaled", "pivot": "middle"},
100106
"triangle": {
101107
"magnetization": {
102108
"show": True,
103-
"size": 1,
109+
"arrow": {
110+
"show": True,
111+
"size": 1,
112+
"sizemode": "scaled",
113+
"offset": 1,
114+
"width": 2,
115+
"style": "solid",
116+
"color": None,
117+
},
104118
"color": {
105119
"north": "#E71111",
106120
"middle": "#DDDDDD",

‎magpylib/_src/display/backend_matplotlib.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ def display_matplotlib(
228228
if legend_maxitems != 0:
229229
ratio *= 1.5 # extend horizontal ratio if legend is present
230230
fig_kwargs["figsize"] = (figsize[0] * ratio, figsize[1])
231-
fig = plt.figure(**fig_kwargs)
231+
fig = plt.figure(**{"tight_layout": True, **fig_kwargs})
232232
elif isinstance(canvas, matplotlib.axes.Axes):
233233
fig = canvas.get_figure()
234234
if max_rows is not None or max_cols is not None:
@@ -339,7 +339,7 @@ def animate(ind): # pragma: no cover
339339
blit=False,
340340
repeat=repeat,
341341
)
342-
plt.tight_layout()
342+
343343
out = ()
344344
if return_fig:
345345
show_canvas = False

‎magpylib/_src/display/traces_core.py

+9-7
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ def make_Dipole(obj, autosize=None, **kwargs) -> Dict[str, Any]:
153153
moment = np.array([0.0, 0.0, 0.0]) if obj.moment is None else obj.moment
154154
moment_mag = np.linalg.norm(moment)
155155
size = style.size
156-
if autosize is not None:
156+
if autosize is not None and style.sizemode == "scaled":
157157
size *= autosize
158158
if moment_mag == 0:
159159
trace = create_null_dim_trace(color=style.color)
@@ -557,7 +557,7 @@ def make_Sensor(obj, autosize=None, **kwargs) -> Dict[str, Any]:
557557
)
558558
no_pix = pixel.shape[0] == 1 and (pixel == 0).all()
559559
one_pix = pixel.shape[0] == 1 and not (pixel == 0).all()
560-
if autosize is not None:
560+
if autosize is not None and style.sizemode == "scaled":
561561
dim *= autosize
562562
if no_pix:
563563
dim_ext = dim
@@ -576,11 +576,13 @@ def make_Sensor(obj, autosize=None, **kwargs) -> Dict[str, Any]:
576576
if not no_pix:
577577
pixel_color = style.pixel.color
578578
pixel_size = style.pixel.size
579-
combs = np.array(list(combinations(pixel, 2)))
580-
vecs = np.diff(combs, axis=1)
581-
dists = np.linalg.norm(vecs, axis=2)
582-
min_dist = np.min(dists)
583-
pixel_dim = dim_ext / 5 if min_dist == 0 else min_dist / 2
579+
pixel_dim = 1
580+
if style.pixel.sizemode == "scaled":
581+
combs = np.array(list(combinations(pixel, 2)))
582+
vecs = np.diff(combs, axis=1)
583+
dists = np.linalg.norm(vecs, axis=2)
584+
min_dist = np.min(dists)
585+
pixel_dim = dim_ext / 5 if min_dist == 0 else min_dist / 2
584586
if pixel_size > 0:
585587
pixel_dim *= pixel_size
586588
poss = pixel[1:] if one_pix else pixel

‎magpylib/_src/style.py

+89-11
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
from magpylib._src.defaults.defaults_utility import validate_property_class
1414
from magpylib._src.defaults.defaults_utility import validate_style_keys
1515

16+
ALLOWED_SIZEMODES = ("scaled", "absolute")
17+
1618

1719
def get_families(obj):
1820
"""get obj families"""
@@ -1493,6 +1495,10 @@ class SensorProperties:
14931495
size: float, default=None
14941496
Positive float for ratio of sensor to canvas size.
14951497
1498+
sizemode: {'scaled', 'absolute'}, default='scaled'
1499+
Defines the scale reference for the sensor size. If 'absolute', the `size` parameters
1500+
becomes the sensor size in millimeters.
1501+
14961502
pixel: dict, Pixel, default=None
14971503
`Pixel` object or dict with equivalent key/value pairs (e.g. `color`, `size`).
14981504
@@ -1513,6 +1519,19 @@ def size(self, val):
15131519
)
15141520
self._size = val
15151521

1522+
@property
1523+
def sizemode(self):
1524+
"""Sizemode of the sensor."""
1525+
return self._sizemode
1526+
1527+
@sizemode.setter
1528+
def sizemode(self, val):
1529+
assert val is None or val in ALLOWED_SIZEMODES, (
1530+
f"The `sizemode` property of {type(self).__name__} must be a one of "
1531+
f"{ALLOWED_SIZEMODES},\nbut received {repr(val)} instead."
1532+
)
1533+
self._sizemode = val
1534+
15161535
@property
15171536
def pixel(self):
15181537
"""`Pixel` object or dict with equivalent key/value pairs (e.g. `color`, `size`)."""
@@ -1540,15 +1559,32 @@ class DefaultSensor(MagicProperties, SensorProperties):
15401559
size: float, default=None
15411560
Positive float for ratio of sensor to canvas size.
15421561
1562+
sizemode: {'scaled', 'absolute'}, default='scaled'
1563+
Defines the scale reference for the sensor size. If 'absolute', the `size` parameters
1564+
becomes the sensor size in millimeters.
1565+
15431566
pixel: dict, Pixel, default=None
15441567
`Pixel` object or dict with equivalent key/value pairs (e.g. `color`, `size`).
15451568
15461569
arrows: dict, ArrowCS, default=None
15471570
`ArrowCS` object or dict with equivalent key/value pairs (e.g. `color`, `size`).
15481571
"""
15491572

1550-
def __init__(self, size=None, pixel=None, arrows=None, **kwargs):
1551-
super().__init__(size=size, pixel=pixel, arrows=arrows, **kwargs)
1573+
def __init__(
1574+
self,
1575+
size=None,
1576+
sizemode=None,
1577+
pixel=None,
1578+
arrows=None,
1579+
**kwargs,
1580+
):
1581+
super().__init__(
1582+
size=size,
1583+
sizemode=sizemode,
1584+
pixel=pixel,
1585+
arrows=arrows,
1586+
**kwargs,
1587+
)
15521588

15531589

15541590
class SensorStyle(BaseStyle, SensorProperties):
@@ -1602,6 +1638,10 @@ class Pixel(MagicProperties):
16021638
- matplotlib backend: Pixel size is the marker size.
16031639
- plotly backend: Relative distance to nearest neighbor pixel.
16041640
1641+
sizemode: {'scaled', 'absolute'}, default='scaled'
1642+
Defines the scale reference for the pixel size. If 'absolute', the `size` parameters
1643+
becomes the pixel size in millimeters.
1644+
16051645
color: str, default=None
16061646
Defines the pixel color@property.
16071647
@@ -1610,8 +1650,14 @@ class Pixel(MagicProperties):
16101650
Only applies for matplotlib plotting backend.
16111651
"""
16121652

1613-
def __init__(self, size=1, color=None, symbol=None, **kwargs):
1614-
super().__init__(size=size, color=color, symbol=symbol, **kwargs)
1653+
def __init__(self, size=1, sizemode=None, color=None, symbol=None, **kwargs):
1654+
super().__init__(
1655+
size=size,
1656+
sizemode=sizemode,
1657+
color=color,
1658+
symbol=symbol,
1659+
**kwargs,
1660+
)
16151661

16161662
@property
16171663
def size(self):
@@ -1628,6 +1674,19 @@ def size(self, val):
16281674
)
16291675
self._size = val
16301676

1677+
@property
1678+
def sizemode(self):
1679+
"""Sizemode of the pixel."""
1680+
return self._sizemode
1681+
1682+
@sizemode.setter
1683+
def sizemode(self, val):
1684+
assert val is None or val in ALLOWED_SIZEMODES, (
1685+
f"The `sizemode` property of {type(self).__name__} must be a one of "
1686+
f"{ALLOWED_SIZEMODES},\nbut received {repr(val)} instead."
1687+
)
1688+
self._sizemode = val
1689+
16311690
@property
16321691
def color(self):
16331692
"""Pixel color."""
@@ -1763,8 +1822,6 @@ class Arrow(Line):
17631822
Positive number that defines the line width.
17641823
"""
17651824

1766-
_allowed_sizemodes = ("scaled", "absolute")
1767-
17681825
def __init__(self, show=None, size=None, **kwargs):
17691826
super().__init__(show=show, size=size, **kwargs)
17701827

@@ -1796,14 +1853,14 @@ def size(self, val):
17961853

17971854
@property
17981855
def sizemode(self):
1799-
"""Positive number defining the sizemode of the arrows."""
1856+
"""Sizemode of the arrows."""
18001857
return self._sizemode
18011858

18021859
@sizemode.setter
18031860
def sizemode(self, val):
1804-
assert val is None or val in self._allowed_sizemodes, (
1861+
assert val is None or val in ALLOWED_SIZEMODES, (
18051862
f"The `sizemode` property of {type(self).__name__} must be a one of "
1806-
f"{self._allowed_sizemodes},\nbut received {repr(val)} instead."
1863+
f"{ALLOWED_SIZEMODES},\nbut received {repr(val)} instead."
18071864
)
18081865
self._sizemode = val
18091866

@@ -1941,6 +1998,10 @@ class DipoleProperties:
19411998
size: float
19421999
Positive value for ratio of dipole size to canvas size.
19432000
2001+
sizemode: {'scaled', 'absolute'}, default='scaled'
2002+
Defines the scale reference for the dipole size. If 'absolute', the `size` parameters
2003+
becomes the dipole size in millimeters.
2004+
19442005
pivot: str
19452006
The part of the arrow that is anchored to the X, Y grid.
19462007
The arrow rotates about this point. Can be one of `['tail', 'middle', 'tip']`.
@@ -1961,6 +2022,19 @@ def size(self, val):
19612022
)
19622023
self._size = val
19632024

2025+
@property
2026+
def sizemode(self):
2027+
"""Sizemode of the dipole."""
2028+
return self._sizemode
2029+
2030+
@sizemode.setter
2031+
def sizemode(self, val):
2032+
assert val is None or val in ALLOWED_SIZEMODES, (
2033+
f"The `sizemode` property of {type(self).__name__} must be a one of "
2034+
f"{ALLOWED_SIZEMODES},\nbut received {repr(val)} instead."
2035+
)
2036+
self._sizemode = val
2037+
19642038
@property
19652039
def pivot(self):
19662040
"""The part of the arrow that is anchored to the X, Y grid.
@@ -1987,13 +2061,17 @@ class DefaultDipole(MagicProperties, DipoleProperties):
19872061
size: float, default=None
19882062
Positive float for ratio of dipole size to canvas size.
19892063
2064+
sizemode: {'scaled', 'absolute'}, default='scaled'
2065+
Defines the scale reference for the dipole size. If 'absolute', the `size` parameters
2066+
becomes the dipole size in millimeters.
2067+
19902068
pivot: str, default=None
19912069
The part of the arrow that is anchored to the X, Y grid.
19922070
The arrow rotates about this point. Can be one of `['tail', 'middle', 'tip']`.
19932071
"""
19942072

1995-
def __init__(self, size=None, pivot=None, **kwargs):
1996-
super().__init__(size=size, pivot=pivot, **kwargs)
2073+
def __init__(self, size=None, sizemode=None, pivot=None, **kwargs):
2074+
super().__init__(size=size, sizemode=sizemode, pivot=pivot, **kwargs)
19972075

19982076

19992077
class DipoleStyle(BaseStyle, DipoleProperties):

‎tests/test_obj_BaseGeo.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -481,7 +481,7 @@ def test_describe():
481481
+ "model3d=Model3d(data=[], showdefault=True), opacity=None, path=Path(frames=None,"
482482
+ " line=Line(color=None, style=None, width=None), marker=Marker(color=None,"
483483
+ " size=None, symbol=None), numbering=None, show=None), pixel=Pixel(color=None,"
484-
+ " size=1, symbol=None), size=None) "
484+
+ " size=1, sizemode=None, symbol=None), size=None, sizemode=None) "
485485
)
486486
desc = re.sub("id=*[0-9]*[0-9]", "id=REGEX", desc)
487487
assert desc == test

0 commit comments

Comments
 (0)
Please sign in to comment.