Am 07.05.2008, 08:24 Uhr, schrieb Chris.Barker <[EMAIL PROTECTED]>:

> Nitro wrote:
>>> Perhaps some profiling is in order to see how difference there really  
>>> is.
>>
>> Ok, I'll whip up a quick peformance test on my machine and see how it
>> goes.
>
> That would be great.

Done, the benchmark code and the result on my machine are attached. It  
mainly tests lines, filled lines (polygons) and bitmaps. It uses DC and GC  
and tries various methods to draw. It also tries different combinations  
(huge number of objects & low object complexity, low number of objects &  
high object complexity, performance against object size (fillrate)). The  
code is not very readable and was a quick hack. There might be  
problems/errors in it. It only covers a small subset of objects that can  
be drawn, but I think the general results won't change a lot if I'd add  
those tests as well.
The main conclusion seems to be that GCs look a lot prettier and are as  
much slower as they are prettier :-) Bitmaps are about 100 times slower  
(!) (I am not sure if the DC.DrawBitmap function only has on-off  
transparency though). On the other hand some of the functions that only  
fill areas are about the same speed (DC and GC) and if you take into  
account that the GC actually does more work (transparent gradient  
background + more generic transform) while the DC only does a solid fill  
then the GCs perform ok in the "filling" category. Same is true for some  
of the line drawing functions.
My results were obtained on Vista x64, so keep this in mind. I am curious  
about the results for other platforms. I hope the test code will work on  
them. What you should see are some transformed circles, sometimes just  
their outlined, sometimes filled red. The bitmap is just a portion of your  
screen with a transparent (white) circle in the center.
I consider my machine fairly fast and I think GCs can probably be too slow  
in some cases, especially with bitmaps.

> And perhaps suggest optimizations for GCs. One thing noted on the list
> in the past is that apparently it takes longer to create a path than to
> render it.

I haven't benchmarked this in-depth, but it seems either this varies  
heavily by platform or only matters for lots of objects.

> exactly -- it has the advantage of being O(1) to do the hit test -- thus
> fast enough for mouse-over tests, regardless of the complexity of the
> drawing. The downsides are: you're drawing every hitable object twice,
> so it's slower there, and you can only get the top object under the
> mouse if there is more than one object.

This is true. Might be hard to select an object which is completely hidden  
behind another one. We might investigate the graphics path contains way  
suggested by robin.

>> The good thing about the system is we don't have to worry about how
>> objects are drawn exactly, we just use the result. If we'd want to do
>> hit-testing without drawing anything we'd need to know how complex  
>> pathes
>> are drawn, which areas are filled etc, so that's not a good idea.
>
> no -- way too much work for complex objects

Maybe Contains can help here. Of course wxDC doesn't have that...

> The other options I know are:
> 2) first do a Bounding Box check, then draw the object in a small bitmap
> surrounding the mouse point, and see if that pixel changes -- this is
> similar to the current system, but you draw on demand, instead of ahead
> of time, and you're only drawing one object at a time, so any color
> change will be detected, so anti-aliasing is OK.
>
> The downside of both of those is that they are O(n) (and the constant is
> larger), but some of that could be improved with a spatial index
> (O(log(n)), and maybe it's plenty fast anyway. Bounding box checks are
> pretty quick.

Yes. We should first find out whether we want to be GC-only or if we want  
to use DC in addition or even mix both approaches.

> I get it now. I think I like it, but... ( note: playing the skeptic here)
>
> 1) It just may be more complicated than we need -- it should be easy to
> define new Objects, and having to define multiple classes to do so feels
> onerous. On the other hand, you could probably re-use a lot of existing
> classes and mix-ins and combine them in different ways to get new things
> without writing much new code.
>
> 2) maybe it's because this example is a fairly simple, but I fail to see
> the advantage of the FlowerRenderer class over a Flower class with a
> Render method. It seems that FlowerData and FlowerRenderer are so
> closely coupled that there is not a lot of point to separating them like
> that. Note that if .radius, .center and .noBlades are properties, then a
> subclass could redefine the getters to pull from a database, etc. also.

