"Connectors" for SVG

A Proposal for a Syntax Extension

as well as a Demonstrator Implementation

Created: July 10, 2014

Last update: November 22, 2014

Motivation

SVG is a vector graphic language designed for usage in the web. As such, it may connect points given by coordinates through straight line segments and curve segments. As a single point sometimes is used more than once, it might be helpful to define a point by its coordinates once, and whenever some graphical object might use that point, it would just give a reference to that point instead of its coordinates. This would make shifting or moving some parts of the complete scenario easier.

Practical usecases might be:

Simple street map:
A simple street map might consist of crosspoints and endpoints of streets connected via straight line segments, some of the segments might have additional constraints or attributes (footpath, one-way-road, ...). Thus, a street (attributed by its name) might be modeled as a traverse, as well as those parts with special restrictions or attributes. And those traverses would be given as a sequence of references to the crosspoints and endpoints.
Annotations to graphical presentations:
So I might have the annotation positioned at various places (for screen presentation at various screen sizes, for print-out, ...), and would need to change just a single coordinate pair for that instead of hundreds ...
A highly dynamical usecase:
Once, my students implemented an actual map of the trams in Karlsruhe, and as trams are moving, mostly, and as you might have interconnections and annotations to them as well (and the other end of the interconnection might move in another way, and the annotation might not be moved at all), changing the coordinates of a single refpoint via a client-side script language would be a "nice thing to have" instead of going through all the coordinates of related points and adapt them properly via that client-side script language.

Somewhat more abstract usecases are:

Any line or connection between points in different transformation contexts:
Whenever two points are located in two different transformation contexts, SVG does not allow to draw a line between them without recalculating the coordinates of one point, at least, since the coordinates of the two connecting points must be given in only one and the same transformation context. It might be the context from the first, or the context from the second point, or even another context, but the coordinates of the other point, or of both points, need to be given for that very context where the command drawing the line or connection is being coded.
Graph theory:
Graphs are mostly nodes and interconnections. The position of the nodes might be moveable in order to view the graph from several perspectives, but the interconnections should stay the same between the moved (or moving) nodes.

The term "connector" for this proposal originates basically from graph theory. This proposal enhances SVG basically just with the abilities to define "reference points", and to use references to those reference points within some elements of SVG. The "connectors" themselves would be accomplished by those already existing SVG elements (i.e. <polyline>, <polygon>, and <path>).

There had been proposals for "connectors" in SVG in the past. One official editor's draft is available on the W3C website [1]. Another proposal originates also from a W3C SVG Working Group member [2]. The latter proposal for connectors was also discussed in a W3C SVG Working Group teleconference meeting [3]. And now, these proposals are accomplished by another simple one [4].

Syntax extension

The key extension is the implementation of a <jr:point /> tag, with a refpointid in order to reference the point and its coordinates (x,y) in the form <jr:point refid="refpointid" point="x,y" /> or <jr:point refid="refpointid" x="x" y="y" />. This is complemented by the possibility to use a reference to such a reference point via refpoint(refpointid) as well as a reference to a so-called "port", which is a reference point <jr:point refid="refportid" /> defined as part of another graphical object being reinstantiated with <jr:use id="refuseid" >, via refport(refuseid,refportid). Both references may replace ordinary point coordinates in all respective attributes of tags for graphical objects, such as jr:d, jr:points, jr:xy (the latter as a replacement for both x and y attributes), and similar attributes. The new tag as well as the attributes using references, actually need to be in a special namespace jr, just for implementation reasons, and in order to have a strict separation of old standard SVG syntax and new SVG syntax extension.

Demonstrator of a "connector" proposal realization

Short (but hopefully complete) usage description

Current limitations of the implementation

