binary connectors - modelint/flatland-model-diagram-editor GitHub Wiki
A binary connector attaches one node face to another with one or more contiguous line segments. A node face is the top, right, left or bottom edge of a node rectangle.
Just like nodes, each connector has a name taken from the model file. In the case of an Executable UML class diagram, regardless of notation, the name is an R number such as R1
, R250
or possibly OR6
(an ordinal relationship). You should see these in the relationships section of your .xmm (model) file.
A connector terminates with a stem for each face attachment. The stem is a short non-bending line segment that ‘stems’ from the node face. Each stem has a root end (on the node face) and a vine end some distance away from the node face in the same line segment. The length of a stem is usually just far enough away from the root to overlap any decoration (arrows, circle, etc). In other words, stems are short and you only care about the root end in your layout file. Namely, you care about the node face where it attaches. Since a stem cannot bend, you need to leave enough room in your layout so that a stem does not overlap another node or connector.
A stem can be named. In the case of a class model it might be a verb phrase like is flying
or is flown by
. The syntax for defining a binary connector is a bit more complex than anything else in the layout file, so we’re going to build it up starting with simple examples and end up with the full syntax at the end.
<indent><side><connector_name> : <t_stem> : <p_stem>
Connector specifications are indented below the connectors
keyword. The <side>
element is this:
[ + | - ]
The plus or minus before the connector name tells us where to place it. For a vertical connector, it will be on the left or right side of the connector and for a horizontal connector, it will be above or below it. Plus is above or to the right since coordinate values increase in those directions. The <connector_name>
is a name found in the model file like R1
, assuming that we are drawing a class diagram.
In the model file, one side of a relationship will be specified before another. Prior to layout, there is no concept of left and right or top and bottom, so we just use the arbitrary names ’t’ and ‘p’. They could have just as easily been ‘a’ and ‘b’, but that wouldn’t be as imaginative as ’Terrance’ and ‘Phillip’.
In the diagram, we need to map the t and p relationship sides to corresponding stems. In a connector specification, the first face to be specified (moving left to right) is the t stem followed by the p stem. Both of the stem specifications have the same internal syntax which is:
<side>/<linewrap> <face>[<position>]|<node_name>
<side>
is another plus/minus choice, but this time it tells us which side of the stem to place the stem name. Plus is above/right of the stem. Then there is a slash followed by a <line_wrap>
which must be a positive integer one or greater representing the number of lines you want to wrap the stem name text. If you have a short name, just leave it at 1. But if you have a long name, you might want to split it to two or three lines. The text will automatically be aligned left or right depending on which side of the stem it is placed.
The <face> is:
[ t | l | b | r ]
representing the face where the stem attaches (top, left, bottom or right).
By default, the stem will attach to the center of the face. This can be:
[ * | +1 | +2 | -1 | -2 ]
The *
choice says, ‘Just run the line straight across from the other stem and attach it where ever it meets the face’. For a non-bending binary association you must specify the *
option on one and only one of the stems. This ensures that you always
get a perfectly straight line.
The positive and negative numbers represent offsets from the center. Positive numbers are higher or to the right depending on orientation. A number like +1
(yes you need to specify the +
sign) indicates one ‘notch’ upward or to the right. The node face is divided into five notches with 0 at the center. So you have two equally distance notches going one way and two going the other. The bigger the face, the more distance covered, so it is all size relative.
So if a stem anchors to a right or left face and +1
is specified, the stem will be one notch upward from the center. If the stem anchors to a top or bottom face and -2
is specified the stem will be two notches left of center. If you don’t specify any value, the stem will be centered on the node face.
Notches are computed in such a way as to never be too close to the node corner.
In this example we anchor the t stem two notches up with a floating anchor on the p stem.
connectors
-R1 : +/1 r+2|Aircraft : +/2 l*|Pilot
Which yields:
The three sections, separated by the :
delimiter specify the connector name, t stem and p stem.
The connector name R1
corresponds to a relationship name found in the model file. The minus sign specifies that it is drawn underneath the connector.
The t stem specification plus sign tells us to put the stem name above the connector with the name kept as a single line of text. It also specifies that the stem is attached to the right face of the Aircraft
node, two notches up from center.
The p stem specification also puts its stem name above the connector with text wrapped on two lines. Note that the text is automatically right aligned since the node face is on the right. Since the t stem has a fixed anchor and this is a straight (no bend) connector, the p stem must specify a floating anchor with the *
symbol.
The floating anchor can be on either the t or p stem, but not both. So you can do this:
connectors
-R1 : +/1 r*|Aircraft : -/1 l-1|Pilot
Here we see that the p stem has the fixed anchor, the p stem name is now all on one line. Also we moved the connector name down underneath the connector. Unfortunately, the p stem name runs too close to the connector name. To clean things up, you can move the connector name back to the top of the connector or wrap the p stem name on two lines as before.
You may note that this whole business with naming the sides t and p keeps us from having to refer to the stem name text in the layout file. So instead of referring to is flown by
, we just refer to the ‘p side’ and avoid some duplicate text entry.
You will often need to make small adjustments when a diagram is generated to keep it looking nice. Fortunately, you make these changes in a text editor instead of dragging pixels around and without touching your model file.
Now let’s draw a binary connector with a single bend.
nodes
Aircraft 1,1
Pilot 3,2
connectors
-R1 : +/1 r|Aircraft : +/2 b|Pilot
We’ve moved the Pilot
node so that it is above the Aircraft
node by two columns and one column over to the right. We put the t stem on the right face of Aircraft
and the p stem on the bottom face of Pilot
. Due to the bend, we can’t use a floating anchor, so the *
symbol is missing. We draw from the center of one face to the center of the other face.
Now take a look at the connector name R1
in the diagram. It’s underneath the bend closest to the t stem. Flatland chooses the nearest t stem bend by default, applying the plus/minus sign placement as before. To move the connector name to the second bend, you specify the bend you want, counting (starting with 1) from the t stem bend. So we can select the only other choice, bend number 2.
nodes
Aircraft 1,1
Pilot 3,2
connectors
-R1.2 : +/1 r|Aircraft : +/2 b|Pilot
So our more advanced connector name placement syntax is actually:
<side><name>[.<bendplace>]?
That’s a side, followed by a name followed by an optional bend placement number which must be a counting number greater or equal to one and less than or equal to the total number of bends in the connector.
We can add as many bends to a binary connector as we like. But once we get past the first bend, or corner, we need to specify a bit more information. That’s because a single bend is computed by drawing a straight line to the opposing anchor point x or y distance and then making a right angled turn toward that same anchor point. But it is not clear where to make that turn if the immediate destination is not the opposing anchor point.
To solve this problem, Flatland uses a Lane-Rut system for specifying where to bend in a row or column. Since rows and columns are treated the same for bending purposes, we just call them lanes. We then subdivide a lane into ruts, much like we divided up portions of a node face with anchor points.
The center of a lane is the default bending point, zero. We then count two ruts upward/rightward with a plus sign or downward/leftward with a minus sign.
nodes
Aircraft 1,1
Pilot 1,3
connectors
+R1.2 : -/1 t|Aircraft : +/1 t|Pilot : L2R-1
In the above specification we have both nodes in the same row (row 1) and we want to draw a U shaped connector over the top, so a binary connector with two bends, one above each node. Note that both connection points are top center and that there is a new :
separated section at the end specifying the U bend in lane 2, rut -1.
The connector is drawn upward from the t stem, not to the middle of the next row, but one notch below the center. You don’t need to refer to row or column in your bend placement since Flatland can figure that out as it draws. So you just say L2
which turns out to be a row. And the R-1
part tells Flatland to draw up to the -1 rut. Let’s change the rut to +2 and see what we get.
nodes
Aircraft 1,1
Pilot 1,3
connectors
+R1.2 : -/1 t|Aircraft : +/1 t|Pilot : L2R+2
The highest (or rightmost) rut is chosen when you specify R+2
If you just want the bend centered in the lane, just leave out the rut choice.
nodes
Aircraft 1,1
Pilot 1,3
connectors
+R1.2 : -/1 t|Aircraft : +/1 t|Pilot : L2
The general rule is that you need n-1 bend specifications where n is the number of bends in your binary connector. So, for a single bend, you don’t supply any.
Let’s try three bends.
nodes
Aircraft 1,1
Pilot 2,4
connectors
+R1.2 : +/1 t|Aircraft : +/1 l|Pilot : L3R-2 L3
Notice that there are two bend placements now.
The t stem is attached to the Aircraft
node, so the first bend runs to lane 3, rut -2 (row 3, down two notches from center) and the second runs to the center of lane 3 (a column this time).
So the full syntax for a lane placement specification is:
: L<row or col number>[R<side><rut number>]? ...
That’s a colon with one space on each side followed by one or more bend placements. Each bend placement must specify a lane number prefaced by L
with an optional plus/minus side and rut number both prefaced with R
.
Both stems of a reflexive connector attach to faces of the same node. In this example, the t stem attaches to the top face and the p stem attaches to the right face, both near the top right corner of the node.
nodes
Waypoint 1,1
connectors
+R1.3 : -/1 t+2|Waypoint : -/1 r+2|Waypoint : L2R-2 L2R-2
As you can see, this requires two bend placement specifications at the end, each in the -2 rut of lane 2.
Notice that the connector name is placed at the third bend position. Third from the t stem which we can see attaches to the top face. So you know that you count the bend placements going clockwise, in this example. Especially in cases like this it is helpful to order the t and p stems to match the same order you find in the .xmm model file.