- From: Amelia Bellamy-Royds <amelia.bellamy.royds@gmail.com>
- Date: Tue, 11 Nov 2014 17:17:59 -0700
- To: www-svg <www-svg@w3.org>
- Message-ID: <CAFDDJ7znOZxxqk3eAi8amusShsupo0w-oBdM6+Oay1JONYpaDw@mail.gmail.com>
[Note: I wrote this reply last week, but accidentally only sent it to Tab Atkins, not the SVG list, and didn't notice until he replied today. I no longer 100% agree with everything below, but we'll post the conversation in the order it happened for posterity! --ABR] Why not simplify it further: <marker-pattern> = [<marker-gap> <marker-ref> ]+ | [ <marker-ref> <marker-gap>]+ | none <marker-gap> = <length> | <percentage> <marker-ref> = none | <url> | child | <child-selector> In other words, you must alternate length measures and marker references in equal number but you can start with either a marker or a gap. If you start with a gap, the initial marker will be offset by that point; repeats will be the same whether you put the gap at the start or end of the sequence. An odd number of gaps confuses the repeat pattern. If an author wants to place multiple markers at the same point, just use a gap of zero. I don't really see a use case for `none` within a pattern, since you could instead use calc() to combine the two gaps into one. However, I don't see harm in it. It could potentially be useful in animating between two sequences. Implementers will have to factor in a "none" behaviour anyways, in case a marker reference is invalid. And it allows re-use of the <marker-ref> grammar defined for other properties. _______________________ After simplifying that much, I couldn't help complicating it up again... Looking through the previous discussion on this topic from 2012 [1], there seemed to be agreement that multiple independent marker patterns could be given as a comma-separated list, so you could create a psuedo-random pattern by having different repeat lengths for each pattern. Is there still interest in this? It would be useful in creating imperfect repeating brush mark effects. There was also discussion of defining patterns per path segment or for the path as a whole. I assume this was part of a move to do the same for stroke-dasharray, although I can't find the equivalent dasharray settings in the current draft specs. It would also be useful to add the stretch/compress options from `stroke-dashadjust`. *Side note: As currently defined, would stroke-dashadjust *always* apply per segment, not per path? That could result in weird effects on closed shapes (where you want an even number of dashes) that have many smooth curve segments of differing lengths. Maybe that's where a [segment|path] additional keyword option could go.* There would be no logical equivalent to the dashes or gaps options from stroke-dashadjust, since markers themselves would never stretch or compress. However, there would be a benefit to having a option to include an extra gap at the start/end of the path or segment, so as to avoid overlapping the pattern on start/end or vertex markers. For want of a better word I'm using "space" to means "add space on either end". The final grammar therefore would be <marker-pattern-list> = <marker-pattern># | none <marker-pattern> = [ [segment|path]? || [stretch|compress]? || space? ] <marker-pattern-sequence> <marker-pattern-sequence> = [<marker-gap> <marker-ref> ]+ | [ <marker-ref> <marker-gap>]+ <marker-gap> and <marker-ref> as above. "path" would be the default for the first keyword; if stretch/compress isn't specified, the lengths would be calculated precisely and the space keyword would have no effect. I haven't included a "none" keyword for the stretch/compress option, since that would cause confusion with "none" as the first value of a marker-pattern-sequence. None of this would alter the current definition of the shorthand marker property, since the slash would still identify the start of a pattern setting. Examples: A) Alternate two markers evenly along the path, offset from the start (Example 15 in the current draft): marker-pattern: 40 url(#m1) 40 url(#m2); B) Position a marker at every vertex and evenly spaced between, no closer than 20px from each other: marker-pattern: segment stretch url(#m1) 20px; marker-end: url(#m1); /* The pattern is a marker followed by a gap, which will be restarted on every segment, so there will always be a marker at the beginning of a segment. The pattern will be stretched to evenly cover the length of each segment so that other markers won't get too close to the next vertex. However, since the pattern ends in a gap, the marker-end property is set to mark the end of the final segment. */ C) Create an erratic pattern of the same 10px-wide brush mark repeating itself, overlapping along the path; ensure that at least some marks always fall exactly on the vertices: marker-pattern: segment compress 5px url(#brush), url(#brush) 7px, 9px url(#brush); /* The last two patterns would be evenly repeated along the entire path since the keywords apply per pattern. The 7px pattern would include the very start of the path, the 9px pattern would not. Neither would be forced to include the end of the path, but they might if the distance matched exactly. The 5px pattern would not include the start of each segment, but would be rounded off to include the end of every segment since "space" is not specified. */ D) Alternate a major marker with two instances of a minor marker. The space between consecutive minor markers is slightly smaller than the space on either side of the major marker. Adjust the entire pattern to cover the complete path an even number of times, but leave a space on either end. marker-pattern: stretch space path 15 url(#minor) 20 url(#major) 20 url(#minor); /* The total pattern length is 15+20+20 = 55 units; however, we also need to repeat the initial gap at the end of the path, so the pattern will be scaled such that scale*(55n + 15) exactly equals the path length, where n is Math.floor( (pathLength - 15)/55 ). */ Note that the "space" requirement would generate the exact same result if the pattern was defined in marker>gap order instead of gap>marker order; in this case, the extra space would be added at the beginning of the path: marker-pattern: stretch space path url(#minor) 20 url(#major) 20 url(#minor) 15; E) Draw a path-specific child marker (e.g., a highway number) overtop of a generic marker (e.g., the highway number sign), making sure to have at least one sign on every path segment but avoiding markers on vertices: marker-pattern: segment compress space 40 url(#highway-marker) 0 child; /* The total pattern length is 40 units; the repeated space is also 40; the compress requirement is calculated per segment such that scale*(40n + 40) equals the segment length, where n is Math.ceil( (segmentLength - 40)/40 ). Each pattern starts with an (approx.) 40 unit gap, then draws the first marker, then a zero-length "gap" and draw the second marker on top, leaving the final gap without a marker. */ Note that this assumes that for a single marker pattern, markers would be painted in the order they are given (highway sign before the child with the highway number). For a list of separate patterns, it would be best to use the top to bottom painting order used in CSS background images and the new multiple-fill syntax (i.e., the last pattern in the list would be painted first, at the bottom of the stack). In other words, the following pattern would look the same as the above, assuming that each marker is much smaller than the 40 unit gaps: marker-pattern: segment compress space 40 child, segment compress space 40 url(#highway-marker); _________________________ One final suggestion: A separate property, markerPatternUnits, equivalent to markerUnits, that would control whether lengths are proportional to strokeWidth (and therefore potentially proportional to the size of the markers) instead of being defined in user space. An equivalent property for stroke-dasharray could ensure that markers and dashes are synchronized. It would also allow dotted line patterns where the dots are always square (or any chosen aspect ratio) regardless of the stroke width. Most importantly, it *could* be defined such as to make it easy to implement the popular "drawing the shape" stroke-dasharray animation. Currently (SVG1.1), percentage values aren't clearly defined for markerUnits="strokeWidth"; implementations tested (Firefox and Chrome) seem to ignore the markerUnits setting and just use the userSpace definition of 100%. The SVG2 draft suggests that the definition of 100% for markerUnits="strokeWidth" should be inherited from the userSpace, but scaled proportional to strokeWidth. I'm not sure if this was an intentional change from current implementations. It would be very convenient if, instead, when markerUnits, markerPatternUnits, or strokeDasharrayUnits were set to strokeWidth, percentages would be relative to path length. If that is a backwards compatibility issue for markerUnits, it could only apply to the patterns which are measured along path length anyway. If you added the extra rule that for "segment" marker or dash patterns, 100% is the length of the segment, then you could define vertex markers, segment markers, corner dashes and so on just in terms of patterns. E.g., the stroke-dashcorner effect (Figure 9 in the draft specs) would be strokeDasharrayUnits: strokeWidth; stroke-width: 2px; stroke-dashadjust: segment; /* assuming a segment/path option added to the dashadjust syntax */ stroke-dasharray: 8 calc(100%-16) 8 0; /* 8 unit dash at the start and end of every segment, where 1unit=strokeWidth=2px */ The marker-segment example (Example 14/Figure 16) with circles on the vertices and x-marks at mid-points would be: markerPatternUnits: strokeWidth; marker-pattern: segment url(#Circle) 50% url(#Cross) 50%; The above proposal assumes that the xxxUnits attributes would be upgraded from regular XML attributes to CSS properties/presentation attributes. Which would be useful in many situations for many of these units (e.g., in filters it would be very nice to be able to specify primitiveUnits on individual filter elements). For markers, it would allow you the option to specify them on the path instead of on the marker, and have the style inherit. The camelCase vs hyphenated syntax is unfortunate, though. Not sure if it would be worth defining duplicate XML attribute names to make the CSS more consistent. [1]: http://lists.w3.org/Archives/Public/www-svg/2012Nov/0023.html (and subsequent) On 5 November 2014 16:32, Tab Atkins Jr. <jackalmage@gmail.com> wrote: > On Wed, Nov 5, 2014 at 3:29 PM, Tab Atkins Jr. <jackalmage@gmail.com> > wrote: > > Here's a grammar that expresses what we want better: > > > > <marker-gap>? <marker-ref-group> [ <marker-gap> <marker-ref-group> ]* > > <marker-gap>? > > <marker-gap> = <length> | <percentage> > > <marker-ref-group> = none | <marker-ref>+ > > Line-wrapping made this harder to read - the first two lines are the > marker-pattern grammar. > > ~TJ > >
Received on Wednesday, 12 November 2014 00:18:27 UTC