Introduction wxArt2D
The wxArt2D library is a platform independent 2D vector graphics library. Essentially a document containing a hierarchy of graphical vector objects, is rendered to one or more views. A view can be displayed on a device such as a window, a bitmap or a printer. An advanced drawing context is used to render the objects within the document to the device context. The document communicates changes in its objects to the views, and the views will automatically redraw those areas that have changed, as a result of modification to the document's graphical objects. For window device contexts a double buffered drawing context is used, which results in flicker free drawing and editing.
The main reason for creating wxArt2D is to have a tree of graphical objects, and a display engine to display and edit those objects with minimum hassle. For a small dedicated drawing, normally wxDc is used in wxWindows. The wxArt2D library relieves the user of all problems involved in view refreshing, editing features such as undo redo and the reading and writing of graphics to files.
To resolve the limited drawing capabilities of wxDC, Robert Roebling started the a2dCanvas class, it was based on similar principles to those used in the GnomeCanvas. GnomeCanvas is not using wxDC at all. The idea is to have the graphical primitives render themselves to a bitmap image. Writing of these rendering routines is complex, and in principle they do not quickly offer much more than wxDC already does. Therefore after a while the development went on using the wxDC approach but do the actual drawing to a bitmap. Double buffering techniques were still possible, but on the other hand, wxDC's limited drawing features were not resolved this way. I therefore made the decision to create a more advanced drawing context, that would be able to work with matrixes, double buffering, world coordinates, brushes and pens for gradient filling and new drawing primitives such as vector paths. As a bonus processing speed is increased. I designed this drawing context class to be an abstract class, in order to allow (even) more advanced implementations in the future, for instance, the implementation of alpha compositing using the antigrain rendering engine, or an OpenGl 2D drawing Context. The end result was an abstract drawing context specificially designed for use within the wxArt2D library. This drawing context is used to render the graphical objects stored in the drawing document. The new drawing context can still be used as a standalone drawing context on a wxWindow as well.
A specification for wxArt2D has never been written. Many of the features are based on experiences with a similar graphical program. Some ideas were taken from the GnomeCanvas and the Java Graphic2D classes. The greatest source of inspiration I have found in the SVG (Scalable Vector Graphics) format specification, which has now become a standard for 2D graphics on the Internet. wxArt2D is not a SVG compliant library, but it does certainly offer many of its features. Special attention was given to speed and memory usage. Features that you do not use, should not use memory in the basic graphical objects.
WxArt2D can be compared with other canvas libraries like GnomeCanvas and TclTk canvas. But it goes a few steps further, because of it not being only a canvas. It offers ways to load and save the graphical data, as well as editing capabilities. The a2dCanvas view that displays the graphical data, does not contain this data, instead the data is stored in a document. The coordinates system is in world coordinates, which makes it easy to zoom. The graphical data stored in a document can be viewed on several canvas windows at the same time. The state of the graphical data stored, is updated in idle time. The application just has to know that the document has changed and flag the objects that changed, the display engine take care of the rest. a2dCanvas uses Double buffering which offers improved speed and flicker free updating.
The top classes in the wxArt2D library are based on a derivative of the wxWindows DocView classes. They were modified heavily to use events instead of virtual functions together with reference counting. The idea behind the DocView classes, is to have a document manager which controls a set of documents and views of these documents. The document manager keeps track of the current active view and its document. In wxArt2D this document manager, is at the same time the command processor for the whole application. In addition to that each document has its own command processor to undo changes made to the document. Graphical a2dCanvasObjects are stored in a a2dCanvasDocument, after that a view called a2dCanvasView takes care of displaying this document in a a2dCanvas window, or in a bitmap. Printing is also possible. Additionally, a2dCanvas can act as a simple canvas window, without the need to have it under control of the DocView classes.
For scripting languages linked with wxWidgets (like wxPython), having a a2dCanvas is essential to be able to do efficient drawing. The reason is that drawing using a wxDc in a scripting language, requires a lot of communication between the wxWidgets core library and the script. WxArt2D has a broad range of graphical objects, which are created once and remain in memory. After that the task of the scripting language is minimized to changing the view of the data. The challenge in the design of the wxArt2D library, is in the fact that it is a library and not a program. It has to be as extendable as possible, without too much compromising on features, memory usage, or speed.
Some main features:
wxArt2D uses matrixes (rotate, scale, translate, mirror) to place canvas objects on a drawing. It is possible to have multiple references to such objects, in order to draw the same picture at several positions, all from a single instance. Groups can be formed by the addition of children to the objects and this is how a hierarchy is created. This is more or less equivalent to an XML DOM tree approach. On top of the hierarchy, objects are located on layers which are drawn in a given order. Each canvas object can have dynamic properties added to it, either visible or invisible.
Coordinates are in doubles or long types, and the type needs to be chosen at compile time. Doubles are used to have enough dynamic range to work with small numbers more easily.
A Viewport is set in double world coordinates and scrolling of the Viewport is possible in a window.
The data is stored separately from the display engine, and the same data can be displayed on different views simultaneously. The views called a2dCanvasView use a a2dCanvas to display themselves in a window. In each view the displayed part can be an arbitrary part or level in a hierarchy within a a2dCanvasDocument.
A Graphical device context (a2dDrawer2D) may use an advanced flicker free and double buffered display engine. Next to drawing flicker free, this is also the way to do alpha blending. Using the buffer, one can draw/fill transparent primitives using bitmap defined patterns. With only a straight forward wxDc based design this would be almost impossible, or be very slow. Since a2dDrawer2D is an abstract class, different types of derived drawing contexts can be used to render a document. The first drawing context classes, to be implemented, use the wxDc class to do the drawing and wxDC is best for speed. The AntiGrain library is used to implement the drawing context when doing alpha compositing.
The library is able to display relatively large amounts of data (chip designs), but at the same time you can edit/manipulate the drawn objects, without further memory requirements within the canvas object. In many software designs, everything needed to do this is put in a base class. In wxArt2D this is prevented whereever possible ensuring a quick redraw of isolated parts of the drawing document. This is a requirement of being able to edit a big drawing, or the redisplay of parts of the drawing changed during an animation. The underlying principle is boudingboxes for the canvas objects, and clipping of these to the rectangle that is to be redrawn. Editing tools work on special copies of an object to do the editing, and place the changes back into the original object. New editing tools can add editing handles for any desired shapes. In fact the handles added to an editing tool allow editing of the a2dCanvasObject itself. The drawing tools for adding or modifying new canvas objects, are either unique for each primitive and/or derived primitive, or they work on all types of primitives simultaneously. The drawing tools intercept the events for the canvas window and start drawing new objects in a flicker free manner and using the backup buffer to redraw/restore the background. While drawing an object, zoom in/zoom out is possible, making it possible to do very accurate drawing. All changes made to a graphical object via a tool, are sent as commands to the canvas document's command processor. The command processor maintains a history of commands, and this is used to implement undo, redo.
Moving and copying objects is also done with tools, it is fast because it uses the backup buffer to redraw the background while dragging.
For connected objects like in diagrams and schematics there is the pin object. Pins are added to a canvas object as children. The pins can be made on the fly and added at any position within a a2dCanvasObject. Pins can be connected to other pins residing in other a2dCanvasObject's. The typical wire/rubberband diagrams are achieved by wiregraph objects. The whole idea of having a dynamic pin object, is to not always have all the handles and pin drawing stuff inside the base a2dCanvasObject, but only when they are needed.
The use of a high level Drawing Context called a2dDrawer2D made it possible to draw rotated ellipses etc., and also to be capable of transforming relative world coordinates to device coordinates and visa versa. Thus it is possible to concentrate and optimize the drawing of primitives inside this class. The Fill and Stroke objects specifically designed for a2dDrawer2D, make gradient fills and the like possible. By shifting all matrix calculation into the drawing context, the user can always draw objects which are relative to vertex (0,0). The placing of the object, even if it is a deeply nested child, is automatically calculated. This makes writing new objects a simple exercise.
The objects use Fill objects for filling, and Stroke objects for drawing the outline. Moreover, the outline of a primitive can be influenced by a Contour Object. In the end everything is drawn as polygons or polylines. For instance filling an object using a Gradient Fill Object means the polygon will be filled using pixels going in 2D through a range of colors according to a certain algorithm. Where possible the basic wxWidgets or Platform API is used to draw more quickly.
Properties can be added to objects, which may be user or reserved properties. There are ways to render properties along with their parent object.
All objects in a view, can intercept events. When the mouse pointer hits an object, a range of events is generated. The user can intercept these events via a static event table. This is almost equal to event processing in wxWidgets itself, but using a much smaller event handler base class. Therefore memory requirements within the a2dCanvasObject is minimal. Hittest's on objects do not only detect the boundingbox, but also the outline and interior region of the objects. There is a mask available to define when a hit is reported for an object.
There is also a vector path object, which allows you to generate any weird 2D graphics that come to mind.
The basic a2dCanvasObject has a broad range of functionality, including many functions capable of manipulating the data according to flags in a mask or using properties set for certain objects.
WxArt2D uses IOHandlers for reading and writing data to and from THE a2dCanvasDocument. The IOHandlers can be plugged into the documents via document templates. Currently there are three format handlers for input and output. CVG which stands for Canvas Vector Graphics, is an XML based format. CVG is specifically designed for the wxArt2D library. It is easily extended with new objects created by the user himself. The trick is that objects read in are created using the Dynamic class creation available in wxWidgets allowing every new class to be reconstructed from CVG. Objects saved to the file and loaded back in are up to the user, but can be leveraged from the base library of existing objects. This means that the the new object can recursively read other objects when creating itself. Settings for layers are stored in CVG as well. The second format is SVG Scalable Vector Graphics. This is a relatively new format for the web, and one which I believe has a great future. WxArt2D can write and read SVG but not all exotic features are supported at this time. Be aware that wxArt2D is not designed to read SVG as defined in its DOM, and in fact is a translation of the a2dCanvasDocument's internal data structure to and from SVG. The third IO handler is for loading and saving the GDSII format. This format is used in the Chips industry. It is expected that more format IOHandlers will be written in the future.
The wxArt2D core has simple graphical objects, whilst other modules in the library implement more complex objects. It is easy to derive new canvas objects, which in turn can use other a2dCanvasObject's internally or decide to draw itself using a wxDrawing context. One of the more advanced modules is for displaying curves or groups of curves. Markers may be added to the curves, and the curves may be edited.
Algorithms can be written using a visitor class concept to facilitate manipulating the contents of a drawing document. The visitor can traverse a document hierarchy from the outside and functions may be called while traversing each object in the document in order to provide any desired algorithms.