8000 First baby step in getting arbitrary non-linear transformations into · matplotlib/matplotlib@f52f5a4 · GitHub
[go: up one dir, main page]

Skip to content

Commit f52f5a4

Browse files
committed
First baby step in getting arbitrary non-linear transformations into
the pipeline. svn path=/branches/transforms/; revision=3869
1 parent c957d1f commit f52f5a4

File tree

7 files changed

+96
-70
lines changed

7 files changed

+96
-70
lines changed

lib/matplotlib/axes.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -633,9 +633,15 @@ def _set_lim_and_transforms(self):
633633
self.transAxes = mtransforms.BboxTransform(
634634
mtransforms.Bbox.unit(), self.bbox)
635635
# self.set_transform(self.transAxes)
636-
self.transData = mtransforms.BboxTransform(
637-
self.viewLim, self.bbox)
638-
636+
# self.transData = mtransforms.BboxTransform(
637+
# self.viewLim, self.bbox)
638+
self.preDataTransform = mtransforms.BboxTransform(
639+
self.viewLim, mtransforms.Bbox.unit())
640+
self.dataTransform = mtransforms.TestLogTransform()
641+
# self.dataTransform = mtransforms.Affine2D().scale(1.5)
642+
self.transData = self.preDataTransform + self.dataTransform + mtransforms.BboxTransform(
643+
mtransforms.Bbox.unit(), self.bbox)
644+
639645

640646
def get_position(self, original=False):
641647
'Return the axes rectangle left, bottom, width, height'

lib/matplotlib/axis.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1032,17 +1032,16 @@ def _update_label_position(self, bboxes, bboxes2):
10321032
else:
10331033
bbox = Bbox.union(bboxes)
10341034
bottom = bbox.ymin
1035-
1036-
self.label.set_position( (x, bottom-self.LABELPAD*self.figure.dpi / 72.0))
1037-
1035+
self.label.set_position( (x, bottom - self.LABELPAD*self.figure.dpi / 72.0))
1036+
10381037
else:
10391038
if not len(bboxes2):
10401039
top = self.axes.bbox.ymax
10411040
else:
10421041
bbox = bbox_union(bboxes2)
10431042
top = bbox.ymax
1044-
1045-
self.label.set_position( (x, top+self.LABELPAD*self.figure.dpi.get()/72.0))
1043+
1044+
self.label.set_position( (x, top+self.LABELPAD*self.figure.dpi / 72.0))
10461045

10471046
def _update_offset_text_position(self, bboxes, bboxes2):
10481047
"""

lib/matplotlib/backends/backend_agg.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,8 @@
8484
from matplotlib.font_manager import findfont
8585
from matplotlib.ft2font import FT2Font, LOAD_DEFAULT
8686
from matplotlib.mathtext import MathTextParser
87-
from matplotlib.transforms import Bbox
87+
from matplotlib.path import Path
88+
from matplotlib.transforms import Affine2D, Bbox
8889

8990
from _backend_agg import RendererAgg as _RendererAgg
9091

@@ -117,8 +118,8 @@ def __init__(self, width, height, dpi):
117118
debug=False)
118119
if __debug__: verbose.report('RendererAgg.__init__ _RendererAgg done',
119120
'debug-annoying')
120-
self.draw_path = self._renderer.draw_path
121-
self.draw_markers = self._renderer.draw_markers
121+
#self.draw_path = self._renderer.draw_path
122+
#self.draw_markers = self._renderer.draw_markers
122123
self.draw_image = self._renderer.draw_image
123124
self.copy_from_bbox = self._renderer.copy_from_bbox
124125
self.restore_region = self._renderer.restore_region
@@ -129,6 +130,17 @@ def __init__(self, width, height, dpi):
129130
if __debug__: verbose.report('RendererAgg.__init__ done',
130131
'debug-annoying')
131132

133+
# MGDTODO: This is a hack for now to allow for arbitrary transformations
134+
def draw_path(self, gc, path, trans, rgbFace=None):
135+
new_path, affine = path.transformed_without_affine(trans)
136+
self._renderer.draw_path(gc, new_path, affine, rgbFace)
137+
138+
# MGDTODO: This is a hack for now to allow for arbitrary transformations
139+
def draw_markers(self, gc, marker_path, marker_trans, path, trans, rgbFace=None):
140+
assert marker_trans.is_affine()
141+
new_path, affine = path.transformed_without_affine(trans)
142+
self._renderer.draw_markers(gc, marker_path, marker_trans, new_path, affine, rgbFace)
143+
132144
def draw_mathtext(self, gc, x, y, s, prop, angle):
133145
"""
134146
Draw the math text using matplotlib.mathtext

