[css-shapes-2] `curve to` keyword `using` seems a bit off
We adopted https://github.com/w3c/csswg-drafts/pull/9797 which allowed reordering and switched via to using but we typically use prepositions, not verbs, for these types of keywords and also using is a bit vague in any case. Opening this issue to look for alternatives.
My opening bid is around. :)
per, with
See https://github.com/w3c/csswg-drafts/issues/10644#issuecomment-2261027738 Perhaps we can avoid this keyword at all and use ordering, this would also allow us to specify positions for curve control points without breaking relative positions.
towards?
towards?
per / with are more accurate for describing "control points"... though as (per) my previous comment we might be able to get rid of this keyword altogether.
@noamr Ordering alone with a long sequence of length values is hard to understand (this is one of the problems with the existing SVG paths syntax), so I think having a keyword here is helpful to signal the start of the control points.
Also the control points should have some kind of separator themselves, as a sequence of 4 values that are actually two pairs is confusing to parse (as a human).
@noamr Ordering alone with a long sequence of length values is hard to understand (this is one of the problems with the existing SVG paths syntax), so I think having a keyword here is helpful to signal the start of the control points.
Also the control points should have some kind of separator themselves, as a sequence of 4 values that are actually two pairs is confusing to parse (as a human).
Yea, using / as a separator in the example
@fantasai the thing with any of these words is that they collide with the by/to that represents absolute vs relative. So using order with a separator like / instead and still allowing by/to seemed the least verbose.
See also https://github.com/w3c/csswg-drafts/issues/10666
Seems like in the other discussion with kept being what both myself and @smfr defaulted to. Perhaps this is the right word?
Proposing the following to finish up the bezier curve saga:
withis the word- We use
/to separate between the two control points in a cubic bezier, no need for it in quadratic/smooth curves.
@smfr @tabatkins @fantasai what say you?
Could you type out some examples of the various absolute/relative versions? I think this matches what I suggested at https://github.com/w3c/csswg-drafts/issues/10644#issuecomment-2375464531
Okay, so curve to 10px 10px with 20px 20 / 0px 0px?
And yeah, that seems to match @smfr's suggestion in #10644, so this sounds good.
Okay, so
curve to 10px 10px with 20px 20 / 0px 0px?
Exactly
And yeah, that seems to match @smfr's suggestion in #10644, so this sounds good.
I stole this proposal from that comment (or more like, formalized that comment into a proposed syntax).
Could you type out some examples of the various absolute/relative versions? I think this matches what I suggested at #10644 (comment)
A few examples, I think they look alright:
shape(
from 10px 50%,
curve by 20px 20px with 10px 30px,
curve by 20% 30% with 10% 10% / 50px 1rem,
curve to 100% 100% with 20px 30px from start,
curve by 20px -15px with 50% 50% from origin / 30px 30px,
smooth curve to 50% calc(30px + 10%) with 15x 15px from start,
smooth curve by 100px 10vh
)```
What is origin here? The reference box top left, or the value specified in the from command?
Is it an error to use segh and segv with from origin?
What is
originhere? The reference box top left, or the value specified in thefromcommand?
The reference box top left, it's equivalent to the origin of to commands.
Is it an error to use
seghandsegvwithfrom origin?
We didn't resolve on segh and segv yet.
We didn't resolve on segh and segv yet.
But assuming we do, I don't see anything that would make them problematic to use in a from origin point. The from * (or by/to) keywords just represent a translation.
(Tangent, but: they need to be segw and segh, to match all the viewport/etc units.)
Are we assuming that absolute values also support <position>, so that these are valid:
curve to bottom right with 20px 30px from start...
curve to bottom right with center right / center..
and these are invalid:
curve by bottom right with 20px 20px...
curve by 20px 20px with center left from start...
Here's a stab at the grammar:
shape() = shape( <'fill-rule'>? from <coordinate-pair>, <shape-command>#)
<shape-command> = <move-command> | <line-command> | <hv-line-command> |
<curve-command> | <smooth-command> | <arc-command> | close
<coordinate-pair> = <length-percentage>{2}
<relative-control-point> = [<coordinate-pair> [from start | from end]?]
<to-control-point> = [<position> | <relative-control-point>]
<by-to> = by | to
<move-command> = move [to <position>] | [by <coordinate-pair>]
<line-command> = line [to <position>] | [by <coordinate-pair>]
<hv-line-command> = [hline | vline] <by-to> <length-percentage>
<curve-command> = curve [to <position> with <to-control-point> [/ <to-control-point>]?]
| [by <coordinate-pair> with <relative-control-point> [/ <relative-control-point>]?]
<smooth-command> = smooth [to <position> [with <to-control-point>]?]
| [by <coordinate-pair> [with <relative-control-point>]?]
<arc-command> = arc [to <position>] | [by <coordinate-pair>] of <length-percentage>{1,2} [<arc-sweep>? || <arc-size>? || [rotate <angle>]?]
Notes:
<position>is only allowed in the "to" argument, and for "to" control pointsfrom startandfrom endare allowed in both "to" and "by" commands- the destination point comes before the control points, using the
withkeyword for control points - two control points are separated with a slash
- In the
arccommand, the radius comes after the destination point (seems better than arbitrary order, but I could be persuaded). - There's no way to use positions in
hlineorvlinebut maybe we should allowvline to bottom? Not sure how to write that grammar.
Here's a stab at the grammar:
shape() = shape( <'fill-rule'>? from <coordinate-pair>, <shape-command>#) <shape-command> = <move-command> | <line-command> | <hv-line-command> | <curve-command> | <smooth-command> | <arc-command> | close <coordinate-pair> = <length-percentage>{2} <relative-control-point> = [<coordinate-pair> [from start | from end]?] <to-control-point> = [<position> | <relative-control-point>] <by-to> = by | to <move-command> = move [to <position>] | [by <coordinate-pair>] <line-command> = line [to <position>] | [by <coordinate-pair>] <hv-line-command> = [hline | vline] <by-to> <length-percentage> <curve-command> = curve [to <position> with <to-control-point> [/ <to-control-point>]?] | [by <coordinate-pair> with <relative-control-point> [/ <relative-control-point>]?] <smooth-command> = smooth [to <position> [with <to-control-point>]?] | [by <coordinate-pair> [with <relative-control-point>]?] <arc-command> = arc [to <position>] | [by <coordinate-pair>] of <length-percentage>{1,2} [<arc-sweep>? || <arc-size>? || [rotate <angle>]?]Notes:
<position>is only allowed in the "to" argument, and for "to" control pointsfrom startandfrom endare allowed in both "to" and "by" commands- the destination point comes before the control points, using the
withkeyword for control points- two control points are separated with a slash
- In the
arccommand, the radius comes after the destination point (seems better than arbitrary order, but I could be persuaded).- There's no way to use positions in
hlineorvlinebut maybe we should allowvline to bottom? Not sure how to write that grammar.
Mostly looks good! I'd also add from origin which makes control points in a relative curve absolute.
Mostly looks good! I'd also add from origin which makes control points in a relative curve absolute.
Should we allow <position> from origin in <relative-control-point> then? This would make with top left from origin valid, even if it reads oddly.
Similarly, should it be valid (but a no-op) to specify from origin in an an absolute control point?
Mostly looks good! I'd also add from origin which makes control points in a relative curve absolute.
Should we allow
<position> from originin<relative-control-point>then? This would makewith top left from originvalid, even if it reads oddly.Similarly, should it be valid (but a no-op) to specify
from originin an an absolute control point?
I don't see the point TBH, this only makes sense for relative control points. [<coordinate-pair> [from (start|end|origin)]. When you have a <position> it's already absolute.
Adjusted grammar (just adding from origin):
shape() = shape( <'fill-rule'>? from <coordinate-pair>, <shape-command>#)
<shape-command> = <move-command> | <line-command> | <hv-line-command> |
<curve-command> | <smooth-command> | <arc-command> | close
<coordinate-pair> = <length-percentage>{2}
<relative-control-point> = [<coordinate-pair> [from [start | end | origin]]?]
<to-control-point> = [<position> | <relative-control-point>]
<by-to> = by | to
<move-command> = move [to <position>] | [by <coordinate-pair>]
<line-command> = line [to <position>] | [by <coordinate-pair>]
<hv-line-command> = [hline | vline] <by-to> <length-percentage>
<curve-command> = curve [to <position> with <to-control-point> [/ <to-control-point>]?]
| [by <coordinate-pair> with <relative-control-point> [/ <relative-control-point>]?]
<smooth-command> = smooth [to <position> [with <to-control-point>]?]
| [by <coordinate-pair> [with <relative-control-point>]?]
<arc-command> = arc [to <position>] | [by <coordinate-pair>] of <length-percentage>{1,2} [<arc-sweep>? || <arc-size>? || [rotate <angle>]?]
@astearns WDYT about resolving on the above grammar asynchronously? Not many people get involved in the shape() discussion on the calls anyway.
We need to specify how interpolation works between curve and smooth segments with different from start/from end/from origin values. Should we do the same we do for affinity, which is to say that they have to match? More specifically, if the from specifier is missing for a control point, we consider it to be from origin for an absolute "to" segment, and from start for a relative "by" segment. We then compare the "from" specifiers for each control point, and only allow blending if they match for all segments.
Also, earlier I suggested dropping the smooth command in favor of using auto for control points. I like this simplification because it eliminates a segment type, and makes the behavior of where smooth gets its control points a little less mysterious. I think if we want to do that, we have to do that now, or it will never happen.
Not many people get involved in the shape() discussion on the calls anyway.
I think that'll depend somewhat on who you get involved (e.g. @LeaVerou cares a lot about this stuff and @SebastianZ and @kizu pay a lot of attention to syntax); and how well you are able to convey your ideas when you intro the topic. It's definitely trickier to convey a complicated syntax, but if you break it down I think people will be able to follow. We were able to do that for L4 <position>, for example.
The grammar looks pretty good to me, fwiw. I think you're missing a grouping operator or two for the arc command? Also I'd recommend dropping the <by-to> production since it's only used once and the expansion is almost as short.
I think the reorderability question is maybe worth bringing up on a call, since it's a higher-level question. You'll have to walk a bit through the potentially-reorderable segments of the syntax and explain what that would mean. But afaict it wouldn't be ambiguous, and allowing reordering allows a different mental model while you're writing the command, i.e. I can then write the parameters in the order that the line "passes through" them instead of jumping to the destination of each segment and then walking back to describe the nuances of how we got there.
Not many people get involved in the shape() discussion on the calls anyway.
I think that'll depend somewhat on who you get involved (e.g. @LeaVerou cares a lot about this stuff and @SebastianZ and @kizu pay a lot of attention to syntax); and how well you are able to convey your ideas when you intro the topic. It's definitely trickier to convey a complicated syntax, but if you break it down I think people will be able to follow. We were able to do that for L4
<position>, for example.
OK, thanks for the feedback. I'll give that a go!
@astearns I removed the async resolution label, thanks for adding it. Will try to convey this on a call like @fantasai suggested.
Not many people get involved in the shape() discussion on the calls anyway.
I think that'll depend somewhat on who you get involved (e.g. @LeaVerou cares a lot about this stuff and @SebastianZ and @kizu pay a lot of attention to syntax);
Thank you for pointing me at this issue, @fantasai! While this issue initially was discussing one keyword, it seems to have turned into a more general discussion about shape()s grammar. Please let me know if I am wrong about that assumption and that discussion should happen in another issue! Though for now I'll provide my feedback on this here.
Disclaimer: So far I wasn't much involved regarding shape(), so I might be wrong about some assumptions.
Though looking at the grammar, I think the bracketing between the to and by parts and the spacing are a bit off and the question mark modifiers are unnecessary in the arc command. Also, the wording regarding the control points should be consistent.
Besides that general feedback on the syntax, I have to say it wasn't clear at all from a first reading that the with keyword is meant to indicate the control point(s). And the meaning of the start, end, and origin keywords is also not clear to me yet.
And it's also confusing that <to-control-point> can be a <relative-control-point>, as the to keyword always indicates an absolute point, right?
Also, is it really valid to have multple close commands an that they can appear in the middle of the command list and not just at the end?
- There's no way to use positions in hline or vline but maybe we should allow vline to bottom ? Not sure how to write that grammar.
Questions is, what should be allowed? As I understand it, all horizontal keywords for hline and all vertical ones for `vline.
Also I'd recommend dropping the
<by-to>production since it's only used once and the expansion is almost as short.
In addition to that, I'd split the to and by productions. While they can syntactically be combined, they have different semantic meaning. So it makes sense to separate them.
Considering all the above, my syntax approach is
shape() = shape( <'fill-rule'>? from <coordinate-pair>, <shape-command># )
<shape-command> = <move-command> | <line-command> | <horizontal-line-command> |
<vertical-line-command> | <curve-command> | <smooth-command> | <arc-command> | close
<coordinate-pair> = <length-percentage>{2}
<relative-control-point> = <coordinate-pair> [ from [ start | end | origin ] ]?
<to-control-point> = [ <position> | <relative-control-point> ]
<move-command> = move [ to <position> | by <coordinate-pair> ]
<line-command> = line [ to <position> | by <coordinate-pair> ]
<horizontal-line-command> = hline [ to [ <length-percentage> | left | center | right | x-start | x-end ] | by <length-percentage> ]
<vertical-line-command> = vline [ to [ <length-percentage> | top | center | bottom | y-start | y-end ] | by <length-percentage> ]
<curve-command> = curve [ to <position> with <to-control-point> [ / <to-control-point> ]?
| by <coordinate-pair> with <relative-control-point> [ / <relative-control-point> ]? ]
<smooth-command> = smooth [ to <position> [ with <to-control-point> ]?
| by <coordinate-pair> [ with <relative-control-point> ]? ]
<arc-command> = arc [ to <position> | by <coordinate-pair> ]
of <length-percentage>{1,2} [ <arc-sweep> || <arc-size> || rotate <angle> ]
Sebastian
Thank you for cleaning up my bad grammar!
I have to say it wasn't clear at all from a first reading that the
withkeyword is meant to indicate the control point(s).
We've been through via, using and with; it's hard to find a good name.
And the meaning of the
start,end, andoriginkeywords is also not clear to me yet.
These describe how to interpret the <coordinate-pair>. start means that the coordinates are offsets from the segment start, end means that the coordinates are offsets from the segment end. And origin means that they are relative to the origin of the reference box. These options allow for "point-anchored" control points, which is not possible in SVG, which only allows offsets from the origin (in absolute segments) and from the segment start (in relative segments).
And it's also confusing that
can be a , as the to keyword always indicates an absolute point, right?
The from start/end qualifier on a control point allows the author to specify a start/end-relative offset even for a <to-control-point>, which is an enhancement over SVG paths.
Also, is it really valid to have multiple close commands an that they can appear in the middle of the command list and not just at the end
Yes, a shape can consist of multiple sub-paths.
For the arc syntax, [ <arc-sweep> || <arc-size> || rotate <angle> ] is wrong; all of these are optional, which is unchanged from the existing draft.
Thanks @SebastianZ! With the arc changes, I think it should look like this:
shape() = shape( <'fill-rule'>? from <coordinate-pair>, <shape-command># )
<shape-command> = <move-command> | <line-command> | <horizontal-line-command> |
<vertical-line-command> | <curve-command> | <smooth-command> | <arc-command> | close
<coordinate-pair> = <length-percentage>{2}
<relative-control-point> = <coordinate-pair> [ from [ start | end | origin ] ]?
<to-control-point> = [ <position> | <relative-control-point> ]
<move-command> = move [ to <position> | by <coordinate-pair> ]
<line-command> = line [ to <position> | by <coordinate-pair> ]
<horizontal-line-command> = hline [ to [ <length-percentage> | left | center | right | x-start | x-end ] | by <length-percentage> ]
<vertical-line-command> = vline [ to [ <length-percentage> | top | center | bottom | y-start | y-end ] | by <length-percentage> ]
<curve-command> = curve [ to <position> with <to-control-point> [ / <to-control-point> ]?
| by <coordinate-pair> with <relative-control-point> [ / <relative-control-point> ]? ]
<smooth-command> = smooth [ to <position> [ with <to-control-point> ]?
| by <coordinate-pair> [ with <relative-control-point> ]? ]
<arc-command> = arc [ to <position> | by <coordinate-pair> ]
&& of <length-percentage>{1,2}
&& <arc-sweep>?
&& <arc-size>?
&& [rotate <angle>]?
]