Differences between revisions 2 and 3
Revision 2 as of 2008-08-07 19:52:39
Size: 24472
Comment:
Revision 3 as of 2008-08-07 19:56:43
Size: 24470
Comment:
Deletions are marked like this. Additions are marked like this.
Line 48: Line 48:
     {{attachment:rootdraw2.gif"}}
     {{attachment:rootdraw.gif"}}
     {{attachment:rootdraw2.gif}}
     {{attachment:rootdraw.gif}}

Most Important Classes in CanvasModule

Here an overview of the most important classes.

a2dCanvasObject

Every part of a drawing contained within a a2dCanvasDocument is derived from the a2dCanvasObject base class. Every a2dCanvasObject can have nested a2dCanvasObject's , called children. The base class has a list a2dCanvasObjectList, adding to this list is the way to add children to the a2dCanvasObject. WxCanvasDocument itself contains one root a2dCanvasObject , and its children and nested children form the contents of the document. They are displayed when a a2dCanvasDocument is rendered to a a2dCanvasView. Children are added to the a2dCanvasDocument to fill the document. But since the children itself can have children also etc., in the end this results in a tree like structure. All other nested a2dCanvasObject's can be reached by recursive iterating over the child list of the root object of the a2dCanvasDocument and the deeper nested child objects. A child of one a2dCanvasObject can at the same time be a child of other a2dCanvasObject's, this is a key feature of the wxArt2D library. In derived a2dCanvasObject classes, one can add extra a2dCanvasObject pointers as members. This is called adding a reference to a a2dCanvasObject. Adding end and begin objects to polylines and lines is a good example of one case where extra references will be needed. In principle any new type of a2dCanvasObject can decide to use other a2dCanvasObject's references inside itself. Another use of references is on the undo stack. Objects can easily be switched from the document to the undo stack and back. The references only need to respect the reference counting mechanism which is used within a a2dCanvasObject. Reference Counting is used to keep track of all references to one instance of a a2dCanvasObject. The "parent pointing object" tells that the object pointed to is Owned, resulting in incrementing its internal reference count. When the parent object no longer needs the object pointed to, it Releases it. If all pointers pointing to a a2dCanvasObject are released and the reference count reaches zero, the object will automatically be deleted for real. As consequence a a2dCanvasObject has not just one parent, and the document structure is not a strict tree, its branches can touch eachother making it more a graph structure.

Pins are used to connect a2dCanvasObject's. Several a2dPin objects can be added as childs to its parent a2dCanvasObject. A pin has ( as all other child objects) a relative position towards its parent a2dCanvasObject. Therefore pins can be added at any position within a a2dCanvasObject. One a2dPin can be connected to another wxCanvasPins in a different a2dCanvasObject, and that is how a2dCanvasObjects are connected. The pin always have a pointer back to its parent, which makes it possible to walk through connected objects via its pins. In general the same a2dCanvasObjects can be added to several parent objects, but for a pin this is not allowed. A pin can only have one parent and it also saves that parent internal. When connected a pin is only connected to one pin on Another a2dCanvasObject. Deleting the Parent Object should also really delete those Children Objects which are a2dPin's, even if they are connected to other pins. When a pin is deleted it will automatically disconnect itself.

A global a2dCanvasObject instance called wxNullCanvasObject is often used to switch a reference from nothing to something. For instance when a Line should have no begin and end points, the members of the class holding this information will point to wxNullCanvasObject. All functions are aware of the fact that wxNullCanvasObject means it should not be used and ignored. Because wxNullCanvasObject is reference counted as any other a2dCanvasObject, this makes it much easier to switch reference counted pointers.

By default an object renders itself and its children, for derived object this can be extended to also render references to other objects which were stored as members of the object and are not part of the default child list. A Line with endpoints being references to other canvas objects is a good example of that. The difference is that children are always placed relative to (0,0) of the a2dCanvasObject with its matrix already applied, while in case of endpoint's on a line, the end objects are placed relative to both endpoints of the line.

Each a2dCanvasObject derived class always has a matrix to position itself relative to its parent objects. Where the parents can be one of the following: a a2dCanvasObject to which it was added as a child a a2dCanvasObjectReference any other object containing pointers/references to the object.

The position of a a2dCanvasObject is defined by a 3*2 matrix. The matrix defines the rotation, scaling, skew, mirror and at last a translation which defines the actual position. The matrix is the same and relative to all parent objects of the a2dCanvasObject. It is not very usefull to place an object two times as a child within the same parent object. This will simply render the object twice at the same position. And changing the position of the child , meaning changing its matrix, will effect both in the same manner. If one still wants to do something like this, one should add an extra level using a a2dCanvasObjectReference or a plain a2dCanvasObject with the above child as its child, which can be placed twice at a different position. See a2dCanvasDocument storage.

The child objects placed in the a2dCanvasObjectList of a a2dCanvasObject, are placed relative to that parent a2dCanvasObject. In different words, the child object its matrix is relative to the parent object. But a child is nothing more then a reference to another a2dCanvasObject, and other a2dCanvasObject may exist that also use the same object as there child. So a a2dCanvasObject can have many parent objects. Things go wrong when parent objects are inserted as children in their own nested children, this will result in a circular calls to rendering routines.

a2dCanvasObject is derived from wxCanvasEvtHandler, therefore you can have event tables for canvas objects. Events are redirected from a a2dCanvasView to the a2dCanvasObject. The lowest nested a2dCanvasObject which is hit by the mouse and is displayed on top of others, will get events first. If the event is not handled, it will travel up the hierarchy of a2dCanvasObjects. The a2dCanvasView receives events from a2dCanvas ( derived from wxWindow ) on its turn. See Events and Hittests

WxCanvasObjects has also a list to add properties. wxCanvasPropertyBase Properties are invisible, but using a special a2dCanvasObject called a2dVisibleProperty(), which has a reference to a wxCanvasPropertyBase, they can be displayed as text.

All a2dCanvasObject have a boundingbox defined in worldcoordinates, and this eqaul for all a2dCanvasView's displaying the object. To cope with world as well as pixel like objects, there is a pixel and world extend that is added to the boundingbox of the object when needed. See BoundingBoxes.

During rendering of each a2dCanvasObjectits boundingbox is checked. If it is within in the Update area it will be rendered, else not. Therefore the boundingboxes must always be up to date. Rendering takes time, so all objects that are outside the Update area will be skipped, which is very good for speed.

The boundingbox of an object is always relative to its parent objects, because it already includes the matrix of the object itself. Still an untransformed boundingbox can be retreived also, but that one is not stored, and calculated each time.

a2dCanvasDocument

a2dCanvasDocument holds a2dCanvasObjects organized in a tree like structure. Each a2dCanvasObject can have a list of child a2dCanvasObjects. Every a2dCanvasObject is placed on a layer. A layer is a 3D z level, and is used when rendering the objects. Objects with the same layer id are rendered at the same level, while objects on another layer are rendered on top or in the back of that first layer. WxCanvasDocument has a root a2dCanvasObject, and its children is the first level in the document. If those child a2dCanvasObjects also have children themself, we have a second level of hierarchy. This is the standard manner of building groups of a2dCanvasObjects. Specific classes derived from a2dCanvasObject, can have more ways to store pointers to a2dCanvasObjects. And that way adds hierarchy in a different way. A good example is adding endpoints to a line segment. The endpoints are two pointers to a2dCanvasObjects. The position of Children is relative to the position of the parent object holding the children. For the end objects of a line this would be hard to use. Here the begin/end objects should be relative to the position of the begin and endpoint of the line. Yet Another way of nesting is by using wxCanvasRefObject's, which must be seen as a specialized a2dCanvasObject with just one child object. WxCanvasObjects are reference counted, every pointer to the same a2dCanvasObject brings more hierarchy to the a2dCanvasDocument. Children of a a2dCanvasObject, are in fact references to other a2dCanvasObjects. And the same a2dCanvasObjectcan be a child of two different parent object.

The layers settings are part of a a2dCanvasDocument too. The layers setting define the default Strokes and Fills for a2dCanvasObjects on a certain layer. a2dCanvasObject are placed on layers by setting a layer id for it. The layers are drawn in a certain order, which is part of the layer settings. In practice it just means that the a2dCanvasDocument is rendering layer after layer. While rendering one layer, only a2dCanvasObject's with that layer id are rendered.

All a2dCanvasObjects contained within a a2dCanvasDocument know the a2dCanvasDocument to which they belong, a pointer to the a2dCanvasDocument is a member of every a2dCanvasObject. The a2dCanvasDocument pointer is the only connection a a2dCanvasObject has to the outside world. For drawing the iretation context a2dIterC, is given as a parameter to the drawing functions. Via the drawing context the a2dCanvasView can be reached from within a a2dCanvasObject. On its turn a2dCanvasView has a a2dDrawer2D which is used to draw/render the a2dCanvasObject in some way. See a2dCanvasView and a2dDrawer2D.

Rendering start at the ShowObject set within the a2dCanvasView. Therefore a2dCanvasView does not always start rendering at the a2dCanvasDocument its rootobject, it can be any a2dCanvasObject that is nested within the a2dCanvasDocument. See a2dCanvasDocument organization for a simple case. Here the a2dCanvasObjects are on three layers. One first level a2dCanvasObject has five objects, four on layer1 and one on layer2. One of its objects is a a2dCanvasObjectReference to an image on layer3. This same image is also placed directly in the top of a2dCanvasDocument.

See in this figure a2dCanvasDocument as seen from the top how the above will look like when rendered from the a2dCanvasDocument.

  • rootdraw2.gif rootdraw.gif

ZChanges in a a2dCanvasDocument are announced to the a2dCanvasView views which display the document via a a2dDocumentEvent of type wxEVT_UPDATE_VIEWS. First the changed object sets itself and the document as having pending objects. In idle time this is checked, resulting in the update event sent to the a2dDocviewGlobals EventDistributer. This will distribute the event to all a2dCanvasView views, and the right ones will start updating themselfs. After that all boundingboxes are recalculated for the changed objects, and another Update event is sent. This cycle makes sure all changed areas on all view are redrawn. If a certain a2dCanvasView is not shown/visible at the moment its internal buffer will still be updated. As soon as the a2dCanvasView is shown it will re-use what is in the backup buffer when possible. The result is that minimizing a whole application and maximizing it again does not result in a total redraw. For the moment this seems best. Currently in a single view/document setup the a2dCanvas window intercept idle events and redirects them to a2dCanvasDocument. In a multiple document setup, the idle events come from the frames attached to the a2dCanvasView's. It would be easier to have a2dCanvasDocument recieve Onidle events directly, but this is not possible for a non wxWindow class. Therefore it is required to intercept Onidle events somewhere in a wxWindow and redirect them to the a2dCanvasDocument class. The docview frame classes like a2dDocumentFrame and a2dView do this by default.

As an example, assume one a2dCanvasObject that is part of a certain a2dCanvasDocument is moved in position, as result of a mouse move event. This means that all a2dCanvasView views for this a2dCanvasDocument need two updates. One for the old position of the object and one for the new. To achieve this, the Translate member of a2dCanvasObject translates the object, and sets it as Pending.

void <<Dox(a2dCanvasObject)>>::Translate( double x, double y )
{
    m_lworld.Translate(x,y);
    SetPending(TRUE);
}

The SetPending call, sets a pending flag in the object and flags the a2dCanvasDocument that there are pending objects. In the first OnIdle call to a a2dCanvas/a2dCanvasDocument, the programs discovers that there are pending objects, and this triggers the updating of the pending objects on all a2dCanvasViews that use the a2dCanvasDocument. See a2dCanvasView

a2dCanvasView and a2dDrawer2D

See DrawingAndUpdating][drawing and updating

a2dRenderImage

a2dRenderImage is a special kind of a2dCanvasObject, placed within a a2dCanvasDocument structure, it act's like an Image with a width and height. The contents of the image is the result of rendering another a2dCanvasDocument structure to the image. a2dRenderImage is using a a2dMemDcDrawer internal to render the document part. The drawer its buffer is copied to a wxImage, this image is scalled and cashed and after that displayed when the a2dRenderImage object is rendered from the document where it is placed into.

a2dCanvas

a2dCanvas is a wxWindow used for displaying a buffer rendered by a a2dCanvasView its a2dDrawer2D. It takes care of redefining mapping of the a2dCanvasView, based on the scrollbar positions and size of the window. Restricting the amount of scrolling is possible. WxCanvas displays the a2dCanvasView buffer which contains a part rendered from a a2dCanvasDocument. WxCanvas its scrolling is in world coordinates. WxCanvas also intercepts OnIdle and Onpaint events. During those events, updating/redrawing of pending areas or objects takes place. The OnIdle event calls a2dCanvasDocument to update things for all a2dCanvasViews sharing the same a2dCanvasDocument.

The contents displayed on the a2dCanvas can be written to a SVG file. It takes into account the current visible part on the a2dCanvas window.

a2dCanvasObjectReference

A a2dCanvasObjectReference is a a2dCanvasObject that displays another a2dCanvasObject at a different position, without duplicating the data of the object to display. In principle this is the same for every child added to a a2dCanvasObject, which are references also. But this type does have some specific features. References to a a2dCanvasObject in other a2dCanvasDocument's, than the one where the reference is placed in, is no problem. You can make libraries containing many used objects this way. The object that is referenced within a a2dCanvasObjectReference will be deleted if its refcount is 0. If the object referenced is not inside the same a2dCanvasDocument (same m_root), it will not be deleted. When deleting a wxCanvasDocuent, a a2dCanvasObject is either really deleted through a a2dCanvasObject or a reference depending on which one makes the refcount reach zero. If a reference to an extranal object exists (different m_root), that object will be deleted when the a2dCanvasDocument containing that object is deleted. References to external objects, will result in rendering that particular object using layersettings that are defined for the a2dCanvasDocument that contains the external object. So colours, strokes (width) and fills used in an external reference, will use the layersettings of the external a2dCanvasDocument. But for calculating pixel based object values, the Active Drawer is still used. A a2dCanvasObjectReference to a canvasobject placed inside the same parent a2dCanvasObject will have has its own boundingbox. But the a2dCanvasObjectReference uses the position/matrix of the canvasobject. The canvasobject matrix is multiplied with the matrix of the a2dCanvasObjectReference. So a circle at (8,10) , referenced by a2dCanvasObjectReference somewhere else at (2,3), will be drawn at (10,13).

a2dNamedProperty

The a2dNamedProperty is the base class for adding properties to any a2dCanvasObject. These properties can be added to the propertylist of a a2dPropObject from which a2dCanvasObject is derived. Properties are stored in the a2dNamedPropertyList property list. Properties can be added and removed while the program is running, and memory is freed when removing a property. Because of this it is ideal to use them for storing temporary information in a a2dCanvasObject, for which you prefer not to use memory all the time, but only when needed. The basic a2dCanvasObject editing features are setup like that.

All property classes are derived from a2dNamedProperty. Each property has an id which makes it unique. The use of the property classes is not only within the a2dCanvasObject itself, a a2dCanvasView uses them to set the current style for drawing and the layer settings of a document also uses properties for setting the style of a layer. a2dNamedProperty does have a specific id as defined by its a2dPropertyId and via that id it has a name too. The properties containing basic types ( bool int double void*), can hold as a value the types in question. A property can be made visible on the canvas using a special a2dCanvasObject called a2dVisibleProperty. This a2dVisibleProperty is added to the parent a2dCanvasObject rendering the property as a normal canvas child object. It takes care of displaying the property in text form, using the stringpresentation of the property. Some properties have a special meaning for the parent a2dCanvasObject. a2dShadowStyleProperty and a2dClipPathProperty influence the rendering of its parent a2dCanvasObject. a2dClipPathProperty has a reference to a a2dCanvasObject containing a polygon, which will be used to clip the drawing of this property its parent a2dCanvasObject object. It is possible to display the clipping object itself too.

a2dCanvasObject can have style references a2dFill, a2dStroke and a2dContour, which are stored as a a2dNamedProperty. These properties are treated in a special manner, in this case to set the color, outline etc. of the a2dCanvasObject containing the properties. The a2dStyleProperty is used to store extra style properties, to be used for other purposes within the a2dCanvasObject. For instance a2dShadowStyleProperty is derived from a2dStyleProperty, and the color etc. of the shadow can be set independent of the a2dCanvasObject its own style. If style properties are not available, layers settings will be used for the default style to render the object. The basic rendering routines in a2dCanvasObject always set the style to be used for the a2dDrawer2D, either based on the style properties if available, or else based on the layer setting of the document. This all does not mean that the object can and should only use this style, in fact it may set any style it wants to draw itself. The above mechanism is just the default way to set style for a a2dCanvasObject, and works well for simple primitives. In case of complex objects, like curve containers etc, clearly more is needed. And therefore in such an object more style is stored, either via properties or directly as members of the class.

When useful more then one of the same property can be added to an object.

The following picture visible properties objects demonstrates the combination of properties using a2dVisibleProperty to show those properties as normal child objects within the parent a2dCanvasObject.

  • width='553' height='446'

Hit tests, rendering and bounding box calculation is always inclusive the properties that are made visible using a2dVisibleProperty Objects. a2dVisibleProperty is just acting the same as any other normal child of a a2dCanvasObject. a2dClipPathProperty has a reference to a a2dCanvasObject containing a polygon, if this polygon itself is added as a child to the parent a2dCanvasObject, it will be displayed as any other child. You can even edit the polygon interactif, in order to change the clipping property. The idea behind displaying properties indirectly via an a2dCanvasObject, is to be able to edit them in the same manner that one can do with all a2dCanvasObject's.

See PropertySystem more on properties.

a2dIterC

a2dIterC is used while traversing a document to render its contents to the screen or when processing mouse events. It is called the iterative context. When rendering, via this class one will have access to the a2dDrawer2D and a2dCanvasView. When doing a hittest, the hit margin is available via this class. A reference to a a2dIterC class is passed as an argument to the rendering, hit test and event processing functions. In general all functions doing matrix calculations, in order to find the absolute position of the object, will use a a2dIterC as parameter. In combination with a2dIterCU, the matrix calculation for each new child level in a2dIterC is calculated. While traversing the document a2dIterCU object are pushed on and popped from the stack. This is automatically done, since the a2dIterCU are just local variables. The constructor of a2dIterCU take care of multiplying the matrix of the new child object with the foregoing parent matrixes. The resulting matrix is set to the a2dIterC. And in the destructor of a2dIterCU the old matrix without the child included is set again to the a2dIterC. This is a very convenient way to make sure matrix calculations are done right. Together with the matrix to convert from relative coordinates to absolute coordinates, also the inverse matrix is calculated. This is often used in hit testing.

wxArt2D: CanvasModuleMainClasses (last edited 2008-08-08 22:43:18 by KlaasHolwerda)