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
Matplotlib-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/matplotlib-devel

Reply via email to