I'm learning how to animate svg.
I can animate between two shapes with calcMode="spline", I can animate between three shapes without calcMode="spline", but I can't animate between three shapes with calcMode="spline".
Here's my example :
<animate
attributeName="d"
attributeType="XML"
begin="0"
from="M662.1,170.5c-0.2,0-0.3-0.1-0.4-0.3l-3.5-7.9c-0.1-0.2,1-0.1,1-0.1l3.2,7.7c0.1,0.2,0,0.5-0.2,0.6
C662.2,170.5,662.1,170.5,662.1,170.5z"
to="M704,146.5c-0.6,0-1.1-0.3-1.4-0.9l-12.4-28.1c-0.3-0.7,3.6-0.5,3.6-0.5l11.6,27.4c0.3,0.8,0,1.6-0.8,2
C704.4,146.4,704.2,146.5,704,146.5z"
dur="2s"
values="
M662.1,170.5c-0.2,0-0.3-0.1-0.4-0.3l-3.5-7.9c-0.1-0.2,1-0.1,1-0.1l3.2,7.7c0.1,0.2,0,0.5-0.2,0.6
C662.2,170.5,662.1,170.5,662.1,170.5z;
M704,146.5c-0.6,0-1.1-0.3-1.4-0.9l-16.4-25.1c-0.3-0.7,3.6-0.5,3.6-0.5l15.6,24.4
c0.3,0.8,0,1.6-0.8,2C704.4,146.4,704.2,146.5,704,146.5z;
M704,146.5c-0.6,0-1.1-0.3-1.4-0.9l-12.4-28.1c-0.3-0.7,3.6-0.5,3.6-0.5l11.6,27.4c0.3,0.8,0,1.6-0.8,2
C704.4,146.4,704.2,146.5,704,146.5z"
calcMode = "spline"
keySplines = "0 0.75 0.25 1"
keyTimes = "0;1;2"
/>
If I remove values an adjust keyTimes="0;1", it works.
If I remove calcMode, keySplines and keyTimes, it works.
But it doesn't work together.
Did I made a mistake somewhere, or can't we use both together?
Thanks for helping!
From the SVG Specification:
the ‘keySplines’ values are the control points. Thus, there must be one fewer sets of control points than there are ‘keyTimes’.
You've one keySplines value so you need 2 keyTimes values. Alternatively, you need to have 2 keySplines values in order to support 3 keyTimes values.
Related
My question: Can svg <marker> elements inherit color from the <line> they are referenced on?
The background: I have a D3 graph that has different styled lines, and I want my lines to have arrows at the end.
So at the top of my <svg> I have const defs = svg.append('defs'); and then further along I generate my defs using a generator function:
function makeDefs(defs: Selection<SVGDefsElement, unknown, null, undefined>, color: string, status: string) {
const markerSize = 3
const start = defs.append
.append('marker')
.attr('id', `arrow-start-${color}-${status}`)
.attr('viewBox', '-5 -10 20 20')
.attr('markerWidth', markerSize)
.attr('markerHeight', markerSize)
.attr('orient', 'auto-start-reverse');
start
.append('path')
.attr(
'd',
status === 'PUBLISHED' ? customPaths.arrowLarge : customPaths.arrowSmall
)
.attr('stroke', color)
.attr('fill', color);
}
And use it like so:
makeDefs(defs, 'red', 'DRAFT');
And then I add the markers to my lines with:
// d3 code to draw the lines etc
line.attr(
'marker-start',
(d) =>
`url(
#arrow-start-${d.color}-${d.status}
)`
);
This all works great, my arrows have lines. But my current setup feels burdensome and clunky. I have about 20 colors and 3 statuses. With my current setup that would be 60 different:
makeDefs(defs, 'one-of-20-colors', 'one-of-3-statues');
My understanding of markers is that they can inherit color using the currentcolor attribute. Currently my <defs> sit up top underneath my main <svg> so any color inherited is inherited directly from that top level svg which is not what I want. The issue in my case is my <line> elements are the elements who's color I want to inherit, but according to the MDN docs <line>s cannot have <defs> as children, thus leaving me with the only option, of defining all my <defs> up front all at once.
Is there a trick or some attribute I'm missing here?
Any way to pass color to my marker when doing:
line.attr(
'marker-start',
(d) =>
`url(
#arrow-start-${d.color}-${d.status}
)`
);
?
For what is is worth, I'm currently wrapping all my <line>s in <g>. I suppose I could wrap them in <svg>s instead, and apply the fill and stroke there, and then define my <defs> per svg container? I tried this briefly and swapping the <g> for an <svg> broke a lot, but I'm not even sure if it would work, or be better for that matter.
I would like to draw the family of circles using gnuplot svg terminal.
Svg has a command circle for this. All the images which I have found use svg path not circle, but circle is better because it takes less size ( optimisation).
In the gnuplot source code I have not found any command for using svg circle
grep -nR "circle"
Is it possible to plot circle in gnuplot which gives circle not path svg command?
Point types 5 and 6 are implemented as
/* 5 circle */
"\t<circle id='gpPt5' stroke-width='%.3f' stroke='currentColor' cx='0' cy='0' r='1'/>\n"
/* 6 circle (disk) filled */
"\t<use xlink:href='#gpPt5' id='gpPt6' fill='currentColor' stroke='none'/>\n"
So in principle you could place a lot of points, changing the x,y position and size of each one. This would be cumbersome in practice because the units of "pointsize" have no connection to the axis coordinates of the plot. You would have to empirically determine a scale factor, and it would change if you changed anything about the plot size or layout.
Here's a proof-of-principle example:
unset border
unset tics
set size square
set angle degree
plot sample [t=0:720] '+' using ($1*cos($1)):($1*sin($1)):($1/100.) with points pt 6 ps var
well, a SVG file is basically a text file. So, every program which can do some calculations and export some text can in principle create such a SVG file.
One attempt... (to be optimized). Basically, I started from an Inkscape SVG file with a single circle to get the text around the actual circle.
Now gnuplot generates this "Header" and "Footer" and a sequence of circles.
Code:
### generate svg output
reset session
Header = \
'<?xml version="1.0" encoding="UTF-8"?> '."\n".\
'<!-- Created with Inkscape (http://www.inkscape.org/) --> '."\n".\
'<svg width="200mm" height="200mm" version="1.1" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> '."\n".\
' <metadata> '."\n".\
' <rdf:RDF> '."\n".\
' <cc:Work rdf:about=""> '."\n".\
' <dc:format>image/svg+xml</dc:format> '."\n".\
' <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/> '."\n".\
' <dc:title/> '."\n".\
' </cc:Work> '."\n".\
' </rdf:RDF> '."\n".\
' </metadata> '
CircleTemplate = \
' <circle cx="'.'%g'.'" cy="'.'%g'.'" r="'.'%g'.'" fill="none" stroke="#f00" stroke-linecap="round" stroke-width=".1" style="paint-order:stroke markers fill"/> '
Footer = ' </svg> '
set print "Circles.svg"
print Header
Max = 90.
Factor1 = 12.
Factor2 = 2.5
do for [i=5:Max] {
print sprintf(CircleTemplate,100+Factor2*i/Max*cos(i*pi/Max*Factor1),100+Factor2*i/Max*sin(i*pi/Max*Factor1),i)
}
print Footer
set print
### end of code
Result:
This is not possible. I made a feature request.
I'am working with a svg file to get the path of a triangles in a triangulated image using Delaunay triangulation .the paths are represented as below:
<path
fill="rgba(148,148,147,255)"
stroke="rgba(0,0,0,255)"
d="M410,224 L411,227 L410,227 L410,224"
/>
<path
fill="rgba(232,232,232,255)"
stroke="rgba(0,0,0,255)"
d="M179,108 L180,107 L183,113 L179,108"
/>
I want to define a color for each 2 values, i mean for example the segment M179,108 L180,107 in blue and l183,113 L179,108 in red.How can I do that ?
Thank you in advance.
I get an XML or JSON with paths only, and I need to recreate the SVG image.
I create an empty
<svg xmlns='http://www.w3.org/2000/svg' version='1.1'></svg>,
I add a <g transform="scale(1 -1)" fill='#aaa' stroke='black' stroke-width='5' ></g> in it, and then in this element I add all of the paths in it (e.g. <path d= ... />).
In the end I get a SVG image, but because I haven't set the viewBox attribute in the SVG element the image isn't properly displayed - when I open it in browser, a part of it is displayed full size.
Can the viewBox be calculated from the values from the paths?
Thank you!
Similar to Martin Spa's answer, but a better way to do get the max viewport area is using the getBBox function:
var clientrect = path.getBBox();
var viewBox = clientrect.x+' '+clientrect.y+' '+clientrect.width+' '+clientrect.height;
You can then set the viewbox to these co-ordinates.
n.b. i think you can change the viewbox of an svg after it's rendered so you may have to re-render the svg.
OK so I solved it the following way:
removed all letters from the paths string and made an array out of it with
var values = pathValue.split('L').join(' ').split('M').join(' ').split('z').join('').split(' ');
found max and min from those values:
var max = Math.max.apply( Math, values );
var min = Math.min.apply( Math, values );
set the viewBox:
viewBox = max min max max
This worked in my case excellent. Hope that it will be helpful to someone else too.
I want to add text to the centre of a path and align it horizontally, NOT align along the path.
I have the following text running along the path at the centre, but I want to display it so that it is horizontal, no matter what angle the path is heading.
<text text-anchor="middle">
<textPath xlink:href="#SomePath" startOffset="50%">Some Text</textPath>
</text>
If I understand correctly you are after each individual letter being straight (i.e. pointing North) but following the path. Something like this:
Looking at the current SVG standard this does not seem to be possible.
For horizontal text layout flows, the
reference orientation for a given
glyph is the vector that starts at the
intersection point on the path to
which the glyph is attached and which
points in the direction 90 degrees
counter-clockwise from the angle of
the curve at the intersection point.
The image above is generated from SVG but this was achieved (as you can see from the imperfections) by applying individual kerning (rotation) to each letter by applying the rotate attribute:
<text id="text2897">
<textPath xlink:href="#path2890" id="textPath3304">
<tspan
rotate="69.238731 53.737518 40.30315 24.801943 358.96658 358.96658 4.1336575 357.93317 351.73267 351.73267 351.73267 348.63242 343.46533 343.46533 346.56558 347.599 347.599 347.599 347.599 347.599 347.599 346.56558 345.53217 344.49875 343.46533"
id="tspan2899">
Some sample text for path
</tspan>
</textPath>
</text>
You can calculate the necessary adjustments in rotation in script quite easily:
var tp = document.getElementById("textpath");
var rotate = "";
for(var i = 0; i < tp.getNumberOfChars(); i++)
{
rotate += -tp.getRotationOfChar(i) + " ";
}
tp.setAttribute("rotate", rotate);