Re: Thoughts about variable-width strokes in SVG2

On Mon, May 6, 2013 at 8:12 PM, Brian Birtles <bbirtles@mozilla.com> wrote:

> Hi,
>
> We have previously resolved to add variable-width stroke to SVG2 and I
> thought I'd contribute some suggestions about how to spec it.
>
> There are a number of ways of representing variable-width stroke so I've
> tried to narrow down the options down based on a particular use case I have.
>
> Some of the questions to address:
>
> 1) How should we position the widths?
>
>   Some possibilities:
>
>   A) Associate stroke widths with percentage offsets along the path.
> (Where offsets are omitted, widths are evenly distributed between those
> widths which have an assigned offset based on path length.)
>

I think this makes the most sense.


>
>   B) Associate stroke widths with absolute offsets along the path
> (adjusted by pathLength). (Where offsets are omitted, the behavior is as
> with (A).) Setting pathLength would basically make this the same as (A).
>
>   C) Associate stroke widths with the points in the path (i.e. you don't
> specify any offsets but just treat them as parallel arrays)
>
>   When the number of widths doesn't match the number of points in the path
> you'd spread the widths out along the path based on *index*. Basically, you
> initially assume the points are evenly spaced and distribute the widths
> along the path. For example, if you had three widths, the first width would
> specify the width at the start point, the second width would specify the
> width at the median point in the path (or mid-way between the two middle
> points if you have an even number of points) and the last width would
> specify the width at the end of the path.
>
> 2) Should the widths be absolute lengths or magnification factors?
>

They should be magnification so you can apply different strokewidths


>
> 3) Should the widths be a single symmetrical value applied to both sides
> of the path or allow it to be asymmetrical?
>

We shouldn't make it assymmetrical (for now). Otherwise the author will
have to figure out if there's an inside/outside of a path


>
> 4) Should the widths be allowed to be negative? (This mostly only makes
> sense if you allow asymmetrical values)
>

No.


>
>
> The use case I have: a pointer-event based drawing app.
>
> Relevant features:
>
> * Paths are built up in response to pointer events.
> * Initially points are just tacked only a <polyline> but then when the
> interaction completes (touchend/mouseup etc.) it is converted to a <path>
> by applying some smoothing.
> * The smoothing may remove points.
> * The smoothing may change the length of the path due to the values chosen
> for the cubic Bezier control points.
> * The radius of the pointer event (or pressure if we can get some reliable
> readings) is used to control the stroke width at that point.
> * The user can erase parts of the path using pointer events. We detect
> intersections and subdivide the path accordingly. Typically we just remove
> points and create a new subpath (we may add points but generally the points
> are so close together its not necessary).
>
>
> Regarding question 1 (how to position the widths), option (A) is
> problematic. Every time you add a point to the <polyline> you would have to
> adjust all the other stroke width offsets since they are based on a
> percentage of the path length and the path length has changed. That's
> clumsy and slow (and speed really matters for this app on mobile).
>

How common would that be?
Even so, you have to stroke again if you add a point so the calculation
needs to be redone anyway.


>
> Option (B) might be do-able. You'd have to calculate the pathLength each
> time you got a point and unfortunately the pathLength member is specified
> on SVGPathElement, but not SVGPolylineElement. You'd probably do it with
> script by building up a cumulative path length each time you added a point.
>
> Option (C) is the most natural fit for this application: you get a pointer
> event with a co-ordinate and a radius/pressure reading at the same time.
> You simply append the coordinate to the list of points and the appropriate
> stroke width to the list of widths. Easy and fast (except that the UA has
> to re-parse the entire stroke-widths property, but that's a problem with
> all approaches).
>
> How about smoothing and erasing?
>
> If, as a part of smoothing/erasing you remove the point at index 37, what
> you probably want is to remove the stroke-width at that point. Obviously
> option (C) is easiest for this since it's trivial to remove the
> stroke-width at the same offset.
>
> If, as a part of smoothing, you add bezier control points that make the
> path length longer, again, option (C) is probably going to give you the
> most intuitive results since the stroke widths don't shift in position.
>
> If, as a part of erasing you subdivide a long segment, add points and
> create a new subpath, it gets more complicated. For (A) and (B) do
> percentages apply to the path or to each sub-path? Presumably it is to the
> path as a whole. To stop other widths from moving around now that the
> pathLength has changed you'd probably have to adjust them all accordingly.
> For (C) you'd probably just interpolate adjacent stroke width values and
> apply them to the newly created points.
>
>
> Regarding question 2, this application could use either absolute stroke
> widths or magnification factors.
>
> For pressure, magnification factors are probably more useful. For example,
> you set the stroke-width to '15px' based on the drawing tool chosen, then,
> based on the pressure you assign stroke-widths: 0.8, 1.1 etc.
>
> If you're adjusting the stroke width based on the radius of the
> pointer-event, an absolute stroke width may make more sense.
>
> Overall, my hunch is magnification factors would be best since it allows
> you to fatten/thin the whole path easily, but that's just a guess. It's
> probably possible to support both if, for example, we allowed each
> positioned stroke width to be specified as <percentage>|<length> like we
> already do for stroke-width.
>
>
> Regarding question 3, this application does not need asymmetrical stroke
> widths.
>
>
> Regarding question 4, this application does not need negative stroke
> widths.
>
>
> Of course this is not the only application but it's a real-world one (and
> I suspect a common one) that would benefit from variable stroke width right
> now.
>
> I'm less interested in guessing about other applications although I'd be
> prepared to concede that using stroke-widths to taper off the end of a long
> stroke *might* be common. For that case we could either say:
>
> - Deal with it later using some as-yet unspecified addition to
> variable-width stroking
> - Deal with it now using variable-width stroking by allowing an optional
> (index-based?) offset
> - Deal with it now or later by another means such as an extension of
> stroke-linecap.
>
>
> In summary:
> * For drawing-style apps, variable stroke-width is easiest to use when the
> individual widths are tied to points in the path.
> * Some work is probably required to work out the best algorithm for lining
> up the widths when the number of widths differs from the number of points
> in the path.
> * The ability to fix a given width to a particular offset *might* be
> useful for tapering etc.
> * Both absolute stroke widths and magnification factors would be
> acceptable.
> * Asymmetrical stroke widths may not be necessary.
> * Negative stroke widths may not be necessary.
>
> Best regards,
>
> Brian
>
>

Received on Tuesday, 7 May 2013 04:00:29 UTC