Fix getTotalLength() and getPointAtLength() for optimized rect and ellipse renderers
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 18 Nov 2019 21:45:40 +0000 (21:45 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 18 Nov 2019 21:45:40 +0000 (21:45 +0000)
https://bugs.webkit.org/show_bug.cgi?id=204213

Patch by Said Abou-Hallawa <sabouhallawa@apple.com> on 2019-11-18
Reviewed by Simon Fraser.

LayoutTests/imported/w3c:

* web-platform-tests/svg/types/elements/SVGGeometryElement-rect-expected.txt:

Source/WebCore:

If the renderer does not create a Path to draw the shape, we need to create
a temporary Path from the SVGElement then use it to answer the questions
of these functions and finally delete it.

* platform/graphics/Path.cpp:
(WebCore::Path::traversalStateAtLength const):
(WebCore::Path::pointAtLength const):
No need for the 'success'. It is never used.

(WebCore::Path::normalAngleAtLength const): Deleted.
This function is not used.

* platform/graphics/Path.h:
* rendering/svg/RenderSVGShape.cpp:
(WebCore::RenderSVGShape::updateShapeFromElement):
Creating the Path from the SVGELement is moved to createPath().

(WebCore::RenderSVGShape::getTotalLength const):
(WebCore::RenderSVGShape::getPointAtLength const):
Create a temporary Path if the renderer draws the shape without creating
the Path.

(WebCore::RenderSVGShape::createPath const):
* rendering/svg/RenderSVGShape.h:

* rendering/svg/SVGTextLayoutEngine.cpp:
(WebCore::SVGTextLayoutEngine::layoutTextOnLineOrPath):
* svg/SVGAnimateMotionElement.cpp:
(WebCore::SVGAnimateMotionElement::buildTransformForProgress):
Use PathTraversalState.success() to check instead of getting the same
value through an argument to traversalStateAtLength().

LayoutTests:

The following tests were added in r231955. The SVGElements in these were
defined such that a path has to be created to render any of them. Change
all the tests, except the polygon and the polyline, such that no path is
needed to render them. This will exercise the code path where we need to
create a temporary Path to answer the questions of getTotalLength() and
getPointAtLength().

* svg/dom/SVGGeometry-circle-expected.txt:
* svg/dom/SVGGeometry-circle.xhtml:
* svg/dom/SVGGeometry-ellipse-expected.txt:
* svg/dom/SVGGeometry-ellipse.xhtml:
* svg/dom/SVGGeometry-line-expected.txt:
* svg/dom/SVGGeometry-line.xhtml:
* svg/dom/SVGGeometry-polygon-expected.txt:
* svg/dom/SVGGeometry-polygon.xhtml:
* svg/dom/SVGGeometry-polyline-expected.txt:
* svg/dom/SVGGeometry-polyline.xhtml:
* svg/dom/SVGGeometry-rect-expected.txt:
* svg/dom/SVGGeometry-rect.xhtml:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@252563 268f45cc-cd09-0410-ab3c-d52691b4dbfc

22 files changed:
LayoutTests/ChangeLog
LayoutTests/imported/w3c/ChangeLog
LayoutTests/imported/w3c/web-platform-tests/svg/types/elements/SVGGeometryElement-rect-expected.txt
LayoutTests/svg/dom/SVGGeometry-circle-expected.txt
LayoutTests/svg/dom/SVGGeometry-circle.xhtml
LayoutTests/svg/dom/SVGGeometry-ellipse-expected.txt
LayoutTests/svg/dom/SVGGeometry-ellipse.xhtml
LayoutTests/svg/dom/SVGGeometry-line-expected.txt
LayoutTests/svg/dom/SVGGeometry-line.xhtml
LayoutTests/svg/dom/SVGGeometry-polygon-expected.txt
LayoutTests/svg/dom/SVGGeometry-polygon.xhtml
LayoutTests/svg/dom/SVGGeometry-polyline-expected.txt
LayoutTests/svg/dom/SVGGeometry-polyline.xhtml
LayoutTests/svg/dom/SVGGeometry-rect-expected.txt
LayoutTests/svg/dom/SVGGeometry-rect.xhtml
Source/WebCore/ChangeLog
Source/WebCore/platform/graphics/Path.cpp
Source/WebCore/platform/graphics/Path.h
Source/WebCore/rendering/svg/RenderSVGShape.cpp
Source/WebCore/rendering/svg/RenderSVGShape.h
Source/WebCore/rendering/svg/SVGTextLayoutEngine.cpp
Source/WebCore/svg/SVGAnimateMotionElement.cpp

index 63fd57f..2a88a92 100644 (file)
@@ -1,3 +1,30 @@
+2019-11-18  Said Abou-Hallawa  <sabouhallawa@apple.com>
+
+        Fix getTotalLength() and getPointAtLength() for optimized rect and ellipse renderers
+        https://bugs.webkit.org/show_bug.cgi?id=204213
+
+        Reviewed by Simon Fraser.
+
+        The following tests were added in r231955. The SVGElements in these were
+        defined such that a path has to be created to render any of them. Change
+        all the tests, except the polygon and the polyline, such that no path is
+        needed to render them. This will exercise the code path where we need to
+        create a temporary Path to answer the questions of getTotalLength() and
+        getPointAtLength().
+
+        * svg/dom/SVGGeometry-circle-expected.txt:
+        * svg/dom/SVGGeometry-circle.xhtml:
+        * svg/dom/SVGGeometry-ellipse-expected.txt:
+        * svg/dom/SVGGeometry-ellipse.xhtml:
+        * svg/dom/SVGGeometry-line-expected.txt:
+        * svg/dom/SVGGeometry-line.xhtml:
+        * svg/dom/SVGGeometry-polygon-expected.txt:
+        * svg/dom/SVGGeometry-polygon.xhtml:
+        * svg/dom/SVGGeometry-polyline-expected.txt:
+        * svg/dom/SVGGeometry-polyline.xhtml:
+        * svg/dom/SVGGeometry-rect-expected.txt:
+        * svg/dom/SVGGeometry-rect.xhtml:
+
 2019-11-18  Zalan Bujtas  <zalan@apple.com>
 
         Block layout invalidation logic triggers excessive layout on height percentage descendants
index 2344e16..a61f0b5 100644 (file)
@@ -1,3 +1,12 @@
+2019-11-18  Said Abou-Hallawa  <sabouhallawa@apple.com>
+
+        Fix getTotalLength() and getPointAtLength() for optimized rect and ellipse renderers
+        https://bugs.webkit.org/show_bug.cgi?id=204213
+
+        Reviewed by Simon Fraser.
+
+        * web-platform-tests/svg/types/elements/SVGGeometryElement-rect-expected.txt:
+
 2019-11-18  Wenson Hsieh  <wenson_hsieh@apple.com>
 
         [Clipboard API] Add support for Clipboard.writeText()
index 1156cb8..c6d665e 100644 (file)
@@ -1,4 +1,4 @@
 
-FAIL getTotalLength and getPointAtLength do not take pathLength into account assert_equals: expected 600 but got 0
+PASS getTotalLength and getPointAtLength do not take pathLength into account 
 FAIL getPointAtLength() returns instance of DOMPoint assert_true: expected true got false
 
index 0e76ada..0c33c81 100644 (file)
@@ -6,43 +6,40 @@ On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE
 
 Test isPointInFill()
 PASS c1.isPointInFill({}) is false
-PASS c1.isPointInFill({x: 50, y: 50}) is true
-PASS c1.isPointInFill({x: 0, y: 50}) is true
-PASS c1.isPointInFill({x: 100, y: 50}) is true
-PASS c1.isPointInFill({x: 50, y: 0}) is true
-PASS c1.isPointInFill({x: 50, y: 100}) is true
-PASS c1.isPointInFill({x: -1, y: 50}) is false
-PASS c1.isPointInFill({x: 101, y: 50}) is false
-PASS c1.isPointInFill({x: 50, y: -1}) is false
-PASS c1.isPointInFill({x: 50, y: 101}) is false
-PASS c1.isPointInFill({x: 90, y: 90}) is false
-PASS c1.isPointInFill({x: 10, y: 10}) is false
-PASS c1.isPointInFill({x: 90, y: 10}) is false
-PASS c1.isPointInFill({x: 10, y: 90}) is false
+PASS c1.isPointInFill({x: 109, y: 60}) is true
+PASS c1.isPointInFill({x: 111, y: 60}) is false
+PASS c1.isPointInFill({x: 60, y: 109}) is true
+PASS c1.isPointInFill({x: 60, y: 111}) is false
+PASS c1.isPointInFill({x: 11, y: 60}) is true
+PASS c1.isPointInFill({x: 9, y: 60}) is false
+PASS c1.isPointInFill({x: 60, y: 11}) is true
+PASS c1.isPointInFill({x: 60, y: 9}) is false
 
 Test isPointInStroke()
 PASS c1.isPointInStroke({}) is false
-PASS c1.isPointInStroke({x: 50, y: 50}) is false
-PASS c1.isPointInStroke({x: 100, y: 51}) is false
-PASS c1.isPointInStroke({x: 100, y: 49}) is true
-PASS c1.isPointInStroke({x: 0, y: 50}) is true
-PASS c1.isPointInStroke({x: 52, y: 100}) is true
-PASS c1.isPointInStroke({x: 48, y: 100}) is false
+PASS c1.isPointInStroke({x: 101, y: 60}) is true
+PASS c1.isPointInStroke({x: 99, y: 60}) is false
+PASS c1.isPointInStroke({x: 60, y: 101}) is true
+PASS c1.isPointInStroke({x: 60, y: 99}) is false
+PASS c1.isPointInStroke({x: 19, y: 60}) is true
+PASS c1.isPointInStroke({x: 21, y: 60}) is false
+PASS c1.isPointInStroke({x: 60, y: 19}) is true
+PASS c1.isPointInStroke({x: 60, y: 21}) is false
 
 Test getTotalLength()
 PASS c1.getTotalLength() is within 0.1 of 314.1592653589793
 
 Test getPointAtLength()
-PASS c1.getPointAtLength(0).x is within 0.1 of 100
-PASS c1.getPointAtLength(0).y is within 0.1 of 50
-PASS c1.getPointAtLength(Math.PI * 100 / 4).x is within 0.1 of 50
-PASS c1.getPointAtLength(Math.PI * 100 / 4).y is within 0.1 of 100
-PASS c1.getPointAtLength(Math.PI * 100 / 2).x is within 0.1 of 0
-PASS c1.getPointAtLength(Math.PI * 100 / 2).y is within 0.1 of 50
-PASS c1.getPointAtLength(Math.PI * 100 / 8).x is within 1 of 85.35
-PASS c1.getPointAtLength(Math.PI * 100 / 8).y is within 1 of 85.35
-PASS c1.getPointAtLength(Math.PI * 100 * 2).x is within 0.1 of 100
-PASS c1.getPointAtLength(Math.PI * 100 * 2).y is within 0.1 of 50
+PASS c1.getPointAtLength(0).x is within 0.1 of 110
+PASS c1.getPointAtLength(0).y is within 0.1 of 60
+PASS c1.getPointAtLength(Math.PI * 100 / 4).x is within 0.1 of 60
+PASS c1.getPointAtLength(Math.PI * 100 / 4).y is within 0.1 of 110
+PASS c1.getPointAtLength(Math.PI * 100 / 2).x is within 0.1 of 10
+PASS c1.getPointAtLength(Math.PI * 100 / 2).y is within 0.1 of 60
+PASS c1.getPointAtLength(Math.PI * 100 / 8).x is within 1 of 95.35
+PASS c1.getPointAtLength(Math.PI * 100 / 8).y is within 1 of 95.35
+PASS c1.getPointAtLength(Math.PI * 100 * 2).x is within 0.1 of 110
+PASS c1.getPointAtLength(Math.PI * 100 * 2).y is within 0.1 of 60
 PASS successfullyParsed is true
 
 TEST COMPLETE
index 7641911..e84e37e 100644 (file)
@@ -4,7 +4,7 @@
 </head>
 <body onload="run()">
 <svg id="svg" xmlns="http://www.w3.org/2000/svg" width="200" height="200">
-    <circle id="c1" cx="50" cy="50" r="50" fill="none" stroke="black" stroke-width="20" stroke-dashoffset="20" stroke-dasharray="20 20"/>
+    <circle id="c1" cx="60" cy="60" r="50" fill="none" stroke="black" stroke-width="20"/>
 </svg>
 <p id="description"></p>
 <div id="console"></div>
@@ -19,30 +19,27 @@ function run() {
 
     debug("");
     debug("Test isPointInFill()");
-    shouldBe("c1.isPointInFill({})", "false");
-    shouldBe("c1.isPointInFill({x: 50, y: 50})", "true");
-    shouldBe("c1.isPointInFill({x: 0, y: 50})", "true");
-    shouldBe("c1.isPointInFill({x: 100, y: 50})", "true");
-    shouldBe("c1.isPointInFill({x: 50, y: 0})", "true");
-    shouldBe("c1.isPointInFill({x: 50, y: 100})", "true");
-    shouldBe("c1.isPointInFill({x: -1, y: 50})", "false");
-    shouldBe("c1.isPointInFill({x: 101, y: 50})", "false");
-    shouldBe("c1.isPointInFill({x: 50, y: -1})", "false");
-    shouldBe("c1.isPointInFill({x: 50, y: 101})", "false");
-    shouldBe("c1.isPointInFill({x: 90, y: 90})", "false");
-    shouldBe("c1.isPointInFill({x: 10, y: 10})", "false");
-    shouldBe("c1.isPointInFill({x: 90, y: 10})", "false");
-    shouldBe("c1.isPointInFill({x: 10, y: 90})", "false");
+    shouldBeFalse("c1.isPointInFill({})", "false");
+    shouldBeTrue("c1.isPointInFill({x: 109, y: 60})");
+    shouldBeFalse("c1.isPointInFill({x: 111, y: 60})");
+    shouldBeTrue("c1.isPointInFill({x: 60, y: 109})");
+    shouldBeFalse("c1.isPointInFill({x: 60, y: 111})");
+    shouldBeTrue("c1.isPointInFill({x: 11, y: 60})");
+    shouldBeFalse("c1.isPointInFill({x: 9, y: 60})");
+    shouldBeTrue("c1.isPointInFill({x: 60, y: 11})");
+    shouldBeFalse("c1.isPointInFill({x: 60, y: 9})");
 
     debug("");
     debug("Test isPointInStroke()");
-    shouldBe("c1.isPointInStroke({})", "false");
-    shouldBe("c1.isPointInStroke({x: 50, y: 50})", "false");
-    shouldBe("c1.isPointInStroke({x: 100, y: 51})", "false");
-    shouldBe("c1.isPointInStroke({x: 100, y: 49})", "true");
-    shouldBe("c1.isPointInStroke({x: 0, y: 50})", "true");
-    shouldBe("c1.isPointInStroke({x: 52, y: 100})", "true");
-    shouldBe("c1.isPointInStroke({x: 48, y: 100})", "false");
+    shouldBeFalse("c1.isPointInStroke({})");
+    shouldBeTrue("c1.isPointInStroke({x: 101, y: 60})");
+    shouldBeFalse("c1.isPointInStroke({x: 99, y: 60})");
+    shouldBeTrue("c1.isPointInStroke({x: 60, y: 101})");
+    shouldBeFalse("c1.isPointInStroke({x: 60, y: 99})");
+    shouldBeTrue("c1.isPointInStroke({x: 19, y: 60})");
+    shouldBeFalse("c1.isPointInStroke({x: 21, y: 60})");
+    shouldBeTrue("c1.isPointInStroke({x: 60, y: 19})");
+    shouldBeFalse("c1.isPointInStroke({x: 60, y: 21})");
 
     debug("");
     debug("Test getTotalLength()");
@@ -50,16 +47,16 @@ function run() {
 
     debug("");
     debug("Test getPointAtLength()");
-    shouldBeCloseTo("c1.getPointAtLength(0).x", 100, 0.1);
-    shouldBeCloseTo("c1.getPointAtLength(0).y", 50, 0.1);
-    shouldBeCloseTo("c1.getPointAtLength(Math.PI * 100 / 4).x", 50, 0.1);
-    shouldBeCloseTo("c1.getPointAtLength(Math.PI * 100 / 4).y", 100, 0.1);
-    shouldBeCloseTo("c1.getPointAtLength(Math.PI * 100 / 2).x", 0, 0.1);
-    shouldBeCloseTo("c1.getPointAtLength(Math.PI * 100 / 2).y", 50, 0.1);
-    shouldBeCloseTo("c1.getPointAtLength(Math.PI * 100 / 8).x", 85.35, 1);
-    shouldBeCloseTo("c1.getPointAtLength(Math.PI * 100 / 8).y", 85.35, 1);
-    shouldBeCloseTo("c1.getPointAtLength(Math.PI * 100 * 2).x", 100, 0.1);
-    shouldBeCloseTo("c1.getPointAtLength(Math.PI * 100 * 2).y", 50, 0.1);
+    shouldBeCloseTo("c1.getPointAtLength(0).x", 110, 0.1);
+    shouldBeCloseTo("c1.getPointAtLength(0).y", 60, 0.1);
+    shouldBeCloseTo("c1.getPointAtLength(Math.PI * 100 / 4).x", 60, 0.1);
+    shouldBeCloseTo("c1.getPointAtLength(Math.PI * 100 / 4).y", 110, 0.1);
+    shouldBeCloseTo("c1.getPointAtLength(Math.PI * 100 / 2).x", 10, 0.1);
+    shouldBeCloseTo("c1.getPointAtLength(Math.PI * 100 / 2).y", 60, 0.1);
+    shouldBeCloseTo("c1.getPointAtLength(Math.PI * 100 / 8).x", 95.35, 1);
+    shouldBeCloseTo("c1.getPointAtLength(Math.PI * 100 / 8).y", 95.35, 1);
+    shouldBeCloseTo("c1.getPointAtLength(Math.PI * 100 * 2).x", 110, 0.1);
+    shouldBeCloseTo("c1.getPointAtLength(Math.PI * 100 * 2).y", 60, 0.1);
 
     finishJSTest();
 }
index c6a4961..da9a3ac 100644 (file)
@@ -6,44 +6,40 @@ On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE
 
 Test isPointInFill()
 PASS e1.isPointInFill({}) is false
-PASS e1.isPointInFill({x: 75, y: 50}) is true
-PASS e1.isPointInFill({x: 0, y: 50}) is true
-PASS e1.isPointInFill({x: 150, y: 50}) is true
-PASS e1.isPointInFill({x: 75, y: 0}) is true
-PASS e1.isPointInFill({x: 75, y: 100}) is true
-PASS e1.isPointInFill({x: -1, y: 50}) is false
-PASS e1.isPointInFill({x: 151, y: 50}) is false
-PASS e1.isPointInFill({x: 75, y: -1}) is false
-PASS e1.isPointInFill({x: 75, y: 101}) is false
-PASS e1.isPointInFill({x: 140, y: 90}) is false
-PASS e1.isPointInFill({x: 10, y: 10}) is false
-PASS e1.isPointInFill({x: 140, y: 10}) is false
-PASS e1.isPointInFill({x: 10, y: 90}) is false
+PASS e1.isPointInFill({x: 159, y: 60}) is true
+PASS e1.isPointInFill({x: 161, y: 60}) is false
+PASS e1.isPointInFill({x: 85, y: 109}) is true
+PASS e1.isPointInFill({x: 85, y: 111}) is false
+PASS e1.isPointInFill({x: 11, y: 60}) is true
+PASS e1.isPointInFill({x: 9, y: 60}) is false
+PASS e1.isPointInFill({x: 85, y: 11}) is true
+PASS e1.isPointInFill({x: 85, y: 9}) is false
 
 Test isPointInStroke()
 PASS e1.isPointInStroke({}) is false
-PASS e1.isPointInStroke({x: 75, y: 50}) is false
-PASS e1.isPointInStroke({x: 150, y: 51}) is false
-PASS e1.isPointInStroke({x: 150, y: 49}) is true
-PASS e1.isPointInStroke({x: 0, y: 52}) is true
-PASS e1.isPointInStroke({x: 0, y: 48}) is false
-PASS e1.isPointInStroke({x: 77, y: 100}) is false
-PASS e1.isPointInStroke({x: 73, y: 100}) is true
+PASS e1.isPointInStroke({x: 151, y: 60}) is true
+PASS e1.isPointInStroke({x: 149, y: 60}) is false
+PASS e1.isPointInStroke({x: 85, y: 101}) is true
+PASS e1.isPointInStroke({x: 85, y: 99}) is false
+PASS e1.isPointInStroke({x: 19, y: 60}) is true
+PASS e1.isPointInStroke({x: 21, y: 60}) is false
+PASS e1.isPointInStroke({x: 85, y: 19}) is true
+PASS e1.isPointInStroke({x: 85, y: 21}) is false
 
 Test getTotalLength()
 PASS e1.getTotalLength() is within 5 of 392.6990816987241
 
 Test getPointAtLength()
-PASS e1.getPointAtLength(0).x is within 0.1 of 150
-PASS e1.getPointAtLength(0).y is within 0.1 of 50
-PASS e1.getPointAtLength(Math.PI * (75 + 50) / 4).x is within 3 of 75
-PASS e1.getPointAtLength(Math.PI * (75 + 50) / 4).y is within 3 of 100
-PASS e1.getPointAtLength(Math.PI * (75 + 50) / 2).x is within 3 of 0
-PASS e1.getPointAtLength(Math.PI * (75 + 50) / 2).y is within 3 of 50
-PASS e1.getPointAtLength(Math.PI * (75 + 50) / 8).x is within 3 of 123
-PASS e1.getPointAtLength(Math.PI * (75 + 50) / 8).y is within 3 of 88
-PASS e1.getPointAtLength(Math.PI * (75 + 50) * 2).x is within 3 of 150
-PASS e1.getPointAtLength(Math.PI * (75 + 50) * 2).y is within 3 of 50
+PASS e1.getPointAtLength(0).x is within 0.1 of 160
+PASS e1.getPointAtLength(0).y is within 0.1 of 60
+PASS e1.getPointAtLength(Math.PI * (75 + 50) / 4).x is within 3 of 85
+PASS e1.getPointAtLength(Math.PI * (75 + 50) / 4).y is within 3 of 110
+PASS e1.getPointAtLength(Math.PI * (75 + 50) / 2).x is within 3 of 10
+PASS e1.getPointAtLength(Math.PI * (75 + 50) / 2).y is within 3 of 60
+PASS e1.getPointAtLength(Math.PI * (75 + 50) / 8).x is within 3 of 133
+PASS e1.getPointAtLength(Math.PI * (75 + 50) / 8).y is within 3 of 98
+PASS e1.getPointAtLength(Math.PI * (75 + 50) * 2).x is within 3 of 160
+PASS e1.getPointAtLength(Math.PI * (75 + 50) * 2).y is within 3 of 60
 PASS successfullyParsed is true
 
 TEST COMPLETE
index 19abec5..1756d8d 100644 (file)
@@ -4,7 +4,7 @@
 </head>
 <body onload="run()">
 <svg id="svg" xmlns="http://www.w3.org/2000/svg" width="200" height="200">
-    <ellipse id="e1" cx="75" cy="50" rx="75" ry="50" fill="none" stroke="black" stroke-width="20" stroke-dashoffset="20" stroke-dasharray="20 20"/>
+    <ellipse id="e1" cx="85" cy="60" rx="75" ry="50" fill="none" stroke="black" stroke-width="20"/>
 </svg>
 <p id="description"></p>
 <div id="console"></div>
@@ -19,31 +19,28 @@ function run() {
 
     debug("");
     debug("Test isPointInFill()");
-    shouldBe("e1.isPointInFill({})", "false");
-    shouldBe("e1.isPointInFill({x: 75, y: 50})", "true");
-    shouldBe("e1.isPointInFill({x: 0, y: 50})", "true");
-    shouldBe("e1.isPointInFill({x: 150, y: 50})", "true");
-    shouldBe("e1.isPointInFill({x: 75, y: 0})", "true");
-    shouldBe("e1.isPointInFill({x: 75, y: 100})", "true");
-    shouldBe("e1.isPointInFill({x: -1, y: 50})", "false");
-    shouldBe("e1.isPointInFill({x: 151, y: 50})", "false");
-    shouldBe("e1.isPointInFill({x: 75, y: -1})", "false");
-    shouldBe("e1.isPointInFill({x: 75, y: 101})", "false");
-    shouldBe("e1.isPointInFill({x: 140, y: 90})", "false");
-    shouldBe("e1.isPointInFill({x: 10, y: 10})", "false");
-    shouldBe("e1.isPointInFill({x: 140, y: 10})", "false");
-    shouldBe("e1.isPointInFill({x: 10, y: 90})", "false");
+    shouldBeFalse("e1.isPointInFill({})");
+    shouldBeTrue("e1.isPointInFill({x: 159, y: 60})");
+    shouldBeFalse("e1.isPointInFill({x: 161, y: 60})");
+
+    shouldBeTrue("e1.isPointInFill({x: 85, y: 109})");
+    shouldBeFalse("e1.isPointInFill({x: 85, y: 111})");
+    shouldBeTrue("e1.isPointInFill({x: 11, y: 60})");
+    shouldBeFalse("e1.isPointInFill({x: 9, y: 60})");
+    shouldBeTrue("e1.isPointInFill({x: 85, y: 11})");
+    shouldBeFalse("e1.isPointInFill({x: 85, y: 9})");
 
     debug("");
     debug("Test isPointInStroke()");
-    shouldBe("e1.isPointInStroke({})", "false");
-    shouldBe("e1.isPointInStroke({x: 75, y: 50})", "false");
-    shouldBe("e1.isPointInStroke({x: 150, y: 51})", "false");
-    shouldBe("e1.isPointInStroke({x: 150, y: 49})", "true");
-    shouldBe("e1.isPointInStroke({x: 0, y: 52})", "true");
-    shouldBe("e1.isPointInStroke({x: 0, y: 48})", "false");
-    shouldBe("e1.isPointInStroke({x: 77, y: 100})", "false");
-    shouldBe("e1.isPointInStroke({x: 73, y: 100})", "true");
+    shouldBeFalse("e1.isPointInStroke({})");
+    shouldBeTrue("e1.isPointInStroke({x: 151, y: 60})");
+    shouldBeFalse("e1.isPointInStroke({x: 149, y: 60})");
+    shouldBeTrue("e1.isPointInStroke({x: 85, y: 101})");
+    shouldBeFalse("e1.isPointInStroke({x: 85, y: 99})");
+    shouldBeTrue("e1.isPointInStroke({x: 19, y: 60})");
+    shouldBeFalse("e1.isPointInStroke({x: 21, y: 60})");
+    shouldBeTrue("e1.isPointInStroke({x: 85, y: 19})");
+    shouldBeFalse("e1.isPointInStroke({x: 85, y: 21})");
 
     debug("");
     debug("Test getTotalLength()");
@@ -51,16 +48,16 @@ function run() {
 
     debug("");
     debug("Test getPointAtLength()");
-    shouldBeCloseTo("e1.getPointAtLength(0).x", 150, 0.1);
-    shouldBeCloseTo("e1.getPointAtLength(0).y", 50, 0.1);
-    shouldBeCloseTo("e1.getPointAtLength(Math.PI * (75 + 50) / 4).x", 75, 3);
-    shouldBeCloseTo("e1.getPointAtLength(Math.PI * (75 + 50) / 4).y", 100, 3);
-    shouldBeCloseTo("e1.getPointAtLength(Math.PI * (75 + 50) / 2).x", 0, 3);
-    shouldBeCloseTo("e1.getPointAtLength(Math.PI * (75 + 50) / 2).y", 50, 3);
-    shouldBeCloseTo("e1.getPointAtLength(Math.PI * (75 + 50) / 8).x", 123, 3);
-    shouldBeCloseTo("e1.getPointAtLength(Math.PI * (75 + 50) / 8).y", 88, 3);
-    shouldBeCloseTo("e1.getPointAtLength(Math.PI * (75 + 50) * 2).x", 150, 3);
-    shouldBeCloseTo("e1.getPointAtLength(Math.PI * (75 + 50) * 2).y", 50, 3);
+    shouldBeCloseTo("e1.getPointAtLength(0).x", 160, 0.1);
+    shouldBeCloseTo("e1.getPointAtLength(0).y", 60, 0.1);
+    shouldBeCloseTo("e1.getPointAtLength(Math.PI * (75 + 50) / 4).x", 85, 3);
+    shouldBeCloseTo("e1.getPointAtLength(Math.PI * (75 + 50) / 4).y", 110, 3);
+    shouldBeCloseTo("e1.getPointAtLength(Math.PI * (75 + 50) / 2).x", 10, 3);
+    shouldBeCloseTo("e1.getPointAtLength(Math.PI * (75 + 50) / 2).y", 60, 3);
+    shouldBeCloseTo("e1.getPointAtLength(Math.PI * (75 + 50) / 8).x", 133, 3);
+    shouldBeCloseTo("e1.getPointAtLength(Math.PI * (75 + 50) / 8).y", 98, 3);
+    shouldBeCloseTo("e1.getPointAtLength(Math.PI * (75 + 50) * 2).x", 160, 3);
+    shouldBeCloseTo("e1.getPointAtLength(Math.PI * (75 + 50) * 2).y", 60, 3);
 
     finishJSTest();
 }
index bba4a22..76c45a6 100644 (file)
@@ -6,31 +6,36 @@ On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE
 
 Test isPointInFill()
 PASS l1.isPointInFill({}) is false
-PASS l1.isPointInFill({x: 0, y: 50}) is true
-PASS l1.isPointInFill({x: 50, y: 50}) is true
-PASS l1.isPointInFill({x: 100, y: 50}) is true
-PASS l1.isPointInFill({x: 50, y: 49}) is false
-PASS l1.isPointInFill({x: 50, y: 51}) is false
+PASS l1.isPointInFill({x: 0, y: 10}) is true
+PASS l1.isPointInFill({x: 0, y: 11}) is false
+PASS l1.isPointInFill({x: 100, y: 10}) is true
+PASS l1.isPointInFill({x: 100, y: 11}) is false
+PASS l1.isPointInFill({x: 101, y: 10}) is false
+PASS l1.isPointInFill({x: 101, y: 11}) is false
 
 Test isPointInStroke()
-PASS l1.isPointInStroke({x: 19, y: 50}) is false
-PASS l1.isPointInStroke({x: 20, y: 50}) is true
-PASS l1.isPointInStroke({x: 39, y: 50}) is true
-PASS l1.isPointInStroke({x: 40, y: 50}) is true
-PASS l1.isPointInStroke({x: 41, y: 50}) is false
+PASS l1.isPointInStroke({ }) is true
+PASS l1.isPointInStroke({x: 0, y: 0}) is true
+PASS l1.isPointInStroke({x: 0, y: 21}) is false
+PASS l1.isPointInStroke({x: 50, y: 0}) is true
+PASS l1.isPointInStroke({x: 50, y: 21}) is false
+PASS l1.isPointInStroke({x: 100, y: 0}) is true
+PASS l1.isPointInStroke({x: 100, y: 21}) is false
+PASS l1.isPointInStroke({x: 101, y: 0}) is false
+PASS l1.isPointInStroke({x: 101, y: 21}) is false
 
 Test getTotalLength()
-PASS l1.getTotalLength() is within 1 of 100
+PASS l1.getTotalLength() is within 0.1 of 100
 
 Test getPointAtLength()
 PASS l1.getPointAtLength(0).x is within 0.1 of 0
-PASS l1.getPointAtLength(0).y is within 0.1 of 50
+PASS l1.getPointAtLength(0).y is within 0.1 of 10
 PASS l1.getPointAtLength(50).x is within 0.1 of 50
-PASS l1.getPointAtLength(50).y is within 0.1 of 50
+PASS l1.getPointAtLength(50).y is within 0.1 of 10
 PASS l1.getPointAtLength(100).x is within 0.1 of 100
-PASS l1.getPointAtLength(100).y is within 0.1 of 50
+PASS l1.getPointAtLength(100).y is within 0.1 of 10
 PASS l1.getPointAtLength(150).x is within 0.1 of 100
-PASS l1.getPointAtLength(150).y is within 0.1 of 50
+PASS l1.getPointAtLength(150).y is within 0.1 of 10
 PASS successfullyParsed is true
 
 TEST COMPLETE
index d8dd88e..338a776 100644 (file)
@@ -4,7 +4,7 @@
 </head>
 <body onload="run()">
 <svg id="svg" xmlns="http://www.w3.org/2000/svg" width="200" height="200">
-    <line id="l1" x1="0" y1="50" x2="100" y2="50" fill="none" stroke="black" stroke-width="20" stroke-dashoffset="20" stroke-dasharray="20 20"/>
+    <line id="l1" x1="0" y1="10" x2="100" y2="10" fill="none" stroke="black" stroke-width="20"/>
 </svg>
 <p id="description"></p>
 <div id="console"></div>
@@ -20,34 +20,39 @@ function run() {
     debug("");
     debug("Test isPointInFill()");
     shouldBe("l1.isPointInFill({})", "false");
-    shouldBe("l1.isPointInFill({x: 0, y: 50})", "true");
-    shouldBe("l1.isPointInFill({x: 50, y: 50})", "true");
-    shouldBe("l1.isPointInFill({x: 100, y: 50})", "true");
-    shouldBe("l1.isPointInFill({x: 50, y: 49})", "false");
-    shouldBe("l1.isPointInFill({x: 50, y: 51})", "false");
+    shouldBeTrue("l1.isPointInFill({x: 0, y: 10})");
+    shouldBeFalse("l1.isPointInFill({x: 0, y: 11})");
+    shouldBeTrue("l1.isPointInFill({x: 100, y: 10})");
+    shouldBeFalse("l1.isPointInFill({x: 100, y: 11})");
+    shouldBeFalse("l1.isPointInFill({x: 101, y: 10})");
+    shouldBeFalse("l1.isPointInFill({x: 101, y: 11})");
 
     debug("");
     debug("Test isPointInStroke()");
-    shouldBe("l1.isPointInStroke({x: 19, y: 50})", "false");
-    shouldBe("l1.isPointInStroke({x: 20, y: 50})", "true");
-    shouldBe("l1.isPointInStroke({x: 39, y: 50})", "true");
-    shouldBe("l1.isPointInStroke({x: 40, y: 50})", "true");
-    shouldBe("l1.isPointInStroke({x: 41, y: 50})", "false");
+    shouldBeTrue("l1.isPointInStroke({ })");
+    shouldBeTrue("l1.isPointInStroke({x: 0, y: 0})");
+    shouldBeFalse("l1.isPointInStroke({x: 0, y: 21})");
+    shouldBeTrue("l1.isPointInStroke({x: 50, y: 0})");
+    shouldBeFalse("l1.isPointInStroke({x: 50, y: 21})");
+    shouldBeTrue("l1.isPointInStroke({x: 100, y: 0})");
+    shouldBeFalse("l1.isPointInStroke({x: 100, y: 21})");
+    shouldBeFalse("l1.isPointInStroke({x: 101, y: 0})");
+    shouldBeFalse("l1.isPointInStroke({x: 101, y: 21})");
 
     debug("");
     debug("Test getTotalLength()");
-    shouldBeCloseTo("l1.getTotalLength()", 100, 1);
+    shouldBeCloseTo("l1.getTotalLength()", 100, 0.1);
 
     debug("");
     debug("Test getPointAtLength()");
     shouldBeCloseTo("l1.getPointAtLength(0).x", 0, 0.1);
-    shouldBeCloseTo("l1.getPointAtLength(0).y", 50, 0.1);
+    shouldBeCloseTo("l1.getPointAtLength(0).y", 10, 0.1);
     shouldBeCloseTo("l1.getPointAtLength(50).x", 50, 0.1);
-    shouldBeCloseTo("l1.getPointAtLength(50).y", 50, 0.1);
+    shouldBeCloseTo("l1.getPointAtLength(50).y", 10, 0.1);
     shouldBeCloseTo("l1.getPointAtLength(100).x", 100, 0.1);
-    shouldBeCloseTo("l1.getPointAtLength(100).y", 50, 0.1);
+    shouldBeCloseTo("l1.getPointAtLength(100).y", 10, 0.1);
     shouldBeCloseTo("l1.getPointAtLength(150).x", 100, 0.1);
-    shouldBeCloseTo("l1.getPointAtLength(150).y", 50, 0.1);
+    shouldBeCloseTo("l1.getPointAtLength(150).y", 10, 0.1);
 
     finishJSTest();
 }
index 8ddf86a..cfba67c 100644 (file)
@@ -5,36 +5,40 @@ On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE
 
 
 Test isPointInFill()
-PASS p1.isPointInFill({}) is true
-PASS p1.isPointInFill({x: 100, y: 0}) is true
-PASS p1.isPointInFill({x: 152, y: 0}) is false
-PASS p1.isPointInFill({x: 150, y: 50}) is true
-PASS p1.isPointInFill({x: 150, y: 100}) is true
-PASS p1.isPointInFill({x: 150, y: 101}) is false
-PASS p1.isPointInFill({x: 100, y: 100}) is true
-PASS p1.isPointInFill({x: 75, y: 50}) is true
+PASS p1.isPointInFill({}) is false
+PASS p1.isPointInFill({x: 9, y: 9}) is false
+PASS p1.isPointInFill({x: 10, y: 10}) is true
+PASS p1.isPointInFill({x: 159, y: 9}) is false
+PASS p1.isPointInFill({x: 160, y: 10}) is true
+PASS p1.isPointInFill({x: 161, y: 110}) is false
+PASS p1.isPointInFill({x: 160, y: 109}) is true
+PASS p1.isPointInFill({x: 9, y: 109}) is false
+PASS p1.isPointInFill({x: 10, y: 110}) is true
 
 Test isPointInStroke()
-PASS p1.isPointInStroke({x: 0, y: 0}) is false
-PASS p1.isPointInStroke({x: 20, y: 0}) is true
-PASS p1.isPointInStroke({x: 150, y: 0}) is true
-PASS p1.isPointInStroke({x: 150, y: 35}) is true
-PASS p1.isPointInStroke({x: 75, y: 50}) is false
+PASS p1.isPointInStroke({x: 20, y: 20}) is true
+PASS p1.isPointInStroke({x: 21, y: 21}) is false
+PASS p1.isPointInStroke({x: 150, y:  20}) is true
+PASS p1.isPointInStroke({x: 149, y: 21}) is false
+PASS p1.isPointInStroke({x: 150, y: 100}) is true
+PASS p1.isPointInStroke({x: 149, y: 99}) is false
+PASS p1.isPointInStroke({x: 20, y: 100}) is true
+PASS p1.isPointInStroke({x: 21, y: 99}) is false
 
 Test getTotalLength()
 PASS p1.getTotalLength() is within 1 of 500
 
 Test getPointAtLength()
-PASS p1.getPointAtLength(0).x is within 0.1 of 0
-PASS p1.getPointAtLength(0).y is within 0.1 of 0
-PASS p1.getPointAtLength(150).x is within 0.1 of 150
-PASS p1.getPointAtLength(150).y is within 0.1 of 0
-PASS p1.getPointAtLength(200).x is within 0.1 of 150
-PASS p1.getPointAtLength(200).y is within 0.1 of 50
-PASS p1.getPointAtLength(300).x is within 0.1 of 100
-PASS p1.getPointAtLength(300).y is within 0.1 of 100
-PASS p1.getPointAtLength(450).x is within 0.1 of 0
-PASS p1.getPointAtLength(450).y is within 0.1 of 50
+PASS p1.getPointAtLength(0).x is within 0.1 of 10
+PASS p1.getPointAtLength(0).y is within 0.1 of 10
+PASS p1.getPointAtLength(150).x is within 0.1 of 160
+PASS p1.getPointAtLength(150).y is within 0.1 of 10
+PASS p1.getPointAtLength(250).x is within 0.1 of 160
+PASS p1.getPointAtLength(250).y is within 0.1 of 110
+PASS p1.getPointAtLength(400).x is within 0.1 of 10
+PASS p1.getPointAtLength(400).y is within 0.1 of 110
+PASS p1.getPointAtLength(500).x is within 0.1 of 10
+PASS p1.getPointAtLength(500).y is within 0.1 of 10
 PASS successfullyParsed is true
 
 TEST COMPLETE
index 6c9f059..433607b 100644 (file)
@@ -4,7 +4,7 @@
 </head>
 <body onload="run()">
 <svg id="svg" xmlns="http://www.w3.org/2000/svg" width="200" height="200">
-    <polygon id="p1" points="0,0 150,0 150,100 0,100" fill="none" stroke="black" stroke-width="20" stroke-dashoffset="20" stroke-dasharray="20 20"/>
+    <polygon id="p1" points="10,10 160,10 160,110 10,110" fill="none" stroke="black" stroke-width="20"/>
 </svg>
 <p id="description"></p>
 <div id="console"></div>
@@ -19,22 +19,26 @@ function run() {
 
     debug("");
     debug("Test isPointInFill()");
-    shouldBe("p1.isPointInFill({})", "true");
-    shouldBe("p1.isPointInFill({x: 100, y: 0})", "true");
-    shouldBe("p1.isPointInFill({x: 152, y: 0})", "false");
-    shouldBe("p1.isPointInFill({x: 150, y: 50})", "true");
-    shouldBe("p1.isPointInFill({x: 150, y: 100})", "true");
-    shouldBe("p1.isPointInFill({x: 150, y: 101})", "false");
-    shouldBe("p1.isPointInFill({x: 100, y: 100})", "true");
-    shouldBe("p1.isPointInFill({x: 75, y: 50})", "true");
+    shouldBeFalse("p1.isPointInFill({})");
+    shouldBeFalse("p1.isPointInFill({x: 9, y: 9})");
+    shouldBeTrue("p1.isPointInFill({x: 10, y: 10})");
+    shouldBeFalse("p1.isPointInFill({x: 159, y: 9})");
+    shouldBeTrue("p1.isPointInFill({x: 160, y: 10})");
+    shouldBeFalse("p1.isPointInFill({x: 161, y: 110})");
+    shouldBeTrue("p1.isPointInFill({x: 160, y: 109})");
+    shouldBeFalse("p1.isPointInFill({x: 9, y: 109})");
+    shouldBeTrue("p1.isPointInFill({x: 10, y: 110})");
 
     debug("");
     debug("Test isPointInStroke()");
-    shouldBe("p1.isPointInStroke({x: 0, y: 0})", "false");
-    shouldBe("p1.isPointInStroke({x: 20, y: 0})", "true");
-    shouldBe("p1.isPointInStroke({x: 150, y: 0})", "true");
-    shouldBe("p1.isPointInStroke({x: 150, y: 35})", "true");
-    shouldBe("p1.isPointInStroke({x: 75, y: 50})", "false");
+    shouldBeTrue("p1.isPointInStroke({x: 20, y: 20})");
+    shouldBeFalse("p1.isPointInStroke({x: 21, y: 21})");
+    shouldBeTrue("p1.isPointInStroke({x: 150, y:  20})");
+    shouldBeFalse("p1.isPointInStroke({x: 149, y: 21})");
+    shouldBeTrue("p1.isPointInStroke({x: 150, y: 100})");
+    shouldBeFalse("p1.isPointInStroke({x: 149, y: 99})");
+    shouldBeTrue("p1.isPointInStroke({x: 20, y: 100})");
+    shouldBeFalse("p1.isPointInStroke({x: 21, y: 99})");
 
     debug("");
     debug("Test getTotalLength()");
@@ -42,16 +46,16 @@ function run() {
 
     debug("");
     debug("Test getPointAtLength()");
-    shouldBeCloseTo("p1.getPointAtLength(0).x", 0, 0.1);
-    shouldBeCloseTo("p1.getPointAtLength(0).y", 0, 0.1);
-    shouldBeCloseTo("p1.getPointAtLength(150).x", 150, 0.1);
-    shouldBeCloseTo("p1.getPointAtLength(150).y", 0, 0.1);
-    shouldBeCloseTo("p1.getPointAtLength(200).x", 150, 0.1);
-    shouldBeCloseTo("p1.getPointAtLength(200).y", 50, 0.1);
-    shouldBeCloseTo("p1.getPointAtLength(300).x", 100, 0.1);
-    shouldBeCloseTo("p1.getPointAtLength(300).y", 100, 0.1);
-    shouldBeCloseTo("p1.getPointAtLength(450).x", 0, 0.1);
-    shouldBeCloseTo("p1.getPointAtLength(450).y", 50, 0.1);
+    shouldBeCloseTo("p1.getPointAtLength(0).x", 10, 0.1);
+    shouldBeCloseTo("p1.getPointAtLength(0).y", 10, 0.1);
+    shouldBeCloseTo("p1.getPointAtLength(150).x", 160, 0.1);
+    shouldBeCloseTo("p1.getPointAtLength(150).y", 10, 0.1);
+    shouldBeCloseTo("p1.getPointAtLength(250).x", 160, 0.1);
+    shouldBeCloseTo("p1.getPointAtLength(250).y", 110, 0.1);
+    shouldBeCloseTo("p1.getPointAtLength(400).x", 10, 0.1);
+    shouldBeCloseTo("p1.getPointAtLength(400).y", 110, 0.1);
+    shouldBeCloseTo("p1.getPointAtLength(500).x", 10, 0.1);
+    shouldBeCloseTo("p1.getPointAtLength(500).y", 10, 0.1);
 
     finishJSTest();
 }
index 9e6bd50..c9d70de 100644 (file)
@@ -5,36 +5,40 @@ On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE
 
 
 Test isPointInFill()
-PASS p1.isPointInFill({}) is true
-PASS p1.isPointInFill({x: 100, y: 0}) is true
-PASS p1.isPointInFill({x: 152, y: 0}) is false
-PASS p1.isPointInFill({x: 150, y: 50}) is true
-PASS p1.isPointInFill({x: 150, y: 100}) is true
-PASS p1.isPointInFill({x: 150, y: 101}) is false
-PASS p1.isPointInFill({x: 100, y: 100}) is true
-PASS p1.isPointInFill({x: 75, y: 50}) is true
+PASS p1.isPointInFill({}) is false
+PASS p1.isPointInFill({x: 9, y: 9}) is false
+PASS p1.isPointInFill({x: 10, y: 10}) is true
+PASS p1.isPointInFill({x: 159, y: 9}) is false
+PASS p1.isPointInFill({x: 160, y: 10}) is true
+PASS p1.isPointInFill({x: 161, y: 110}) is false
+PASS p1.isPointInFill({x: 160, y: 109}) is true
+PASS p1.isPointInFill({x: 9, y: 109}) is false
+PASS p1.isPointInFill({x: 10, y: 110}) is true
 
 Test isPointInStroke()
-PASS p1.isPointInStroke({x: 0, y: 0}) is false
-PASS p1.isPointInStroke({x: 20, y: 0}) is true
-PASS p1.isPointInStroke({x: 150, y: 0}) is true
-PASS p1.isPointInStroke({x: 150, y: 35}) is true
-PASS p1.isPointInStroke({x: 75, y: 50}) is false
+PASS p1.isPointInStroke({x: 20, y: 20}) is true
+PASS p1.isPointInStroke({x: 21, y: 21}) is false
+PASS p1.isPointInStroke({x: 150, y:  20}) is true
+PASS p1.isPointInStroke({x: 149, y: 21}) is false
+PASS p1.isPointInStroke({x: 150, y: 100}) is true
+PASS p1.isPointInStroke({x: 149, y: 99}) is false
+PASS p1.isPointInStroke({x: 20, y: 100}) is true
+PASS p1.isPointInStroke({x: 21, y: 99}) is false
 
 Test getTotalLength()
 PASS p1.getTotalLength() is within 1 of 400
 
 Test getPointAtLength()
-PASS p1.getPointAtLength(0).x is within 0.1 of 0
-PASS p1.getPointAtLength(0).y is within 0.1 of 0
-PASS p1.getPointAtLength(150).x is within 0.1 of 150
-PASS p1.getPointAtLength(150).y is within 0.1 of 0
-PASS p1.getPointAtLength(200).x is within 0.1 of 150
-PASS p1.getPointAtLength(200).y is within 0.1 of 50
-PASS p1.getPointAtLength(300).x is within 0.1 of 100
-PASS p1.getPointAtLength(300).y is within 0.1 of 100
-PASS p1.getPointAtLength(450).x is within 0.1 of 0
-PASS p1.getPointAtLength(450).y is within 0.1 of 100
+PASS p1.getPointAtLength(0).x is within 0.1 of 10
+PASS p1.getPointAtLength(0).y is within 0.1 of 10
+PASS p1.getPointAtLength(150).x is within 0.1 of 160
+PASS p1.getPointAtLength(150).y is within 0.1 of 10
+PASS p1.getPointAtLength(200).x is within 0.1 of 160
+PASS p1.getPointAtLength(200).y is within 0.1 of 60
+PASS p1.getPointAtLength(300).x is within 0.1 of 110
+PASS p1.getPointAtLength(300).y is within 0.1 of 110
+PASS p1.getPointAtLength(450).x is within 0.1 of 10
+PASS p1.getPointAtLength(450).y is within 0.1 of 110
 PASS successfullyParsed is true
 
 TEST COMPLETE
index 6549f18..75ed991 100644 (file)
@@ -4,7 +4,7 @@
 </head>
 <body onload="run()">
 <svg id="svg" xmlns="http://www.w3.org/2000/svg" width="200" height="200">
-    <polyline id="p1" points="0,0 150,0 150,100 0,100" fill="none" stroke="black" stroke-width="20" stroke-dashoffset="20" stroke-dasharray="20 20"/>
+    <polyline id="p1" points="10,10 160,10 160,110 10,110" fill="none" stroke="black" stroke-width="20"/>
 </svg>
 <p id="description"></p>
 <div id="console"></div>
@@ -19,22 +19,26 @@ function run() {
 
     debug("");
     debug("Test isPointInFill()");
-    shouldBe("p1.isPointInFill({})", "true");
-    shouldBe("p1.isPointInFill({x: 100, y: 0})", "true");
-    shouldBe("p1.isPointInFill({x: 152, y: 0})", "false");
-    shouldBe("p1.isPointInFill({x: 150, y: 50})", "true");
-    shouldBe("p1.isPointInFill({x: 150, y: 100})", "true");
-    shouldBe("p1.isPointInFill({x: 150, y: 101})", "false");
-    shouldBe("p1.isPointInFill({x: 100, y: 100})", "true");
-    shouldBe("p1.isPointInFill({x: 75, y: 50})", "true");
+    shouldBeFalse("p1.isPointInFill({})");
+    shouldBeFalse("p1.isPointInFill({x: 9, y: 9})");
+    shouldBeTrue("p1.isPointInFill({x: 10, y: 10})");
+    shouldBeFalse("p1.isPointInFill({x: 159, y: 9})");
+    shouldBeTrue("p1.isPointInFill({x: 160, y: 10})");
+    shouldBeFalse("p1.isPointInFill({x: 161, y: 110})");
+    shouldBeTrue("p1.isPointInFill({x: 160, y: 109})");
+    shouldBeFalse("p1.isPointInFill({x: 9, y: 109})");
+    shouldBeTrue("p1.isPointInFill({x: 10, y: 110})");
 
     debug("");
     debug("Test isPointInStroke()");
-    shouldBe("p1.isPointInStroke({x: 0, y: 0})", "false");
-    shouldBe("p1.isPointInStroke({x: 20, y: 0})", "true");
-    shouldBe("p1.isPointInStroke({x: 150, y: 0})", "true");
-    shouldBe("p1.isPointInStroke({x: 150, y: 35})", "true");
-    shouldBe("p1.isPointInStroke({x: 75, y: 50})", "false");
+    shouldBeTrue("p1.isPointInStroke({x: 20, y: 20})");
+    shouldBeFalse("p1.isPointInStroke({x: 21, y: 21})");
+    shouldBeTrue("p1.isPointInStroke({x: 150, y:  20})");
+    shouldBeFalse("p1.isPointInStroke({x: 149, y: 21})");
+    shouldBeTrue("p1.isPointInStroke({x: 150, y: 100})");
+    shouldBeFalse("p1.isPointInStroke({x: 149, y: 99})");
+    shouldBeTrue("p1.isPointInStroke({x: 20, y: 100})");
+    shouldBeFalse("p1.isPointInStroke({x: 21, y: 99})");
 
     debug("");
     debug("Test getTotalLength()");
@@ -42,16 +46,16 @@ function run() {
 
     debug("");
     debug("Test getPointAtLength()");
-    shouldBeCloseTo("p1.getPointAtLength(0).x", 0, 0.1);
-    shouldBeCloseTo("p1.getPointAtLength(0).y", 0, 0.1);
-    shouldBeCloseTo("p1.getPointAtLength(150).x", 150, 0.1);
-    shouldBeCloseTo("p1.getPointAtLength(150).y", 0, 0.1);
-    shouldBeCloseTo("p1.getPointAtLength(200).x", 150, 0.1);
-    shouldBeCloseTo("p1.getPointAtLength(200).y", 50, 0.1);
-    shouldBeCloseTo("p1.getPointAtLength(300).x", 100, 0.1);
-    shouldBeCloseTo("p1.getPointAtLength(300).y", 100, 0.1);
-    shouldBeCloseTo("p1.getPointAtLength(450).x", 0, 0.1);
-    shouldBeCloseTo("p1.getPointAtLength(450).y", 100, 0.1);
+    shouldBeCloseTo("p1.getPointAtLength(0).x", 10, 0.1);
+    shouldBeCloseTo("p1.getPointAtLength(0).y", 10, 0.1);
+    shouldBeCloseTo("p1.getPointAtLength(150).x", 160, 0.1);
+    shouldBeCloseTo("p1.getPointAtLength(150).y", 10, 0.1);
+    shouldBeCloseTo("p1.getPointAtLength(200).x", 160, 0.1);
+    shouldBeCloseTo("p1.getPointAtLength(200).y", 60, 0.1);
+    shouldBeCloseTo("p1.getPointAtLength(300).x", 110, 0.1);
+    shouldBeCloseTo("p1.getPointAtLength(300).y", 110, 0.1);
+    shouldBeCloseTo("p1.getPointAtLength(450).x", 10, 0.1);
+    shouldBeCloseTo("p1.getPointAtLength(450).y", 110, 0.1);
 
     finishJSTest();
 }
index c2b3260..e57e3e8 100644 (file)
@@ -6,44 +6,39 @@ On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE
 
 Test isPointInFill()
 PASS r1.isPointInFill({}) is false
-PASS r1.isPointInFill({x: 48, y: 0}) is false
-PASS r1.isPointInFill({x: 52, y: 0}) is true
-PASS r1.isPointInFill({x: 98, y: 0}) is true
-PASS r1.isPointInFill({x: 102, y: 0}) is false
-PASS r1.isPointInFill({x: 150, y: 38}) is false
-PASS r1.isPointInFill({x: 150, y: 42}) is true
-PASS r1.isPointInFill({x: 150, y: 58}) is true
-PASS r1.isPointInFill({x: 150, y: 62}) is false
-PASS r1.isPointInFill({x: 48, y: 100}) is false
-PASS r1.isPointInFill({x: 52, y: 100}) is true
-PASS r1.isPointInFill({x: 98, y: 100}) is true
-PASS r1.isPointInFill({x: 102, y: 100}) is false
-PASS r1.isPointInFill({x: 0, y: 38}) is false
-PASS r1.isPointInFill({x: 0, y: 42}) is true
-PASS r1.isPointInFill({x: 0, y: 58}) is true
-PASS r1.isPointInFill({x: 0, y: 62}) is false
-PASS r1.isPointInFill({x: 75, y: 50}) is true
+PASS r1.isPointInFill({x: 9, y: 9}) is false
+PASS r1.isPointInFill({x: 10, y: 10}) is true
+PASS r1.isPointInFill({x: 159, y: 9}) is false
+PASS r1.isPointInFill({x: 160, y: 10}) is true
+PASS r1.isPointInFill({x: 161, y: 110}) is false
+PASS r1.isPointInFill({x: 160, y: 109}) is true
+PASS r1.isPointInFill({x: 9, y: 109}) is false
+PASS r1.isPointInFill({x: 10, y: 110}) is true
 
 Test isPointInStroke()
-PASS r1.isPointInStroke({x: 52, y: 0}) is false
-PASS r1.isPointInStroke({x: 72, y: 0}) is true
-PASS r1.isPointInStroke({x: 92, y: 0}) is false
-PASS r1.isPointInStroke({x: 112, y: 10}) is true
+PASS r1.isPointInStroke({x: 20, y: 20}) is true
+PASS r1.isPointInStroke({x: 21, y: 21}) is false
+PASS r1.isPointInStroke({x: 150, y:  20}) is true
+PASS r1.isPointInStroke({x: 149, y: 21}) is false
+PASS r1.isPointInStroke({x: 150, y: 100}) is true
+PASS r1.isPointInStroke({x: 149, y: 99}) is false
+PASS r1.isPointInStroke({x: 20, y: 100}) is true
+PASS r1.isPointInStroke({x: 21, y: 99}) is false
 
 Test getTotalLength()
-PASS r1.getTotalLength() is within 1 of 424
+PASS r1.getTotalLength() is within 0.1 of 500
 
 Test getPointAtLength()
-PASS r1.getPointAtLength(0).x is within 1 of 50
-PASS r1.getPointAtLength(0).y is within 1 of 0
-PASS r1.getPointAtLength(25).x is within 1 of 75
-PASS r1.getPointAtLength(25).y is within 1 of 0
-PASS r1.getPointAtLength(200).x is within 3 of 112
-PASS r1.getPointAtLength(200).y is within 3 of 100
-PASS r1.getPointAtLength(300).x is within 3 of 14
-PASS r1.getPointAtLength(300).y is within 3 of 88
-PASS r1.getPointAtLength(400).x is within 3 of 27
-PASS r1.getPointAtLength(400).y is within 3 of 5
+PASS r1.getPointAtLength(0).x is within 0.1 of 10
+PASS r1.getPointAtLength(0).y is within 0.1 of 10
+PASS r1.getPointAtLength(150).x is within 0.1 of 160
+PASS r1.getPointAtLength(150).y is within 0.1 of 10
+PASS r1.getPointAtLength(250).x is within 0.1 of 160
+PASS r1.getPointAtLength(250).y is within 0.1 of 110
+PASS r1.getPointAtLength(400).x is within 0.1 of 10
+PASS r1.getPointAtLength(400).y is within 0.1 of 110
+PASS r1.getPointAtLength(500).x is within 0.1 of 10
+PASS r1.getPointAtLength(500).y is within 0.1 of 10
 PASS successfullyParsed is true
 
 TEST COMPLETE
index f83f9a2..f0e246d 100644 (file)
@@ -4,7 +4,7 @@
 </head>
 <body onload="run()">
 <svg id="svg" xmlns="http://www.w3.org/2000/svg" width="200" height="200">
-    <rect id="r1" rx="50" ry="40" width="150" height="100" fill="none" stroke="black" stroke-width="20" stroke-dashoffset="20" stroke-dasharray="20 20"/>
+    <rect id="r1" x="10" y="10" width="150" height="100" fill="none" stroke="black" stroke-width="20"/>
 </svg>
 <p id="description"></p>
 <div id="console"></div>
@@ -19,48 +19,43 @@ function run() {
 
     debug("");
     debug("Test isPointInFill()");
-    shouldBe("r1.isPointInFill({})", "false");
-    shouldBe("r1.isPointInFill({x: 48, y: 0})", "false");
-    shouldBe("r1.isPointInFill({x: 52, y: 0})", "true");
-    shouldBe("r1.isPointInFill({x: 98, y: 0})", "true");
-    shouldBe("r1.isPointInFill({x: 102, y: 0})", "false");
-    shouldBe("r1.isPointInFill({x: 150, y: 38})", "false");
-    shouldBe("r1.isPointInFill({x: 150, y: 42})", "true");
-    shouldBe("r1.isPointInFill({x: 150, y: 58})", "true");
-    shouldBe("r1.isPointInFill({x: 150, y: 62})", "false");
-    shouldBe("r1.isPointInFill({x: 48, y: 100})", "false");
-    shouldBe("r1.isPointInFill({x: 52, y: 100})", "true");
-    shouldBe("r1.isPointInFill({x: 98, y: 100})", "true");
-    shouldBe("r1.isPointInFill({x: 102, y: 100})", "false");
-    shouldBe("r1.isPointInFill({x: 0, y: 38})", "false");
-    shouldBe("r1.isPointInFill({x: 0, y: 42})", "true");
-    shouldBe("r1.isPointInFill({x: 0, y: 58})", "true");
-    shouldBe("r1.isPointInFill({x: 0, y: 62})", "false");
-    shouldBe("r1.isPointInFill({x: 75, y: 50})", "true");
+    shouldBeFalse("r1.isPointInFill({})");
+    shouldBeFalse("r1.isPointInFill({x: 9, y: 9})");
+    shouldBeTrue("r1.isPointInFill({x: 10, y: 10})");
+    shouldBeFalse("r1.isPointInFill({x: 159, y: 9})");
+    shouldBeTrue("r1.isPointInFill({x: 160, y: 10})");
+    shouldBeFalse("r1.isPointInFill({x: 161, y: 110})");
+    shouldBeTrue("r1.isPointInFill({x: 160, y: 109})");
+    shouldBeFalse("r1.isPointInFill({x: 9, y: 109})");
+    shouldBeTrue("r1.isPointInFill({x: 10, y: 110})");
 
     debug("");
     debug("Test isPointInStroke()");
-    shouldBe("r1.isPointInStroke({x: 52, y: 0})", "false");
-    shouldBe("r1.isPointInStroke({x: 72, y: 0})", "true");
-    shouldBe("r1.isPointInStroke({x: 92, y: 0})", "false");
-    shouldBe("r1.isPointInStroke({x: 112, y: 10})", "true");
+    shouldBeTrue("r1.isPointInStroke({x: 20, y: 20})");
+    shouldBeFalse("r1.isPointInStroke({x: 21, y: 21})");
+    shouldBeTrue("r1.isPointInStroke({x: 150, y:  20})");
+    shouldBeFalse("r1.isPointInStroke({x: 149, y: 21})");
+    shouldBeTrue("r1.isPointInStroke({x: 150, y: 100})");
+    shouldBeFalse("r1.isPointInStroke({x: 149, y: 99})");
+    shouldBeTrue("r1.isPointInStroke({x: 20, y: 100})");
+    shouldBeFalse("r1.isPointInStroke({x: 21, y: 99})");
 
     debug("");
     debug("Test getTotalLength()");
-    shouldBeCloseTo("r1.getTotalLength()", 424, 1);
+    shouldBeCloseTo("r1.getTotalLength()", 500, 0.1);
 
     debug("");
     debug("Test getPointAtLength()");
-    shouldBeCloseTo("r1.getPointAtLength(0).x", 50, 1);
-    shouldBeCloseTo("r1.getPointAtLength(0).y", 0, 1);
-    shouldBeCloseTo("r1.getPointAtLength(25).x", 75, 1);
-    shouldBeCloseTo("r1.getPointAtLength(25).y", 0, 1);
-    shouldBeCloseTo("r1.getPointAtLength(200).x", 112, 3);
-    shouldBeCloseTo("r1.getPointAtLength(200).y", 100, 3);
-    shouldBeCloseTo("r1.getPointAtLength(300).x", 14, 3);
-    shouldBeCloseTo("r1.getPointAtLength(300).y", 88, 3);
-    shouldBeCloseTo("r1.getPointAtLength(400).x", 27, 3);
-    shouldBeCloseTo("r1.getPointAtLength(400).y", 5, 3);
+    shouldBeCloseTo("r1.getPointAtLength(0).x", 10, 0.1);
+    shouldBeCloseTo("r1.getPointAtLength(0).y", 10, 0.1);
+    shouldBeCloseTo("r1.getPointAtLength(150).x", 160, 0.1);
+    shouldBeCloseTo("r1.getPointAtLength(150).y", 10, 0.1);
+    shouldBeCloseTo("r1.getPointAtLength(250).x", 160, 0.1);
+    shouldBeCloseTo("r1.getPointAtLength(250).y", 110, 0.1);
+    shouldBeCloseTo("r1.getPointAtLength(400).x", 10, 0.1);
+    shouldBeCloseTo("r1.getPointAtLength(400).y", 110, 0.1);
+    shouldBeCloseTo("r1.getPointAtLength(500).x", 10, 0.1);
+    shouldBeCloseTo("r1.getPointAtLength(500).y", 10, 0.1);
 
     finishJSTest();
 }
index 238621a..4aa920f 100644 (file)
@@ -1,3 +1,42 @@
+2019-11-18  Said Abou-Hallawa  <sabouhallawa@apple.com>
+
+        Fix getTotalLength() and getPointAtLength() for optimized rect and ellipse renderers
+        https://bugs.webkit.org/show_bug.cgi?id=204213
+
+        Reviewed by Simon Fraser.
+
+        If the renderer does not create a Path to draw the shape, we need to create
+        a temporary Path from the SVGElement then use it to answer the questions
+        of these functions and finally delete it.
+
+        * platform/graphics/Path.cpp:
+        (WebCore::Path::traversalStateAtLength const):
+        (WebCore::Path::pointAtLength const):
+        No need for the 'success'. It is never used.
+
+        (WebCore::Path::normalAngleAtLength const): Deleted.
+        This function is not used.
+
+        * platform/graphics/Path.h:
+        * rendering/svg/RenderSVGShape.cpp:
+        (WebCore::RenderSVGShape::updateShapeFromElement):
+        Creating the Path from the SVGELement is moved to createPath().
+
+        (WebCore::RenderSVGShape::getTotalLength const):
+        (WebCore::RenderSVGShape::getPointAtLength const):
+        Create a temporary Path if the renderer draws the shape without creating
+        the Path.
+
+        (WebCore::RenderSVGShape::createPath const):
+        * rendering/svg/RenderSVGShape.h:
+
+        * rendering/svg/SVGTextLayoutEngine.cpp:
+        (WebCore::SVGTextLayoutEngine::layoutTextOnLineOrPath):
+        * svg/SVGAnimateMotionElement.cpp:
+        (WebCore::SVGAnimateMotionElement::buildTransformForProgress):
+        Use PathTraversalState.success() to check instead of getting the same
+        value through an argument to traversalStateAtLength().
+
 2019-11-18  Zalan Bujtas  <zalan@apple.com>
 
         Block layout invalidation logic triggers excessive layout on height percentage descendants
index 29692aa..a099ad5 100644 (file)
@@ -53,7 +53,7 @@ float Path::length() const
 }
 #endif
 
-PathTraversalState Path::traversalStateAtLength(float length, bool& success) const
+PathTraversalState Path::traversalStateAtLength(float length) const
 {
     PathTraversalState traversalState(PathTraversalState::Action::VectorAtLength, length);
 
@@ -61,18 +61,12 @@ PathTraversalState Path::traversalStateAtLength(float length, bool& success) con
         traversalState.processPathElement(element);
     });
 
-    success = traversalState.success();
     return traversalState;
 }
 
-FloatPoint Path::pointAtLength(float length, bool& success) const
+FloatPoint Path::pointAtLength(float length) const
 {
-    return traversalStateAtLength(length, success).current();
-}
-
-float Path::normalAngleAtLength(float length, bool& success) const
-{
-    return traversalStateAtLength(length, success).normalAngle();
+    return traversalStateAtLength(length).current();
 }
 
 void Path::addRoundedRect(const FloatRect& rect, const FloatSize& roundingRadii, RoundedRectStrategy strategy)
index 77d32e7..7a2f4a5 100644 (file)
@@ -134,9 +134,8 @@ namespace WebCore {
         FloatRect strokeBoundingRect(StrokeStyleApplier* = 0) const;
 
         float length() const;
-        PathTraversalState traversalStateAtLength(float length, bool& success) const;
-        FloatPoint pointAtLength(float length, bool& success) const;
-        float normalAngleAtLength(float length, bool& success) const;
+        PathTraversalState traversalStateAtLength(float length) const;
+        FloatPoint pointAtLength(float length) const;
 
         WEBCORE_EXPORT void clear();
         bool isNull() const { return !m_path; }
index 544e51e..7d8861a 100644 (file)
@@ -79,7 +79,7 @@ RenderSVGShape::~RenderSVGShape() = default;
 
 void RenderSVGShape::updateShapeFromElement()
 {
-    m_path = makeUnique<Path>(pathFromGraphicsElement(&graphicsElement()));
+    m_path = createPath();
     processMarkerPositions();
 
     m_fillBoundingBox = calculateObjectBoundingBox();
@@ -347,19 +347,12 @@ bool RenderSVGShape::isPointInStroke(const FloatPoint& point)
 
 float RenderSVGShape::getTotalLength() const
 {
-    if (m_path)
-        return m_path->length();
-
-    return 0;
+    return hasPath() ? path().length() : createPath()->length();
 }
 
 FloatPoint RenderSVGShape::getPointAtLength(float distance) const
 {
-    if (!m_path)
-        return { };
-
-    bool isValid;
-    return m_path->pointAtLength(distance, isValid);
+    return hasPath() ? path().pointAtLength(distance) : createPath()->pointAtLength(distance);
 }
 
 bool RenderSVGShape::nodeAtFloatPoint(const HitTestRequest& request, HitTestResult& result, const FloatPoint& pointInParent, HitTestAction hitTestAction)
@@ -503,6 +496,11 @@ void RenderSVGShape::drawMarkers(PaintInfo& paintInfo)
     }
 }
 
+std::unique_ptr<Path> RenderSVGShape::createPath() const
+{
+    return makeUnique<Path>(pathFromGraphicsElement(&graphicsElement()));
+}
+
 void RenderSVGShape::processMarkerPositions()
 {
     m_markerPositions.clear();
index 7f250bf..d2467d6 100644 (file)
@@ -122,6 +122,8 @@ private:
 
     bool shouldGenerateMarkerPositions() const;
     FloatRect markerRect(float strokeWidth) const;
+    
+    std::unique_ptr<Path> createPath() const;
     void processMarkerPositions();
 
     void fillShape(const RenderStyle&, GraphicsContext&);
index 2333701..a0e9237 100644 (file)
@@ -531,9 +531,8 @@ void SVGTextLayoutEngine::layoutTextOnLineOrPath(SVGInlineTextBox& textBox, Rend
             if (textPathOffset > m_textPathLength)
                 break;
 
-            bool success = false;
-            auto traversalState(m_textPath.traversalStateAtLength(textPathOffset, success));
-            ASSERT(success);
+            auto traversalState(m_textPath.traversalStateAtLength(textPathOffset));
+            ASSERT(traversalState.success());
 
             FloatPoint point = traversalState.current();
             x = point.x();
index fd34544..d7db0ac 100644 (file)
@@ -190,10 +190,9 @@ void SVGAnimateMotionElement::buildTransformForProgress(AffineTransform* transfo
 {
     ASSERT(!m_animationPath.isEmpty());
 
-    bool success = false;
     float positionOnPath = m_animationPath.length() * percentage;
-    auto traversalState(m_animationPath.traversalStateAtLength(positionOnPath, success));
-    if (!success)
+    auto traversalState(m_animationPath.traversalStateAtLength(positionOnPath));
+    if (!traversalState.success())
         return;
 
     FloatPoint position = traversalState.current();