1

In plain svg, the <line> tag seems to do that just fine, e.g.

<g>
    <line class="link" x1="180" y1="280" x2="560" y2="280"></line>
</g>
<g>
    <circle class="node" cx="180" cy="280" id="n1" r="18"></circle>
    <circle class="node" cx="560" cy="280" id="n2" r="18"></circle>
</g>

Here, the line element draws a line connecting the two defined nodes.

In RaphaelJS 2, is there a method for doing this? It seems like the most likely one is path, but when I try:

var paper = Raphael(document.getElementById("raphael-test"), 600, 600);
var c1 = paper.circle(180, 280, 18);
var c2 = paper.circle(560, 280, 18);
var edge = paper.path("M180,280L560,280");

the line extends into both circles, reaching the center of the circle. Visually I would like the line to just touch the line of circle. Of course, I can just work out the geometry and subtract the radius of the circle for each end given the pair coordinates. But I wonder if some method is already available from RaphaelJS 2, as this seems to be a very common functionality.

MLister
  • 10,022
  • 18
  • 64
  • 92

1 Answers1

4

No. While it might seem like common functionality to you, this is actually a very bespoke usage for an SVG abstraction. If Raphael were to support something like this then you can imagine that feature requests would extend to things like drawing between arbitrary shapes without overlap.

However, Raphael can help you with the computation as it is capable of computing path intersections. This works when you have a path string to represent your geometry.

http://raphaeljs.com/reference.html#Raphael.pathIntersection

See: http://jsfiddle.net/sDNMv/

// Computes a path string for a circle
Raphael.fn.circlePath = function(x , y, r) {      
  return "M" + x + "," + (y-r) + "A"+r+","+r+",0,1,1,"+(x - 0.1)+","+(y-r)+" z";
} 

// Computes a path string for a line
Raphael.fn.linePath = function(x1, y1, x2, y2) {
    return "M" + x1 + "," + y1 + "L" + x2 + "," + y2;
}

var x1 = 180,
    y1 = 280,
    r1 = 18,

    x2 = 400,
    y2 = 280,
    r2 = 18,

    paper = Raphael(document.getElementById("raphael-test"), 600, 600),
    c1 = paper.circle(x1, y1, r1),
    c2 = paper.circle(x2, y2, r2),

    // Compute the path strings
    c1path = paper.circlePath(x1, y1, r1),
    c2path = paper.circlePath(x2, y2, r2),
    linePath = paper.linePath(x1, y1, x2, y2),

    // Get the path intersections
    // In this case we are guaranteed 1 intersection, but you could find any intersection of interest
    c1i = Raphael.pathIntersection(linePath, c1path)[0],
    c2i = Raphael.pathIntersection(linePath, c2path)[0],


    line = paper.path(paper.linePath(c1i.x, c1i.y, c2i.x, c2i.y));​

You could also consider using getPointAtLength

http://raphaeljs.com/reference.html#Raphael.getPointAtLength

Since for circles, the points of intersection are the points on the line between them at length r and (distance between - r)

Matt Esch
  • 22,661
  • 8
  • 53
  • 51
  • Thank you for this useful answer. I find the circlePath function very useful. Please permit me to use it verbatim in my work. – Ashraf Sabry Dec 25 '12 at 09:04