Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Create svg arcs between two points

Tags:

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:

  1. Everything should be symmetrical to to the imaginary line that connects the two points.
  2. From 1, it's obvious that if the number of connections is:

    • odd: we do not display the straight line
    • even: we display the straight line
  3. There should be a value k which defines the distance between two connections between same points.

  4. 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)
  • Last two numbers are also clear. The path ends in 300,100
  • I also saw that if I put 0 instead of 20, I obtain a straight line.
  • If I replace 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.

like image 705
Ionică Bizău Avatar asked Aug 04 '15 08:08

Ionică Bizău


People also ask

How do you draw a curved line in SVG?

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).

How path works SVG?

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.)

What is the path element?

The <path> element is used to define a path. The following commands are available for path data: M = moveto. L = lineto.


2 Answers

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>
like image 177
r3mainer Avatar answered Oct 15 '22 02:10

r3mainer


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 } 
like image 25
Peter O. Avatar answered Oct 15 '22 03:10

Peter O.