Diagram Graphs and Pins

The wxArt2D library has fundamental ingredients for making complex graph drawings which are editable. A graph here is a set of canvas objects, which are connected directly or using wires. It has bin setup as flexible we could think of. So things like several flows in one drawing using classes of pins which are connect able to each other and not to others, is part of the design. Same for the tools which are used to edit a graph. Like the standard dragging tool, it is fully aware that objects can be part of a graph. And it makes sure that connected objects do stay connected, or that unconnected objects become connected at the right moment. This functionality of course has complicated the design of wxArt2D, but when not implementing it at the very base of the library in all aspects, it would have bin useless.

As always in wxArt2D, the objects are derived from a2dCanvasObject's, and for the wires the same applies, it is just a specialized a2dCanvasObject. The way to define how a canvas object is connected to a wire or other object, is by using a pin (a2dPin) at the position where the wire should connect to the canvas object. The pin is a child of the canvas object which is connected to the wire, and the wire has a pin child too, at the same position. Internal in the two pins the connection of the one pin to the other is made. This is no more then a pointer from the pin to the other and visa versa. Any canvas object can have pins as childs, and pins can connect, so all canvas objects can connect with each other directly or using a wire to indicate that two are more objects are connected. A wire like canvas object behaves differently in many occasions.

(picture object pin pin wire pin pin object and result next to it )

The main implementation of a wire object is currently in a2dCanvasWirePolylineL, but if needed other types of wires can be implented. Most easily by deriving from it, but you may also implement your own. As the name suggest, the wire is a a2dCanvasPolylineL derived object. The difference from a polyline with pins added as childs, is in the way it treats those pins and what happens if connected objects are dragged around. If for instance two non wire objects are dragged apart, automatically a wire will be created in between. There are several ways to reroute a wire when the object are dragged. The most simple one just make a straight wire between the new position of the begin and end pin, the more complex routing uses an algorithm, to make a new polyline which has 90 degree angles and routes as much as possible around existing other wires and objects.

(picture routing)

Hierarchy in the form of grouped objects which can be itself grouped objects, is done by adding a2dCanvasObject's as children to a parent a2dCanvasObject. The top of this hierarchy is always the m_rootobject of a a2dCanvasDocument. So a2dPin's which are added to a parent a2dCanvasObject, is in principle no more then a simple hierarchy. You can add any other a2dCanvasObject as a child to the same parent. When the parent is a plain a2dCanvasObject, all that is rendered, are the children and pins. To make sure the pins are displayed on top of the other child objects, a flag is set to a pin, which results in rendering it later then the other children. The pin object itself is/can be rendered differently depending on it being connected or not. There are special feedback functions, which are used by (editing) tools, to change the mode of pin rendering depending on the possible connections at the point where the cursor position is. For instance when trying to create a new wire and connect it to an existing object, the object will give feedback by changing the rendering as long as the cursor is on top or near the pin on that object. Even if the object has no pins, like a standard rectangle, it can automatically create pins on the object. This we call dynamic pins. When connection is eventually made, the pin will remain in the object, else it will automatically be released. For most basic objects, this type of feedback is implemented, and for your own objects you can do the same. Still in case of a special designed library of connect able objects, one would mainly encounter pins at fixed positions within the objects. The CVG file format supports this, while dynamically created pins can only be implemented via a virtual function in C++ for that a2dCanvasObject. But as soon as dynamic pins are connected, they will be saved to CVG.

All the above is sufficient for creating simple graphs, like flows for programs, are any other graph where there are only connected objects, and all objects can connect to each other without restriction. In several situations more is needed. One might need a control and at the same time a data flow on the objects in the drawing, or certain types of objects can be connected while others can not. This functionality is achieved by adding a2dPinClass objects to a a2dPin. Certain pin classes may connect to each other while others may not, a2dConnectionGenerator implements a basic mechanism using an array of a2dPinClassMap's. But all its functions are virtual, so you can define any other ways of connecting objects. The a2dConnectionGenerator is asked to perform a specific task related to connecting objects, e.g to supply a new wire or a template for a new wire, given the object and the two pins which need to be connected by that wire. If NULL is returned, it is clear that those pin can not be connected. Those function are used within the library to automatically created new wires, and also by the tools to know which type of wire can start on which object etc. Interactive Feedback functions called from editing tools towards a2dCanvasObject's in the drawing, normally also use the a2dConnectionGenerator to find out if a connection is possible, and if the answer is positive, it while change its rendering mode accordingly. As said, a2dConnectionGenerator is generating new wires, therefore the way to make an application use different wire classes then the default a2dWirePolylineL, is by setting different wire templates to it.


