I want to connect two SVG points (e.g. the centers of two circles) using arcs. If there is only one connection, the line (<path>
) will be straight. If there are two connections, both will be rounded and will be symmetrical, this way:
So, in fact, there are few rules:
From 1, it's obvious that if the number of connections is:
There should be a value k
which defines the distance between two connections between same points.
The tangent that goes through the middle of the elliptical arc should be parallel with the straight line that connects the two points. And obviously, the middle of the line will be perpendicular to the tangent.
I'm struggling to get a formula to calculate the A
parameters in the <path>
element.
What I did until now is:
<path d="M100 100, A50,20 0 1,0 300,100" stroke="black" fill="transparent"/>
M100 100
is clear: that's the starting point (move to 100,100)300,100
0
instead of 20
, I obtain a straight line.1,0
with 1,1
, the path is flipped.What I don't know is how to calculate the A parameters. I read the docs, but the imagine is still unclear to me. How to calculate these values?
svg { width: 100%; height: 100%; position: absolute; }
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>JS Bin</title> </head> <body> <?xml version="1.0" standalone="no" ?> <svg version="1.1" xmlns="http://www.w3.org/2000/svg"> <!-- Connect A(100,100) with B(300, 100) --> <path d="M100 100, A50,0 0 1,0 300,100" stroke="black" fill="transparent" /> <path d="M100 100, A50,20 0 1,0 300,100" stroke="black" fill="transparent" /> <path d="M100 100, A50,20 0 1,1 300,100" stroke="black" fill="transparent" /> <path d="M100 100, A50,30 0 1,0 300,100" stroke="black" fill="transparent" /> <path d="M100 100, A50,30 0 1,1 300,100" stroke="black" fill="transparent" /> <!-- A(100, 100) B(300, 400) --> <path d="M100 100, A50,0 57 1,0 300,400" stroke="black" fill="transparent" /> <path d="M100 100, A50,20 57 1,0 300,400" stroke="black" fill="transparent" /> <path d="M100 100, A50,20 57 1,1 300,400" stroke="black" fill="transparent" /> </svg> </body> </html>
I'm using SVG.js to create the paths.
The other type of curved line that can be created using SVG is the arc, called with the A command. Arcs are sections of circles or ellipses. For a given x-radius and y-radius, there are two ellipses that can connect any two points (as long as they're within the radius of the circle).
A path is defined in SVG using the 'path' element. The basic shapes are all described in terms of what their equivalent path is, which is what their shape is as a path. (The equivalent path of a 'path' element is simply the path itself.)
The <path> element is used to define a path. The following commands are available for path data: M = moveto. L = lineto.
You're making life very difficult for yourself by requiring circular arcs.
If you use quadratic curves instead, then the geometry becomes very simple — just offset the central X coordinate by half the difference in Y coordinates, and vice versa.
function arc_links(dwg,x1,y1,x2,y2,n,k) { var cx = (x1+x2)/2; var cy = (y1+y2)/2; var dx = (x2-x1)/2; var dy = (y2-y1)/2; var i; for (i=0; i<n; i++) { if (i==(n-1)/2) { dwg.line(x1,y1,x2,y2).stroke({width:1}).fill('none'); } else { dd = Math.sqrt(dx*dx+dy*dy); ex = cx + dy/dd * k * (i-(n-1)/2); ey = cy - dx/dd * k * (i-(n-1)/2); dwg.path("M"+x1+" "+y1+"Q"+ex+" "+ey+" "+x2+" "+y2).stroke({width:1}).fill('none'); } } } function create_svg() { var draw = SVG('drawing').size(300, 300); arc_links(draw,50,50,250,50,2,40); arc_links(draw,250,50,250,250,3,40); arc_links(draw,250,250,50,250,4,40); arc_links(draw,50,250,50,50,5,40); draw.circle(50).move(25,25).fill('#fff').stroke({width:1}); draw.circle(50).move(225,25).fill('#fff').stroke({width:1}); draw.circle(50).move(225,225).fill('#fff').stroke({width:1}); draw.circle(50).move(25,225).fill('#fff').stroke({width:1}); } create_svg();
<script src="https://cdnjs.cloudflare.com/ajax/libs/svg.js/2.3.2/svg.min.js"></script> <div id="drawing"></div>
Here is a solution that uses arcs, as asked for, rather than quadratic curves.
// Internal function function connectInternal(x1,y1,x2,y2,con){ var dx=x2-x1 var dy=y2-y1 var dist=Math.sqrt(dx*dx+dy*dy) if(dist==0 || con==0){ return "M"+x1+","+y1+"L"+x2+","+y2 } var xRadius=dist*0.75 var yRadius=dist*0.3*(con*0.75) var normdx=dx/dist if(normdx<-1)normdx=-1 if(normdx>1)normdx=1 var angle=Math.acos(normdx)*180/Math.PI if(x1>x2){ angle=-angle } return "M"+x1+","+y1+"A"+xRadius+","+yRadius+","+ angle+",00"+x2+","+y2+ "M"+x1+","+y1+"A"+xRadius+","+yRadius+","+ angle+",01"+x2+","+y2 } // Returns an SVG path that represents // "n" connections between two points. function connect(x1,y1,x2,y2,n){ var ret="" var con=n if(con%2==1){ ret+=connectInternal(x1,y1,x2,y2,con) con-=1 } for(var i=2;i<=con;i+=2){ ret+=connectInternal(x1,y1,x2,y2,i) } return ret }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With