lib/matplotlib/lines.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1102,12 +1102,14 @@ def get_dash_capstyle(self):
11021102
"""
11031103
return self._dashcapstyle
11041104

1105+
11051106
def get_solid_capstyle(self):
11061107
"""
11071108
Get the cap style for solid linestyles
11081109
"""
11091110
return self._solidcapstyle
11101111

1112+
11111113
def is_dashed(self):
11121114
'return True if line is dashstyle'
11131115
return self._linestyle in ('--', '-.', ':')

lib/matplotlib/path.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,13 @@ def iter_endpoints(self):
7070
yield vertices[i]
7171
i += 1
7272

73+
def transformed(self, transform):
74+
return Path(transform.transform(self.vertices), self.codes)
75+
76+
def transformed_without_affine(self, transform):
77+
vertices, affine = transform.transform_without_affine(self.vertices)
78+
return Path(vertices, self.codes), affine
79+
7380
_unit_rectangle = None
7481
#@classmethod
7582
def unit_rectangle(cls):

lib/matplotlib/text.py

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,8 @@ def _get_layout(self, renderer):
185185

186186
xmin, ymin = thisx, thisy
187187
lines = self._text.split('\n')
188-
188+
189+
# MGDTODO: whs could be a numpy.array
189190
whs = []
190191
# Find full vertical extent of font,
191192
# including ascenders and descenders:
@@ -260,27 +261,21 @@ def _get_layout(self, renderer):
260261
else: offsety = ty - ymin
261262

262263
xmin += offsetx
263-
xmax += offsetx
264264
ymin += offsety
265-
ymax += offsety
266265

267266
bbox = Bbox.from_lbwh(xmin, ymin, width, height)
268267

269-
270-
271268
# now rotate the positions around the first x,y position
272269
xys = M.transform(offsetLayout)
273-
tx = xys[:, 0]
274-
ty = xys[:, 1]
275-
tx += offsetx
276-
ty += offsety
270+
xys[:, 0] += offsetx
271+
xys[:, 1] += offsety
277272

278273
# now inverse transform back to data coords
279274
inverse_transform = self.get_transform().inverted()
280275
xys = inverse_transform.transform(xys)
281276

282-
xs, ys = zip(*xys)
283-
277+
xs, ys = xys[:, 0], xys[:, 1]
278+
284279
ret = bbox, zip(lines, whs, xs, ys)
285280
self.cached[key] = ret
286281
return ret
@@ -331,15 +326,15 @@ def draw(self, renderer):
331326

332327
for line, wh, x, y in info:
333328
x, y = trans.transform_point((x, y))
334-
329+
335330
if renderer.flipy():
336331
canvasw, canvash = renderer.get_canvas_width_height()
337332
y = canvash-y
338-
333+
339334
renderer.draw_text(gc, x, y, line,
340335
self._fontproperties, angle,
341336
ismath=self.is_math_text(line))
342-
337+
343338
def get_color(self):
344339
"Return the color of the text"
345340
return self._color
@@ -407,7 +402,9 @@ def get_prop_tup(self):
407402
return (x, y, self._text, self._color,
408403
self._verticalalignment, self._horizontalalignment,
409404
hash(self._fontproperties), self._rotation,
410-
self.get_transform(),
405+
# MGDTODO: Find a better way to determine if the
406+
# transform as changed
407+
str(self.get_transform())
411408
)
412409

413410
def get_text(self):

lib/matplotlib/transforms.py

Lines changed: 47 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -256,10 +256,10 @@ def set(self, other):
256256
self.invalidate()
257257

258258
def transformed(self, transform):
259-
return Bbox(transform(self._points))
259+
return Bbox(transform.transform(self._points))
260260

261261
def inverse_transformed(self, transform):
262-
return Bbox(transform.inverted()(self._points))
262+
return Bbox(transform.inverted().transform(self._points))
263263

264264
def expanded(self, sw, sh):
265265
width = self.width
@@ -408,13 +408,13 @@ def transform(self, points):
408408
# MGDTODO: The major speed trap here is just converting to
409409
# the points to an array in the first place. If we can use
410410
# more arrays upstream, that should help here.
411-
if not isinstance(points, npy.ndarray):
412-
import traceback
413-
print '-' * 60
414-
print 'A non-numpy array was passed in for transformation. Please '
415-
print 'correct this.'
416-
print "".join(traceback.format_stack())
417-
print points
411+
# if not isinstance(points, npy.ndarray):
412+
# import traceback
413+
# print '-' * 60
414+
# print 'A non-numpy array was passed in for transformation. Please '
415+
# print 'correct this.'
416+
# print "".join(traceback.format_stack())
417+
# print points
418418
mtx = self.get_matrix()
419419
points = npy.asarray(points, npy.float_)
420420
points = points.transpose()
@@ -563,6 +563,10 @@ def __init__(self, x_transform, y_transform):
563563
self._y = y_transform
564564
self.set_children(['_x', '_y'])
565565

566+
def __repr__(self):
567+
return "BlendedGenericTransform(%s,%s)" % (self._x, self._y)
568+
__str__ = __repr__
569+
566570
def transform(self, points):
567571
# MGDTODO: Optimize the case where one of these is
568572
# an affine
@@ -590,28 +594,6 @@ def is_separable(self):
590594
return True
591595

592596

593-
class BlendedSeparableTransform(Transform):
594-
input_dims = 2
595-
output_dims = 2
596-
597-
def __init__(self, x_transform, y_transform):
598-
# Here we ask: "Does it blend?"
599-
assert x_transform.is_separable()
600-
assert y_transform.is_separable()
601-
assert x_transform.input_dims == x.transform.output_dims == 1
602-
assert y_transform.input_dims == y.transform.output_dims == 1
603-
604-
Transform.__init__(self)
605-
self._x = x_transform
606-
self._y = y_transform
607-
self.set_children(['_x', '_y'])
608-
609-
def transform(self, points):
610-
x_points = self._x(points[:, 0])
611-
y_points = self._y(points[:, 1])
612-
return npy.vstack((x_points[:, 0:1], y_points[:, 1:2])).transpose()
613-
614-
615597
class BlendedAffine2D(Affine2DBase, Transform):
616598
def __init__(self, x_transform, y_transform):
617599
assert x_transform.is_affine()
@@ -666,6 +648,10 @@ def __init__(self, a, b):
666648
self._a = a
667649
self._b = b
668650
self.set_children(['_a', '_b'])
651+
652+
def __repr__(self):
653+
return "CompositeGenericTransform(%s, %s)" % (self._a, self._b)
654+
__str__ = __repr__
669655

670656
def transform(self, points):
671657
return self._b.transform(self._a.transform(points))
@@ -724,10 +710,21 @@ class TestLogTransform(Transform):
724710
input_dims = 2
725711
output_dims = 2
726712
def transform(self, xy):
727-
return xy * 2
713+
marray = npy.ma.masked_where(xy <= 0.0, xy * 10.0)
714+
return npy.log10(marray)
715+
716+
def inverted(self):
717+
return TestInvertLogTransform()
728718

719+
720+
class TestInvertLogTransform(Transform):
721+
input_dims = 2
722+
output_dims = 2
723+
def transform(self, xy):
724+
return npy.power(10, xy) / 10.0
725+
729726
def inverted(self):
730-
return self
727+
return TestLogTransform()
731728

732729

733730
class BboxTransform(Affine2DBase):
@@ -825,7 +822,7 @@ def interval_contains_open(interval, val):
825822

826823
assert bbox.bounds == (10, 15, 10, 10)
827824

828-
print npy.asarray(bbox)
825+
assert tuple(npy.asarray(bbox).flatten()) == (10, 15, 20, 25)
829826

830827
bbox.intervalx = (11, 21)
831828
bbox.intervaly = (16, 26)
@@ -859,29 +856,35 @@ def interval_contains_open(interval, val):
859856
scale = Affine2D().scale(10, 20)
860857
assert scale.to_values() == (10, 0, 0, 20, 0, 0)
861858
rotation = Affine2D().rotate_deg(30)
862-
print rotation.to_values() == (0.86602540378443871, 0.49999999999999994,
859+
assert rotation.to_values() == (0.86602540378443871, 0.49999999999999994,
863860
-0.49999999999999994, 0.86602540378443871,
864861
0.0, 0.0)
865862

866863
points = npy.array([[1,2],[3,4],[5,6],[7,8]], npy.float_)
867-
translated_points = translation(points)
864+
translated_points = translation.transform(points)
868865
assert (translated_points == [[11., 22.], [13., 24.], [15., 26.], [17., 28.]]).all()
869-
scaled_points = scale(points)
866+
scaled_points = scale.transform(points)
870867
print scaled_points
871-
rotated_points = rotation(points)
868+
rotated_points = rotation.transform(points)
872869
print rotated_points
873870

874-
tpoints1 = rotation(translation(scale(points)))
871+
tpoints1 = rotation.transform(translation.transform(scale.transform(points)))
875872
trans_sum = scale + translation + rotation
876-
tpoints2 = trans_sum(points)
877-
print tpoints1, tpoints2
878-
print tpoints1 == tpoints2
873+
tpoints2 = trans_sum.transform(points)
879874
# Need to do some sort of fuzzy comparison here?
880-
# assert (tpoints1 == tpoints2).all()
875+
assert (tpoints1.round() == tpoints2.round()).all()
881876

877+
print points
878+
879+
comp = TestLogTransform() + Affine2D().rotate_deg(15)
880+
tpoints = comp.transform(points)
881+
itpoints = comp.inverted().transform(tpoints)
882+
print tpoints, itpoints
883+
assert (points.round() == itpoints.round()).all()
884+
882885
# Here are some timing tests
883886
points = npy.asarray([(random(), random()) for i in xrange(10000)])
884-
t = timeit.Timer("trans_sum(points)", "from __main__ import trans_sum, points")
887+
t = timeit.Timer("trans_sum.transform(points)", "from __main__ import trans_sum, points")
885888
print "Time to transform 10000 x 10 points:", t.timeit(10)
886889

887890
__all__ = ['Transform', 'Affine2D']

0 commit comments

Comments
 (0)
0