Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 4e8c7fe

Browse files
authored
Merge pull request #7873 from QuLogic/LockedBbox
ENH: Add a LockableBbox type.
2 parents c38205a + 56362fe commit 4e8c7fe

File tree

2 files changed

+159
-0
lines changed

2 files changed

+159
-0
lines changed

lib/matplotlib/tests/test_transforms.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -600,3 +600,37 @@ def test_transformed_patch_path():
600600
# from the transform).
601601
patch.set_radius(0.5)
602602
assert_allclose(tpatch.get_fully_transformed_path().vertices, points)
603+
604+
605+
@pytest.mark.parametrize('locked_element', ['x0', 'y0', 'x1', 'y1'])
606+
def test_lockable_bbox(locked_element):
607+
other_elements = ['x0', 'y0', 'x1', 'y1']
608+
other_elements.remove(locked_element)
609+
610+
orig = mtransforms.Bbox.unit()
611+
locked = mtransforms.LockableBbox(orig, **{locked_element: 2})
612+
613+
# LockableBbox should keep its locked element as specified in __init__.
614+
assert getattr(locked, locked_element) == 2
615+
assert getattr(locked, 'locked_' + locked_element) == 2
616+
for elem in other_elements:
617+
assert getattr(locked, elem) == getattr(orig, elem)
618+
619+
# Changing underlying Bbox should update everything but locked element.
620+
orig.set_points(orig.get_points() + 10)
621+
assert getattr(locked, locked_element) == 2
622+
assert getattr(locked, 'locked_' + locked_element) == 2
623+
for elem in other_elements:
624+
assert getattr(locked, elem) == getattr(orig, elem)
625+
626+
# Unlocking element should revert values back to the underlying Bbox.
627+
setattr(locked, 'locked_' + locked_element, None)
628+
assert getattr(locked, 'locked_' + locked_element) is None
629+
assert np.all(orig.get_points() == locked.get_points())
630+
631+
# Relocking an element should change its value, but not others.
632+
setattr(locked, 'locked_' + locked_element, 3)
633+
assert getattr(locked, locked_element) == 3
634+
assert getattr(locked, 'locked_' + locked_element) == 3
635+
for elem in other_elements:
636+
assert getattr(locked, elem) == getattr(orig, elem)

lib/matplotlib/transforms.py

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1095,6 +1095,131 @@ def get_points(self):
10951095
return points
10961096

10971097

1098+
class LockableBbox(BboxBase):
1099+
"""
1100+
A :class:`Bbox` where some elements may be locked at certain values.
1101+
1102+
When the child bounding box changes, the bounds of this bbox will update
1103+
accordingly with the exception of the locked elements.
1104+
"""
1105+
def __init__(self, bbox, x0=None, y0=None, x1=None, y1=None, **kwargs):
1106+
"""
1107+
Parameters
1108+
----------
1109+
bbox : Bbox
1110+
The child bounding box to wrap.
1111+
1112+
x0 : float or None
1113+
The locked value for x0, or None to leave unlocked.
1114+
1115+
y0 : float or None
1116+
The locked value for y0, or None to leave unlocked.
1117+
1118+
x1 : float or None
1119+
The locked value for x1, or None to leave unlocked.
1120+
1121+
y1 : float or None
1122+
The locked value for y1, or None to leave unlocked.
1123+
1124+
"""
1125+
if not bbox.is_bbox:
1126+
raise ValueError("'bbox' is not a bbox")
1127+
1128+
BboxBase.__init__(self, **kwargs)
1129+
self._bbox = bbox
1130+
self.set_children(bbox)
1131+
self._points = None
1132+
fp = [x0, y0, x1, y1]
1133+
mask = [val is None for val in fp]
1134+
self._locked_points = np.ma.array(fp, np.float_,
1135+
mask=mask).reshape((2, 2))
1136+
1137+
def __repr__(self):
1138+
return "LockableBbox(%r, %r)" % (self._bbox, self._locked_points)
1139+
1140+
def get_points(self):
1141+
if self._invalid:
1142+
points = self._bbox.get_points()
1143+
self._points = np.where(self._locked_points.mask,
1144+
points,
1145+
self._locked_points)
1146+
self._invalid = 0
1147+
return self._points
1148+
get_points.__doc__ = Bbox.get_points.__doc__
1149+
1150+
if DEBUG:
1151+
_get_points = get_points
1152+
1153+
def get_points(self):
1154+
points = self._get_points()
1155+
self._check(points)
1156+
return points
1157+
1158+
@property
1159+
def locked_x0(self):
1160+
"""
1161+
float or None: The value used for the locked x0.
1162+
"""
1163+
if self._locked_points.mask[0, 0]:
1164+
return None
1165+
else:
1166+
return self._locked_points[0, 0]
1167+
1168+
@locked_x0.setter
1169+
def locked_x0(self, x0):
1170+
self._locked_points.mask[0, 0] = x0 is None
1171+
self._locked_points.data[0, 0] = x0
1172+
self.invalidate()
1173+
1174+
@property
1175+
def locked_y0(self):
1176+
"""
1177+
float or None: The value used for the locked y0.
1178+
"""
1179+
if self._locked_points.mask[0, 1]:
1180+
return None
1181+
else:
1182+
return self._locked_points[0, 1]
1183+
1184+
@locked_y0.setter
1185+
def locked_y0(self, y0):
1186+
self._locked_points.mask[0, 1] = y0 is None
1187+
self._locked_points.data[0, 1] = y0
1188+
self.invalidate()
1189+
1190+
@property
1191+
def locked_x1(self):
1192+
"""
1193+
float or None: The value used for the locked x1.
1194+
"""
1195+
if self._locked_points.mask[1, 0]:
1196+
return None
1197+
else:
1198+
return self._locked_points[1, 0]
1199+
1200+
@locked_x1.setter
1201+
def locked_x1(self, x1):
1202+
self._locked_points.mask[1, 0] = x1 is None
1203+
self._locked_points.data[1, 0] = x1
1204+
self.invalidate()
1205+
1206+
@property
1207+
def locked_y1(self):
1208+
"""
1209+
float or None: The value used for the locked y1.
1210+
"""
1211+
if self._locked_points.mask[1, 1]:
1212+
return None
1213+
else:
1214+
return self._locked_points[1, 1]
1215+
1216+
@locked_y1.setter
1217+
def locked_y1(self, y1):
1218+
self._locked_points.mask[1, 1] = y1 is None
1219+
self._locked_points.data[1, 1] = y1
1220+
self.invalidate()
1221+
1222+
10981223
class Transform(TransformNode):
10991224
"""
11001225
The base class of all :class:`TransformNode` instances that

0 commit comments

Comments
 (0)