Differences between revisions 7 and 24 (spanning 17 versions)
Revision 7 as of 2008-08-05 19:16:51
Size: 66119
Comment:
Revision 24 as of 2016-05-03 09:29:36
Size: 40332
Editor: inetproxy-p
Comment:
Deletions are marked like this. Additions are marked like this.
Line 1: Line 1:
= Canvas module = = Canvas Module =
Line 3: Line 3:
<<TableOfContents>> The canvas module is using the general module and the xmlparse module. Drawable objects or stored in a <<Dox(a2dDrawing)>>, and displayed on a <<Dox(a2dCanvas)>>s. What part is displayed on the canvas is set through its <<Dox(a2dDrawingPart)>>.
Line 5: Line 5:
Other topics on this module: Interactive editing and drawing of <<Dox(a2dCanvasObject)>>s is implemented with <<Dox(a2dStTool)>> and <<Dox(a2dStToolContr)>>.
Using those tools one can create interactive editing applications.
Line 7: Line 8:
/DrawingAndUpdating A controller can be plugged into a <<Dox(a2dDrawingPart)>>. A a2dDrawer2D is an abstract drawing context class, each <<Dox(a2dDrawingPart)>> has one, which it uses to draw a <<Dox(a2dDrawing)>> on to the <<Dox(a2dCanvas)>>.
Line 9: Line 10:
The controller intercepts all events, from the a2dCanvas window, before they are handled by the <<Dox(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.
Line 10: Line 12:
   %SEARCH{ "CanvasModule;a2dCanvas" type="regex" scope="all" nosearch="on" nototal="on" header="| *Topic: * | *Summary: * |" format="| $topic | $summary |")>> The <<Dox(a2dDrawingPart)>> on its turn redirects events to the a2dCanvasObjects which are part of the <<Dox(a2dDrawing)>>, that is displayed by that <<Dox(a2dDrawingPart)>>.
Line 12: Line 14:
== Abstract == 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 <<Dox(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 <<Dox(a2dDrawRectangleTool)>>, that rectangle tool can temporary push a zoomtool <<Dox(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 <<Dox(a2dDragTool)>>.
Line 14: Line 19:
The canvas module classes form a platform independent library to use for 2D Vector Graphics. It allows you to define a hierarchy of graphical objects, which can be displayed using a view. This view can be a window, a bitmap or some printer like device. On the top wxArt2D classes are build upon the ../DocviewModule classes. See <<Dox(a2dBaseTool)>> for what tools are available.
Line 16: Line 21:
The first section is a list of one liners to quickly get an idea what ../CanvasModule is all about. Next the global Design of the ../CanvasModule library will be explained. At last based on a list of features, you will get an idea what can be done with this library. Several standard dialogs specialized for editing purposes, are part of this module. Think of things like settings layer style and rendering order <<Dox(LayerPropertiesDialog)>>.
Line 18: Line 23:
The ../EditorModule is build on top of the ../CanvasModule, but extends and uses many of the ingredients which are located in the ../CanvasModule.

== Features as One Liners ==

The next list of one liners, should give an idea what to expect in the wxArt2D library.

   * canvas for displaying graphical objects
   * zooming to any part of the world coordinates system
   * graphical data stored independent from the display engines
   * The same or different graphical data can be displayed on multiple view objects at the same time.
   * modified canvas objects will be automatically updated in idle time, unless the user prevents it.
   * only areas occupied by a modified object will be updated, and not the whole display.
   * data can be saved and loaded as SVG and CVG (wxArt2D specific format)
   * New readers and writers can easily be added
   * advanged clipping strategy for quickly rendering specific parts of the graphical data
   * pluggable tools for manipulating the data displayed, like draw, delete, drag, copy, edit
   * toolcontroller stack based to allow zooming while drawing is in progress
   * references to graphical objects, to display the same object at N places on a canvas
   * reference counting system for graphical objects, stroke's, fill's and dynamic properties
   * affine matrix transformations for each canvas object
   * nested hierarchy of graphical objects
   * rendering engine abstract and several implementations can be used at the same time
   * Double buffering for quick refreshing and flicker free editing and drawing
   * scrolling, with only partial updating when possible
   * rendering (display and editing) on several canvas objects with same or different canvas documents and levels within a document.
   * visible and invisible properties on canvas objects
   * layers/levels of drawing, with drawing order that can be set independent of the layer order
   * library (clipart) of objects possible, with reference to objects in the library
   * fast draging using backup buffer
   * flexible style objects, e.g. gradient fill styles
   * quick fill and stroke setting when no change to previous setting, by using pointers and reference counting.
   * multiple documents and views, simular to the wxWindows Docview classes
   * optimized for minimum memory usage in base canvas object. Only when used a feature requires memory.
   * mouse events for canvas objects via standard wxWindows event handling system.
   * curve object, and groups of curves and markers on those curves
   * eval of string functions, used in curve object
   * world coordinate objects can be extended in pixels, for making pixel objects
   * pixel and world coordinate pen width
   * vector path object with line, arc, cubic and qaudratic bezier curve segments
   * command language for automatic processing (in progress and hopefully python in the future)
   * redo and undo in combination with tools and command processor
   * printing of views
   * bitmap or window rendering
   * clippath can be set for each object
   * double buffered rendering in combination with direct drawing to see progress
   * communication between objects, by means of a special canvas event manager

== How is it organized ==

The wxArt2D library is structered like a multiple document and view framework. A document manager has a list of documents. Each document has a list of views. A View is associated with a wxFrame or Subwindows within a frame. Frames and Sub-Windows are owned by parent windows and at the top by the wxApp class. WxWindows always deletes its windows automatically, so when a view is removed from a document, the associated window can be flagged for destroy. The destroy does not happen directly, instead when appropriate it is done by wxWindows. If a Frame with an associated view is closed, the view will be deleted and removed from the document. The figure wxArt2D storage show an application having 3 documents, document 1 has 3 views. View1 is using Frame1 to display itself. View 2 and 3 use Frame2 to display themselfs. Document 2 has 2 views, and they use Frame3 for the display. At last Document 3 has one view, displayed on the Parent Frame of the application.

Communication in the docview classes is done by sending events. A view sents a viewclose event to its display window and document, when it wants to close. When a <<Dox(a2dView)>> sents events to a <<Dox(a2dDocumentViewWindow)>>, the last will either handle the event or sent it up to the parent window, which is often a wxFrame. This way views inform a wxFrame of closing, and the frame can react by destroying itself, or by disconnecting the view.

New views and documents are/can be generated by the templates <<Dox(a2dViewTemplate)>> and <<Dox(a2dDocumentTemplate)>>. Those templates generate a view or document, and inform a <<Dox(a2dViewConnector)>> of such an event. The <<Dox(a2dViewConnector)>> will then be responsible for connecting the new views to the application its windows. The <<Dox(a2dViewConnector)>> may first generate a new wxFrame plus a <<Dox(a2dDocumentViewWindow)>>, and use that window to connect the new view to. In other application, it may use an existing <<Dox(a2dDocumentViewWindow)>>, disconnect the connected view, and connect the new view to the window.

A view always has only one wxWindow pointer internal, which it uses to display itself on to. So if a wxFrame displays two views, this means that the wxFrame has two wxWindows as children, and those two windows are used by the <<Dox(a2dView)>>'s to display the view.

In wxArt2D each Document contains <<Dox(a2dCanvasObject)>>'s in a hierarchy. Each view can display a different part of the hierarchy. A view is independent from other views, and only changes when the document is changed or the view its viewport is modified.

     {{attachment:docviewhier.gif}}

WxArt2D can also be used in a more simple manner. The Document Manager is not used in that situation. The figure wxArt2D storage single <<Dox(a2dCanvas)>> contains one <<Dox(a2dCanvas)>>. The <<Dox(a2dCanvas)>> which is derived from <<Dox(a2dDocumentViewWindow)>>, and by itself is part of a wxFrame. It owns one <<Dox(a2dCanvasView)>> and <<Dox(a2dCanvasDocument)>>. When destroying the window also the <<Dox(a2dCanvasView)>> and <<Dox(a2dCanvasDocument)>> or deleted. The above is already enough to display graphic data from the document. And changes made to the document will be updated automatically. Next to this you may plug a <<Dox(a2dToolContr)>> into the <<Dox(a2dCanvasView)>>. The <<Dox(a2dToolContr)>> allows to push and pop tools on a stack. The tools are interactive, and in general used to change the views visible area (zoom), or they can add <<Dox(a2dCanvasObject)>>'s and manipulate the <<Dox(a2dCanvasObject)>>'s in the document. Here <<Dox(a2dCanvas)>> its <<Dox(a2dCanvasDocument)>> is containing two <<Dox(a2dCanvasObject)>>'s at the top, both contains two nested <<Dox(a2dCanvasObject)>>'s themselfs. Events coming in at the <<Dox(a2dCanvas)>> window, are redirected to the <<Dox(a2dCanvasView)>> first. The <<Dox(a2dCanvasView)>> redirects the events to the toolcontroller, when it is available. If not handled in the tool controller, the event is futher processed in <<Dox(a2dCanvasView)>>. <<Dox(a2dCanvasView)>> does a hit test and redirects the event's to the <<Dox(a2dCanvasObject)>>'s that are hit by the mouse pointer. The tool controller will redirect the events to the first tool on the tool stack. The tool that is active can handle the event, and do a hit test on any <<Dox(a2dCanvasObject)>> itself. The object hit will now recieve events directly from the tool. In case of drawing a new object, the tool will handle all mouse events, and based on those events draw a new object. When the drawing is finished, the object will be added to the document via the <<Dox(a2dCanvasCommandProcessor)>>. This directly ensures being able to undo redo.

     {{attachment:cansinhier.gif|width='602'|height='496'}}

The figure wxArt2D Multiple document shows a more complicated setup. In a multiple document configuration <<Dox(a2dCanvasView)>> can have a tool controller inserted in each <<Dox(a2dCanvasView)>>. A tool controller has a stack of tools. One Tool is active in general. The active tool intercepts events coming from the <<Dox(a2dCanvas)>> window via the wxDarwer and tool controller. Tools can be used to interactive draw new objects and/or to edit the existing objects. This can happen independent in several <<Dox(a2dCanvas)>> windows at the same time. Even when it concerns the same document. All synchronization is taken care of.

     {{attachment:cantoolhier.gif|width='618'|height='602'}}

 <<Dox(a2dCanvasView)>> derived classes are able to display a drawing contained within a tree as a hiearchy of drawable objects all derived from <<Dox(a2dCanvasObject)>>. This is called a <<Dox(a2dCanvasDocument)>>. A <<Dox(a2dCanvas)>> uses a <<Dox(a2dCanvasView)>> to display part of a <<Dox(a2dCanvasDocument)>>. And its main purpose is to redirect events to and from a <<Dox(a2dCanvasView)>>. <<Dox(a2dCanvas)>> is able to scroll a <<Dox(a2dCanvasView)>>. Scrolling means that the viewport of the <<Dox(a2dCanvasView)>> is changed when scrollbar events are received by the <<Dox(a2dCanvas)>> window. Hiearchy is created by adding children to a <<Dox(a2dCanvasObject)>> or by using Special Reference objects. In principle every type of hierarchy is using references to <<Dox(a2dCanvasObject)>>'s. All canvas objects are reference counted, and removing the last reference to a <<Dox(a2dCanvasObject)>>'s will also delete the object. Only the way the objects are placed in the hiearchy of objects ( e.g children or subobjects of more advanced objects like end points of lines ), defines how they will be rendered. References make it possible to place/render a small drawing at several places on a view while only storing it once in memory. Internal <<Dox(a2dCanvasDocument)>> uses a <<Dox(a2dCanvasView)>> as a graphical context to render objects to the <<Dox(a2dCanvas)>> window or any other type of device. The viewport set for the <<Dox(a2dCanvasView)>> is defined in worldcoordinates and the mapping matrix takes care of converting <<Dox(a2dCanvasObject)>> (relative) world coordinates to device coordinates.

The WxArt2D library at the bottom is based on a modifed version of the DocView wxWindow classes. 'Therefore there is a <<Dox(a2dDocument)>> called <<Dox(a2dCanvasDocument)>> and a <<Dox(a2dView)>> called <<Dox(a2dCanvasView)>>. Both <<Dox(a2dDocument)>> and <<Dox(a2dView)>> are derived wxEvtHandler, and can receive events. The <<Dox(a2dCanvasDocument)>> always holds one <<Dox(a2dCanvasObject)>> which is called the root object. All other <<Dox(a2dCanvasObject)>>'s in a document are nested children of this object. The <<Dox(a2dCanvasView)>> class holds a pointer to a <<Dox(a2dDrawer2D)>> class. This class is used to draw basic primitives on a device, while the parent class <<Dox(a2dCanvasView)>> is doing all the update work of modified objects and redraw areas. The <<Dox(a2dDrawer2D)>> is an abstract baseclass for the device specific drawers. Because <<Dox(a2dDrawer2D)>> is abstract and contains mainly basic drawing routines, <<Dox(a2dDrawer2D)>>'s can be written for anything that one can possibly be used to drawn on. Being a wxWindow, wxBitmap, wxImage, Printer, as long as a <<Dox(a2dDrawer2D)>> for that type of the device is available, it can be done. There can be several <<Dox(a2dCanvasDocument)>>'s open at the same time, and each can be displayed at several <<Dox(a2dCanvasView)>>'s too. This is done in such a way that quick updating of areas on all the views is still garanteed.

Events from the <<Dox(a2dCanvas)>> class are first handled by its <<Dox(a2dCanvasView)>>, <<Dox(a2dCanvasView)>> redirect mouse events to its ShowObject, which is the top <<Dox(a2dCanvasObject)>> to be shown on that <<Dox(a2dCanvasView)>>. From there events go to nested a2dCanvasObjects. When the event is not handled at the lowest, at travels up to the parent a2dCanvasObjects.

The graphical objects with baseclass <<Dox(a2dCanvasObject)>> are all stored in the class <<Dox(a2dCanvasDocument)>>. The top <<Dox(a2dCanvasObject)>>'s in a tree of <<Dox(a2dCanvasObject)>>'s is called the root object, and is located in the <<Dox(a2dCanvasDocument)>>. A <<Dox(a2dCanvasView)>> is given a pointer to a <<Dox(a2dCanvasObject)>> within a <<Dox(a2dCanvasDocument)>> which needs to be displayed. This can be the <<Dox(a2dCanvasDocument)>> its root object or any of its nested <<Dox(a2dCanvasObject)>> children. The root object does contain as children <<Dox(a2dCanvasObject)>>'s in a recursive manner, this is the way to create hiearchy. Any level within the tree of <<Dox(a2dCanvasObject)>>'s <<Dox(a2dCanvasDocument)>> (being a <<Dox(a2dCanvasObject)>> with or without children ) can be displayed on a <<Dox(a2dCanvasView)>>. At the same time a different <<Dox(a2dCanvasObject)>>/level within the same document can be displayed on a different <<Dox(a2dCanvasView)>>.

For quick redraws on each view, a boundingbox in world coordinates is stored inside every <<Dox(a2dCanvasObject)>>. This boundingbox is used to clip the objects in the document to the area that is currently updated. The boudingboxes are stored in worldcoordinates and not pixels/device coordinates. The reason is that the objects itself are defined relative to its parent objects, for this a matrix is used. The matrix translates, scales, mirrors and rotates the object, relative to any parent object which contains a reference to this object. Since an object can have several parents at the same time, it does not have a predefined absolute position in world or device coordinates. Therefore the boudingbox of an object can only be stored relative to the parent objects. The boudingbox is in relative world coordinates instead of device, to make it easy to transform the boudingbox to an absolute position, inclusive rotation and scaling. The boudingbox includes the local matrix transformation that the object has. In order to clip an object to the arae that needs to be redrawn on a certain device, we need an absolute boundingbox for the object. To be able to calculate this absolute boudingbox, and also the absolute object coordinates, during the rendering the accumulated matrix is calculated. This matrix defines the position that the object to render will have on a drawing device, and each time the rendering goes deeper into hiearchy, this matrix is extended to include that level of hiearchy also. This matrix makes it possible to transform relative object coordinates to absolute world coordinates. Knowing the drawer that is being rendered, the calculated absolute object coordinates can be transformed to device coordinates in the end. In reality the accumulated matrix is directly combined inside the <<Dox(a2dDrawer2D)>> to do this conversion from relative world coordinates into device coordinates in one matrix calculation. The pen width is not included in the boundingbox, but a world extend and a pixel extend are calculated for canvasobject that are added to the boundingbox when needed.

BoundingBoxes are calculated independently from the rendering stage of the document. They are not used only for clipping to the redraw area, but also to bring the document from one state into a new state after changing the object(s) document. When changing a <<Dox(a2dCanvasObject)>> a flag is set which eventually triggers the redrawing of this object. All the areas which are occupied by the object on all views where it is displayed upon will be redrawn. The trick is that this will be done for the area occupied by the object in the state before the change, and after that for the area occupied by the object in the new state. The old area is available in the boundingbox of the object, just before recalculating it after a change to the object. Both the old and new area are reported to the <<Dox(a2dCanvasView)>>'s that display the object. The real redrawing/updating of those areas is done when all the areas have bin reported. This has the advantage that changes to many objects can be done before redrawing will take place. A <<Dox(a2dCanvasView)>> has a list of areas which need to be redrawn, and if some changed objects overlap, the redraw areas are combined into one. After a document has reported all the changed areas to its <<Dox(a2dCanvasView)>>'s, it will start redrawing the areas. The redrawing starts at the drawer its top object, and traverses the document, meanwhile redrawing all objects in the area that is being updated. This is done for all update areas assembled in the <<Dox(a2dCanvasView)>>. Before really redrawing an object, the rendering routine checks if the object needs to be rendered anyway, by first transforming only the boudingbox to absolute coordinates. If the resulting boudingbox intersects the area to be redrawn, real rendering of the object will take place. Every object that is completely outside the area that needs updating will not be drawn, the others will be clipped to the area to update. Graphical Clipping is not done inside the object itself, but is part of the <<Dox(a2dCanvasView)>>, which is always used as the drawing context to do the basic drawing within a <<Dox(a2dCanvasObject)>>. Still a rough boundingbox check is performed when rendering a document its tree of <<Dox(a2dCanvasObject)>>'s. The clip status ( inside, outside, intersecting ) of the parent object against a <<Dox(a2dCanvasView)>> its display or update area is calculated, and is transferred to the child objects. Once a parent object is completely inside the update area , there is no need to check all its children.

<<Dox(a2dDrawer2D)>> can be compared with a wxDc class, and for the wxDc based <<Dox(a2dDcDrawer)>> it indeed uses the SetClippingRegion to prevent drawing outside the area to be redrawn. There is one more thing to add, an object can have a clipping path property set for it, which means that the object will be rendered only inside this clippingpath, the rest will not be vissible. This feature is also achieved inside the <<Dox(a2dDrawer2D)>>. It is an additional clipping on top of the rectangular clipping for an, area needing an update. There is a clipping stack inside <<Dox(a2dDrawer2D)>> that makes it possible to push and pop this object clipping path. The relative defined object clipping path is first transformed to absolute coordinates, and then pushed onto the stack. When the object has been drawn, it is poped from the stack again.

The mechanism within a <<Dox(a2dCanvasView)>> for maintaining areas to be redrawn and to update those areas in idle time, is all rectangular based. The <<Dox(a2dCanvasView)>> views understand how to update themselfs after a change to the data in a <<Dox(a2dCanvasDocument)>>. The only thing the <<Dox(a2dCanvasView)>> needs is the areas that where invalidated by the changes. It is possible that an invalidated area within the document is only visible on one view and not another, still damaged areas are reported to all views, but they are clipped to each specific view. Actually there are two mechanisms the <<Dox(a2dCanvasView)>> uses, in order to know what needs to be redrawn. There is one list of areas to be redrawn in each <<Dox(a2dCanvasView)>>, it can be filled directly by a program (e.g. editing tools). In second method <<Dox(a2dCanvasView)>> will check a flag inside the <<Dox(a2dCanvasDocument)>> that it needs to render. The flag within a <<Dox(a2dCanvasDocument)>> is used to tell that it has so called pending objects. It is automatically set when an object changes. After a change each object needs to report this via the <<Dox(a2dCanvasDocument)>> pointer that is a member of each <<Dox(a2dCanvasObject)>>. Next to setting a flag inside the <<Dox(a2dCanvasDocument)>> object, the changed <<Dox(a2dCanvasObject)>> also sets a pending flag for itself. If the above flag is set, the <<Dox(a2dCanvasDocument)>>'s know that there are somewhere inside the document <<Dox(a2dCanvasObject)>>'s that did change and need to be rerendered. This is detected in idle time, and <<Dox(a2dCanvasDocument)>> tells its a2dCanvasView's to search for the pending objects. Each <<Dox(a2dCanvasView)>> will search those objects first, and add their current absolute boudingbox to the list of areas that need be redrawn. Next <<Dox(a2dCanvasDocument)>> calculates the new boundingbox, for all the changed objects. And once more orders the a2dCanvasView's to add those new boundingboxes to the list of redraw areas. When all invalidated areas are added to the pending area list of each <<Dox(a2dCanvasView)>>, the pending flags of all canvasobjects are reset. Each <<Dox(a2dCanvasView)>> will now traverse the document for each invalidated area in its pendingarea list, and only re-render the objects overlapping each invalidated area. This normally happens in idle time or after an onpaint event. So in the end, the <<Dox(a2dCanvasView)>> views will only redisplay those areas that are indeed visible on it. And the whole mechanism of pending objects prevents redrawing all views completely when only a small part has changed.

<<Dox(a2dCanvas)>> takes care of scrolling and resizing, and handling mouse events, but most of the work is done by its <<Dox(a2dCanvasView)>>, and often calls are redirected to the <<Dox(a2dCanvasView)>>. When the user scrolls, the mapping for the <<Dox(a2dCanvasView)>> is redefined, and scrollbars are adjusted. <<Dox(a2dCanvas)>> isues the right update calls for <<Dox(a2dCanvasView)>> to have efficient scrolling. The <<Dox(a2dCanvasView)>> then calls the Render function of the <<Dox(a2dCanvasDocument)>>, which will redraw what needs to be in the areas that where scrolled in the <<Dox(a2dCanvas)>> window. So rendering is only done in those areas that really need to be rerendered. The same for a resize, it redefines the <<Dox(a2dCanvasView)>> mapping and sets the new bitmap buffer for the <<Dox(a2dCanvasView)>> based on the new size.

<<Dox(a2dCanvas)>> uses a rectangular world coordinate system that is in Unit X,Y, wich means that vertexes can be meters, inch as well as bigs versus time. How large <<Dox(a2dCanvasObject)>>'s are on the screen is not coupled with the Units, all coordinates (independent of units) are stored as doubles or longs. When mapping the whole or part of a <<Dox(a2dCanvasDocument)>> to the window the programmer can decide that 1 centimeter is indeed one centimeter on the screen also. When rendering a <<Dox(a2dCanvasObject)>> its data is converted/presented into basic primitives defined in world coordinates, those primitives can be drawn with the <<Dox(a2dCanvasView)>> that is active. Often the data in the canvasobject is directly related to the basic primitives e.g width and height of a rectangle object, but this is not a must. How the basic primitive coordinates are displayed on a certain <<Dox(a2dCanvasView)>> view, is defined by the viewer its mapping. The <<Dox(a2dCanvasView)>> views are designed for 2D (because of layers, actually 2.5D), and you can only draw in 2D on it. So if you put a 2D projection of the world map in a <<Dox(a2dCanvasDocument)>>, you can tell a <<Dox(a2dCanvasView)>> to only display Amsterdam. Even if the world object itself is really 3D, and inside the Mya2dCanvasWorldObject it is/can be stored as 3D coordinates, the drawing of Amsterdam will and needs to be 2D. Therefore in such a situation when rendering part of the 3D world object, this part is first projected to 2D within the object itself. Of course you can do this projection in such a manner that e.g. cube is eventually drawn as a 3D cube, but using 2D drawing methods. The projection task from 3D to 2D can be done via a matrix which transforms the 3D coordinates inside the Mya2dCanvasWorldObject to 2D coordinates. When can of course use any kind of data within a <<Dox(a2dCanvasObject)>>, as long as the generated drawing is in 2D. This 2D drawing is placed relative to the parent <<Dox(a2dCanvasObject)>> of the actual object to render. When rendering the object those coordinates will be transformed to an absolute position, which is defined by the accumulated matrix on the path leading from some top object in the <<Dox(a2dCanvasDocument)>> to the a2dCanvasWorldObject to render. After this transformation to absolute 2D coordinates there is a second transformation inside <<Dox(a2dCanvasView)>>, which defines the viewport. The viewport is best described as the eye position in 2D. This second transformation, transforms absolute coordinates to window coordinates. In reality a total transform matrix is set for <<Dox(a2dCanvasView)>> to do both the transformation to absolute followed by translation into device in one matrix. For the moment i advice to use a square coordinate system, meaning the projection from world to device coordinates is the same in X and Y. Although every transform from object to view is 2 dimensional, the object itself is totaly free in generating the 2D canvas objects/primitives representing the object in some way. Those 2D objects will be drawn with the <<Dox(a2dCanvasView)>> that is given as a parameter to the object rendering function via its <<Dox(a2dIterC)>> pointer when it is rendered.

Some more examples. Asume you want to display a curve with a logarithmic scale, the conversion of the data from logarithmic to relative linear, needs to take place within the object, the rest is taken care of by the display rendering routines. The same situation will occur in the following cases: polar coordinates to rectangular, fractal objects generating 2d graphics on the basis of some formula. In general a <<Dox(a2dCanvasObject)>> needs to project itself into relative 2D coordinates before the display engine wil do the rest.

The main <<Dox(a2dDrawer2D)>> implementation, called <<Dox(a2dMemDcDrawer)>>, has build in double buffering. Everything drawn to the <<Dox(a2dCanvasView)>> view is drawn with <<Dox(a2dMemDcDrawer)>>, which draws to a bitmap buffer. When drawing is ready, <<Dox(a2dCanvasView)>> blits the buffer to the display window. This garanties flicker free drawing, and makes it possible to optimize redrawing in many ways. Another important use is redrawing parts that become visible when overlapping windows are (re)moved. This will lead to a paint event for the areas that will become visible, and all that a <<Dox(a2dCanvas)>>/<<Dox(a2dCanvasView)>> does to redisplay those parts, is blitting the right parts from the double buffer to the screen. Filling the double buffer is happening normally in idle (OnIdle) time. The double buffer in <<Dox(a2dMemDcDrawer)>> makes it possible to implement dragging. Drawing and editing tools that make use of the buffer, work in such a manner that they work flicker free and fast. This strategy is called double buffering. An often used trick using the buffer is, freeze the buffer and draw and drag another object on the device which is a window. During the drag etc., the buffer is used to quickly redraw what was behind the object while dragging, and only the object that is dragged will be really redrawn.

A <<Dox(a2dCanvasDocument)>> can be loaded and saved in several formats. As output and as input there are currently SVG and CVG (canvas vector graphics), the last one specially designed for the wxArt2D Library. The SVG loader, can load a subset of SVG. Adding other input and output formats is done via IOHandlers, and for all formats other than the CVG format the writing of a canvasobject is not integrated with the object itself as a member function. This makes it easy to plugin new handlers without changing the library itself. See Parsers for reading and Output formats.
You can find a general editing class in <<Dox(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.
Line 115: Line 28:
== Tools == Tools ==
Line 117: Line 30:
The WxArt2D library uses Tools to drag, move, rotate, scale, select, edit, group, ungroup a2dCanvasObjects that are displayed on a <<Dox(a2dCanvas)>>. A set of tools is maintained by a toolcontroller, which 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 <<Dox(a2dCanvasView)>> object when you need it. The <<Dox(a2dCanvasView)>> is the drawing for all types of canvas objects like <<Dox(a2dCanvas)>> and <<Dox(a2dCanvasSim)>>. 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 <<Dox(a2dCanvasObject)>> to edit. This copy of the <<Dox(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 <<Dox(a2dCanvasCommandProcessor)>> from within the copy object that is being edited, from there 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 <<Dox(a2dCanvasCommandProcessor)>> of the <<Dox(a2dCanvasDocument)>>. This is how the orginal object stays in sync with the copy that is being edited. The Number of commands sent, define what exactly can be undone. So for a drag only the start and end of the drag will result in a command. The WxArt2D library uses Tools to drag, move, rotate, scale, select, edit, group, ungroup a2dCanvasObjects that are displayed on a <<Dox(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 <<Dox(a2dDrawingPart)>> object when you need it. The <<Dox(a2dDrawingPart)>> is used inside all types of canvas objects like <<Dox(a2dCanvas)>> and <<Dox(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 <<Dox(a2dCanvasObject)>> to edit. This copy of the <<Dox(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 <<Dox(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 <<Dox(a2dCanvasCommandProcessor)>> of the <<Dox(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.
Line 119: Line 32:
Any <<Dox(a2dCanvasObject)>> derived object is editable. The default editing mode only modifies the transform the matrix of the <<Dox(a2dCanvasObject)>>. The derived object itself is free to implement any other type of editing functionality it needs. When <<Dox(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 <<Dox(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 <<Dox(a2dCanvasObject)>> is Rendered can be changed when in editing mode by changing the rendering behaviour when the editing flag is set. Drawing handles etc., are stored inside the <<Dox(a2dCanvasObject)>> itself. This can be as real C++ members, or by using a2dCanvasPropertyBase properties and childobjects like <<Dox(a2dHandle)>>. The last is the best is you want to optimize memory usage, since they can be created dynamically at the start of the editing and removed at the end. So all the extra data needed to edit an object is not stored in the <<Dox(a2dCanvasDocument)>> all the time, only temporarely 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 propably 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. Any <<Dox(a2dCanvasObject)>> derived object is editable. The default editing mode only modifies the transform the matrix of the <<Dox(a2dCanvasObject)>>. The derived object itself is free to implement any other type of editing functionality it needs. When <<Dox(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 <<Dox(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 <<Dox(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 <<Dox(a2dCanvasObject)>> itself. This can be as real C++ members, or by using a2dCanvasPropertyBase properties and childobjects like <<Dox(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 <<Dox(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.
Line 121: Line 34:
The Command processor processes a2dCanvasCommand's and saves on the undo stack data to reverse the change from the old state into the new state of the canvasobject in some manner. Not only editing result in commands, also drawing new object are added to the document via commands. All modifications to a document can therefore be Undone. 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.
Line 123: Line 36:
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 choosen automatically when no specific object editing is implemented. 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.
Line 131: Line 44:
The canvas tools use the <<Dox(a2dCanvasCommandProcessor)>> to modify objects in a <<Dox(a2dCanvasDocument)>>. 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 <<Dox(a2dCanvasCommandProcessor)>> is local to the document, and only maintains the changes to its document. A second global <<Dox(a2dCentralCanvasCommandProcessor)>> is used to load processing scripts/files, containing commands that can modify the data in the whole application. It can execute command like strings, to modify the contents of <<Dox(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 <<Dox(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. For the moment the central command processor will contain only very basic string commands that can be executed. The <<Dox(a2dCentralCanvasCommandProcessor)>> does also have member functions to directly add basic objects to a document. 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 sent to a central eventmanager, telling all registrated tools or dialogs, that something has changed resulting in updating themselfs ( as soon as the program becomes idle). The canvas tools use the <<Dox(a2dCanvasCommandProcessor)>> to modify objects in a <<Dox(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 <<Dox(a2dCanvasCommandProcessor)>> is local to the drawing, and only maintains the changes to its drawing. A second global <<Dox(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 <<Dox(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 <<Dox(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 <<Dox(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).
Line 142: Line 57:
<<Dox(a2dCanvas)>> is able to display data that is stored in a <<Dox(a2dCanvasDocument)>> object. Internally it uses a <<Dox(a2dView)>> called<<Dox(a2dCanvasView)>> to do the rendering. <<Dox(a2dCanvasDocument)>> in general is not depending on the displaying device, it can also exist without a <<Dox(a2dCanvasView)>>/<<Dox(a2dCanvas)>>. The document uses an abstract <<Dox(a2dDrawer2D)>> to draw itself to that <<Dox(a2dCanvasView)>> view. The <<Dox(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 <<Dox(a2dCanvasView)>> takes care of mapping part of the document to the device and all update mechanisms or organized in here. But each <<Dox(a2dCanvasView)>> has a <<Dox(a2dDrawer2D)>> to do the actual drawing. The <<Dox(a2dCanvasDocument)>> does use the <<Dox(a2dDrawer2D)>> to draw itself. To optimize drawing speed, only changed objects in the document will be redrawn. A boundingbox will be calculated to clip the canvas objects in the document against the <<Dox(a2dCanvasView)>>. The boundingbox 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 boundingbox, it is added when the boundingbox is asked for. The reason is that object have there boundingbox defined relative to it parent objects, to calculate the absolute boudingboxes at the top level in a wxDrawer, it required to transform the relative boundingbox first to the absolute position, and then to add the stroke size to it. <<Dox(a2dCanvas)>> is able to display data that is stored in a <<Dox(a2dDrawing)>> object. Internally it uses a <<Dox(a2dDrawingPart)>> to do the rendering. <<Dox(a2dDrawing)>> in general is not depending on the displaying device, it can also exist without a <<Dox(a2dDrawingPart)>> and/or <<Dox(a2dCanvas)>>. The <<Dox(a2dDrawingPart)>> uses an abstract <<Dox(a2dDrawer2D)>> to draw a drawing. The <<Dox(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 <<Dox(a2dDrawingPart)>> takes care of mapping part of the drawing to the device and all update mechanisms or organized in here. But <<Dox(a2dDrawer2D)>> does the actual drawing. The <<Dox(a2dDrawing)>> does use the <<Dox(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 <<Dox(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 <<Dox(a2dDrawer2D)>>. For that it transforms the relative bounding box first to the absolute position, and then the stroke size is added.
Line 144: Line 59:
The <<Dox(a2dCanvasDocument)>> contains one root <<Dox(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 <<Dox(a2dCanvasView)>> is set to the document iterations context. This iteration context class <<Dox(a2dIterC)>>, is used when traversing the document hierarchy. This iteration context is given as a parameter to the a2dCanvasDocumentrendering functions. The translation to device coordinates is done within the <<Dox(a2dCanvasView)>> class. Each <<Dox(a2dCanvasObject)>> must set the Absolute matrix to <<Dox(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 <<Dox(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 a2dDrawer2Dits basic primitive drawing functions. Those functions are used by the object to display itself in some manner on the <<Dox(a2dCanvasView)>>, which is a <<Dox(a2dView)>>. The <<Dox(a2dCanvasView)>> will eventually or directly display itself on the device, which is in general a <<Dox(a2dCanvas)>>. Of course the <<Dox(a2dCanvasObject)>> has a limited set of drawing primitives to display itself, being everything that is supported by <<Dox(a2dDrawer2D)>>. But since amoungst them is drawing Images and Bitmaps, for complex things one can render the object to a bitmap in any way that is needed. After that, the bitmap can be drawn with <<Dox(a2dDrawer2D)>> to the device. The <<Dox(a2dDrawing)>> contains one root <<Dox(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 <<Dox(a2dDrawingPart)>> is set to the drawing iterations context. This iteration context class <<Dox(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 <<Dox(a2dDrawingPart)>> class. Each <<Dox(a2dCanvasObject)>> must set the Absolute matrix to <<Dox(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 <<Dox(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 <<Dox(a2dDrawingPart)>>. The <<Dox(a2dDrawingPart)>> will eventually or directly display itself on the device, which is in general a <<Dox(a2dCanvas)>>. Of course the <<Dox(a2dCanvasObject)>> has a limited set of drawing feautures to display itself, being everything that is supported by <<Dox(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 <<Dox(a2dDrawer2D)>> to the device.
Line 148: 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(a2dDrawingPart)>> 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.
Line 152: Line 67:
The rendering of a document its contents is not limited to a window. <<Dox(a2dCanvasView)>> can be used to display a <<Dox(a2dCanvasDocument)>> drawing on a wxWindow derived class. But internal it uses <<Dox(a2dDrawer2D)>> to do the drawing. A <<Dox(a2dDrawer2D)>> can draw also to a bitmap or printer etc. <<Dox(a2dCanvasView)>> just uses a WxDrawer2D to display a bitmap in a window. A special <<Dox(a2dCanvasObject)>>'s called <<Dox(a2dRenderImage)>> is available to be able to use a wxDrawer view with its own document as part of another <<Dox(a2dCanvasDocument)>>. As such it is a <<Dox(a2dCanvasObject)>> inside a higher level <<Dox(a2dCanvasDocument)>>. It all depends on the implementation of a <<Dox(a2dDrawer2D)>> how and where the <<Dox(a2dCanvasDocument)>> is displayed. For the <<Dox(a2dCanvasDocument)>> that contains the data it makes no difference at all. The only limitations is that the render member function of a <<Dox(a2dCanvasObject)>> can only use the drawing capabilities that or offered by the <<Dox(a2dDrawer2D)>> base class. The <<Dox(a2dDrawer2D)>> implements the simple drawing of primitives, its parent <<Dox(a2dCanvasView)>> takes care of advanged update schemas. It controls a <<Dox(a2dCanvasDocument)>> asking it to re-render certain areas that need an update. A flag is set in a <<Dox(a2dCanvasDocument)>> when it is modified. The <<Dox(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 <<Dox(a2dCanvasDocument)>>. This update sequence takes into account the area occupied by the object in its previous and current state. When this is ready the <<Dox(a2dCanvasView)>>'s will start redrawing the modified areas at the next idle event in <<Dox(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 <<Dox(a2dCanvasDocument)>> can be made, before real redrawing takes place only once. Since redrawing areas are combined in <<Dox(a2dCanvasView)>>, this minimizes the number of redraws a lot. The rendering of a drawing its contents is not limited to a window. <<Dox(a2dDrawingPart)>> can be used to display a <<Dox(a2dDrawing)>> drawing on a wxWindow derived class. But internal it uses <<Dox(a2dDrawer2D)>> to do the drawing. A <<Dox(a2dDrawer2D)>> can draw also to a bitmap or printer etc. <<Dox(a2dDrawingPart)>> just uses a a2dDrawer2D to display a bitmap in a window. A special <<Dox(a2dCanvasObject)>>'s called <<Dox(a2dRenderImage)>> is available to be able to use a a2dDrawingPart with its own drawing as part of another <<Dox(a2dDrawing)>>. As such it is a <<Dox(a2dCanvasObject)>> inside a higher level <<Dox(a2dDrawing)>>. It all depends on the implementation of a <<Dox(a2dDrawer2D)>> how and where the <<Dox(a2dDrawing)>> is displayed. For the <<Dox(a2dDrawing)>> that contains the data it makes no difference at all. The only limitations is that the render member function of a <<Dox(a2dCanvasObject)>> can only use the drawing capabilities that or offered by the <<Dox(a2dDrawer2D)>> base class. The <<Dox(a2dDrawer2D)>> implements the simple drawing of primitives, its parent <<Dox(a2dDrawingPart)>> takes care of advanced update schemes. It controls a <<Dox(a2dDrawing)>> asking it to re-render certain areas, which need an update. A flag is set in a <<Dox(a2dDrawing)>> when it is modified. The <<Dox(a2dDrawing)>> 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 a2dDrawingPart's used by the <<Dox(a2dDrawing)>>. This update sequence takes into account the area occupied by the object in its previous and current state. When this is ready the <<Dox(a2dDrawingPart)>>'s will start redrawing the modified areas at the next idle event in <<Dox(a2dDrawingPart)>>. 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 <<Dox(a2dDrawing)>> can be made, before real redrawing takes place only once. Because redrawing areas are combined in <<Dox(a2dDrawingPart)>>, this minimizes the number of redraws a lot.
Line 154: Line 69:
Rendering of a <<Dox(a2dCanvasObject)>>'s and its children, is all aranged through recursive calls to <<Dox(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 <<Dox(a2dDrawer2D)>>, else the layer of the object will be used to set the style. The function also calls <<Dox(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 <<Dox(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 <<Dox(a2dCanvasObject)>> rendering. Rendering of a <<Dox(a2dCanvasObject)>>'s and its children, is all arranged through recursive calls to <<Dox(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 <<Dox(a2dDrawer2D)>>, else the layer of the object will be used to set the style. The function also calls <<Dox(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, fill pattern, outline etc. but the DoRender function really draws the circle using the active <<Dox(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 <<Dox(a2dCanvasObject)>> rendering.
Line 158: Line 73:
When a certain <<Dox(a2dCanvasObject)>> changes, it sets itselfs pending, and informs its <<Dox(a2dCanvasDocument)>> that it needs an update, by setting a flag in there also. In idle time the <<Dox(a2dCanvasDocument)>> flag is checked, and if TRUE all a2dCanvasObjects are checked and all pending a2dCanvasObjects issue pending update areas to the <<Dox(a2dCanvasView)>> instances that the <<Dox(a2dCanvasDocument)>> uses to display itself. Those update areas are based on the previous and the current area occupied by the <<Dox(a2dCanvasObject)>> (e.g. old and new boundingbox of object made absolute and converted to device coordinates). The <<Dox(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 <<Dox(a2dCanvasView)>> with the so called ShowObject() and continues by traversing its children whcihc are all stored in the <<Dox(a2dCanvasDocument)>> . The iteration context <<Dox(a2dIterC)>> class is used to hold the current <<Dox(a2dCanvasView)>>. This last has the <<Dox(a2dDrawer2D)>>, which <<Dox(a2dCanvasObject)>> uses to draw itself on the active drawer. The above in the end results in all the <<Dox(a2dCanvasView)>>'s having up to date buffers, containing the wanted viewports from the <<Dox(a2dCanvasDocument)>>. In case of a <<Dox(a2dCanvas)>>, the contents of the <<Dox(a2dDrawer2D)>> buffer is displayed on screen. In the <<Dox(a2dCanvasView)>>::OnIdle and in <<Dox(a2dCanvasDocument)>>::OnIdle the <<Dox(a2dCanvasDocument)>> and its <<Dox(a2dCanvasView)>>'s are checked for available updated, and after redrawing them to the buffer they will be blitted to the screen. When a certain <<Dox(a2dCanvasObject)>> changes, it sets itselfs pending, and informs its <<Dox(a2dDrawing)>> that it needs an update, by setting a flag in there also. In idle time the <<Dox(a2dDrawing)>> flag is checked, and if TRUE all a2dCanvasObjects are checked and all pending a2dCanvasObjects issue pending update areas to the <<Dox(a2dDrawingPart)>> instances that the <<Dox(a2dDrawing)>> uses to display itself. Those update areas are based on the previous and the current area occupied by the <<Dox(a2dCanvasObject)>> (e.g. old and new boundingbox of object made absolute and converted to device coordinates). The <<Dox(a2dDrawingPart)>>'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 <<Dox(a2dDrawingPart)>> with the so called ShowObject() and continues by traversing its children which are all stored in the <<Dox(a2dDrawing)>> . The iteration context <<Dox(a2dIterC)>> class is used to hold the current <<Dox(a2dDrawingPart)>>. This last has the <<Dox(a2dDrawer2D)>>, which <<Dox(a2dCanvasObject)>> uses to draw itself on the active a2dDrawingPart. The above in the end results in all the <<Dox(a2dDrawingPart)>>'s having up to date buffers, containing the wanted view ports from the <<Dox(a2dDrawing)>>. In case of a <<Dox(a2dCanvas)>>, the contents of the <<Dox(a2dDrawer2D)>> buffer is displayed on screen. In the <<Dox(a2dDrawingPart)>>::OnIdle and in <<Dox(a2dDrawing)>>::OnIdle the <<Dox(a2dDrawing)>> and its <<Dox(a2dDrawingPart)>>'s are checked for available updated, and after redrawing them to the buffer they will be blitted to the screen.
Line 160: Line 75:
When changing many objects, this results in many areas needing an update. Each area is added to the updatelist, which is maintained within each <<Dox(a2dCanvasView)>>. Inside <<Dox(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 <<Dox(a2dTiles)>> When changing many objects, this results in many areas needing an update. Each area is added to the updatelist, which is maintained within each <<Dox(a2dDrawingPart)>>. Inside <<Dox(a2dDrawingPart)>> 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 <<Dox(a2dTiles)>>
Line 163: Line 78:
The update process in a document is recursive, it calculates new boudingboxes for all objects which did change. The update process in a document is recursive, it calculates new bouding boxes for all objects which did change.
Line 169: Line 84:
All the data in a <<Dox(a2dCanvasDocument)>> is organized in groups of <<Dox(a2dCanvasObject)>>'s, Every <<Dox(a2dCanvasObject)>> can have a list of nested <<Dox(a2dCanvasObject)>>'s , which are called children. a2dCanvasDocument itself always has one <<Dox(a2dCanvasObject)>>, which is called the root object. The children and nested children of the root object are displayed when a whole <<Dox(a2dCanvasDocument)>> is rendered to a <<Dox(a2dCanvasView)>>. Any nested child can be used by <<Dox(a2dCanvasView)>> to start rendering only part of the document. Children are added to the <<Dox(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 <<Dox(a2dCanvasObject)>>'s can be reached by recursive iterating over the document its children. A child of one <<Dox(a2dCanvasObject)>> can at the same time be a child of other <<Dox(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 <<Dox(a2dCanvasObject)>>'s having members that point to other <<Dox(a2dCanvasObject)>>'s or storing <<Dox(a2dCanvasObject)>>'s on the Undo stack). To implement the above <<Dox(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 <<Dox(a2dCanvasObject)>> or gone ( reference count reaches zero), the object will automatically be deleted for real. As consequence a <<Dox(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. All the data in a <<Dox(a2dDrawing)>> is organized in groups of <<Dox(a2dCanvasObject)>>'s, Every <<Dox(a2dCanvasObject)>> can have a list of nested <<Dox(a2dCanvasObject)>>'s , which are called children. a2dDrawing itself always has one <<Dox(a2dCanvasObject)>>, which is called the root object. The children and nested children of the root object are displayed when a whole <<Dox(a2dDrawing)>> is rendered to a <<Dox(a2dDrawingPart)>>. Any nested child can be used by <<Dox(a2dDrawingPart)>> to start rendering only part of the document. Children are added to the <<Dox(a2dDrawing)>> 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 <<Dox(a2dCanvasObject)>>'s can be reached by recursive iterating over the document its children. A child of one <<Dox(a2dCanvasObject)>> can at the same time be a child of other <<Dox(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 <<Dox(a2dCanvasObject)>>'s having members that point to other <<Dox(a2dCanvasObject)>>'s or storing <<Dox(a2dCanvasObject)>>'s on the Undo stack). To implement the above <<Dox(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 <<Dox(a2dCanvasObject)>> or gone ( reference count reaches zero), the object will automatically be deleted for real. As consequence a <<Dox(a2dCanvasObject)>> has not just one parent, and the document structure is not a strict tree, its branches touch each other making it more a graph structure.
Line 180: Line 95:
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 parent<<Dox(a2dCanvasObject)>> as a child, since it will overlap itself (one matrix). See <<Dox(a2dCanvasObject)>> and <<Dox(a2dCanvasDocument)>> storage. The matrix defines the position, rotation, scaling etc. relative to all parent objects. It is not advised to place the same object twice in the same parent<<Dox(a2dCanvasObject)>> as a child, since it will overlap itself (one matrix). See <<Dox(a2dCanvasObject)>> and <<Dox(a2dDrawing)>> storage.
Line 184: Line 99:
Position, rotation and scaling etc. for each <<Dox(a2dCanvasObject)>> is set by an affine matrix. The <<Dox(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 <<Dox(a2dCanvasObject)>>. This matrix is always relative to the parent(s). To find the absolute position an object has, seen from the <<Dox(a2dCanvasDocument)>> root group or any other parent object, all the matrixes of <<Dox(a2dCanvasObject)>>'s and <<Dox(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 <<Dox(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 <<Dox(a2dDrawer2D)>>, followed by rendering itself with that <<Dox(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 <<Dox(a2dDrawer2D)>> will take care of the rest. All objects must be able to render itself making use of a <<Dox(a2dDrawer2D)>>. The Basic <<Dox(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 <<Dox(a2dDrawer2D)>> it is converted to a polygon in order to be able to handle this. How a circle inside a <<Dox(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. Position, rotation and scaling etc. for each <<Dox(a2dCanvasObject)>> is set by an affine matrix. The <<Dox(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 <<Dox(a2dCanvasObject)>>. This matrix is always relative to the parent(s). To find the absolute position an object has, seen from the <<Dox(a2dCanvasDocument)>> root group or any other parent object, all the matrices of <<Dox(a2dCanvasObject)>>'s and <<Dox(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 <<Dox(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 <<Dox(a2dDrawer2D)>>, followed by rendering itself with that <<Dox(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 <<Dox(a2dDrawer2D)>> will take care of the rest. All objects must be able to render itself making use of a <<Dox(a2dDrawer2D)>>. The Basic <<Dox(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 scaled in X and Y different, in fact inside <<Dox(a2dDrawer2D)>> it is converted to a polygon in order to be able to handle this. How a circle inside a <<Dox(a2dCanvasObject)>> is drawn, depends on the matrix it has and also on the matrices 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.
Line 186: Line 101:
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 <<Dox(a2dCanvasDocument)>> for a drawing. 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 <<Dox(a2dDrawing)>> for a drawing.
Line 190: Line 105:
A layer is an abstract drawing level, which acts like a sort of Z coordinate for a2dCanvasObjects. The idea is to put <<Dox(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 <<Dox(a2dCanvasDocument)>> maintains a list of layers, each <<Dox(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 <<Dox(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 <<Dox(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 <<Dox(a2dLayerInfo)>> objects. This object is also derived from <<Dox(a2dCanvasObject)>>, and because of this a layer list can be rendered, and maybe even more important stored into a file as part of a <<Dox(a2dCanvasDocument)>>. A layer is an abstract drawing level, which acts like a sort of Z coordinate for a2dCanvasObjects. The idea is to put <<Dox(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 <<Dox(a2dDrawing)>> maintains a list of layers, each <<Dox(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 accross nested <<Dox(a2dCanvasObject)>>'s. So if there are nested children (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 <<Dox(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 child objects are rendered in the right order. The layers are defined within a layer list containing <<Dox(a2dLayerInfo)>> objects. This object is also derived from <<Dox(a2dCanvasObject)>>, and because of this a layer list can be rendered, and maybe even more important stored into a file as part of a <<Dox(a2dDrawing)>>.
Line 194: Line 109:
The <<Dox(a2dCanvasDocument)>> is a tree like structure of <<Dox(a2dCanvasObject)>>'s, where the simple objects are the leafs on the tree. A <<Dox(a2dCanvasObject)>> with children and <<Dox(a2dCanvasObjectReference)>> make the branches in the tree structure. Those object's contain references to other <<Dox(a2dCanvasObject)>>'s in the form of simple C++ pointers. But in principle any <<Dox(a2dCanvasObject)>> derived class which has pointers to other <<Dox(a2dCanvasObject)>>'s give shape to the hiearchy of the <<Dox(a2dCanvasDocument)>>. The <<Dox(a2dCanvasObjectReference)>> object has only one child which is a reference to another <<Dox(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 <<Dox(a2dCanvasObject)>> either from a parent <<Dox(a2dCanvasObject)>> or <<Dox(a2dCanvasObjectReference)>>, or any other type of <<Dox(a2dCanvasObject)>>'s which uses other <<Dox(a2dCanvasObject)>>'s internal. Reference counting is done within each <<Dox(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 <<Dox(a2dImage)>>), is only stored once, the positioning within the grid is done by making references to one and the same <<Dox(a2dCanvasObject)>>. Of course when a referenced object is changed, all references to it will display the change too. The <<Dox(a2dDrawing)>> is a tree like structure of <<Dox(a2dCanvasObject)>>'s, where the simple objects are the leafs on the tree. A <<Dox(a2dCanvasObject)>> with children and <<Dox(a2dCanvasObjectReference)>> make the branches in the tree structure. Those object's contain references to other <<Dox(a2dCanvasObject)>>'s in the form of simple C++ pointers. But in principle any <<Dox(a2dCanvasObject)>> derived class which has pointers to other <<Dox(a2dCanvasObject)>>'s give shape to the hiearchy of the <<Dox(a2dCanvasDocument)>>. The <<Dox(a2dCanvasObjectReference)>> object has only one child which is a reference to another <<Dox(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 <<Dox(a2dCanvasObject)>> either from a parent <<Dox(a2dCanvasObject)>> or <<Dox(a2dCanvasObjectReference)>>, or any other type of <<Dox(a2dCanvasObject)>>'s which uses other <<Dox(a2dCanvasObject)>>'s internal. Reference counting is done within each <<Dox(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 <<Dox(a2dImage)>>), is only stored once, the positioning within the grid is done by making references to one and the same <<Dox(a2dCanvasObject)>>. Of course when a referenced object is changed, all references to it will display the change too.
Line 202: Line 117:
Due to the use of references, you can make references to a2dCanvasObjects outside its own <<Dox(a2dCanvasDocument)>> also. This way it is easy to build <<Dox(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 <<Dox(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 <<Dox(a2dCanvasDocument)>> and therefore will used the layers settings defined by that particular document, even when the rendering routine is called from inside another <<Dox(a2dCanvasDocument)>>. Due to the use of references, you can make references to a2dCanvasObjects outside its own <<Dox(a2dDrawing)>> also. This way it is easy to build <<Dox(a2dDrawing)>> 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 <<Dox(a2dDrawing)>> 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 <<Dox(a2dDrawing)>> and therefore will used the layers settings defined by that particular document, even when the rendering routine is called from inside another <<Dox(a2dDrawing)>>.
Line 206: Line 121:
a2dCanvasObjects can have properties attached to it. Properties are derived from a2dCanvasPropertyBase. Properties are kept as a list inside the <<Dox(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 <<Dox(a2dCanvasObject)>> called <<Dox(a2dVisibleProperty)>>. This is added to the <<Dox(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 <<Dox(a2dShadowStyleProperty)>> and <<Dox(a2dClipPathProperty)>> influence the rendering of its parent <<Dox(a2dCanvasObject)>>. a2dCanvasClipPathProperty has a reference to a <<Dox(a2dCanvasObject)>>, which will be used to clip the property its parent <<Dox(a2dCanvasObject)>> object. It is possible to display the clipping object itself also, simply by adding it as a child to the <<Dox(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 <<Dox(a2dCanvasObject)>> in a predefined manner. a2dCanvasObject can also have style properties (<<Dox(a2dFill)>>, <<Dox(a2dStroke)>> and <<Dox(a2dContour)>>), again these properties are treated in a special manner, in this case to set the color, outline etc. of the <<Dox(a2dCanvasObject)>> containing the properties. See a2dCanvasPropertyBase for a drawing. a2dCanvasObjects can have properties attached to it. Properties are derived from a2dCanvasPropertyBase. Properties are kept as a list inside the <<Dox(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 <<Dox(a2dCanvasObject)>> called <<Dox(a2dVisibleProperty)>>. This is added to the <<Dox(a2dCanvasObject)>> containing the property as a normal child object, and takes care of displaying the property in text form, using the string presentation of the property. There are properties to add basic types like bool, int, double, string to an object. Other properties like <<Dox(a2dShadowStyleProperty)>> and <<Dox(a2dClipPathProperty)>> influence the rendering of its parent <<Dox(a2dCanvasObject)>>. a2dCanvasClipPathProperty has a reference to a <<Dox(a2dCanvasObject)>>, which will be used to clip the property its parent <<Dox(a2dCanvasObject)>> object. It is possible to display the clipping object itself also, simply by adding it as a child to the <<Dox(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 <<Dox(a2dCanvasObject)>> in a predefined manner. a2dCanvasObject can also have style properties (<<Dox(a2dFill)>>, <<Dox(a2dStroke)>> and <<Dox(a2dContour)>>), again these properties are treated in a special manner, in this case to set the color, outline etc. of the <<Dox(a2dCanvasObject)>> containing the properties. See a2dCanvasPropertyBase for a drawing.
Line 208: Line 123:
== Stroke, Fill and Contour properties == == Stroke and Fill properties ==
Line 210: Line 125:
<<Dox(a2dCanvasObject)>> uses style properties as <<Dox(a2dFill)>>, <<Dox(a2dStroke)>> and <<Dox(a2dContour)>>, to change the colour used for filling and outline, and influencing the contour of a shape. Those classes are derived from a2dCanvasPropertyBase, and stored as normal properties in the propertylist of the object. 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 <<Dox(a2dFill)>>, <<Dox(a2dStroke)>> and <<Dox(a2dContour)>>. The layer id of the <<Dox(a2dCanvasObject)>> will be used to choose the style from the layer settings table. See Style properties for more. <<Dox(a2dCanvasObject)>> uses style properties as <<Dox(a2dFill)>>, <<Dox(a2dStroke)>> and <<Dox(a2dContour)>>, to change the colour used for filling and outlining a shape. Those classes are wrapped into properties <<Dox(a2dFillProperty)>> and <<Dox(a2dStrokeProperty)>>, derived from <<Dox(a2dNamedProperty)>>, and stored as normal properties in the propertylist of the object <<Dox(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 <<Dox(a2dFill)>> and <<Dox(a2dStroke)>> . The layer id of the <<Dox(a2dCanvasObject)>> will be used to choose the style from the layer settings table. See Style properties for more.
Line 220: Line 135:
== <<Dox(a2dCanvasObject)>> mask == == a2dCanvasObject mask ==
Line 222: Line 137:
Each <<Dox(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 <<Dox(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 <<Dox(a2dCanvasObject)>> reports a hit or not. Each <<Dox(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 <<Dox(a2dDrawing)>> 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 <<Dox(a2dCanvasObject)>> reports a hit or not.
Line 226: Line 141:
You can recursive search a <<Dox(a2dCanvasDocument)>> object for a2dCanvasObjects having a certain mask, classtype, property etc. It is also possible to collect those objects and put them in a <<Dox(a2dCanvasObjectList)>>. Next to this there are functions to move delete copy transform objects with a certain mask. You can do a recursive search on a <<Dox(a2dDrawing)>> object for a2dCanvasObjects having a certain mask, classtype, property etc. It is also possible to collect those objects and put them in a <<Dox(a2dCanvasObjectList)>>. Next to this there are functions to move delete copy transform objects with a certain mask.
Line 230: Line 145:
Each <<Dox(a2dCanvasObject)>> can be extended to be able to connect itself to other <<Dox(a2dCanvasObject)>>'s. This way <<Dox(a2dCanvasObject)>>'s can form diagrams e.g electronic schematic's. To connect <<Dox(a2dCanvasObject)>>'s, <<Dox(a2dPin)>>'s are added as children to the <<Dox(a2dCanvasObject)>>'s to connect. Every <<Dox(a2dPin)>> in a specific <<Dox(a2dCanvasObject)>> can connect to <<Dox(a2dPin)>>'s in other <<Dox(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 <<Dox(a2dPin)>>'s as childs. But you can generate such pins also by using program code if needed. So a2dCanvasObjects or connected through its <<Dox(a2dPin)>>'s, either using special wire a2dCanvasObjects that stretch as rubberbands, or directly to another <<Dox(a2dCanvasObject)>>. Tools often take into account the fact that a <<Dox(a2dCanvasObject)>> can have pins, e.g during a drag it will try to connect to other objects in the end. Also when moving <<Dox(a2dCanvasObject)>>'s apart, wire objects may be created to keep the connection intact. Because <<Dox(a2dPin)>>'s are normal <<Dox(a2dCanvasObject)>>'s, they will be stored to a file as part of the document when saving it in CVG format. See Connected Objects. Each <<Dox(a2dCanvasObject)>> can be extended to be able to connect itself to other <<Dox(a2dCanvasObject)>>'s. This way <<Dox(a2dCanvasObject)>>'s can form diagrams e.g electronic schematic's. To connect <<Dox(a2dCanvasObject)>>'s, <<Dox(a2dPin)>>'s are added as children to the <<Dox(a2dCanvasObject)>>'s to connect. Every <<Dox(a2dPin)>> in a specific <<Dox(a2dCanvasObject)>> can connect to <<Dox(a2dPin)>>'s in other <<Dox(a2dCanvasObject)>>'s. Through pin objects, canvas can be connected with each other, 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 add <<Dox(a2dPin)>>'s as childs. But you can generate such pins also by using program code if wanted. So a2dCanvasObjects or connected through its <<Dox(a2dPin)>>'s, either using special wire a2dCanvasObjects that stretch as rubberbands, or directly to another <<Dox(a2dCanvasObject)>>. Tools often take into account the fact that a <<Dox(a2dCanvasObject)>> can have pins, e.g during a drag it will try to connect to other objects in the end. Also when moving <<Dox(a2dCanvasObject)>>'s apart, wire objects may be created to keep the connection intact. Because <<Dox(a2dPin)>>'s are normal <<Dox(a2dCanvasObject)>>'s, they will be stored to a file as part of the drawing when saving it in CVG format. See Connected Objects.
Line 233: Line 148:

----
CategoryModules

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 a2dDrawingPart 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 drawing its contents is not limited to a window. a2dDrawingPart can be used to display a a2dDrawing 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. a2dDrawingPart just uses a a2dDrawer2D to display a bitmap in a window. A special a2dCanvasObject's called a2dRenderImage is available to be able to use a a2dDrawingPart with its own drawing as part of another a2dDrawing. As such it is a a2dCanvasObject inside a higher level a2dDrawing. It all depends on the implementation of a a2dDrawer2D how and where the a2dDrawing is displayed. For the a2dDrawing 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 a2dDrawingPart takes care of advanced update schemes. It controls a a2dDrawing asking it to re-render certain areas, which need an update. A flag is set in a a2dDrawing when it is modified. The a2dDrawing 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 a2dDrawingPart's used by the a2dDrawing. This update sequence takes into account the area occupied by the object in its previous and current state. When this is ready the a2dDrawingPart's will start redrawing the modified areas at the next idle event in a2dDrawingPart. 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 a2dDrawing can be made, before real redrawing takes place only once. Because redrawing areas are combined in a2dDrawingPart, this minimizes the number of redraws a lot.

Rendering of a a2dCanvasObject's and its children, is all arranged 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, fill pattern, 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 a2dDrawing that it needs an update, by setting a flag in there also. In idle time the a2dDrawing flag is checked, and if TRUE all a2dCanvasObjects are checked and all pending a2dCanvasObjects issue pending update areas to the a2dDrawingPart instances that the a2dDrawing 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 a2dDrawingPart'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 a2dDrawingPart with the so called ShowObject() and continues by traversing its children which are all stored in the a2dDrawing . The iteration context a2dIterC class is used to hold the current a2dDrawingPart. This last has the a2dDrawer2D, which a2dCanvasObject uses to draw itself on the active a2dDrawingPart. The above in the end results in all the a2dDrawingPart's having up to date buffers, containing the wanted view ports from the a2dDrawing. In case of a a2dCanvas, the contents of the a2dDrawer2D buffer is displayed on screen. In the a2dDrawingPart::OnIdle and in a2dDrawing::OnIdle the a2dDrawing and its a2dDrawingPart'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 a2dDrawingPart. Inside a2dDrawingPart 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 bouding boxes for all objects which did change.

See for more DrawingAndUpdating.

Grouping objects

All the data in a a2dDrawing is organized in groups of a2dCanvasObject's, Every a2dCanvasObject can have a list of nested a2dCanvasObject's , which are called children. a2dDrawing 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 a2dDrawing is rendered to a a2dDrawingPart. Any nested child can be used by a2dDrawingPart to start rendering only part of the document. Children are added to the a2dDrawing 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 each other 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 advised to place the same object twice in the same parenta2dCanvasObject as a child, since it will overlap itself (one matrix). See a2dCanvasObject and a2dDrawing 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 matrices 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 scaled 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 matrices 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 a2dDrawing 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 a2dDrawing 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 accross nested a2dCanvasObject's. So if there are nested children (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 child objects 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 a2dDrawing.

References

The a2dDrawing 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 a2dDrawing also. This way it is easy to build a2dDrawing 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 a2dDrawing 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 a2dDrawing and therefore will used the layers settings defined by that particular document, even when the rendering routine is called from inside another a2dDrawing.

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 string presentation 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 and Fill 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 a2dDrawing 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 do a recursive search on a a2dDrawing 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. Through pin objects, canvas can be connected with each other, 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 add a2dPin's as childs. But you can generate such pins also by using program code if wanted. 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 drawing when saving it in CVG format. See Connected Objects.

  • width='489'

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