About Pins

General

A a2dCanvasObject can connect to other a2dCanvasObject, and a special type of a2dCanvasObject is a connect object ( with IsConnect() returning true ). Such an object is meant to be used as a connection object in between other normal objects. In General this is a wire like object. Currently the main implementation of such an object is a2dWirePolylineL. Let's call this object wire. A wire can be bidirectional or directional, or non directional. With this is meant that a wire can go only in certain direction, e.g. from an output to an input. Objects are connected to one another via a2dPin's ( in short pin ). A pin is added as a child to a parent a2dCanvasObject, and a pin itself can connect to one other pin inside another a2dCanvasObject. Connecting two pins is no more then setting a pointer in a pin to another, and doing the same in the second pin. Pins are allowed to connect to other pins on the basis of a2dPinClass objects. Each pin does have a a2dPinClass set to it. The a2dPinClass itself has a list of connectable a2dPinClass's. So when one wants to connect a pin to another pin, the two pinclasses in the pins must be connectable. Which is the case if the first pinclass has its connectlist the second and visa versa. Now the powerful part of all this, is that a a2dPin can easily be derived for whatever reason, since the whole connection strategy is separated from it in the from of a2dPinClass objects. Next those pinclass objects can act in groups of connectable pinclasses, which i call flows. A flow can be made directional using special pinclasses for in and out going pins on objects and wires.

a2dConnectionGenerator

Each a2dPinClass has a pointer to a a2dConnectionGenerator. Although a pinclass itself knows exactly which other pinclasses may connect to it, this is not enough to organize all needed. The connection generator does all the extra tasks, which can not be concentrated in one pinclass. Situations like this exist when two objects need to be connected via a wire. Also when automatically generating new pins in object, the connection generator tells what is possible. One application may have several derived a2dConnectionGenerator instances, for example: one for each flow.

Interactive wiring

A tool to draw wire inbetween objects is complex. One wants to start on a pin, but what wire is required for that pin? Or maybe one only wants to draw wires starting at certain types of pins. After starting a wire, one wants to know to which other pins connection is possible. Is it allowed to start wires on existing wires? This process requires feedback from pins in objects. The wire tool uses various feedback id to get informatiom from the document its objects and in there the pins.

We can recognize the following types of connection issues:

When a wire tool needs to start a new wire on an object, there are three ways to do it:

Steps and feedback needed when drawing a wire are:

Layers

A typical connection implementation is when objects on a layer can be connected to other objects on the same layer, or to a fixed set of layers. It maybe also depend on the object its pins to which objects on the same or other layers it may connect. So how would one implement it using the a2dPinClass system. For each layer you define a pin class. Objects placed on the layer, get by default a pin with the layer pinclass. Say they are called PinClassLayerXX where XX is unique for each layer. To a PinClassLayer01 you can add for example connection pin classes PinClassLayer01, PinClassLayer02 and PinClassLayer03. A pin on another object with pinclass PinClassLayer03 and in its connection table PinClassLayer01 may connect to the first pin, since they both have in their connection list the other pin its pinclass. The above has in fact nothing to do with layers directly, only the fact that one uses for objects on a certain layer always the same PinClassLayerXX, makes it seem that all the objects on the layer behave the same way when it comes to connecting to other objects. But if you want some objects to behave different anyway, you can do so. In order to make object coming from library of objects more general, the a2dLayerInfo which defines the properties of a layer in wxArt2D, also may have set a a2dPinClass set to it. This pin class will be the default for object placed on the layer. The default implementation of a2dConnectionGenerator uses this pinclass when generating pins automatically. You just set per layer the default pinclass you would like to use, and all standard objects like rectangles and such will generate pins based on those pinclasses.

wxArt2D: PinsGraphs (last edited 2008-08-05 19:06:19 by KlaasHolwerda)