- From: Rik Cabanier <cabanier@gmail.com>
- Date: Mon, 6 May 2013 21:00:02 -0700
- To: Brian Birtles <bbirtles@mozilla.com>
- Cc: www-svg <www-svg@w3.org>
- Message-ID: <CAGN7qDD3KbOa=sKJK+2e7MOgit0V37FaATfqCrppCrQUY-9sBA@mail.gmail.com>
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