{{ message }}
/ WebKit Public
Implement contain flag for ray() in offset path
```https://bugs.webkit.org/show_bug.cgi?id=240259
<rdar://93374029>

Reviewed by Simon Fraser.

Implement contain flag for ray(). Contains purpose is to have the entire box
being animated be contained within the path. "Contained within the path" is
defined as having the box remain within a circle with the radius of the path
length and positioned at the offset anchor. This solution is adapted from the
repository of the spec writer: https://github.com/ewilligers/petrogale-purpureicollis.

The way this solution works is that you construct a coordinate system with the origin
being the offset anchor. You then calculate the position of each vertex of the box.
Then, rotate the vertices based on the angles difference from the x-axis. Next, using
the circle equation, we want to find an offset such that (x + offset)^2 + y^2 = r^2.
This results in a lower and upper bound for offset: -x - sqrt(r^2 - y^2) <= offset <=
-x + sqrt(r^2 + y^2). Finally we choose the minimal value of these upper and lower
bounds to get the final clamped offset.

This patch currently doesn't take into account if it is not possible to fit the box
within the path, as this will be completed in a seperate patch. Currently, test 4 is
failing due to rounding error, and test 5 is failing due to the unimplemented part.

* Source/WebCore/platform/graphics/GeometryUtilities.cpp:
(WebCore::toRelatedAcuteAngle):
(WebCore::distanceOfPointToSidesOfRect):
(WebCore::verticesForBox):
* Source/WebCore/platform/graphics/GeometryUtilities.h:
* Source/WebCore/rendering/PathOperation.cpp:
(WebCore::RayPathOperation::lengthForContainPath const):
(WebCore::RayPathOperation::pathForReferenceRect const):
* Source/WebCore/rendering/PathOperation.h:
* Source/WebCore/rendering/style/RenderStyle.cpp:
(WebCore::getPathFromPathOperation):
(WebCore::RenderStyle::applyMotionPathTransform const):
* LayoutTests/TestExpectations:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@294520 268f45cc-cd09-0410-ab3c-d52691b4dbfc```
nmoucht committed May 20, 2022
1 parent db510b0 commit 44edfe0eb29422b468c50e99d344064fc97eafb3
Showing 6 changed files with 83 additions and 18 deletions.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
 @@ -5078,9 +5078,6 @@ webkit.org/b/233340 imported/w3c/web-platform-tests/css/motion/offset-anchor-tra webkit.org/b/233344 imported/w3c/web-platform-tests/css/motion/offset-path-ray-007.html [ ImageOnlyFailure ] # CSS motion path needs to implement contain for ray: https://bugs.webkit.org/show_bug.cgi?id=240259. webkit.org/b/233344 imported/w3c/web-platform-tests/css/motion/offset-path-ray-contain-001.html [ ImageOnlyFailure ] webkit.org/b/233344 imported/w3c/web-platform-tests/css/motion/offset-path-ray-contain-002.html [ ImageOnlyFailure ] webkit.org/b/233344 imported/w3c/web-platform-tests/css/motion/offset-path-ray-contain-003.html [ ImageOnlyFailure ] webkit.org/b/233344 imported/w3c/web-platform-tests/css/motion/offset-path-ray-contain-004.html [ ImageOnlyFailure ] webkit.org/b/233344 imported/w3c/web-platform-tests/css/motion/offset-path-ray-contain-005.html [ ImageOnlyFailure ]
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
 @@ -277,7 +277,7 @@ float toPositiveAngle(float angle) } // Compute acute angle from vertical axis static float toRelatedAcuteAngle(float angle) float toRelatedAcuteAngle(float angle) { angle = toPositiveAngle(angle); if (angle < 90) @@ -287,16 +287,24 @@ static float toRelatedAcuteAngle(float angle) return std::abs(360 - angle); } RectEdges distanceOfPointToSidesOfRect(const FloatRect& boundingRect, const FloatPoint& position) RectEdges distanceOfPointToSidesOfRect(const FloatRect& box, const FloatPoint& position) { // Compute distance to each side of the containing box double top = std::abs(position.y()); double bottom = std::abs(position.y() - boundingRect.height()); double bottom = std::abs(position.y() - box.height()); double left = std::abs(position.x()); double right = std::abs(position.x() - boundingRect.width()); double right = std::abs(position.x() - box.width()); return RectEdges(top, right, bottom, left); } std::array verticesForBox(const FloatRect& box, const FloatPoint position) { return { FloatPoint(-position.x(), -position.y()), FloatPoint(box.width() - position.x(), -position.y()), FloatPoint(box.width() - position.x(), box.height() - position.y()), FloatPoint(-position.x(), box.height() - position.y()) }; } double lengthOfRayIntersectionWithBoundingBox(const FloatRect& boundingRect, const std::pair ray) { auto length = lengthOfPointToSideOfIntersection(boundingRect, ray);
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
 @@ -29,6 +29,8 @@ #include "IntRect.h" #include #include namespace WebCore { class FloatQuad; @@ -88,7 +90,13 @@ float angleOfPointToSideOfIntersection(const FloatRect& boundingRect, const std: // Given a box and an offset from the top left corner, calculate the distance of the point from each side RectEdges distanceOfPointToSidesOfRect(const FloatRect&, const FloatPoint&); // Given a box and an offset from the top left corner, construct a coordinate system with this offset as the origin, // and return the vertices of the box in this coordinate system std::array verticesForBox(const FloatRect&, const FloatPoint); float toPositiveAngle(float angle); float toRelatedAcuteAngle(float angle); struct RotatedRect { FloatPoint center;
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
 @@ -70,13 +70,63 @@ double RayPathOperation::lengthForPath() const RELEASE_ASSERT_NOT_REACHED(); } const Path RayPathOperation::pathForReferenceRect() const double RayPathOperation::lengthForContainPath(const FloatRect& elementRect, double computedPathLength, const FloatPoint& anchor, const OffsetRotation rotation) const { // Construct vertices of element for determining if they are within the path length // Anchor point as origin auto vertices = verticesForBox(elementRect, anchor); // Rotate vertices depending on offset rotate or angle if (!rotation.hasAuto()) { auto deg = toRelatedAcuteAngle(toPositiveAngle(m_angle - rotation.angle())); auto angle = deg2rad(deg); std::for_each(vertices.begin(), vertices.end(), [angle] (FloatPoint& p) { p.rotate(angle); }); } Vector, 4> bounds; for (const auto& p : vertices) { // Use equation for circle (offset distance + x)^2 + y^2 <= r^2 to find offset distance that satisfies equation // If no solution for above equation, must minimally increase it, otherwise clamp such that // every point is within path double discriminant = computedPathLength * computedPathLength - p.y() * p.y(); if (discriminant < 0) { // Need to minimally increase path length break; } bounds.append(std::make_pair(-p.x() - std::sqrt(discriminant), -p.x() + std::sqrt(discriminant))); } if (vertices.size() == bounds.size()) { auto lowerBound = std::max_element(bounds.begin(), bounds.end(), [] (std::pair const lhs, std::pair const rhs) { return lhs.first < rhs.first; })->first; auto upperBound = std::min_element(bounds.begin(), bounds.end(), [] (std::pair const lhs, std::pair const rhs) { return lhs.second < rhs.second; })->second; if (lowerBound <= upperBound) return std::max(lowerBound, std::min(upperBound, computedPathLength)); } // TODO: Implement minimally increasing path length to allow all vertices to be within such a path length return computedPathLength; } const Path RayPathOperation::pathForReferenceRect(const FloatRect& elementRect, const FloatPoint& anchor, const OffsetRotation rotation) const { Path path; if (m_containingBlockBoundingRect.isZero()) return path; auto length = lengthForPath(); auto radians = deg2rad(toPositiveAngle(m_angle) - 90); double length = lengthForPath(); if (m_isContaining) length = lengthForContainPath(elementRect, length, anchor, rotation); auto radians = deg2rad(toPositiveAngle(m_angle) - 90.0); auto point = FloatPoint(std::cos(radians) * length, std::sin(radians) * length); path.addLineTo(point); return path;
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
 @@ -196,8 +196,9 @@ class RayPathOperation final : public PathOperation { return RayPathOperation::create(WebCore::blend(m_angle, to.m_angle, context), m_size, m_isContaining); } const Path pathForReferenceRect() const; const Path pathForReferenceRect(const FloatRect& elementRect, const FloatPoint& anchor, const OffsetRotation rotation) const; double lengthForPath() const; double lengthForContainPath(const FloatRect& elementRect, double computedPathLength, const FloatPoint& anchor, const OffsetRotation rotation) const; void setContainingBlockReferenceRect(const FloatRect& boundingRect) {
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
 @@ -1563,7 +1563,7 @@ void RenderStyle::applyCSSTransform(TransformationMatrix& transform, const Float // (implemented in unapplyTransformOrigin) } static std::optional getPathFromPathOperation(const FloatRect& box, const PathOperation& operation) static std::optional getPathFromPathOperation(const FloatRect& box, const PathOperation& operation, const FloatPoint& anchor, OffsetRotation rotation) { switch (operation.type()) { case PathOperation::Shape: @@ -1575,7 +1575,7 @@ static std::optional getPathFromPathOperation(const FloatRect& box, const case PathOperation::Box: return downcast(operation).getPath(); case PathOperation::Ray: return downcast(operation).pathForReferenceRect(); return downcast(operation).pathForReferenceRect(box, anchor, rotation); } RELEASE_ASSERT_NOT_REACHED(); } @@ -1604,18 +1604,19 @@ void RenderStyle::applyMotionPathTransform(TransformationMatrix& transform, cons if (!offsetPath()) return; auto transformOrigin = floatPointForLengthPoint(transformOriginXY(), boundingBox.size()) + boundingBox.location(); auto anchor = transformOrigin; if (!offsetAnchor().x().isAuto()) anchor = floatPointForLengthPoint(offsetAnchor(), boundingBox.size()) + boundingBox.location(); // Shift element to the point on path specified by offset-path and offset-distance. auto path = getPathFromPathOperation(boundingBox, *offsetPath()); auto path = getPathFromPathOperation(boundingBox, *offsetPath(), anchor, offsetRotate()); if (!path) return; auto traversalState = getTraversalStateAtDistance(*path, offsetDistance()); transform.translate(traversalState.current().x(), traversalState.current().y()); // Shift element to the anchor specified by offset-anchor. auto transformOrigin = floatPointForLengthPoint(transformOriginXY(), boundingBox.size()) + boundingBox.location(); auto anchor = transformOrigin; if (!offsetAnchor().x().isAuto()) anchor = floatPointForLengthPoint(offsetAnchor(), boundingBox.size()) + boundingBox.location(); transform.translate(-anchor.x(), -anchor.y()); auto shiftToOrigin = anchor - transformOrigin;