Differences between revisions 22 and 23
Revision 22 as of 2016-05-03 08:29:14
Size: 40506
Editor: inetproxy-p
Comment:
Revision 23 as of 2016-05-03 08:30:32
Size: 40502
Editor: inetproxy-p
Comment:
Deletions are marked like this. Additions are marked like this.
Line 63: Line 63:
Mouse and Character Events are intercepted in a <<Dox(a2dCanvas)>> window and redirected to <<Dox(a2dCanvasView)>>. <<Dox(a2dCanvasView)>> redirects the events to the <<Dox(a2dCanvasObject)>> which is shown on that <<Dox(a2dCanvasView)>>. Often this is the root object of the <<Dox(a2dCanvasDocument)>>. The <<Dox(a2dCanvasObject)>> will redirect the event to its children. In the end the <<Dox(a2dCanvasObject)>> which has the mouse pointer on it, will receive the events. Still other object are tested also, since special events are generated by the <<Dox(a2dCanvasObject)>> itself (e.g leaving and entering objects). If the original event from <<Dox(a2dCanvasView)>> is a mouse event, <<Dox(a2dCanvasObject)>> will add extra info, and sent it as a <<Dox(a2dCanvasObjectMouseEvent)>> to itself. Now a derived <<Dox(a2dCanvasObject)>> can add static event tables, and that is how events are intercepted in derived objects. If a child object does not handle the event eventually, its parent object will get the event. This continues until the event arrives at the <<Dox(a2dCanvasView)>> again. A <<Dox(a2dCanvasObject)>> can also Capture all events which are sent to the <<Dox(a2dCanvas)>> window. Intercepting Events is via a Static Event Table, and in the same manner as intercepting events on a wxWindow. Some a2dCanvasObjects, generate new events based on the basic <<Dox(a2dCanvasObject)>> events. For instance <<Dox(a2dHandle)>> will generate the <<Dox(a2dHandleMouseEvent)>> event and sent it directly to its parent <<Dox(a2dCanvasObject)>>. The parent object does intercept this type of event to implement editing. When dragging a <<Dox(a2dHandle)>>, this will effect the parent object. See Events for more. Mouse and Character Events are intercepted in a <<Dox(a2dCanvas)>> window and redirected to <<Dox(a2dDrawingPart)>>. <<Dox(a2dDrawingPart)>> redirects the events to the <<Dox(a2dCanvasObject)>> which is shown on that <<Dox(a2dDrawingPart)>>. Often this is the root object of the <<Dox(a2dDrawing)>>. The <<Dox(a2dCanvasObject)>> will redirect the event to its children. In the end the <<Dox(a2dCanvasObject)>> which has the mouse pointer on it, will receive the events. Still other object are tested also, since special events are generated by the <<Dox(a2dCanvasObject)>> itself (e.g leaving and entering objects). If the original event from <<Dox(a2dCanvasView)>> is a mouse event, <<Dox(a2dCanvasObject)>> will add extra info, and sent it as a <<Dox(a2dCanvasObjectMouseEvent)>> to itself. Now a derived <<Dox(a2dCanvasObject)>> can add static event tables, and that is how events are intercepted in derived objects. If a child object does not handle the event eventually, its parent object will get the event. This continues until the event arrives at the <<Dox(a2dCanvasView)>> again. A <<Dox(a2dCanvasObject)>> can also Capture all events which are sent to the <<Dox(a2dCanvas)>> window. Intercepting Events is via a Static Event Table, and in the same manner as intercepting events on a wxWindow. Some a2dCanvasObjects, generate new events based on the basic <<Dox(a2dCanvasObject)>> events. For instance <<Dox(a2dHandle)>> will generate the <<Dox(a2dHandleMouseEvent)>> event and sent it directly to its parent <<Dox(a2dCanvasObject)>>. The parent object does intercept this type of event to implement editing. When dragging a <<Dox(a2dHandle)>>, this will effect the parent object. See Events for more.

Canvas Module

The canvas module is using the general module and the xmlparse module. Drawable objects or stored in a a2dDrawing, and displayed on a a2dCanvass. What part is displayed on the canvas is set through its a2dDrawingPart.

