On 9/12/07, Michael Droettboom <[EMAIL PROTECTED]> wrote:
> So, I feel like I'm going in a bit of a circle here, and I might need a
> reality check. I thought I'd better check in and see where you guys
> (who've thought about this a lot longer than I have) see this going. A
> statement of objectives of this part of the task would be helpful.
> (e.g. what's the biggest problem with how transforms work now, and what
> model would be a better fit). John, I know you've mentioned some to me
> before, e.g. the LazyValue concept is quirky and relies on C and the PDF
> stateful transforms model is close, but not quite what we need, etc. I
> feel I have a better sense of the overall code structure now, but you
> guys may have a better "gut" sense of what will fit best.
Here is a brief summary of what I see some of the problems to be with
the existing approach to transformations, and what I would like to see
improved in a refactoring. The three major objectives are clarity,
extensibility and efficiency.
Clarity:
The existing transformation framework, written in C++ and
making extensive use of deferred evaluation of binary operation
trees and values by reference, is difficult for most developers to
understand (and hence enhance). Additionally, since all the heavy
lifting is done in C++, python developers who are not versed in C++
have an additional barrier to making contributions.
Extensibilty:
We would like to make it fairly easy for users to add additional
non-linear transformations. The current framework requires adding a
new function at the C++ layer, and hacking into axes.py to support
additional functions. We would like the existing nonlinear
transformations (log and polar) to be part of a general
infrastructure where users could supply their own nonlinear
functions which map (possibly nonseparable) (xhat, yhat) ->
separable (x, y). There are two parts to this: one pretty easy and
one pretty hard.
The easy part is supporting a transformation which has a separation
callable that takes, eg an Nx2 array and returns and Nx2 array. For
log, this will simply be log(XY), for polar, it will be
r*cos(X[:,0]), r*sin(X[:,1]). Presumably we will want to take
advantage of masked arrays to support invalid transformations, eg
log of nonpositive data.
The harder part is to support axis, tick and label layout
generically. Currently we do this by special casing log and polar,
either with special tick locators and formatters (log) or special
derived Axes (polar).
Efficiency:
There are three parts to the efficiency question: the efficiency of
the transformation itself, the efficiency with which transformation
data structures are updated in the presence of viewlim changes
(panning and zooming, window resizing) and the efficiency in getting
transformed data to the backends. My guess is that the new design
may be slower or not dramatically faster for the first two (which
are not the bottleneck in most cases anyhow) but you might get
sigificant savings on the 3rd.
What we would like to support is something like an operation which
pushes the partially transformed data to the backend, and the
backend then stores this data in a path or other data structure, and
when the viewlimits are changed or the window is resized, the
backend merely needs to get an updated affine to redraw the data. I
say "partially transformed" because in the case of nonlinear or
separable transformations, we will probably want to do the
nonlinear/separation part first, and then push this to the backend
which can build a path (eg an agg::path in agg) and on pan and zoom
we would only need to let the backend know what the current affine
is. There is more than one way to solve this problem: in mpl1 I
used a path dictionary keyed off of a path id which was updated on
renderer changes.
Then the front end (eg Line2D) could do something like
def on_renderer_change(self, renderer):
# on renderer change; path data already has the
# separable/nonlinear part handled
self.pathid = renderer.push_path(pathdata)
Additionally, you would need to track when either the data or
nonlinear mapping function are changed in order to remove the old
path and push out a new one. Then at draw time, you would not need
to push any data to the backend
def on_draw(self, renderer):
# on draw the line just needs to inform the backend to draw the
# cached path with the current separable transformation
renderer.draw_path(self.pathid, gc, septrans)
Ken I believe used some meta class magic to solve this problem. It
may be that the ideal approach is different from either of these so
feel free to experiment and I'll give it some more thought too. Ken
will hopefully pipe in with his perspective too.
-------------------------------------------------------------------------
This SF.net email is sponsored by: Microsoft
Defy all challenges. Microsoft(R) Visual Studio 2005.
http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/
_______________________________________________
Matplotlib-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/matplotlib-devel