1

I'm working on reimplementing a PHP/GD app in client-side SVG. My initial plan is to use Snap or Raphael for that. One of the features of the original app is drawing "fat" curved arrows, for which I need the outline. This is how the original looked:

Original PHP and GD curved arrows

Originally in PHP, I wrote my own catmull-rom function and calculated a central 'spine' curve between the ends and control points, then figured out a normal vector at each spine point to get the points either side of that at the distance I wanted for my fat line. That worked OK, but it's fairly labour-intensive, and it does strange things on tight turns.

Initially, I rewrote my php curve calculation in javascript, and generated large lists of straight line-segments. That works, but it doesn't remain smooth as you zoom (obviously enough).

Point-by-point calculated catmull-rom splines

So for the next javascript version, I started with an SVG path using the R segment type to get my catmull-rom central curve, and I can get the angle (and therefore normal) at any point based on the distance along the curve. But I wanted to try and be more efficient and let SVG calculate the outline curves, by producing only the control points for two new C-R splines passing through control points offset by the normal from the original control points (green in image).

SVG spline construction with some normal points drawn on. Green dots are control points

But - how can I get the angle of the curve (or the tangent vector) at a point on the curve (not a length along it)? I know the coordinates (it's the C-R control point), just not the angle. There doesn't appear to be a method on SVGPathElement to actually retrieve points arbitrarily, (or say, find the nearest point on the path to a point).

Alternatively, how else can I get the same outline? I'm new to SVG, so if there's a way to just let it draw a line with a strokeWidth of 20 (or whatever the width is) and get the outline of that in a modifiable form (I need to add the outline of the arrowhead, and also in some cases run text along the outside edge of the arrow), then that would also work!

Update: The arrowhead shape is proportional to the width of the line, and the proportions can be adjusted (somewhat) by the user. The width is also specified by the user. I think this makes using markers a problem, but happy to be proved wrong. Some of these arrows will have no fill (i.e. transparent), just outline.

Extra thought: is there some property of parametric Catmull-Rom splines, such that the control point is always at t=0.5 (for example), so that I can figure out the length to the control point and use the Snap methods to get the angle? [answer - yes, it's t=1.0, but that doesn't help with the length]

AnotherHowie
  • 818
  • 1
  • 11
  • 28
  • Just wondering, why do you need the outline of the path ? Ie if its something custom that can't be done with a single path and stroke-width, why not overlay two paths over each other with different stroke-widths or something ? Not even sure why it can't be done with one path ? – Ian Jul 22 '15 at 09:05
  • Example... http://jsfiddle.net/55d1rqdu/1/ – Ian Jul 22 '15 at 09:24
  • Well, I was thinking I'd need it to get a clean 1px outline around the shape... I haven't actually tried strokeWidth:20 and strokewidth:19 though. I suspect this will be a little uneven, especially if I use markers for the arrowheads instead of adding on the points manually (which is how the GD version does it). I'll give it a try. It'd be nice to let SVG do all the heavy lifting. – AnotherHowie Jul 22 '15 at 09:56
  • And then I remembered! :-) One of the options is to have an unfilled arrow. So that would need to be the outline only. Although perhaps that could be done with masks, too. – AnotherHowie Jul 22 '15 at 09:58
  • Solution to [calculate tangent](http://stackoverflow.com/questions/16278062/putting-an-arrow-marker-at-specific-point-on-a-path-when-using-the-d3-javascri/16351289#16351289), written in d3.js – Alvin K. Jul 22 '15 at 18:45
  • Yep, I already know how to calculate a tangent given the points either side - the question is: what length should you feed into getPointAtLength() to get the point at a given (x,y) - my known control point? I did look briefly at making a very short path through (x,y) and using Snap.path.intersection(), but that doesn't return length or angle. It's not clear what it does return, honestly - t-value? order number? – AnotherHowie Jul 22 '15 at 18:59

1 Answers1

0

Yes, in Snap.svg you can get the angle at a given point by running:

var pt = path.getPointAtLength(x,y);
console.log("Angle at "+[x,y]+":" + pt.alpha);

the return value has a property called "alpha", that is the angle at the given point.