Matthias, My thoughts on your ideas below. I've cc'd this to the floatcanvas mailing list. I'd like to hold the technical discussion there, as it will archive it,and allow others to participate. If you're not already a member, you can sign up here:
http://mail.mithis.com/cgi-bin/mailman/listinfo/floatcanvas Nitro wrote: > 1) We need an IRenderer interface ("who"). There will be two concrete > implementation, DCRenderer and GCRenderer. Right now I am not sure if we > can make both share the same interface, but it should be similar enough > to get away with it. The renderer is responsible for drawing an object > (or list of them whenever possible). I'm a bit on the fence about maintaining DC use -- it can be faster and easier, but there a number of features it simply doesn't support, and I'm concerned about the overhead of maintaining two rendering APIs. Perhaps some profiling is in order to see how difference there really is. The other issue is that the hit-test system won't work with anti-aliasing -- can you turn that off with a GC? Or should we go with a different system for that anyway? > 2) Separate the data to be drawn from the draw object itself ("what", > IRenderable). This way you can instantiate thousand circle objects which > all share the same circle object. This saves memory and has another > advantage I'll outline later. Basically IRenderable will have a "Render" > method which gets passed an IRenderer object. Then it can call various > methods on this object. I'm a bit lost here -- I think I need a more concrete example. > 3) Separate the way the data is drawn from the draw object ("how", > ILook). This means things like brushes, pens, fonts. This allows you to > draw the same circle object in different fashions. You just create > different description for the Look. Again, a more concrete example may help me get this -- how is this different than a different Object, with different "Look" properties -- though I like the idea of encapsulating a lot of the "Look" in an class, rather than having it be simply a collection of attributes (or properties, or traits). That will make it easier to copy or change the look, and also provide a consistent API across different Objects. > 4) Based on 2 and 3 it is possible to do more optimization. This means > we need some kind of RenderSet object which binds 2) and 3) together. > For example one can now sort objects by their RenderSet (data and look). > This might enable fc to automatically determine objects suitable for > list drawing without the user having to pay attention to this. If the > look is the same for lots of objects it also saves lots of calls to the > DC's "look" functions. Do you mean avoiding extra calls for DC.SetBrush() and the like? Are these calls expensive enough to bother trying to avoid? ? The sorting can be cached. > Additionally this might screw up the order, Key problem -- if the objects don't overlap, then I suppose order (often called z-order) doesn't matter, I suppose, but often objects do overlap, so now we have to sort by "look" and z-order. My first instinct is that it isn't worth the complication. > "priority" attribute to the RenderSet. Higher priorities are drawn more > front. It has been suggested on the floatcanvas list, and it's a pretty common approach to have a z-order property -- then you can change that value to move objects up and down -- I'm not sure that's the best way to go, though -- I kind of like having an ordered list -- and the methods to move things up and down that list (which FC does not have now). This mirrors a lot of drawing programs (InkScape is a good example), where you can move an object: "up", "down", "to the top", or "to the bottom". > This could replace the current "draw in foreground" mode I don't think so. The key point of the foreground is that it is a separate buffer (actually not buffered), so that you can re-draw the foreground objects often and fast, and the background buffer does not change. This allows animation in front of a complex background, for instance -- it's handy for GUI manipulation of objects, too. > For convenience one can also add an > "enabled" property and if it is set to False skip rendering this object. yup. One key here is that if an object can be on more that one Canvas, then whether it is enabled or not may be a property of both the Object and the canvas -- not just the object. Or is that what your Renderable is -- the connection between a drawobject and a canvas -- hmmm. > 5) Create the concept of a scenegraph ("where"). A scene graph node has > a root node and can have children. Each node holds a transform and the > children inherit this. This seems like a performance killer -- but maybe inmost cases, it won't be a deep structure -- in the simple case, you have one root node, and it has all the objects as children. This would open up the ability to do things like create plotting axes, and the like. > This implies adding yet another set of coordinates > to your suggestion: local coordinates. Meaning objects are always > expressed in local coordinates (for example a circle is likely to be > centered around (0,0)) and then the objects are transformed to world > coordinates via their node's transform. At moment, some DrawObjects are like this: Rectangles, Circles, but others are not: Polygons, Polylines. Certainly, something like a polygon could be expressed in terms of a local coordinate system, and then transformed to world coords later, but what I tried to do in the past is have the coordinate system be natural for the object and use. For example, when one describes a polygon on map on latitude-longitude coordinates, one usually thinks in terms of each vertex having coordinates in world space. In a way, each vertex is its own entity with a position on the earth, rather in in a position relative to the other vertices's. You can translate between the two views, so we could store the internal coords in a relative space, but I wonder what the gain is of the overhead in translation between the two at each step -- it makes a move operation easy, but its harder to display the coordinates. > Note that not every node object > is drawable, nodes are logical entities. got it -- I like that. > For example there will be at > least two concrete implementations, first is the default node (since it > can have children it will replace the current group drawobject, And indeed, the whole canvas or document, or whatever would be a node, wouldn't it? > if you > move this node the children will move automatically, too, since they > inherit the transform). It does make moving easy, yes. > The second implementation is a RenderableNode. > It holds a RenderSet object. I'm still a bit confused to what a RenderSet object is. > The scene graph can further be used to cull away large portions of the > drawing. We can establish a parallel bounding box tree where objects are > inserted/removed from whenever a node is added to the regular scene > graph. would it be parallel? or could it simple be the way that the nodes are stored? Also where would z-order fit in to a node -- would all the objects in a node render all above or all below other nodes? > If somebody feels up to the task he can implement this: > http://en.wikipedia.org/wiki/R-tree I've thought about that -- there is a python wrapper around an r-tree implementation. It could be very handy if we want to re-factor the hit-test code a different way. > That way it's even self balancing > :-) For now I suggest something less sophisticated. I agree -- maybe make an API that could have an r-tree underneath, but keep in simple in implementation for now. > 6) So the common final high level object is probably the RenderableNode > one. It addresses the "object on multiple canvasses" problem as well. > Each canvas is associated with a scenegraph. So if you have 2 canvasses > and you want the same object to be drawn on both of them, you'd do this: > > - create the renderer (let's say GCRenderer) > - create the IRenderable (let's say a line) > - create the ILook (say width = 3, colour = red) > - create the RenderSet (with the newly created renderable and look) > - create a renderable scene node, set its renderset to the newly created > renderset and then attach it to the first canvas > - create a renderable scene node, set its renderset to the newly created > renderset and then attach it to the second canvas > > Note how objects are shared. Let's say we want it visible on one canvas, and not on the other (though maybe we don't need to support that at all) -- could we do that by putting the "visible" flag in the renderable scene node? > There can be helper functions to perform some of the steps (like > creating scene node, renderable and look could all be done in one call). That is key -- while it's be nice to have more control when needed, I really want FC to have a simple API to do simple things -- that's why I put in all the Canvas.Add*** helper functions. One should be able to put an object in a canvas with one call. > 8) How much do we care about backwards compatibility? Open question -- we'll see what folks on the FloatCanvas list say. My thoughts: We will break compatibility. We won't do it gratuitously. > 9) How do bounding boxes interact with arbitrary transformations? If the > transformations are non-continous bigger problems arise. Wow! I'm not sure how easy (or necessary) it is to support a non-continuous transformation -- do you have an example? Even with fairly simple transformations, a rectangle transformed may well no longer be a rectangle. In the current version, I try to only deal with BBs in World coords -- so I'm never looking at the transformed box. > In conclusion there might be more performance overhead coming with this > approach, but I doubt that it will be the bottleneck. The number of draw > calls is probably the main limiting factor (in addition to the drawing > itself). yep, except for very simple objects, like points, and lines. I've never profiled to see how long it takes to, for example, transform a 1000 vertex polygon compared to how long it takes to draw it. > Small addendum: > > It occurred to me that the enabled and priority attributes should be > part of the nodes rather than of the RenderSets. The scene graph is > similar to a Document in the MVC sense Yes, exactly -- I think we need a Document model and that seemed to be where you are going. I think a given node is visible (enabled) or not. As to the priority (z-order), I'm not as sure about that -- it can really only be defined in relative terms, so it's up to the scengraph to keep track of -- or the non-renderable nodes, anyway. > http://en.wikipedia.org/wiki/Model-view-controller , using this for > reference so we are both thinking about the same kind of MVC) Good idea -- there are a LOT of versions of the idea! > Frankly I am not sure whether FloatCanvas should support any kind of > default controller/document model. I think it's critical -- first of all, there can be no view without a model to view. Second, what I'd really like to improve is the ability to manipulate object with a GUI -- when I started the while thing, I saw that as a userland problem, but it's very highly desired,and hard to do cleanly -- we need to make sure FloatCanvas is well structured to support it, and the only way to do that is to have an implementation. That being said, I do want to be able to have the Document model stand on it's own. By the way, I already have another view I'd like to support (not part of floatcanvas, though): A tree view of the document -- you could see the layers, the objects on them, and click on them to see and set properties, make them visible or not, etc. > The user application will have to provide the model and controller part > of the MVC pattern. We should make that possible, but there should be default versions too. > # adapter (assuming user data does not change) > > def adoptMyDatabaseCircle(db_connection): > c = fc.Circle() > c.center, c.radius = db_connection.RetrieveCircleData() > return c Is this an adapter? or just a factory function? Or is there a difference? > # deriving > > class MyCircleFromSomeDatabase(fc.IRenderable): > def __init__(self, db_connection): > self.db = db_connection > > def Render(self, renderer): > center, radius = self.db.RetrieveCircleData() > renderer.DrawCircle(center, radius) It would be nice to be able to do that too. Hmm maybe have the data ( position, dimensions, etc) and the look (color, line thickness, etc) be two properties, and their getters and setters could be overridded ins a subclass to get the data from somewhere else, rather than stroing them directly in the object. > Choosing the right approach is probably dependent on the users data and > its update frequency. exactly. > I am not entirely sure about the MVC deal here, I have the same problem -- Are we working on the level where the Model is the whole document? or does each DrawObject have a Model (the data it represents)? or both? If we can do both without too much overhead, that could be pretty cool. > what do you think? Say a user changes the position of a circle, should > fc be able to automatically change the underlying model (user does not > have to register for any event)? I think so, but I may not quite understand. Do you mean: MyCircle.Move(cx, dy) And the Canvas just updates itself? > In this case it probably makes sense to > divide the IRenderable even further, having it split up in something > like CircleData (holds center, radius) and finally the Render(renderer) > method which knows how to draw a CircleData. What do you think? I'd have to see sample code to see how ugly it gets, but I think I like it. > As a sidenote, sharing objects like outlined in my last email should > also reduce the storage space for persisting by a great deal. I'm still a bit confused about how this can work. Are you talking about a situation in which you have 100 Red circles of diameter 10, but at different locations? so they would all share the same Circle object, but have different Position objects of some sort? I'm not sure how that would work. > I think fc should be split into separate subpackages. subpackages or submodules? Either way, yes. > Example: A user has a database with blossom objects. He can retrieve > number of blades, colour of blades and colour of blossom center. A view > for this would probably draw a circle (blossom center) and a bunch of > polygons in some way. Now say the database changed, then the user has to > make sure the flower data is updated and flagged "dirty" so that the > bounding box is recalculated and all caches are invalidated. This is > basically the model sending an event to the view. yup. I'm thinking lib.pubsub might be a good idea here -- the model isn't sending an event to the view (it should know nothing of the view), it is sending an event out to the world -- and the view happens to be listening for it. An issue here. in the current version, you have to call FloatCanvas.Draw() t0 re-draw. This is so that if you are changing a lot, you can make all the changes, and then re-render, rather than having it re-render with each change. With MVC, that may be too much coupling -- so, does the drawobject send out a message that it has changed, that gets picked up by the Document object, which decides if it wants to send out a message that the document needs updating? This is MVC within MVC... Or model within model, anyway. > Another situation: The > user creates + and - buttons so one can adjust the number of blades in > the view. In this case the buttons control the model which can then > update the view's data. > One problem I always had with the MVC pattern in the past was how to > store data related to the view. Me too. The distinction between View and model is not so clear in practice. > In the flower example above > view data is something like flower is at position (x,y) on screen. This > coordinate attribute is not part of the underlying data model though. You have a model within a model problem here. The position of the flower may not be part of the flower model, but it is part of the document model. I'd tend to say that it's part of the flower too -- it'll just get too ugly to try to be that pure about it. Thanks for all these ideas! -Chris -- Christopher Barker, Ph.D. Oceanographer Emergency Response Division NOAA/NOS/OR&R (206) 526-6959 voice 7600 Sand Point Way NE (206) 526-6329 fax Seattle, WA 98115 (206) 526-6317 main reception [EMAIL PROTECTED] _______________________________________________ FloatCanvas mailing list [email protected] http://mail.mithis.com/cgi-bin/mailman/listinfo/floatcanvas
