- From: Maxim Shemanarev <mcseem@antigrain.com>
- Date: Tue, 21 Jun 2005 12:02:49 -0400
- To: <www-svg@w3.org>
Hello SVG World,
My name is Maxim and I'm working on the Renesis project, http://gosvg.net
There are serious problems in the SVG specification with markers, such as
arrowheads. I would say in SVG 1.1 they are totally useless. SVG 1.2 with
its vectorEffects solves the problem, but it's very complex too.
Markers themselves are very useful to draw diagrams and schemes. But SVG
specifies them in a pretty useless way and very inefficient in its
implementation.
First, let us see a simple example:
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="4in" height="2in"
viewBox="0 0 4000 2000" version="1.1"
xmlns="http://www.w3.org/2000/svg">
<defs>
<marker id="Triangle"
viewBox="0 0 10 10" refX="0" refY="5"
markerUnits="strokeWidth"
markerWidth="4" markerHeight="3"
orient="auto">
<path d="M 0 0 L 10 5 L 0 10 z" />
</marker>
</defs>
<rect x="10" y="10" width="3980" height="1980"
fill="none" stroke="blue" stroke-width="10" />
<desc>Placing an arrowhead at the end of a path.
</desc>
<path d="M 1000 750 L 2000 750 2500 1250"
fill="none" stroke="green" stroke-width="100"
marker-end="url(#Triangle)"
/>
</svg>
The result is as follows:
http://www.web-zaehler.de/resources/renesis/markers/marker1.png
And we see the first problem.
Problem 1.
It's impossible to draw the marker with the same color as the stroke. So, if
I need to draw 10 lines with the same marker I still have to define 10
different markers with colors that correspond to my stroke color.
Problem 2.
The second problem is connected with the first one and it's about a possible
way of its implementation. Markers are rendered in a way similar to
patterns. But first let us see, how SVG handles opacity.
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="2in" height="2in"
viewBox="0 0 2000 2000" version="1.1"
xmlns="http://www.w3.org/2000/svg">
<ellipse cx="1000" cy="500" rx="200" ry="100"
stroke-width="100" stroke="red" stroke-opacity="0.5" fill-opacity="0.5"
/>
<ellipse cx="1000" cy="1000" rx="200" ry="100"
stroke-width="100" stroke="red" opacity="0.5"
/>
</svg>
http://www.web-zaehler.de/resources/renesis/markers/opacity1.png
The first (upper) ellipse is drawn with opacity=0.5 defined separately for
the fill and stroke. The second one has "global" opacity=0.5. This
difference is fundamental, because in the second case we have to create a
separate buffer, render into it with opacity=1.0, and then blend it with the
parent one using opacity=0.5. That's very inefficient. Well, theoretically
we could do that using general vector clipping operations (Constructive Area
Geometry), but it would be even less efficient in most cases.
And the very same thing is applicable to markers! It means in practice that
the implementation shall render the marker in a separate buffer (in general
case) and then put it over the parent buffer using image transformation
methods (rotation, etc). It's not inefficient, it's terribly inefficient.
Again, theoretically we could do everything with vectorial representation
but considering all the details is's so hard that's practically impossible.
That's that.
The following example illustrates it. Note that the mid-maker is clipped to
its viewBox, and then rotated. It's not a big deal to clip it in its
vectorial form and then rotate, but the specification of the opacity makes
us use a separate buffer anyway.
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="4in" height="2in"
viewBox="0 0 4000 2000" version="1.1"
xmlns="http://www.w3.org/2000/svg">
<defs>
<marker id="Triangle"
viewBox="0 0 10 10" refX="0" refY="5"
markerUnits="strokeWidth"
markerWidth="4" markerHeight="3"
orient="auto">
<path d="M 0 0 L 10 5 L 0 10 z" />
</marker>
<marker id="Ellipse"
viewBox="0 0 20 10" refX="10" refY="5"
markerUnits="strokeWidth"
markerWidth="4" markerHeight="3"
orient="auto">
<ellipse cx="10" cy="5" rx="10" ry="8"
stroke-width="2" stroke="blue" opacity="0.5"
/>
</marker>
</defs>
<rect x="10" y="10" width="3980" height="1980"
fill="none" stroke="blue" stroke-width="10" />
<desc>Placing an arrowhead at the end of a path.
</desc>
<path d="M 1000 750 L 2000 750 2500 1250"
fill="none" stroke="green" stroke-width="100"
marker-end="url(#Triangle)"
marker-mid="url(#Ellipse)"
/>
</svg>
http://www.web-zaehler.de/resources/renesis/markers/marker2.png
Problem 3
The "marker-mid" is useles. I saw a lot of diagrams where there are lines
with arrowheads and some markers in the middle of them. It's a very
convenient way to display some additional properties (for example of the
edges of a graph). But I have never saw diagrams with lines (polylines) and
markers in each vertex of it.
So that, the following example doesn't essentially differ from the previous
one. I only replaced the "L" command to "Q" (quadric cure). However, the
middle marker disappeared.
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="4in" height="2in"
viewBox="0 0 4000 2000" version="1.1"
xmlns="http://www.w3.org/2000/svg">
<defs>
<marker id="Triangle"
viewBox="0 0 10 10" refX="0" refY="5"
markerUnits="strokeWidth"
markerWidth="4" markerHeight="3"
orient="auto">
<path d="M 0 0 L 10 5 L 0 10 z" />
</marker>
<marker id="Ellipse"
viewBox="0 0 20 10" refX="10" refY="5"
markerUnits="strokeWidth"
markerWidth="4" markerHeight="3"
orient="auto">
<ellipse cx="10" cy="5" rx="10" ry="8"
stroke-width="2" stroke="blue" opacity="0.5"
/>
</marker>
</defs>
<rect x="10" y="10" width="3980" height="1980"
fill="none" stroke="blue" stroke-width="10" />
<desc>Placing an arrowhead at the end of a path.
</desc>
<path d="M 1000 750 Q 2000 750 2500 1250"
fill="none" stroke="green" stroke-width="100"
marker-end="url(#Triangle)"
marker-mid="url(#Ellipse)"
/>
</svg>
http://www.web-zaehler.de/resources/renesis/markers/marker3.png
Problem 4
Markers in practice are mostly arrowheads. And I would like lines with
arrows to point exactly to the ending coordinates. In other words, I'd like
line (0,0,10,0) with an arrow to end exactly at point (0,10). I can set the
reference point of the marker in such a way but it will look inaccurate
because the line has a certain thickness.
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="4in" height="2in"
viewBox="0 0 4000 2000" version="1.1"
xmlns="http://www.w3.org/2000/svg">
<defs>
<marker id="Triangle"
viewBox="0 0 10 10" refX="10" refY="5"
markerUnits="strokeWidth"
markerWidth="4" markerHeight="3"
orient="auto">
<path d="M 0 0 L 10 5 L 0 10 z" />
</marker>
</defs>
<rect x="10" y="10" width="3980" height="1980"
fill="none" stroke="blue" stroke-width="10" />
<desc>Placing an arrowhead at the end of a path.
</desc>
<path d="M 1000 750 L 2000 750 2500 1250"
fill="none" stroke="green" stroke-width="100"
marker-end="url(#Triangle)"
/>
<circle cx="2500" cy="1250" r="20" fill="red"/>
</svg>
http://www.web-zaehler.de/resources/renesis/markers/marker4.png
The small red circle is the ending point. The only workaround is to shorten
the path. In SVG 1.2 it's possible applying vectorEffects. In SVG 1.1 it's
impossible at all. But vectorEffects are also expensive in general because
they assume creating separate vector buffers for paths.
I suggest to change markers' behaviour in the following way:
1. Markers just add a vector path to the stroke and they are always rendered
with the current stroke color/gradient/pattern. All color, stroke, and
opacity properties in the marker definition are ignored.
2. "marker-mid" is always rendered in the middle of the path, but not in
every vertex of it.
3. Add "shortenPathStart" and "shortenPathEnd" properties to the marker.
McSeem
Received on Wednesday, 22 June 2005 01:30:58 UTC