Well, if you want to separate data (model) from view, then you need one  
object dealing with the data and one with viewing it. The data does not  
have to know how to draw itself. Besides that there might be multiple ways  
to draw the same data. So in essence this is data-view decoupling.
And yes, the IFlowerData is an interface (hence the I- prefix) which means  
that any concrete flower data classes must have these attributes. Then the  
renderers have an interface they can use to access the flower data in  
order to draw it.
So if you want to pull stuff from a database instead of a file or  
whatever, you just subclass IFlowerData, make it retrieve stuff from the  
db and then you can use the same FlowerRenderer for rendering the data  
that came from a db or from a file.

> Perhaps we should select a subset of the current DrawObjects to use as
> samples to see how things will all fit together with the new code --
> including a couple more complex ones, like the PieChart.

Yes, that's why I took the FlowerData since it's like the PieChart.

>> So as you said somewhere below, if you want to have 100 flowers with
>> radius 5 etc, you can create one FlowerData object and one  
>> FlowerRenderer.
>
> Well, no. FlowerData.center is different for each one, so you'd need a
> separate one for each anyway (or have I missed something). They could
> all share a FlowerLook object though.

Well, maybe ICircleData will not have a center property :-). The scene  
node takes already care of transforming it, so its center can always be at  
(0,0). And that's the same reason why you can draw 100 circles with  
radius=10 and have only 1 circle data object (although you have 100 nodes).

>> You could also have multiple FlowerRenderers for the same FlowerData  
>> (they
>> might render it differently).
>
> That is cool -- though you could do this with subclassing too -- which
> is easier to use??

Subclassing what from what?

> I've been thinking a bit about MVD at the DrawObject level. this makes
> sense, as I've always thought of FloatCanvas as benign a tool to
> visualize data, rather than to draw a picture (though it is both, of
> course). So example data may be the population of a bunch of cities,
> each at a location. So the data model looks like:
>
> class PopulationData:
>      population = 50000
>      location = (-89.5, 47.5)
>
> Now you may want to represent those as circles, with the color
> representing population. Or you could want the radius to scale with the
> population -- two views of the same data. However, this gets
> complicated, as I was thinking of radius as being a property of the
> CircleData object, and color as a Property of the CircleLook Object, but
> in this example, either one could represent the data value. So why are
> we keeping them apart? and how do we decide what to put where?

You have a good point there. I need to think about this further. Maybe you  
are right and we should collapse renderable and look into one object ala  
RenderSet and change the RenderSet in this case. Or the IRenderable.Render  
function is not only passed the renderer, but also the look which can then  
be modified by the Renderable. Or the IRenderable can set/return a custom  
look instead of the default one. This needs further thought though.

> Also, to see if I'm understanding you, would there be a Flower object
> that tied these together:
>
> class Flower:
>      def __init__(....)
>       self.Look = FlowerLook
>          self.Data = FlowerData
>       self.Renderer = FlowerRenderer
>      def Render(....)
>          self.Renderer.Render(renderer, self.Data, self.Look)
>
> AS I wrote that, I realized that there is nothing Flower specific to
> that --it's simply a generic DrawObject (or node?). However, some
> objects have a lot more complications -- see ScaledTextBox for example
> -- where would all that other code go?
>
>> If the renderer object is not shared between several different data
>> objects it could also be used to cache render-related things
>
> I do think we need to do that -- I expect that we'll need to cache GC
> paths, for example.
>
>>>> 3) Separate the way the data is drawn from the draw object ("how",
>>>> ILook).
>
> By the way, does the "I" signify an instance?

No, I means interface. Something like the zope.interface package would  
probably be nice, but I don't want to introduce another 3rd party  
dependency to fc.

>> Basically the list is the same as the outcome of sorting the priorities.
>
> yes, but the user has to keep track of the z=order value of all their
> objects in order to do something like move an object up a bit

Yes, I agree it might be a bit messy.

>> What you do with priorties is assign very high numbers to the front
>> objects, say 100000 and upwards. Then you could for instance create two
>> RenderManagers like discussed above (instead of sorting objects these
>> render managers select which objects to render). The first gathers all
>> objects with priorities greater than 100000, the second all with smaller
>> priorities.
>
> OK, so it's kind of like auto-generating layers. It seems a bit like
> magic though -- I like it to be more explicit -- "I want these objects
> in the foreground"

