Work with Arrows in SharpShooter Diagrams

Andrey Akinshin

In this article we are going to briefly review the work with a class with arrows.
 
Connection
Most of the connections in the component are presented by the Connection class. Let’s review some of its properties and methods:

  • StartPoint, EndPoint – start and the end of the connection
  • StartCap, EndCap – starting and ending connection caps
  • GetStartAngle(), GetEndAngle() – starting and ending angles by which the caps drawing is corrected.
  • FontFamily, FontSize, IsItalic, IsBold, TextFill, TextBackground, Text, TextDisplacement – the properties which define the display of the text label for the connection.
  • CustomRoute – additional points for connection building. They work in a different way for each type.
  • RouteType – represents the connection type. Currently 4 connection types are supported:
     
    - Straight – connects start and end points by linear segment; if the CustomRoute is set, it builds a stepped line that connects not only the start and end points, but all the points from the CustomRoute. 
    - Stepped – connects start and end points of the stepped line the elements of which are parallel to the coordinate axes; The elements are taken automatically, but if the CustomRoute is set, the route is built in the way to cover all the points indicated in the route.  
    - RoundedStepped - represents the same stepped line as in the Stepped, but its corners are rounded. 
    - Curved – connects the start and end points of the Bezier cubic curve. If the set CustomRoute has two points they are used as Bezier control points.
  • GetConnectionGeometry() – gets detailed description of how an arrow is drawn (taking into account all the loops and rounding, etc)

Connector
To connect shapes and arrows the objects has some set of ports (the ConnectionPort class). Each port has the ConnectionPortType property that can be set to one of 3 values: Master, Slave, Dual. The shapes usually have Master ports and the Slave ports are on the arrowheads. If we connect such pair of ports the position of the Slave port which is bound to one of the arrowheads will be automatically renewed when moving a shape with the Master port. The connection between ports is set by the Connector class. For convenience, there is also a special class – ConnectionHelper that helps connecting the ports. Among others, it has the Connect, Disconnect, GetConnectors methods. The code below demonstrates how to use it:
 

TextBlockBase shape1, shape2;
 Connection connection;

 // Some code (initialization shape1, shape2, connection)
 // ...

 ConnectionHelper.Connect(shape1.FigurePort, connection.StartPort);
 ConnectionHelper.Connect(shape2.FigurePort, connection.EndPort);

ConnectionLayoutEngine
ConnectionLayoutEngine – is a big class intended for allocating the connections of the Stepped and RoundedStepped types to optimize their performance. Its algorithm has the following purposes:

  • Minimize the route passed by the connections;
  • The route is made bypassing other shapes which crop up along the way;
  • Minimize cross-cuts between the routes
  • Intellectually separate the shapes which must be bypassed from those which should not be bypassed (for example, background elements).
  • Separate the coincided parts of the connections;
  • Loops in the crossover connections parts;
  • Account for the CustomRoute, in other words to enable custom settings to the process of the connection;
  • Unites arrows in hierarchic cluster in case if they start or end in the same port.
  • Select the routes in the human-readable manner

As a rule, in analogical services the route is laid by one of the following scenarios:

  • Routes are recalculated instantaneously, but a route itself is made in a trivial way without accounting for other objects of a document.
  • Routes are recalculated taking into account most of the requirements above, but the recalculation occurs only by completion of some manipulation (for example, if a user moves a shape from one place to another, the routes are recalculated after a user puts a shape down)

Algorithm in SharpShooter Diagrams unites both these approaches: intellectual building of the route is combined with instant reaction on any changes of a document (the routes will be recalculated at the moment of a shaping moving, and not after the move is complete). At the same time, the performance is very fast. Such variety of advantages relates to the usage issue: it is hard for the connections to go through “narrow” paths between the shapes. For stable work, it is recommended to keep the distance between the near-by shapes equal to 40 to enable the connection. Below you can see samples of connections layout in real diagrams:
 

 

 

 
PortConnectionStrategy
The ConnectionLayoutEngine class takes up almost all decision making issues to create a route for the connection. However we can influence its work a little bit. In addition to a list of standard ports (such as the PointPort, LinePort, CirclePort, EllipsePort) which are bound to specific points of a shape (for lines, circle and ellipses the binding to a point is done by the ConnectionParameter property which is included into building of the ConnectionPoint class), most of the shapes have Figure ports (represented by the RectangleFigurePort, EllipseFigurePort classes) which are implemented into the most suitable standard port when drawing the arrows. The permission algorithm wrap in implementations of the IPortConnectionStrategy interface. Each document type has a specific algorithm of ports selection. The most used implementations are the BestDistancePortConnectionStrategy (selects s port to minimize the distance between the start and end of an arrow) and the HierarchyPortConnectionStrategy (it develops the previous algorithm by adding a requirement to create hierarchal beams if several connections are added to a shape). It is possible to define a custom diagram type and set the required PortConnectionStategy for it:
 

 public class CustomDocumentType : DocumentType
 {
  public override IPortConnectionStrategy GetPortConnectionStrategy()
  {
   return new CustomPortConnectionStrategy();
  }
 }

 
BentArrow
Worth to mention that in addition to standard Connections, the shaped objects can take the role of the connections. For example, they are such classes as the AdvancedArrow, BentArrow, QuadraticBezierConnection, and the CubicBezierConnection. They are not affected by the ConnectionLayoutEngine connection layout logic, but this way they can be drawn in any imaginable manner. It is possible to define custom arrows by inheriting them from the ArrowBase class. Besides, among standard components there are many ArrowShape classes – inheritors from the TextBlockBase class which do not implement usual connections logic but can be used for data visualization in the form of arrows. Below you can see some types of arrows:
 

June 6th, 2012

Leave a Comment