Title: [294520] trunk
Revision
294520
Author
nmouchta...@apple.com
Date
2022-05-19 18:22:33 -0700 (Thu, 19 May 2022)

Log Message

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:

Canonical link: https://commits.webkit.org/250776@main

Modified Paths

Diff

Modified: trunk/LayoutTests/TestExpectations (294519 => 294520)


--- trunk/LayoutTests/TestExpectations	2022-05-20 01:17:40 UTC (rev 294519)
+++ trunk/LayoutTests/TestExpectations	2022-05-20 01:22:33 UTC (rev 294520)
@@ -5078,9 +5078,6 @@
 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 ]
 

Modified: trunk/Source/WebCore/platform/graphics/GeometryUtilities.cpp (294519 => 294520)


--- trunk/Source/WebCore/platform/graphics/GeometryUtilities.cpp	2022-05-20 01:17:40 UTC (rev 294519)
+++ trunk/Source/WebCore/platform/graphics/GeometryUtilities.cpp	2022-05-20 01:22:33 UTC (rev 294520)
@@ -277,7 +277,7 @@
 }
 
 // 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 @@
     return std::abs(360 - angle);
 }
 
-RectEdges<double> distanceOfPointToSidesOfRect(const FloatRect& boundingRect, const FloatPoint& position)
+RectEdges<double> 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<double>(top, right, bottom, left);
 }
 
+std::array<FloatPoint, 4> 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<const FloatPoint&, float> ray)
 {
     auto length = lengthOfPointToSideOfIntersection(boundingRect, ray);

Modified: trunk/Source/WebCore/platform/graphics/GeometryUtilities.h (294519 => 294520)


--- trunk/Source/WebCore/platform/graphics/GeometryUtilities.h	2022-05-20 01:17:40 UTC (rev 294519)
+++ trunk/Source/WebCore/platform/graphics/GeometryUtilities.h	2022-05-20 01:22:33 UTC (rev 294520)
@@ -29,6 +29,8 @@
 #include "IntRect.h"
 #include <wtf/Forward.h>
 
+#include <wtf/Vector.h>
+
 namespace WebCore {
 
 class FloatQuad;
@@ -88,7 +90,13 @@
 
 // Given a box and an offset from the top left corner, calculate the distance of the point from each side
 RectEdges<double> 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<FloatPoint, 4> verticesForBox(const FloatRect&, const FloatPoint);
+
 float toPositiveAngle(float angle);
+float toRelatedAcuteAngle(float angle);
 
 struct RotatedRect {
     FloatPoint center;

Modified: trunk/Source/WebCore/rendering/PathOperation.cpp (294519 => 294520)


--- trunk/Source/WebCore/rendering/PathOperation.cpp	2022-05-20 01:17:40 UTC (rev 294519)
+++ trunk/Source/WebCore/rendering/PathOperation.cpp	2022-05-20 01:22:33 UTC (rev 294520)
@@ -70,13 +70,63 @@
     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<std::pair<double, double>, 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<double, double> const lhs, std::pair<double, double> const rhs) {
+            return lhs.first < rhs.first;
+        })->first;
+        
+        auto upperBound = std::min_element(bounds.begin(), bounds.end(),
+            [] (std::pair<double, double> const lhs, std::pair<double, double> 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;

Modified: trunk/Source/WebCore/rendering/PathOperation.h (294519 => 294520)


--- trunk/Source/WebCore/rendering/PathOperation.h	2022-05-20 01:17:40 UTC (rev 294519)
+++ trunk/Source/WebCore/rendering/PathOperation.h	2022-05-20 01:22:33 UTC (rev 294520)
@@ -196,8 +196,9 @@
         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)
     {

Modified: trunk/Source/WebCore/rendering/style/RenderStyle.cpp (294519 => 294520)


--- trunk/Source/WebCore/rendering/style/RenderStyle.cpp	2022-05-20 01:17:40 UTC (rev 294519)
+++ trunk/Source/WebCore/rendering/style/RenderStyle.cpp	2022-05-20 01:22:33 UTC (rev 294520)
@@ -1563,7 +1563,7 @@
     // (implemented in unapplyTransformOrigin)
 }
 
-static std::optional<Path> getPathFromPathOperation(const FloatRect& box, const PathOperation& operation)
+static std::optional<Path> getPathFromPathOperation(const FloatRect& box, const PathOperation& operation, const FloatPoint& anchor, OffsetRotation rotation)
 {
     switch (operation.type()) {
     case PathOperation::Shape:
@@ -1575,7 +1575,7 @@
     case PathOperation::Box:
         return downcast<BoxPathOperation>(operation).getPath();
     case PathOperation::Ray:
-        return downcast<RayPathOperation>(operation).pathForReferenceRect();
+        return downcast<RayPathOperation>(operation).pathForReferenceRect(box, anchor, rotation);
     }
     RELEASE_ASSERT_NOT_REACHED();
 }
@@ -1604,8 +1604,13 @@
     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());
@@ -1612,10 +1617,6 @@
     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;
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to