OK. I'd like to make the "foreground" thing part of a general layer  
concept though.

> However, if you can now blit transparent bitmaps on top of each-other
> fast (something else to profile), then you could kind of auto-generate a
> bunch of separate off screen bitmaps -- each with N objects, and then
> only re-draw the layers that have changes on them -- cool optimization
> (now that memory is cheap!)

Yes, that's what I tried to illustrate with the heat map/road example.

>> For example you could
>> easily add a third layer which is also rendered to a bitmap.
>
> I do want to be able to do multiple layers like that -- the only reason
> FC doesn't have that now is that it was painfully slow to render a
> transparent bitmap a few wx versions ago (2.4 maybe?)

See the benchmark code. DC DrawBitmap seems to be blazingly fast on my  
machine, although there's still the "is alpha 0-255 or just on/off"  
question.

>> So there should probably be a notion of what a renderer draws to. This
>> could either be the window or a bitmap for now. Let's call it
>> RenderSurface.
>
> yup. probably always a bitmap, though. Except maybe for printing, PDF,
> (SVG?), etc.

Yes, or directly drawing onto the window.

> Which reminds me -- have you looked at the SVG model at all? Does it
> follow any of these ideas?

I haven't looked at it yet, all I know is that it's some kind of xml  
document.

>> It might be a performance killer. We can also optimize for the common
>> case. Note that the transforms will be updated lazily. That is, there  
>> is a
>> dirty flag which tells whether the transform has changed. Now when  
>> drawing
>> an object it is only re-transformed if either its own or one of its
>> ancestors' (parent, grandparent, ...) transform has changed since last
>> time.
>
> that means you're caching a lot. as you zoom in and out, move around the
> canvas -- the pixel coords keep changing -- or do you not cache those,
> just the intermediate transforms.

The intermediary transforms would probably be cached. Things like zooming  
can be handled by the DC.SetUserScale and GC.SetTransform methods.

>> class RenderSet(object):
>>      def __init__(self, renderable, look):
>>          self.renderable = renderable
>>          self.look = look
>>
>> Together this is enough information to render an object.
>
> Don't you need the data object too? Or is that referenced by the  
> renderable?

Good question. I think when I wrote that I hadn't think about the document  
model yet. It's also very tightly related to the Population question you  
posed above.

>> I think it's not that easy. Say the user really retrieves the flower  
>> data
>> out of a database. You can't expect him to change his database so the
>> position of flowers are stored there. So i guess we have to be pure  
>> about
>> it and consider the view a separate model.
>
> well, as I stated above,what's "view" and what's "data" is domain
> specific. You might have just the properties of a flower in your DB, and
> the location somewhere else, or the location could be in the DB -- it's
> all application dependent.

Or no location at all! And that's when you need a separate view model.

>> - I'd like to introduce the concept of a camera/view. This holds data
>> about the spot you are looking at, things like zooming etc. It also  
>> allows
>> for multiple views of the same document.
>
> yes, I do want that -- FC now has a ViewPort and a Scale. I'm not sure
> you need anything else for 2-d.

Probably not. Maybe some weird projection :-)

>> - Any coding guidelines?
>
> You mean coding style? Let's follow the wxPython guidelines:
>
> http://wxpython.org/codeguidelines.php
> and
> http://wiki.wxpython.org/wxPython%20Style%20Guide

Ok. Please tell me when you see ugly parts in my code (not counting the  
Benchmark). I'll try to improve upon this then.

> Then there's version support: I'm inclined to say wxPython2.8+ and
> Python 2.5+ (though wxPython supports 2.4), but we might want to poll
> users about that -- I'm often surprised to find people constrained to
> old versions.

Ok, I will developing and testing on wxPython 2.8.7 msw-unicode and python  
2.5.

-Matthias
_______________________________________________
FloatCanvas mailing list
[email protected]
http://mail.mithis.com/cgi-bin/mailman/listinfo/floatcanvas

Reply via email to