[svgwg] Add a method to return the path direction (tangent) angle at a given distance

AmeliaBR has just created a new issue for https://github.com/w3c/svgwg:

== Add a method to return the path direction (tangent) angle at a given distance ==
The [`SVGGeometryElement` interface](https://svgwg.org/svg2-draft/types.html#InterfaceSVGGeometryElement) defines utility methods for working with the geometry of shapes.  These were originally defined [in SVG 1.1](https://www.w3.org/TR/SVG11/paths.html#InterfaceSVGPathElement), but only for `<path>` elements.

One particularly useful method is `getPointAtLength()`, which can be used to implement motion-along-a-path in JavaScript, or to position symbols at even points along a path, polyfilling [pattern markers](https://svgwg.org/specs/markers/#RepeatingMarkers).

But to fully re-create either markers or motion on a path, you also need to know the [directionality (tangent angle)](https://svgwg.org/svg2-draft/paths.html#PathDirectionality) of the path at a given point.  Currently, this is not directly exposed via the API.  You need to fetch two different points and do some trigonometry to get an approximation of the angle.

For example, here's a function I used for scripted motion on a path with auto-rotation:

```js
function update(time) {
    var t = (time % dur)/dur, /* position in repeat cycle */
        distance, /* distance along the path for this bead */
        point,    /* SVGPoint for that distance */
        point2;   /* SVGPoint for a slightly different distance */
    for (var i=0; i<nBeads; i++) {
        distance = trackLength * ( (t + i/nBeads) % 1 );
        point = track.getPointAtLength(distance);
        point2 = track.getPointAtLength((distance+2)%trackLength);
        angle = Math.atan2( (point2.y - point.y), 
                           (point2.x - point.x) ); 
        beads[i].setAttribute("transform", 
            "translate(" + [point.x, point.y] + ")"
            + "rotate(" + angle*180/Math.PI + ")" ); 
    }
    requestAnimationFrame(update); 
}
```

The multiple `getPointAtLength()` calls are inefficient.  But furthermore the method is not exact: in tightly curved corners, you're going to get a lag in the rotation. It's certainly not going to correctly return the mid-point angle for markers at sharp corners.

The question came up in the discussion on https://github.com/w3c/svgwg/issues/333#issuecomment-318720395, so I thought it was worth making a separate issue to keep track.  Comments copied from there:

Paul (@BigBadaboom) wrote:

> While I think of it, it would be quite nice to have the rotation available to the front-end in some form (either the marker slope, or the motion slope, or both). Something like a `getSlopeAtLength()` method.

Frederick (@fsoder) replied:

> And to follow the tangent (sorry, couldn't resist!) from #333 (comment) about getSlopeAtLength(). If that's intended as a companion to SVGGeometryElement.getPointAtLength(), I don't think that would be much work to implement. (Slightly more efficient would be to add a method to return both the point and slope/angle/normal - or even one taking an array of lengths and return an array of [point, slope] tuples, or else we'll probably need to make sure there's a lookup cache to prevent the obvious risk for N^2 behavior...)

For naming, "get slope" probably works.  I'd used `getAngleAtLength()` as a hypothetical function name when leading in to the above JavaScript example in my book. (The context was: "Unfortunately, there is no `getAngleAtLength()` method for paths.  So we need to do a _little_ bit of math ourselves.")  Alternatively, the [text element interfaces](https://svgwg.org/svg2-draft/text.html#InterfaceSVGTextContentElement) use `getRotationOfChar()`, so `getRotationAtLength()` might be consistent with that.

I recognize the implementation usefulness of doing both calculations at once, but that would involve defining a new [geometry interface object](https://drafts.fxtf.org/geometry/) that is a DOMPoint + direction vector.  Which would probably be useful in other contexts, but we'd want to clearly think through what those contexts were in order to create a good general solution.  One option would be just to _extend_ the `DOMPoint` interface, and then return the enhanced point object when calling `getPointAtLength()`.






Please view or discuss this issue at https://github.com/w3c/svgwg/issues/338 using your GitHub account

Received on Friday, 28 July 2017 20:08:22 UTC