Interactive editing and drawing of a2dCanvasObjects is implemented with a2dStTool and a2dStToolContr. Using those tools one can create interactive editing applications.

A controller can be plugged into a a2dDrawingPart. A a2dDrawer2D is an abstract drawing context class, each a2dDrawingPart has one, which it uses to draw a a2dDrawing on to the a2dCanvas.

The controller intercepts all events, from the a2dCanvas window, before they are handled by the a2dDrawingPart class. The controller, has a stack of pushed tools. When a tool is active, it will receive the events, and based on that, manipulate the contents of the drawing, which is displayed on the a2dCanvas.

The a2dDrawingPart on its turn redirects events to the a2dCanvasObjects which are part of the a2dDrawing, that is displayed by that a2dDrawingPart.

The canvas module contains a stack oriented controller class, on which you can push tools used to draw objects, and edit those objects. Other tools are there to copy, drag, delete object. A zoom tool, can be used at all times to zoom into a drawing, even when one is already busy drawing etc. with a tool at that moment. In general one pushes only one are two tools at then time. For instance first push a mastertool, like a2dMasterTagGroups3, which is on its turn decide what other tool needs to be pushed to do a certain action. And if that action is for instance a draw a rectangle tool a2dDrawRectangleTool, that rectangle tool can temporary push a zoomtool a2dZoomTool. A tool is pushed on a stack, and when a tool is popped from the stack, the first tools on the stack will become active again. Like this one can push a zoom tool while busy drawing a polygon. This feature makes it easy to do extremely accurate drawing. Several types of mastertools were development, depending on what type of editing application is needed. For editing a wired diagram, a different master tool is needed as when just wanting to draw something. One can always derived are write its own master tool, or only push one simple drag tool a2dDragTool.

See a2dBaseTool for what tools are available.

Several standard dialogs specialized for editing purposes, are part of this module. Think of things like settings layer style and rendering order LayerPropertiesDialog.

You can find a general editing class in a2dEditorFrame from which you can derive and adept your own. This is part of the EditorModule, since it uses the DocviewModule also. But the a2dEditorFrame is no more then a bunch of menus to start tools or showing dialogs for several actions. In many cases you just want your own editing frames, with has just a few tools to start. And wxArt2D is setup to make this as easy as possible. The goal is to create highly interactive application with ease.

Global Overview

Tools

The WxArt2D library uses Tools to drag, move, rotate, scale, select, edit, group, ungroup a2dCanvasObjects that are displayed on a a2dCanvas. A set of tools are maintained by a toolcontroller. It has a list of tools that can be made active one at the time, or if needed more tools can be activated at the same time. A toolcontroller can be plugged into a a2dDrawingPart object when you need it. The a2dDrawingPart is used inside all types of canvas objects like a2dCanvas and a2dCanvasSim. It takes care of proper updating the view, when something changes in the drawing. A special set of tools has been written to manipulate objects on the canvas. They all work flicker free, and allow zooming while in action, which makes extremely accurate drawing possible. The editing tools (to edit objects interactive), work with a copy of the a2dCanvasObject to edit. This copy of the a2dCanvasObject is set into editing mode by the editing tool, which results in displaying handles for dragging. The editing tool redirects the mouse events ( and others ) to the editing copy object. Each editing handle in the copy object has a predefined action which modifies the copy object, and at predefined moments will modify the original object via commands. Because we want to be able to Undo and Redo changes, commands will be sent to the a2dCanvasCommandProcessor from within the copy object that is being edited. From command processor, the commands will modify the original object. Every fundamental change ( e.g. move x,y or fill and stroke), is sent as a a2dCanvasCommand to the a2dCanvasCommandProcessor of the a2dDrawing. This is how the original object stays in sync with the copy that is being edited. The Number of commands sent, defines what exactly can be undone. So for a drag only the start and end of the drag will result in a command.

