Skip to content

[css-shapes-2] Interpolation between shape() and path() needs more detail #10740

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
smfr opened this issue Aug 14, 2024 · 3 comments · Fixed by #10984
Closed

[css-shapes-2] Interpolation between shape() and path() needs more detail #10740

smfr opened this issue Aug 14, 2024 · 3 comments · Fixed by #10984

Comments

@smfr
Copy link
Contributor

smfr commented Aug 14, 2024

CSS Basic Shapes 2 says that shape() and path() can interpolate if they have the same set of commands, specifically:

For this purpose, commands are "the same" if they use the same command keyword, and use the same keyword. For curve and smooth, they also must have the same number of control points.

But there's some ambiguity here, because shape() takes an initial "from", and it's not clear if that has to correspond to the first Move command in the path. Specifically, it's not clear if these should interpolate:

shape(from 0 0, move to 200px 200px, close)

and

path(M 100 100 z)

Likewise what if the first command in the path is not an absolute move?

shape(from 0 0, line to 200px 200px, close)

and

path(m 100 100 z)
@smfr
Copy link
Contributor Author

smfr commented Aug 14, 2024

cc @noamr @tabatkins

@smfr smfr changed the title [css-shapes-2 [css-shapes-2] Interpolation between shape() and path() needs more detail Aug 14, 2024
smfr added a commit to smfr/WebKit that referenced this issue Aug 14, 2024
https://bugs.webkit.org/show_bug.cgi?id=277709
rdar://133325334

Reviewed by NOBODY (OOPS!).

Support interpolation between `path()` and `shape()` functions as described in [1], where the output is
a `shape()`.

Have `BasicShapePath::canBlend()` and `BasicShapeShape::canBlend()` check to see if the other object
is something that can be blended with, then call into `canBlendWithPath()`. This creates a temporary
BasicShapeShape object to answer the question.

Implement `BasicShapeShape::createFromPath()` to create a BasicShapeShape from a BasicShapePath, using a
`SVGPathParser` with a custom `SVGPathConsumer`. Note an ambiguity in the spec [2] around initial Move
commands vs the "from" in `shape()`.

[1] https://drafts.csswg.org/css-shapes-2/#interpolating-shape
[2] w3c/csswg-drafts#10740

* LayoutTests/TestExpectations:
* LayoutTests/imported/w3c/web-platform-tests/css/css-masking/animations/clip-path-interpolation-shape-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-masking/animations/clip-path-interpolation-shape.html: "via" -> "using"
* LayoutTests/imported/w3c/web-platform-tests/css/motion/animation/offset-path-interpolation-008-expected.txt:
* Source/WebCore/rendering/style/BasicShapes.cpp:
(WebCore::BasicShapePath::canBlend const):
(WebCore::BasicShapePath::blend const):
* Source/WebCore/rendering/style/BasicShapesShape.cpp:
(WebCore::BasicShapeShape::createFromPath):
(WebCore::BasicShapeShape::canBlend const):
(WebCore::BasicShapeShape::canBlendWithPath const):
(WebCore::BasicShapeShape::blend const):
(WebCore::BasicShapeShape::blendWithPath):
* Source/WebCore/rendering/style/BasicShapesShape.h:
* Source/WebCore/svg/SVGPathConsumer.h: Name the arguments, since the order is very non-obvious.
@noamr
Copy link
Collaborator

noamr commented Aug 14, 2024

Good point, the easiest path forward here I think is that path() is always assumed to have from 0 0. It doesn't create special complexity and still allows interpolation with existing path() shapes.

A more complex alternative would be to squash all the first move by|to segments in both shape and path to a single from

webkit-commit-queue pushed a commit to smfr/WebKit that referenced this issue Aug 14, 2024
https://bugs.webkit.org/show_bug.cgi?id=277709
rdar://133325334

Reviewed by Antti Koivisto.

Support interpolation between `path()` and `shape()` functions as described in [1], where the output is
a `shape()`.

Have `BasicShapePath::canBlend()` and `BasicShapeShape::canBlend()` check to see if the other object
is something that can be blended with, then call into `canBlendWithPath()`. This creates a temporary
BasicShapeShape object to answer the question.

Implement `BasicShapeShape::createFromPath()` to create a BasicShapeShape from a BasicShapePath, using a
`SVGPathParser` with a custom `SVGPathConsumer`. Note an ambiguity in the spec [2] around initial Move
commands vs the "from" in `shape()`.

[1] https://drafts.csswg.org/css-shapes-2/#interpolating-shape
[2] w3c/csswg-drafts#10740

* LayoutTests/TestExpectations:
* LayoutTests/imported/w3c/web-platform-tests/css/css-masking/animations/clip-path-interpolation-shape-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-masking/animations/clip-path-interpolation-shape.html: "via" -> "using"
* LayoutTests/imported/w3c/web-platform-tests/css/motion/animation/offset-path-interpolation-008-expected.txt:
* Source/WebCore/rendering/style/BasicShapes.cpp:
(WebCore::BasicShapePath::canBlend const):
(WebCore::BasicShapePath::blend const):
* Source/WebCore/rendering/style/BasicShapesShape.cpp:
(WebCore::BasicShapeShape::createFromPath):
(WebCore::BasicShapeShape::canBlend const):
(WebCore::BasicShapeShape::canBlendWithPath const):
(WebCore::BasicShapeShape::blend const):
(WebCore::BasicShapeShape::blendWithPath):
* Source/WebCore/rendering/style/BasicShapesShape.h:
* Source/WebCore/svg/SVGPathConsumer.h: Name the arguments, since the order is very non-obvious.

Canonical link: https://commits.webkit.org/282243@main
@noamr noamr moved this to Regular agenda items in CSSWG Agenda TPAC 2024 Sep 18, 2024
@css-meeting-bot
Copy link
Member

The CSS Working Group just discussed [css-shapes-2] Interpolation between `shape()` and `path()` needs more detail, and agreed to the following:

  • RESOLVED: Align `from` with the mandated first move.
The full IRC log of that discussion <dbaron> noamr: This is about what happens when you animate betwen a path shape (a pixel based SVg string) and a shape (going farther and farther away from SVG).
<dbaron> noamr: There's already some ambiguities. For example with shape you always start from where theshape starts from, whereas SVG path functions start from 0,0.
<dbaron> noamr: smfr suggested just not animating.
<dbaron> noamr: This creates a cleaner ex.pectation -- just use a tool to convert your path to a shape rather than relying on the browser to convert for you.
<TabAtkins> +1 to pretending that path() has a `from 0 0` argument that tinterpolates with the shape()'s `from` argument.
<dbaron> noamr: The other option is more complex, is that path with moves it will be the first one to mix them together. In SVG path you can do to and by.
<dbaron> noamr: In shape you kind of can.
<dbaron> noamr: I think it's between those 2 options.
<dbaron> noamr: I think prefer to not interpolate between path andshape, but there was pushback in the issue.
<dbaron> noamr: ???
<dbaron> TabAtkins: This issue is about whether shape and path interpolate based on the from stuff.
<dbaron> noamr: I want to address this issue by ... this specific issue of the from is one divergence between path and shape. There might be more in the futuer. This issues makes me think they shouldn't be interpolated.
<dbaron> TabAtkins: There's a trivial transformation of the path string into the shape syntax, we should just do that.
<dbaron> TabAtkins: The path syntax will just turn into a moveto and a closepath if it was a shape.
<dbaron> noamr: So interpolate path as if it always has "From 0 0"
<dbaron> TabAtkins: my assumption this whole time is that the SVG path syntax turns into a bunch of shape commands
<emilio> q+
<dbaron> TabAtkins: Whavever arguments ?? takes given appropriate default value, in this case `from 0 0`, and interpolation is based onthat.
<TabAtkins> s/??/shape()/
<dbaron> emilio: There will probably always be a way to turn a path into a shape. It feels a bit weird to do it automatically for interpolation.
<noamr> q+
<dbaron> TabAtkins: when things are easily inter-convertable, I think it's good to do so.
<dbaron> TabAtkins: for example 3 rect functions: inset(), xywh(), ...
<kbabbitt> q+
<dbaron> emilio: those all compute to same function
<Rossen9> ack emilio
<dbaron> emilio: this distinction is preserved at computed value time
<dbaron> emilio: given how trivial it is to turn a path into a shape, it feels like adding the special code path ???, ... it's fine
<noamr> q+
<dbaron> TabAtkins: I don't have a strong opinion, but doing it since we can seems right.
<dbaron> noamr: The difference here is that usually you start your path with a moveto.
<Rossen9> ack noamr
<TabAtkins> TabAtkins: given how trivial it is to turn a path into a shape, it feels like something we should do for the author ^_^
<dbaron> noamr: If the path doesn'tstart with 0 0.
<dbaron> noamr: so your path will turn into a shape that doesn't have a from.
<dbaron> noamr: It starts it on the wrong foot.
<dbaron> noamr: Something about thsi conversion doesn't feel right.
<dbaron> noamr: It will be not exctly as intended.
<dbaron> noamr: Telling the developer to turn it into a shape yourself and decide if it's a moveto or a from, or use several movetos in the beginning, would be cleaner.
<dbaron> kbabbitt: A couple things: I'm curious why shape starts with this from to begin with. It seems like syntactic sugar for a move but maybe I'm missing something.
<dbaron> TabAtkins: All the shape functions have an origin. That ??? the motion-path.
<dbaron> TabAtkins: A starting point for where the motion path is from rather than forcing them to start at 0,0
<dbaron> kbabbitt: I'll have to take a closer look at motion path.
<dbaron> kbabbitt: I was just looking at the grammar for motion path, it requires that the first command is a move.
<dbaron> kbabbitt: Another alternative: have ?? line up with the first move in the path.
<kbabbitt> s/??/from/
<dbaron> ChrisL: That was a design decision, every path segment creates the start position for the next segment, so has to start with a move.
<dbaron> noamr: We need to explore whether this is enforced in the shape segment in CSS. If it doesn't that solves it.
<kbabbitt> s/grammar for motion path/grammar for SVG path/
<lea> can't we just default to a move to 0,0 if no move is specified?
<TabAtkins> https://drafts.fxtf.org/motion-1/#offset-position-property lets you change the "starting point" of a path that doesn't manually specify its start
<dbaron> noamr: I think the resolution should be to align from with the first segment which is supposed to be a move.
<dbaron> noamr: With the constraint that we will check if this is actually enforced by browsers.
<ChrisL> s/so has to/so the first has to/
<dbaron> Proposed: Align `from` with the mandated first move.
<dbaron> RESOLVED: Align `from` with the mandated first move.

noamr added a commit to noamr/csswg-drafts that referenced this issue Oct 1, 2024
Match the first moveTo of the `path()` with the "from" of the `shape()`

This is guaranteed to work since `path()` always starts with an M or m.

See resolution: w3c#10740 (comment)

Closes w3c#10740
@noamr noamr closed this as completed in d72a80b Oct 22, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: Friday morning
Development

Successfully merging a pull request may close this issue.

4 participants