Transformations in the case of the pure XSLT version:
No special care for transformations is taken into account. The implemementation just does a simple copy and paste of the given coordinates. For a real implementation, the transformations which are valid when defining the <jr:point> (e.g. when defining a <jr:point> as a "port" within an object) should be performed in order to get the viewport coordinates of the reference point. And those viewport coordinates should be used when referencing that <jr:point> via refpoint(refpointid), and no additional transformations which are valid for the object using the refpoint(refpointid), should take place to those viewport coordinates, then.
Transformations in the case of the extended JavaScript version:
For <jr:point> defined within a transformation context, the coordinates are adjusted accordingly via JavaScript. When the element using the refpoint(refpointid) is located within a transformation context, this should also be taken into account for the JavaScript calculation of the proper coordinates. The same is true for some transformation which originates from a viewBox attribute. So, there are no longer any general limitations regarding transformations in the case of the JavaScript version.
If a transformation is applied to an object using refpoint(refpointid), and no inverse transformation exists for that transformation (e.g. scaling with a factor of 0.0), the calculation of the coordinates via JavaScript will fail, most propably resulting in an exception. Thus, no coordinates at all might be calculated in that case.
Syntax check:
No special syntax check is performed. When the beginning refpoint( is written wrong, or even when there is some space between refpoint and (, no replace will take place for that refpoint at all. In case of a missing closing bracket, e.g. refpoint(refpointid, the complete rest of the attribute will be regarded as part of the refpointid, most probably resulting in a reference to an undefined point. And the coordinates x and y need to be comma-separated as x,y in the point attribute of the <jr:point> tag - otherwise, the replacement for the new jr:xy, jr:cxy, jr:xy1, and jr:xy2 attributes will fail;
Coordinates of <jr:use>:
When inserting the instance of a graphical object via <jr:use>, most probably including "ports", the positioning of that object instance is not possible via any reference. Thus, <jr:use> must be given its coordinates via the traditional x and y attributes, and must not use the new attribute jr:xy.
Reference to undefined points:
No care is taken whether a via refpoint(refpointid) referenced point refpointid is defined. In case it is not defined, no coordinates will be filled in for that refpoint(refpointid).
Reference points and relative coordinates:
Within the jr:d attribute of <path> tag, you should not use a path command assuming relative coordinates (i.e. a path command with a lower case letter) immediately followed by refpoint(refpointid). The refpoint(refpointid) will just be replaced with absolute coordinates of the reference point, not taking into account whether the actual drawing command assumes absolute or relative coordinates.

The basic version as a pure XSLT implementation

The following implementation of reference points for SVG is done via XSLT. It is just a demonstrator for the way such an SVG extension might work, and it has several limitations (see above). But it gives a first insight in the proposal and its possible use, at least.

The XSL file for transformation of the enhanced SVG syntax to standard SVG 1.1 syntax: connector2svg-js.xsl

A very simple test file

The first example is just a very simple test file, used to demonstrate the syntax, just to see "how it works" ...

The XML file with an enhanced SVG syntax: test.xml ("view source" to see the enhanced SVG syntax)

The resulting standard SVG file: test.svg

The same XML file as before, but one coordinate of one single point changed: test2.xml

(No standard SVG file provided for the latter case.)

Street map as a somewhat more complex example

The second file is somewhat more complex. It originates from my first real usecase for a connector (i.e. reference point) extension to SVG. It is a simple street map demonstrating how to approach some institution ("Ladenkirche SENFKoRN" in Karlsruhe, to be exact). In that case, the current limitation of refpoint usage to untransformed SVG objects (see above) is a real constraint: All the coordinates of geolocations which were real geo-coordinates (longitude and latitude), have to be transformed to viewport coordinates before the reference point may be defined.

The XML file with an enhanced SVG syntax: strassen.jr.xml

The resulting standard SVG file: strassen.jr.svg

The extended JavaScript version

The extended JavaScript version allows the usage of transformations for the context of the reference points. And the objects which are using those reference points, may also be within any transformation context. Those limitations of the first version of the demonstrator are no longer valid.

The calculation of the transformed coordinates and the replacement of the references with those transformed coordinates is done via JavaScript. For each reference point, a small symbol may be displayed, just for illustration. From the very beginning, those reference points are located at their final position including all transformations according to their given transformation context. The same is true for elements using references to non-transformed reference points, as this version uses also the XSLT search-and-replace for references to those points. But for the elements using references to reference points in transformation contexts, the coordinates are adjusted somewhat delayed via JavaScript. The XSLT does just a search-and-replace for the refpoint(refpointid) with its given coordinates, but the JavaScript does a re-calculation of the coordinates according to the current transformation context of the reference point. This can easily be seen as the difference of the elements' positions before and after the pop-up which appears just for that purpose after some delay of 5 seconds.

The JavaScript file for the adjustion of the coordinates of reference points defined within transformation contexts: connector.js

Another test file including dynamic transformations of the reference points

This is basically the same simple test file as before, but this time, several groups containing reference points get a transformation. Additionally, a "shadow group" is positioned which gets continuously moved (scaled, rotated and translated in varying scaling factor, angle and distance). The edges of the "shadow group" have visible interconnections to the respective edges of the original group. The complete movement is done by adjusting one simple transform of the "shadow group" which contains some reference points. One can also see from that example that the objects themselves are not imposed to the transformation context of the reference points, e.g. they do not scale (font size, stroke width, ...).

The XML file with an enhanced SVG syntax: test3.xml

The resulting standard SVG file: test3.svg

Street map using the JavaScript version

The somewhat more complex example from above, a simple street map. But this time, it gets transformed with the help of the extended JavaScript version of the XSLT. This even allows the use of the unmodified geocoordinates (longitude and latitude), since the positions of all reference points get adjusted with the help of some transformation (scale and translation) applied to the complete group of reference points. And the transformation resulting from the viewBox attribute is taken into account, as well.

The XML file with an enhanced SVG syntax: strassen.js.xml

The resulting standard SVG file: strassen.js.svg

Test file with ports

This is a test file which uses ports within objects which get used several times. The first instance of the original object is positioned static and has connections to three of its ports from a single point located at the left. The two remaining ports of this object are both connected to the port located in the middle of the third, the moving object instance (see below). Two other instances of the same object are located below the first instance in different transformation contexts. Each of the five ports of each of the two instances is connected with the respective port of the other instance. The object located the lowest continuously changes its transformation (position, rotation, scaling).

The XML file with an enhanced SVG syntax: test4.xml

The resulting standard SVG file: test4.svg

Family tree as a concrete usecase for ports

A concrete usecase for connectors between reference points and ports would be a family tree: This can be easily modelled as a graph. To demonstrate the usecase, here is a family tree of Adam and Eve (slightly incomplete). The persons, being modelled as "nodes" of the graph, have relations to other persons, their partner and children, those relationships being modelled as "edges". In the extended SVG syntax, a node prototype is defined as a simple <g id="person"> which gets instantiated via <jr:use xlink:href="#person">. As the <jr:use> might not be positioned via refpoint(refid) (due to the limitations of the demonstrator implementation, see above), the node gets instantiated at concrete given coordinates. But for additional objects related to the node (the textual description, in that case), the prototype node object contains a port not only for the edges to the other nodes, but also a port for that text which gets positioned via refport(refuseid,refportid), then. Thus, it might still be possible to move a node with all its relationships (edges, textual description) by changing one single coordinate.

The XML file with an enhanced SVG syntax: adameva.xml

The resulting standard SVG file: adameva.svg

Combinational and sequential circuits as another concrete usecase for ports

Another concrete usecase for connectors between reference points and ports would be the design of combinational and sequential circuits: Such circuits are basically just graphs, the logic gates forming the nodes (vertices), and the connections forming the directed edges (arcs). To demonstrate the usecase, here are a few simple circuits, a half adder, a full adder, a SR latch, and a 4 bit ripple carry parallel adder, modelled and displayed via SVG enhanced with refererence points. No huge textual description is given, as it uses the same principles as the previous example family tree. Instead, just the links with two variations of the same circuits: an international one using IEC symbols, and a localized one using ANSI symbols.

The XML file with an enhanced SVG syntax: schaltung.xml (IEC version) and schaltung_us.xml (ANSI version)

The resulting standard SVG file: schaltung.svg (IEC version) and schaltung_us.svg (ANSI version)

Communication networks as one more usecase for ports

Another concrete usecase for connectors between reference points and ports would be the design of commmunication networks. To demonstrate the usecase, here is a simple network consisting of several sources, switches, and sinks.

The XML file with an enhanced SVG syntax: netzwerk.xml

The resulting standard SVG file: netzwerk.svg

Summary

Reference points might be a useful extension to SVG. They might be used to define connectors between objects in numerous usecases. Thus, not even a special connector tag would be needed for that.

To realize such reference points, a proposal for a syntax extension to current SVG 1.1 is made. This proposal is complemented by a demonstrator implementation in order to show how the extension might work. This is basically a proof of concept.

The demonstrated usecases and examples might not be very impressive since they are very simple. But just in the case of the reference points within transformation contexts, in combination with dynamic modification of the transformation, there is some space left for even more impressive demonstrations of "real usecases" ... but as the demonstrator is very limited, actually, I am still deliberating how to realize further concrete examples in a most comfortable and elegant way.

References

History of the proposal

The description from my EMail to the W3C SVG list [5]

My approach would be somewhat simpler:
Make a new <point> element,
with coordinates 'x' and 'y' as well as an 'id' attribute (with a value of 'point_id'),
which might be used inside or outside of another graphical object
(inside as a 'port' for the object, and outside as a simple means for reference),
and which will not be rendered (similar to '<symbol>'),
and enhance the syntax of the 'points' attribute (of '<polyline>' and '<polygon>'),
and of the 'd' attribute (of '<path>'),
to allow a 'refpoint(point_id)' (or similar) to be used instead of a pair of coordinates, where applicable.
Thus, I would not need a '<connector>' element,
and the routing and design of the connector would be up to the author.
And the 'refpoint' might be used everywhere within the path,
even as a control point of a bezier curve,
and not just as the start point or end point of a 'connector' for which the shape would still have to be defined. 

Realization

As mentioned in an EMail to the W3C SVG list [5], a first realization might be done via XSLT. So, here is a very quick and simple solution, which is not at all bullet-proof, via XSLT 1. There are some limitations, for sure. E.g., it does not check for syntax errors of the refpoint() construct (missing brackets or similar). And any transformations which are performed in the context of the point and the refpoint environment, are not taken into account, so far. It is just a simple search for the refpoint and replace it via its given coordinates. Thus, transformations which might be active in the context of the point definition, are not taken into account, whereas transformations which might be active in the context of the refpoint usage, and which should not be taken into account (as the refpoint should not be transformed accordingly), are being performed. Instead, for a real solution of that problem, the coordinates of each point should be transformed to the viewport coordinate system and stored as such. And when using a refpoint, first an inverse of the actual transformation which is active where the refpoint is being used, should be performed to those stored coordinates in the viewport coordinate system before replacing the refpoint with those new coordinates in the code. Thus, the reference point should finally appear at its intended position in the viewport coordinate system. And I was not able to perform the XSLT automatically via the appropriate xml-stylesheet declaration in the enhanced SVG syntax file, for whatever reason. The XSLT is started manually via xsltproc, instead. When calling the XML file (which is an enhanced SVG file), the XSLT is performed automatically. But the resulting standard SVG file, which was created via xsltproc, is also available for reference (and for those who use a browser which does not perform the XSLT by itself, for whatever reason)

Changes for the first implementation approach

The attributes of the '<point>' element are refid instead of id, and point instead of x and y (containing both x and y coordinates, in the same format as used with the points and/or d attribute). And in those attributes d and points, the syntax for a reference to a point is refpoint(refpointid).

Another change - special namespace for extensions

There is a new namespace http://jroethig.de/svg with namespace prefix jr which must be used for the new '<point>' element as well as for the existing (but enhanced) '<svg>' root element. I.e. use '<jr:point>' and '<jr:svg>', instead. If the root tag '<svg>' is not in the namespace jr, the XSLT will result in performing just the default transformation (which returns a concatenation of all the text in the file). Also, the attributes points and d of <polyline>, <polygon>, and <path> need to belong to the namespace jr in order to be checked for any refpoint(refpointid).