Any a2dCanvasObject derived object is editable. The default editing mode only modifies the transform the matrix of the a2dCanvasObject. The derived object itself is free to implement any other type of editing functionality it needs. When a2dCanvasObject::EditStart() is called on an object, the object is prepared for editing by (generating and) displaying the editing handles. Event tables intercept the events for the object, and when in editing mode, the mouse events that hit and drag the editing handles will result in changing the object itself. All editing is concentrated and defined within the a2dCanvasObject itself. Editing ends when EditEnd() is called, which removes or hides the editing handles and disables editing mode. When an object is in editing mode a flag is set to indicate this. The way a a2dCanvasObject is Rendered can be changed when in editing mode by changing the rendering behavior when the editing flag is set. Drawing handles etc., are stored inside the a2dCanvasObject itself. This can be as real C++ members, or by using a2dCanvasPropertyBase properties and childobjects like a2dHandle. The last is the best if you want to optimize memory usage, since they can be created dynamically at the start of the editing and removed at the end. Therefore all the extra data needed to edit an object is not stored in the a2dDrawing all the time, instead only temporarily when editing the object. The above requires a certain strategy for editing an object, which is under supervision of the editing tool. Still you are free to develop tools and canvasobject's that take a different approach. For a medium size drawing, it is probably easier to store all editing features inside the object itself. And if the data stored in the object is big anyway, a few extra bytes do not hurt that much.

The Command processor processes a2dCanvasCommand's and saves enough data on the undo stack, to reverse the change from the old state into the new state of the canvas object in some manner. Not only editing results in commands, also drawing a new object, is added to the drawing via commands. All modifications to a drawing can therefore be Undone.

See Editing Objects for a picture of editing in progress. The lowest only is editing the matrix of the object, offering handles to scale, rotate and skew the matrix of the object. This manner of editing will be chosen automatically when no specific object editing is implemented.

  • width='394' width='394' width='394'

Command processor

The canvas tools use the a2dCanvasCommandProcessor to modify objects in a a2dDrawing. After an interactive tool has done a certain operation, it sends the change as a command to the command processor. This makes it possible to implement undo and redo of the tool actions. The a2dCanvasCommandProcessor is local to the drawing, and only maintains the changes to its drawing. A second global a2dCentralCanvasCommandProcessor is used to load processing scripts/files, containing commands that can modify the data in the whole application. It can execute commands, to modify the contents of a2dCanvasDocument's in a non interactive manner, and also has commands to open and close files etc. The graphical user interface of the application can use this commandprocessor to open and close files. The commandprocessor can record the commands, and store them into a file as strings, and that's the way to create macro files. The ultimate goal is to use wxPython scripts for that, and once the wxArt2D library is wrapped into wxPython, this will be possible. This might lead to a second form of a2dCanvasCommandprocessor in the future, which is an integrated (wx)python interpreter. Imagine wxWindows C++ code loading wxPython scripts that modify a2dCanvasDocument's and create new views for the document. The main GUI interface will still be in wxWindows c++ code, but the wxPython interpreter will be able to add extra dialogs and such to extend a program based on the wxArt2D Library.

The a2dCentralCanvasCommandProcessor does also have member functions to directly add basic objects to a drawing. Functions like SetStroke(..) SetFill() AddRectangle( w, h .... ) , make it possible to set the current fill and stroke once for the command processor, and use it for all subsequent objects that are added to the document. When setting things like Fill and Stroke, special pending events are distributed as signal, telling all registered tools or dialogs, that something has changed resulting in updating them selfs ( as soon as the program becomes idle).

Zooming

The part of the world coordinate system that is shown on a a2dCanvasView its device ( e.g a2dCanvas window) can be set to any part of the world coordinate system. Being (MINDOUBLE to MAXDOUBLE in both X and Y) The conversion from worldcoordinates to device coordinates is called mapping. The mapping allows you to map any part/viewport of the worldcoordinate system to a window or bitmap. The size of the canvas window is taken into account to calculate the right mapping. In order to have the same amount of user units per pixel in both X and Y, a given area in world coordinates may be adjusted to achieve this. This makes sure that at least the given area in world coordinates will be shown. Zooming into a drawing is just a matter of setting a new mapping matrix for the a2dCanvasView used for a a2dCanvas, resulting in a new area shown on a a2dCanvas. See Zooming for a picture. Scrolling means also modifying the mapping, but it only shifts, and the width and height of the mapping stay the same. This is why scrolling small amounts can be optimized, only the parts that were not visible are redrawn, the rest is shifted.

  • width='167' width='167'

graphical data stored in a2dCanvasDocument

