Dear List,
dear Michael,

> Looks good.  Does matplotlib still pass all regression tests with this
> change?

It does pass all regression tests that were passed with the git version
I started with. (There were 10 failures which are still there).

In the meantime, I also wrote a class that already uses my extension.
With it, you can plot rotated or sheared images with all backends.
(There were some quirks to get sheard images on some backends,
see examples/api/demo_affine_image.py, but it worked on some backends
only).

While writing it, I found some inconsistencies in matplotlib:

- bounding boxes are not correctly transformed. BboxBase.tranformed only
transformes the outer points of a bounding box, if you rotate it, this will give
wrong results for several angles. I wrote a function that should be correct for
all affine transformations:

def transform_bbox(bbox, trans):
    x0, y0, x1, y1 = bbox.extents
    tx0, ty0 = trans.transform([x0, y0])
    tx1, ty1 = trans.transform([x1, y1])
    tx2, ty2 = trans.transform([x1, y0])
    tx3, ty3 = trans.transform([x0, y1])
    return Bbox.from_extents(min(tx0, tx1, tx2, tx3), min(ty0, ty1, ty2, ty3),
        max(tx0, tx1, tx2, tx3), max(ty0, ty1, ty2, ty3))

- The other inconsistency is that within matplotlib, extents are
defined different:
in imshow,  the parameter extent expects the order (left, right, bottom, top),
while BboxBase.extents is (left, bottom, right, top). This should be changed in
the future, maybe the move to python 3 is a good time for that?

But now to my code to draw images. It's a new class inheriting AxesImage,
but is supposed to once replace AxesImage, as it is compatible.

I'm re-writing _draw_unsampled_image, to actually draw a sampled image.
Thats only because make_image, the method to be rewritten for a sampled
image, is not flexible enough (the caller draws the image, but there is no way
for make_image to tell where that image is to be put). In the future,
the methods
should be renamed (it's a private method, so that's no problem).

class ShearImage(AxesImage):
    def _check_unsampled_image(self, _):
        return True

    def _draw_unsampled_image(self, renderer, gc):
        """
        actually, draw sampled image. This method is more flexible than
        make_image
        """
        mag = renderer.get_image_magnification()
        trans = Affine2D().scale(mag, mag) + self.get_transform() + \
            self.axes.transData.get_affine()
        bbox = self.axes.bbox
        viewLim = transform_bbox(bbox, trans.inverted())

        im, xmin, ymin, dxintv, dyintv, sx, sy = \
            self._get_unsampled_image(self._A, self.get_extent(), viewLim)

        if im is None: return # I'm not if this check is required. -JJL
        im.set_interpolation(self._interpd[self._interpolation])
        im.set_resample(self._resample)

        fc = self.axes.patch.get_facecolor()
        bg = mcolors.colorConverter.to_rgba(fc, 0)
        im.set_bg( *bg)
   # uncomment the following line to see the extent to which the image
   # is drawn
   #     im.set_bg(0, 0, 0, 100)
        numrows, numcols = im.get_size()

        ex = self.get_extent()
        tex = Bbox.from_extents([ex[0], ex[2], ex[1], ex[3]])
        tex = transform_bbox(tex, trans)
        if tex.xmin < bbox.xmin:
            left = bbox.xmin
            tx = 0
        else:
            left = tex.xmin
            tx = tex.xmin - bbox.xmin
        if tex.ymin < bbox.ymin:
            bottom = bbox.ymin
            ty = 0
        else:
            bottom = tex.ymin
            ty = tex.ymin - bbox.ymin
        trans = Affine2D().scale(dxintv / numcols,
                dyintv / numrows).translate(xmin, ymin) + \
                trans + \
                Affine2D().translate(-bbox.xmin - tx, -bbox.ymin - ty)
        im.set_matrix(*trans.get_matrix()[:2, :].T.ravel())

        width = min(tex.xmax, bbox.xmax) - left
        height = min(tex.ymax, bbox.ymax) - bottom
        if width <= 0 or height <= 0:
            return
        im.resize(width * mag, height * mag,
            norm=self._filternorm, radius=self._filterrad)

        im._url = self.get_url()

        renderer.draw_image(gc, left, bottom, im)

Last but not least, a little script to test the above. It shows a rotated
image. You can scale and move the image nicely. If you uncomment the
line mentioned above in ShearImage code, you can see where the
image is actually drawn, and you will see that only the necessary parts
are drawn if the image is smaller than the entire axes.
The test script follows:

from pylab import *
ax = axes()
im = ShearImage(ax)
im.set_data(fromfunction(lambda x, y: sin(x + y ** 2), (100, 100)))
im.set_extent(im.get_extent())
transform = Affine2D().rotate_deg(30)
im.set_transform(transform)
ax.images.append(im)
show()

Greetings

Martin

------------------------------------------------------------------------------
AppSumo Presents a FREE Video for the SourceForge Community by Eric 
Ries, the creator of the Lean Startup Methodology on "Lean Startup 
Secrets Revealed." This video shows you how to validate your ideas, 
optimize your ideas and identify your business strategy.
http://p.sf.net/sfu/appsumosfdev2dev
_______________________________________________
Matplotlib-devel mailing list
Matplotlib-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/matplotlib-devel

Reply via email to