On Jul 25, 2007, at 12:09 PM, John Hunter wrote:
> Hi Ken -- sorry for the radio silence, I'm not intentionally ignoring
> you. Real life has made some demands on my time of late, and probably
> will until next week, but I was able to download, read through and
> test your code.
I appreciate you making the time to take a look at my code in spite
> But I don't think that closes the book on the subject -- for
> example, I have realized I am introducing too much complexity and
> trait forwarding, and I think I know a way to work around this, so
> I will be hacking through my version one more time while at the
> same time taking a closer look at yours.
I'd hardly expect it to close the book on the subject. Although it
functions as a proof-of-concept, my rendering model needs more work
before it can handle special cases like the blended affine
transformation to plot an axis. I've been thinking about how best to
accomplish this and will try to have something for you to look at by
early next week. Please drop me a line when you're satisfied with
your changes to mpl1.py, as I'm looking forward to seeing them.
> I also would like to hear more about the "hard to optimize" part,
> because that is not intuitively clear to me.
Moving to a rendering model when the primitives modify the CTM
instead of replacing it with a pre-calculated value removes the
dependency between primitives and their containers. A Primitive
object, like a Path, may be reused and shared between renderers. The
native version of a Primitive object is stored by the Primitive
object itself, so it can be reused by any Canvas that draws the
Primitive. This also means that you incur very little cost in
clearing a Renderer and then repopulating it with existing
Primitives. An Artist-level implementation of z-order could leverage
this to avoid having to sort primitives by z-order every time a
figure is rendered.
Since primitives modify graphics state instead of replacing it
wholesale the it's easier for the Canvas to push as few changes to
the backend as possible. For example, if none of the child
primitives of a ScaledView have their own transforms then the CTM
only gets modified once when the ScaledView is rendered. The same is
true for all of the other graphics state parameters, like linewidth
and strokecolor. This saves on unnecessary backend overhead (e.g.
creating the same agg.trans_affine repeatedly). It could also lead
to tighter PDF and EPS output.
Finally, the Renderer class is canvas-independent so it can be used
to draw on multiple canvases during the course of its lifetime. I
hope this will substantially simplify the process of saving a figure
that was drawn interactively. I'm also contemplating making
Renderers able to contain child Renderers so parts of a figure can be
selectively updated. For example, drawing the non-Axis children of
an Axes by using a subrenderer could further improve animation
> I confess to being a meta-class ignoramus, so I am intrigued to study
> how you handled the canvas primitive cache problem using meta classes.
I hate to disappoint you, but metaclasses aren't involved. Right now
the caching is implemented cooperatively by Canvas and
CanvasPrimitive to reduce duplicate code.
CanvasPrimitive.get_canvas_primitive() handles the caching logic and
calls CanvasPrimitive.create_canvas_primitive() to create a new
native object if necessary. Subclasses of CanvasPrimitiveoverride
create_canvas_primitive() to delegate the call to the appropriate
method of Canvas, e.g. Path.create_canvas_primitive() returns the
result of Canvas.create_canvas_path(). Subclasses of Canvas that do
not cache Primitives should not override the create_canvas_xxx()
> How for example, if one modifies a Rectangle object which embodies a
> path primitive, would the canvas get the notification to update it's
> internal path representation (egg the agg_path_storage)?.
Native canvas objects are always created at render-time, so rectangle
would just update its associated Path instance when its bounds
changed. The next time the Path is drawn, Canvas.draw_path() would
call CanvasPrimitive.get_canvas_primitive() and end up with a new AGG
path. That being said, it might be more efficient if all Rectangles
share one Path that draws a unit square and change its size by
updating the CTM.
> With traits, I use the trait event handling mechanism so that when
> any of
> the box properties (left, width, top, etc...) are modified, the
> PathPrimitiveAgg would get a callback. So when the user does
> rect.left = 0.1
> the agg path knows to update itself. This is pretty cool.
Yes it is, and I agree that mpl1 should have an attribute-based API
for modifying plot object parameters.
> vis-a-vis properties vs traits, I second Peter's comment that once
> you've written 8,000 setters and getters and manually constructed
> properties, you'll probably evolve toward something like traits, w/o
> all the features. This is a library that has been bug and performance
> vetted in production applications for years, so we should seriously
> consider it as a properties alternative.
Traits does look excellent at what it does, but I'm still of the
opinion that the cost of adding a large external dependency that is
not available in Debian or Ubuntu outweighs the benefits of replacing
vanilla properties with traits.
> I strongly encourage you to download Fernando's mplconfig sandbox
> stuff and try the edit_traits demo in the presence of a wx enabled
I'll give it a try.
> He is somewhat blown away by the fact that he got a sophisticated,
> nested GUI dialog w/o writing *any* code. Since you are a
> committed wx user, you might find this particularly appealing.
No one's committed me yet, mwahaha! Actually, that feature does
little to advance my primary use case for mpl, embedding plots in
applications. I my experience you have to put enough effort into
making the plots look good that you don't want users to be able to
edit them at runtime. That being said, I wouldn't be surprised to
learn that others' experiences differ.
> But at the end of the day, I think the *notification* aspect of
> traits is the killer feature.
I know, and I still think that for matplotlib it's a bad Software
Engineering move due to the implicit inter-object dependencies it
creates. I can't see why drawing plots should require that much
"spooky action at a distance". I could be wrong, but for now I'll
keep working to find a way that doesn't.
> I think your approach of working on a display PDF renderer interface
> is also a good one, for several reasons. It uses an established
> specification instead of a home grown one, and it makes it easier for
> us to consider things like integrating with Kiva or Chaco.
It's nice to hear you like it. Those are two of the specific goals I
have in mind. I also like the idea of having a generic retained
drawing system available in Python.
> I am glad you interpreted my mpl1 sketch in the right vein, as a
> lab in which
> people can advocate ideas as working code.
Well, I'm glad you've taken my criticisms of mpl1.py in the spirit
they were intended in. I also appreciate you taking the time to
review the code I submitted instead of just telling me how very nice
and convenient traits are. ;-)
> Hopefully next week we can come to some consensus and start merging
> our two lines.
That sounds like a plan. I'll try to make some progress on the
blended affines for drawing axis tickmarks and the Artists layer.