a2dCanvas is able to display data that is stored in a a2dDrawing object. Internally it uses a a2dDrawingPart to do the rendering. a2dDrawing in general is not depending on the displaying device, it can also exist without a a2dDrawingPart and/or a2dCanvas. The a2dDrawingPart uses an abstract a2dDrawer2D to draw a drawing. The a2dDrawer2D may decide to directly draw on a device or first store the drawing internally, and when everything is drawn, draw its internally stored drawing to the device. The last is what happens in case of double buffering. The a2dDrawingPart takes care of mapping part of the drawing to the device and all update mechanisms or organized in here. But a2dDrawer2D does the actual drawing. The a2dDrawing does use the a2dDrawer2D to draw what is stored as as canvas objects. To optimize drawing speed, only changed objects in the document will be redrawn. A bounding box will be calculated to clip the canvas objects in the drawing against the a2dDrawingPart. The bounding box is defined in world coordinates, but two extra extends can be set within the object. This makes it possible to use pixel strokes within objects, and to some extend completely pixel defined objects ( e.g. editing handles ). The stroke size of the stroke used within the object is not part of the bounding box, it is added when the bounding box is asked for. The reason is that object have there bounding box defined relative to it parent objects. The absolute bounding boxes are calculated at the top level in a a2dDrawer2D. For that it transforms the relative bounding box first to the absolute position, and then the stroke size is added.

The a2dDrawing contains one root a2dCanvasObject. The root object has, as children, other a2dCanvasObjects and they can be nested. Objects can be defined in world or pixels coordinates, or a mix of that. When rendering starts, the specific a2dDrawingPart is set to the drawing iterations context. This iteration context class a2dIterC, is used when traversing the document hierarchy. This iteration context is given as a parameter to the a2dDrawing rendering functions. The translation to device coordinates is done within the a2dDrawingPart class. Each a2dCanvasObject must set the Absolute matrix to a2dDrawer2D in order to draw itself such that it is automatically converted from relative world coordinates to absolute world coordinates. This matrix is combined with the a2dDrawer2D its mapping matrix for conversion to device coordinates directly. The same combined matrix will be used to calculate device coordinates for the subsequent calls to the a2dDrawer2D its basic primitive drawing functions. Those functions are used by the object to display itself in some manner on the a2dDrawingPart. The a2dDrawingPart will eventually or directly display itself on the device, which is in general a a2dCanvas. Of course the a2dCanvasObject has a limited set of drawing feautures to display itself, being everything that is supported by a2dDrawer2D. But since amongst them is drawing Images and Bitmaps, for complex things one can render the object to a bitmap in any way needed. After that, the bitmap can be drawn with a2dDrawer2D to the device.

Events and Hittest on objects

Mouse and Character Events are intercepted in a a2dCanvas window and redirected to a2dDrawingPart. a2dDrawingPart redirects the events to the a2dCanvasObject which is shown on that a2dDrawingPart. Often this is the root object of the a2dDrawing. The a2dCanvasObject will redirect the event to its children. In the end the a2dCanvasObject which has the mouse pointer on it, will receive the events. Still other object are tested also, since special events are generated by the a2dCanvasObject itself (e.g leaving and entering objects). If the original event from a2dCanvasView is a mouse event, a2dCanvasObject will add extra info, and sent it as a a2dCanvasObjectMouseEvent to itself. Now a derived a2dCanvasObject can add static event tables, and that is how events are intercepted in derived objects. If a child object does not handle the event eventually, its parent object will get the event. This continues until the event arrives at the a2dCanvasView again. A a2dCanvasObject can also Capture all events which are sent to the a2dCanvas window. Intercepting Events is via a Static Event Table, and in the same manner as intercepting events on a wxWindow. Some a2dCanvasObjects, generate new events based on the basic a2dCanvasObject events. For instance a2dHandle will generate the a2dHandleMouseEvent event and sent it directly to its parent a2dCanvasObject. The parent object does intercept this type of event to implement editing. When dragging a a2dHandle, this will effect the parent object. See Events for more.

Rendering a document

