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:
Somewhat more abstract usecases are:
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].
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.
<jr:point refid="refpointid" point=" x,y " />
,
or <jr:point refid="refpointid" x="x" y="y" />
;
refpoint(refpointid)
within the jr:points
and jr:d
attributes
of <polyline>
, <polygon>
, and <path>
instead of a pair of coordinates x,y
within the attributes
points
and d
;
refpoint(refpointid)
within the new jr:xy
, jr:cxy
, jr:xy1
, and jr:xy2
attributes
of <text>
, <circle>
, <ellipse>
, and <line>
instead of single coordinates x
and y
within the respective standard attributes for that purpose,
x
, y
, cx
, cy
, x1
, y1
, x2
, y2
;
refpoint(refpointid)
,
it is also possible to give a reference to a "port" within an "object",
instead,
via refport(refuseid,refportid)
,
where refuseid
is the ID of a <jr:use>
,
and refportid
the ID of a <point>
defined within the object which gets reinstantiated by the <jr:use>
(a "port" being a "reference point" defined within some "graphical object"
which gets reinstantiated via <jr:use>
-
such "reference points" might exist more than once in the instance tree,
and that is why those "ports" need to be referenced via two separate IDs:
the one from the <jr:use>
and the other from the <point>
);
jr:points
and jr:d
attributes,
usage of reference points
via refpoint(refpointid)
,
of ports
via refport(refuseid,refportid)
,
and usual pairs of coordinates x,y
may be mixed;
<line>
,
the two endpoints of the line might be given through different means at the same time,
so jr:xy1
, x2
, and y2
might be used
as well as x1
, y1
, and jr:xy2
;
jr:xy
, jr:cxy
, jr:xy1
, and jr:xy2
attributes
of <text>
, <circle>
, <ellipse>
, and <line>
,
the respective standard attributes
x
, y
, cx
, cy
, x1
, y1
, x2
, y2
,
must not be used for that element;
jr
namespace must be used
for the jr:points
and jr:d
attributes
of <polyline>
, <polygon>
, and <path>
when using refpoint(refpointid)
within;
jr
namespace must also be used
for the new attributes
jr:xy
, jr:cxy
, jr:xy1
, and jr:xy2
;
<jr:use>
reinstantiates an object using ports,
it must be in the jr
namespace;
<jr:point>
tag
and the (enhanced) <jr:svg>
root tag
need to be in the jr
namespace given above,
as well
-
otherwise,
just the default template will be used which results in simple text;
<jr:script />
within <defs>
in your extended SVG file;
<jr:style />
within <defs>
in your extended SVG file;
<?xml-stylesheet type="text/xsl" href="connector2svg-js.xsl" ?>
;
xsltproc connector2svg-js.xsl enhancedsvginput.xml > standardsvgoutput.svg
.
<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.
<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.
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.
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;
<jr:use>
:<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
.
refpoint(refpointid)
referenced point refpointid
is defined.
In case it is not defined,
no coordinates will be filled in for that refpoint(refpointid)
.
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 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
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.)
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 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
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
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
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
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
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)
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
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.
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.
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)
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)
.
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)
.