The rendering of a document its contents is not limited to a window. a2dCanvasView can be used to display a a2dCanvasDocument drawing on a wxWindow derived class. But internal it uses a2dDrawer2D to do the drawing. A a2dDrawer2D can draw also to a bitmap or printer etc. a2dCanvasView just uses a WxDrawer2D to display a bitmap in a window. A special a2dCanvasObject's called a2dRenderImage is available to be able to use a wxDrawer view with its own document as part of another a2dCanvasDocument. As such it is a a2dCanvasObject inside a higher level a2dCanvasDocument. It all depends on the implementation of a a2dDrawer2D how and where the a2dCanvasDocument is displayed. For the a2dCanvasDocument that contains the data it makes no difference at all. The only limitations is that the render member function of a a2dCanvasObject can only use the drawing capabilities that or offered by the a2dDrawer2D base class. The a2dDrawer2D implements the simple drawing of primitives, its parent a2dCanvasView takes care of advanged update schemas. It controls a a2dCanvasDocument asking it to re-render certain areas that need an update. A flag is set in a a2dCanvasDocument when it is modified. The a2dCanvasDocument checks this in idle time and will start traversing the document to find the objects/areas that did change. All changed areas will be reported to all wxDrawer's used by the a2dCanvasDocument. This update sequence takes into account the area occupied by the object in its previous and current state. When this is ready the a2dCanvasView's will start redrawing the modified areas at the next idle event in a2dCanvasView. So the user does not have to take care of anything after modifying or moving objects etc. Another advantage is that many changes to a a2dCanvasDocument can be made, before real redrawing takes place only once. Since redrawing areas are combined in a2dCanvasView, this minimizes the number of redraws a lot.

Rendering of a a2dCanvasObject's and its children, is all aranged through recursive calls to a2dCanvasObject::Render. This function controls how child objects will be rendered, and what effect certain properties do have on the object to render. When an object has private style properties they will be set here as the current style for the active a2dDrawer2D, else the layer of the object will be used to set the style. The function also calls a2dCanvasObject::DoRender, which is virtual and should be overridden to define how a specific object should be rendered. So in case of a circle object, the base class defines color, fillpatern, outline etc. but the DoRender function really draws the circle using the active a2dDrawer2D. You may decide to completely redefine how an Object and its children will be rendered, since the rendering functions or virtual. For instance, assume you want to make a text block object which has only one type of children, being the lines of text in the text block object. In such a case when the text block contains 1000 lines of text, one soon needs an optimized rendering strategy, in order to reduce the number of lines to redraw to a minimum when editing just one line. Therefore in such cases you will override the default a2dCanvasObject rendering.

Automatic Update of Drawing

When a certain a2dCanvasObject changes, it sets itselfs pending, and informs its a2dCanvasDocument that it needs an update, by setting a flag in there also. In idle time the a2dCanvasDocument flag is checked, and if TRUE all a2dCanvasObjects are checked and all pending a2dCanvasObjects issue pending update areas to the a2dCanvasView instances that the a2dCanvasDocument uses to display itself. Those update areas are based on the previous and the current area occupied by the a2dCanvasObject (e.g. old and new boundingbox of object made absolute and converted to device coordinates). The a2dCanvasView's assemble those so called update areas, and as soon as the program is idle they will start re-rendering those areas. When needed, the rendering of pending objects and/or update areas can be forced also. Rendering starts in a2dCanvasView with the so called ShowObject() and continues by traversing its children whcihc are all stored in the a2dCanvasDocument . The iteration context a2dIterC class is used to hold the current a2dCanvasView. This last has the a2dDrawer2D, which a2dCanvasObject uses to draw itself on the active drawer. The above in the end results in all the a2dCanvasView's having up to date buffers, containing the wanted viewports from the a2dCanvasDocument. In case of a a2dCanvas, the contents of the a2dDrawer2D buffer is displayed on screen. In the a2dCanvasView::OnIdle and in a2dCanvasDocument::OnIdle the a2dCanvasDocument and its a2dCanvasView's are checked for available updated, and after redrawing them to the buffer they will be blitted to the screen.

When changing many objects, this results in many areas needing an update. Each area is added to the updatelist, which is maintained within each a2dCanvasView. Inside a2dCanvasView there is a mechanism to detect overlapping areas. This is based on a tilling principle. The buffer is divided in a grid of rectangular areas, and for each rectangle it is recorded if it need to be redrawn or not. So worst case one only needs to redraw all rectangles. If several update areas overlap the same rectangle, that rectangle will only be redrawn once, and not again and again for each update area. The real mechanism is a bit more complex, for more information on that see a2dTiles All this is needed to only (re)render the minimum amount of areas.

The update process in a document is recursive, it calculates new boudingboxes for all objects which did change.

See for more DrawingAndUpdating.

Grouping objects

All the data in a a2dCanvasDocument is organized in groups of a2dCanvasObject's, Every a2dCanvasObject can have a list of nested a2dCanvasObject's , which are called children. a2dCanvasDocument itself always has one a2dCanvasObject, which is called the root object. The children and nested children of the root object are displayed when a whole a2dCanvasDocument is rendered to a a2dCanvasView. Any nested child can be used by a2dCanvasView to start rendering only part of the document. 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 a2dCanvasObject's can be reached by recursive iterating over the document its children. 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. This is not only the case for child objects, also other references can exist (e.g a2dCanvasObject's having members that point to other a2dCanvasObject's or storing a2dCanvasObject's on the Undo stack). To implement the above a2dCanvasObject is a reference counted object. Many pointers can point to the object that is reference counted, and the tell the object that the Own() it. The "pointing object" tells that the object pointed is to be Released when it no longer needs it. This is not the same as a real delete, and therefore is called Release. If all pointers pointing to a a2dCanvasObject or gone ( 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 touch eachother making it more a graph structure.

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 that 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 matrix defines the position, rotation, scaling etc. relative to all parent objects. It is not adviced to place the same object twice in the same parenta2dCanvasObject as a child, since it will overlap itself (one matrix). See a2dCanvasObject and a2dCanvasDocument storage.

Affine Matrix

Position, rotation and scaling etc. for each a2dCanvasObject is set by an affine matrix. The a2dAffineMatrix is used to store translation, scaling, rotation, skew and mirroring in one simple matrix. A nice feature of this type of matrix is, that the can be multiplied with the matrix of the parent objects to find a total matrix which defines the absolute end position of a nested a2dCanvasObject. This matrix is always relative to the parent(s). To find the absolute position an object has, seen from the a2dCanvasDocument root group or any other parent object, all the matrixes of a2dCanvasObject's and a2dCanvasObjectReference's are multiplied recursive when rendering a document starting at some parent object. The resulting matrix is what is called the accumulated matrix. The a2dCanvasObject its own matrix also gets multiplied with the foregoing matrixes of parent objects. The resulting matrix gives the absolute position, rotation etc.. This matrix is set by the object into the active a2dDrawer2D, followed by rendering itself with that a2dDrawer2D. WxDrawer2D allows to draw basic primitives, and in combination with the above matrix, transforms them to the absolute position before drawing the primitive. So if you know how to draw the object in local relative coordinates (without any matrix stuff, simply relative to 0,0 ), then a2dDrawer2D will take care of the rest. All objects must be able to render itself making use of a a2dDrawer2D. The Basic a2dCanvasObject Rendering Function takes care of first setting the absolute transform matrix for the drawer. Having a matrix, can result in circles and ellipses which are rotated and scalled in X and Y different, in fact inside a2dDrawer2D it is converted to a polygon in order to be able to handle this. How a circle inside a a2dCanvasObject is drawn, depends on the matrix it has and also on the matrixes it parents have. In case the scaling in X and Y is the same and there is no rotation, you will get a perfect circle shape. In all other cases you normally get a rotated ellipse drawn to the canvas.

The affine matrix of the object can be changed, either interactive with tools or straight from your program. This will result in the object being updated automatically, which is the case in general when changing an object. See a2dCanvasDocument for a drawing.

Layers for drawing

A layer is an abstract drawing level, which acts like a sort of Z coordinate for a2dCanvasObjects. The idea is to put a2dCanvasObject's with the same layer id on the same abstract Z level. So for example: all objects with layer id 2, will be drawn on top of those with layer id 1. A a2dCanvasDocument maintains a list of layers, each a2dCanvasObject does have a layer id that refers to a layer from that list. The drawing order of the layers can be set by the user and changed on the fly. All objects on a certain layer situated in the nested a2dCanvasObjects are drawn at once, followed by the next layer in the order set etc. Using layers, the overlap of objects can be controlled. Changing the drawing order of the layers will result in moving all objects on a layer to the front are back. Layers by default work accros nested a2dCanvasObject's. So if there are nested childrens (where each canvasobject childlist contains objects on several layers) the drawing order of those objects will be as defined by the layers. All objects on a certain layer, inclusive objects in nested groups, will be drawn by traversing the whole document once. While traversing the document, all objects with the given layer id will be drawn. This is repeated for every visible layer. So children are using their own layer id for drawing, and this is independent of the layer id of the parent object. It is possible to set a flag in a a2dCanvasObject which will have the effect that all children objects will be drawn at once when the object itself is drawn. In that case a new layer iteration is started within the object, and the layers within the object and its childobjects are rendered in the right order. The layers are defined within a layer list containing a2dLayerInfo objects. This object is also derived from a2dCanvasObject, and because of this a layer list can be rendered, and maybe even more important stored into a file as part of a a2dCanvasDocument.

References

The a2dCanvasDocument is a tree like structure of a2dCanvasObject's, where the simple objects are the leafs on the tree. A a2dCanvasObject with children and a2dCanvasObjectReference make the branches in the tree structure. Those object's contain references to other a2dCanvasObject's in the form of simple C++ pointers. But in principle any a2dCanvasObject derived class which has pointers to other a2dCanvasObject's give shape to the hiearchy of the a2dCanvasDocument. The a2dCanvasObjectReference object has only one child which is a reference to another a2dCanvasObject, a reference has its own matrix to position the object that is referenced. This matrix is applied before the referenced object its own matrix. There may be any number of references to a certain a2dCanvasObject either from a parent a2dCanvasObject or a2dCanvasObjectReference, or any other type of a2dCanvasObject's which uses other a2dCanvasObject's internal. Reference counting is done within each a2dCanvasObject to make sure that deletion is done properly. References can save much memory when a whole grid of a certain object is needed. In this case the data for the object ( e.g. a a2dImage), is only stored once, the positioning within the grid is done by making references to one and the same a2dCanvasObject. Of course when a referenced object is changed, all references to it will display the change too.

(poly)Line End and Begin points

The reference system is also used to implement the begin and end objects for lines and polylines. For that, lines and polylines have two build in references to a a2dCanvasObject, which are used to draw begin and end points of the object. Any a2dCanvasObject derived class can be used for it. The begin and end point can use different objects. A scaling factor can be set which is applied as extra scaling for the begin and endpoint. So even if many a2dSLine objects share the same drawing for the begin/end points they still can be different in size. The End and Begin point a2dCanvasObject's, are aligned with the line, or with the polyline begin and end segments. Which means that the those objects are rotated untill its X axis falls inline with the line or polyline which uses them as Begin or EndPoint.

libraries

Due to the use of references, you can make references to a2dCanvasObjects outside its own a2dCanvasDocument also. This way it is easy to build a2dCanvasDocument objects containing often used objects. This i call a library. A good example is begin and end points for lines and polylines. It is possible to make copies of the object inside a library you want to use, but also only making a reference is possible. The layer settings of the a2dCanvasDocument library containing the object are used for rendering the object, instead of the layers setting from where the object is referenced. This is because the library objects have their m_root set to their own a2dCanvasDocument and therefore will used the layers settings defined by that particular document, even when the rendering routine is called from inside another a2dCanvasDocument.

properties

a2dCanvasObjects can have properties attached to it. Properties are derived from a2dCanvasPropertyBase. Properties are kept as a list inside the a2dCanvasObject, and this makes them dynamic, so they can be added and removed on the fly. This is a great advantage when adding temporary data, like when editing an object. A property can be made visible using a special a2dCanvasObject called a2dVisibleProperty. This is added to the a2dCanvasObject containing the property as a normal child object, and takes care of displaying the property in text form, using the stringpresentation of the property. There are properties to add basic types like bool, int, double, string to an object. Other properties like a2dShadowStyleProperty and a2dClipPathProperty influence the rendering of its parent a2dCanvasObject. a2dCanvasClipPathProperty has a reference to a a2dCanvasObject, which will be used to clip the property its parent a2dCanvasObject object. It is possible to display the clipping object itself also, simply by adding it as a child to the a2dCanvasObject that is being clipped. All properties are derived from a2dCanvasPropertyBase and use reference counting, making it possible to use one property for many objects. Only a2dCanvasNameProperty derived properties do have a name, which can be set by the user. All a2dCanvasPropertyBase objects have an id to make them unique. Non a2dCanvasNameProperty derived properties do return their id as the name of the property. There are some reserved properties with names like "NAME_" , "OBJECTTIP" "URL" etc. Those are used by the a2dCanvasObject in a predefined manner. a2dCanvasObject can also have style properties (a2dFill, a2dStroke and a2dContour), again these properties are treated in a special manner, in this case to set the color, outline etc. of the a2dCanvasObject containing the properties. See a2dCanvasPropertyBase for a drawing.

Stroke andFill properties

a2dCanvasObject uses style properties as a2dFill, a2dStroke and a2dContour, to change the colour used for filling and outlining a shape. Those classes are wrapped into properties a2dFillProperty and a2dStrokeProperty, derived from a2dNamedProperty, and stored as normal properties in the propertylist of the object a2dPropObject. If no style properties are set, the layer settings will be used for styling the object. The layer settings define for each layer id a a2dFill and a2dStroke . The layer id of the a2dCanvasObject will be used to choose the style from the layer settings table. See Style properties for more.

The Clipping Property

A a2dCanvasObject has the ability to have a complex shaped clipping set for it. This clipping is defined by inserting a special property called a2dClipPathProperty. This property contains a reference to a a2dCanvasObject (also nested children are possible) which defines the area to which the object will be clipped. The clipping area is defined relative to the object. Therefore clipping areas are translated, rotated, scaled etc. with the matrix defined for the object itself. a2dDrawer2D intersects the currently set clipping path which is already set for it, with the new object clipping path, the result defines the new clipping area. There is a stack in a2dDrawer2D to push and pop clipping areas when walking up and down the document its object tree during rendering.

the name property

Objects do have a name, by default this is the id number that was given to it during creation. It can be changed if wanted, in those cases a reserved property with the name "NAME is attached to the object, which contains this name. In some cases it is stored as part of the object itself as a wxString member. The idea is to minimize memory usage when no name is set.

a2dCanvasObject mask

Each a2dCanvasObject has a mask containing a range of flags. For example the selected flag. Those flags can be set for a specific set of objects using one mask to select those objects, and another mask to define the new mask. This can be done recursive down a a2dCanvasDocument tree. In many other cases the mask is used to select or skip objects with the particular mask. A second mask is available to define how an object reacts to mouse events. It depends on stroke and fill types, and visibility if a a2dCanvasObject reports a hit or not.

searching and collecting

You can recursive search a a2dCanvasDocument object for a2dCanvasObjects having a certain mask, classtype, property etc. It is also possible to collect those objects and put them in a a2dCanvasObjectList. Next to this there are functions to move delete copy transform objects with a certain mask.

Graphs and Diagrams

Each a2dCanvasObject can be extended to be able to connect itself to other a2dCanvasObject's. This way a2dCanvasObject's can form diagrams e.g electronic schematic's. To connect a2dCanvasObject's, a2dPin's are added as children to the a2dCanvasObject's to connect. Every a2dPin in a specific a2dCanvasObject can connect to a2dPin's in other a2dCanvasObject's. This way objects can be connected with eachother, and moving such objects results in other objects being moved also or being resized to keep the connection intact. Building a library of objects which can be connected, is possible on the fly, since the only thing to do is place a2dPin's as childs. But you can generate such pins also by using program code if needed. So a2dCanvasObjects or connected through its a2dPin's, either using special wire a2dCanvasObjects that stretch as rubberbands, or directly to another a2dCanvasObject. Tools often take into account the fact that a a2dCanvasObject can have pins, e.g during a drag it will try to connect to other objects in the end. Also when moving a2dCanvasObject's apart, wire objects may be created to keep the connection intact. Because a2dPin's are normal a2dCanvasObject's, they will be stored to a file as part of the document when saving it in CVG format. See Connected Objects.

  • width='489'

wxArt2D: CanvasModule (last edited 2016-05-03 09:29:36 by inetproxy-p)