Bug 1943761 - Add class alignment to the mozsearch analysis file. r=asuth
[gecko.git] / dom / svg / test / test_pathAnimInterpolation.xhtml
blobcda9607670482ddd594d8439ac468e2d06b6fa25
1 <html xmlns="http://www.w3.org/1999/xhtml">
2 <!--
3 https://bugzilla.mozilla.org/show_bug.cgi?id=619498
4 -->
5 <head>
6 <title>Test interpolation between different path segment types</title>
7 <script src="/tests/SimpleTest/SimpleTest.js"></script>
8 <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
9 </head>
10 <body>
11 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=619498">Mozilla Bug 619498</a>
12 <svg xmlns="http://www.w3.org/2000/svg" id="svg" visibility="hidden"
13 onload="this.pauseAnimations()"/>
14 <script type="application/javascript"><![CDATA[
15 SimpleTest.waitForExplicitFinish();
17 var gSVG = document.getElementById("svg");
19 // Array of all subtests to run. This is populated by addTest.
20 var gTests = [];
22 // Array of all path segment types.
23 var gTypes = "ZMmLlCcQqAaHhVvSsTt".split("");
25 // Property names on the SVGPathSeg objects for the given segment type, in the
26 // order that they would appear in a path data string.
27 var gArgumentNames = {
28 Z: [],
29 M: ["x", "y"],
30 L: ["x", "y"],
31 C: ["x1", "y1", "x2", "y2", "x", "y"],
32 Q: ["x1", "y1", "x", "y"],
33 A: ["r1", "r2", "angle", "largeArcFlag", "sweepFlag", "x", "y"],
34 H: ["x"],
35 V: ["y"],
36 S: ["x2", "y2", "x", "y"],
37 T: ["x", "y"],
40 // All of these prefixes leave the current point at 100,100. Some of them
41 // affect the implied control point if followed by a smooth quadratic or
42 // cubic segment, but no valid interpolations depend on those control points.
43 var gPrefixes = [
44 [1, "M100,100"],
45 [2, "M50,50 M100,100"],
46 [2, "M50,50 m50,50"],
47 [2, "M50,50 L100,100"],
48 [2, "M50,50 l50,50"],
49 [3, "M50,50 H100 V100"],
50 [3, "M50,50 h50 V100"],
51 [3, "M50,50 H100 v50"],
52 [2, "M50,50 A10,10,10,0,0,100,100"],
53 [2, "M50,50 a10,10,10,0,0,50,50"],
54 [4, "M50,50 l50,50 Z m50,50"],
56 // These leave the quadratic implied control point at 125,125.
57 [2, "M50,50 Q75,75,100,100"],
58 [2, "M50,50 q25,25,50,50"],
59 [2, "M75,75 T100,100"],
60 [2, "M75,75 t25,25"],
61 [3, "M50,50 T62.5,62.5 t37.5,37.5"],
62 [3, "M50,50 T62.5,62.5 T100,100"],
63 [3, "M50,50 t12.5,12.5 t37.5,37.5"],
64 [3, "M50,50 t12.5,12.5 T100,100"],
65 [3, "M50,50 Q50,50,62.5,62.5 t37.5,37.5"],
66 [3, "M50,50 Q50,50,62.5,62.5 T100,100"],
67 [3, "M50,50 q0,0,12.5,12.5 t37.5,37.5"],
68 [3, "M50,50 q0,0,12.5,12.5 T100,100"],
70 // These leave the cubic implied control point at 125,125.
71 [2, "M50,50 C10,10,75,75,100,100"],
72 [2, "M50,50 c10,10,25,25,50,50"],
73 [2, "M50,50 S75,75,100,100"],
74 [2, "M50,50 s25,25,50,50"],
75 [3, "M50,50 S10,10,75,75 S75,75,100,100"],
76 [3, "M50,50 S10,10,75,75 s0,0,25,25"],
77 [3, "M50,50 s10,10,25,25 S75,75,100,100"],
78 [3, "M50,50 s10,10,25,25 s0,0,25,25"],
79 [3, "M50,50 C10,10,20,20,75,75 S75,75,100,100"],
80 [3, "M50,50 C10,10,20,20,75,75 s0,0,25,25"],
81 [3, "M50,50 c10,10,20,20,25,25 S75,75,100,100"],
82 [3, "M50,50 c10,10,20,20,25,25 s0,0,25,25"],
85 // These are all of the suffixes whose result is not dependent on whether the
86 // preceding segment types are quadratic or cubic types. Each entry is:
88 // "<fromType><toType>": [fromArguments,
89 // toArguments,
90 // expectedArguments,
91 // expectedArgumentsAdditive]
93 // As an example:
95 // "Mm": [[10, 20], [30, 40], [-30, -20], [-120, -100]]
97 // This will testing interpolating between "M10,20" and "m30,40". All of the
98 // these tests assume that the current point is left at 100,100. So the above
99 // entry represents two kinds of tests, one where additive and one not:
101 // <path d="... M10,20">
102 // <animate attributeName="d" from="... M10,20" to="... m30,40"/>
103 // </path>
105 // and
107 // <path d="... M10,20">
108 // <animate attributeName="d" from="... M10,20" to="... m30,40"
109 // additive="sum"/>
110 // </path>
112 // where the "..." is some prefix that leaves the current point at 100,100.
113 // Each of the suffixes here in gSuffixes will be paired with each of the
114 // prefixes in gPrefixes, all of which leave the current point at 100,100.
115 // (Thus the above two tests for interpolating between "M" and "m" will be
116 // performed many times, with different preceding commands.)
118 // The expected result of the non-additive test is "m-30,-20". Since the
119 // animation is from an absolute moveto to a relative moveto, we first
120 // convert the "M10,20" into its relative form, which is "m-90,-80" due to the
121 // current point being 100,100. Half way through the animation between
122 // "m-90,-80" and "m30,40" is thus "m-30,-20".
124 // The expected result of the additive test is "m-120,-100". We take the
125 // halfway value of the animation, "m-30,-20" and add it on to the underlying
126 // value. Since the underlying value "M10,20" is an absolute moveto, we first
127 // convert it to relative, "m-90,-80", and then add the "m-30,-20" to it,
128 // giving us the result "m-120,-100".
129 var gSuffixes = {
130 // Same path segment type, no conversion required.
131 MM: [[10, 20], [30, 40], [20, 30], [30, 50]],
132 LL: [[10, 20], [30, 40], [20, 30], [30, 50]],
133 CC: [[10, 20, 30, 40, 50, 60], [70, 80, 90, 100, 110, 120],
134 [40, 50, 60, 70, 80, 90], [50, 70, 90, 110, 130, 150]],
135 QQ: [[10, 20, 30, 40], [50, 60, 70, 80], [30, 40, 50, 60], [40, 60, 80, 100]],
136 AA: [[10, 20, 30, 0, 0, 40, 50], [60, 70, 80, 0, 0, 90, 100],
137 [35, 45, 55, 0, 0, 65, 75], [45, 65, 85, 0, 0, 105, 125]],
138 HH: [[10], [20], [15], [25]],
139 VV: [[10], [20], [15], [25]],
140 SS: [[10, 20, 30, 40], [50, 60, 70, 80], [30, 40, 50, 60], [40, 60, 80, 100]],
141 TT: [[10, 20], [30, 40], [20, 30], [30, 50]],
143 // Relative <-> absolute conversion.
144 mM: [[10, 20], [30, 40], [70, 80], [180, 200]],
145 lL: [[10, 20], [30, 40], [70, 80], [180, 200]],
146 cC: [[10, 20, 30, 40, 50, 60], [70, 80, 90, 100, 110, 120],
147 [90, 100, 110, 120, 130, 140], [200, 220, 240, 260, 280, 300]],
148 qQ: [[10, 20, 30, 40], [50, 60, 70, 80],
149 [80, 90, 100, 110], [190, 210, 230, 250]],
150 aA: [[10, 20, 30, 0, 0, 40, 50], [60, 70, 80, 0, 0, 90, 100],
151 [35, 45, 55, 0, 0, 115, 125], [45, 65, 85, 0, 0, 255, 275]],
152 hH: [[10], [20], [65], [175]],
153 vV: [[10], [20], [65], [175]],
154 tT: [[10, 20], [30, 40], [70, 80], [180, 200]],
155 sS: [[10, 20, 30, 40], [50, 60, 70, 80],
156 [80, 90, 100, 110], [190, 210, 230, 250]],
159 // Returns an array of property names that exist on an SVGPathSeg object
160 // corresponding to the given segment type, in the order that they would
161 // be present in a path data string.
162 function argumentNames(aType) {
163 return gArgumentNames[aType.toUpperCase()];
166 // Creates and returns a new element and sets some attributes on it.
167 function newElement(aNamespaceURI, aLocalName, aAttributes) {
168 var e = document.createElementNS(aNamespaceURI, aLocalName);
169 if (aAttributes) {
170 for (let [name, value] of Object.entries(aAttributes)) {
171 e.setAttribute(name, value);
174 return e;
177 // Creates and returns a new SVG element and sets some attributes on it.
178 function newSVGElement(aLocalName, aAttributes) {
179 return newElement("http://www.w3.org/2000/svg", aLocalName, aAttributes);
182 // Creates a subtest and adds it to the document.
184 // * aPrefixLength/aPrefix the prefix to use
185 // * aFromType/aFromArguments the segment to interpolate from
186 // * aToType/aToArguments the segment to interpolate to
187 // * aExpectedType/aExpectedArguments the expected result of the interpolated
188 // segment half way through the animation
189 // duration
190 // * aAdditive whether the subtest is for an additive
191 // animation
192 function addTest(aPrefixLength, aPrefix, aFromType, aFromArguments,
193 aToType, aToArguments, aExpectedType, aExpectedArguments,
194 aAdditive) {
195 var fromPath = aPrefix + aFromType + aFromArguments,
196 toPath = aPrefix + aToType + aToArguments;
198 var path = newSVGElement("path", { d: fromPath });
199 var animate =
200 newSVGElement("animate", { attributeName: "d",
201 from: fromPath,
202 to: toPath,
203 dur: "8s",
204 additive: aAdditive ? "sum" : "replace" });
205 path.appendChild(animate);
206 gSVG.appendChild(path);
208 gTests.push({ element: path,
209 prefixLength: aPrefixLength,
210 from: fromPath,
211 to: toPath,
212 toType: aToType,
213 expectedType: aExpectedType,
214 expected: aExpectedArguments,
215 usesAddition: aAdditive });
218 // Generates an array of path segment arguments for the given type. aOffset
219 // is a number to add on to all non-Boolean segment arguments.
220 function generatePathSegmentArguments(aType, aOffset) {
221 var args = new Array(argumentNames(aType).length);
222 for (let i = 0; i < args.length; i++) {
223 args[i] = i * 10 + aOffset;
225 if (aType == "A" || aType == "a") {
226 args[3] = 0;
227 args[4] = 0;
229 return args;
232 // Returns whether interpolating between the two given types is valid.
233 function isValidInterpolation(aFromType, aToType) {
234 return aFromType.toUpperCase() == aToType.toUpperCase();
237 // Runs the test.
238 function run() {
239 for (let additive of [false, true]) {
240 let indexOfExpectedArguments = additive ? 3 : 2;
242 // Add subtests for each combination of prefix and suffix, and additive
243 // or not.
244 for (let [typePair, suffixEntry] of Object.entries(gSuffixes)) {
245 let fromType = typePair[0],
246 toType = typePair[1],
247 fromArguments = suffixEntry[0],
248 toArguments = suffixEntry[1],
249 expectedArguments = suffixEntry[indexOfExpectedArguments];
251 for (let prefixEntry of gPrefixes) {
252 let [prefixLength, prefix] = prefixEntry;
253 addTest(prefixLength, prefix, fromType, fromArguments,
254 toType, toArguments, toType, expectedArguments, additive);
258 // Test all pairs of segment types that cannot be interpolated between.
259 for (let fromType of gTypes) {
260 let fromArguments = generatePathSegmentArguments(fromType, 0);
261 for (let toType of gTypes) {
262 if (!isValidInterpolation(fromType, toType)) {
263 let toArguments = generatePathSegmentArguments(toType, 1000);
264 addTest(1, "M100,100", fromType, fromArguments,
265 toType, toArguments, toType, toArguments, additive);
271 // Move the document time to half way through the animations.
272 gSVG.setCurrentTime(4);
274 // Inspect the results of each subtest.
275 for (let test of gTests) {
276 let list = test.element.getPathData();
277 is(list.length, test.prefixLength + 1,
278 "Length of animatedPathSegList for interpolation " +
279 (test.usesAddition ? "with addition " : "") +
280 " from " + test.from + " to " + test.to);
282 let seg = list.at(-1);
284 let actual = [];
285 for (let i = 0; i < test.expected.length; i++) {
286 actual.push(seg.values[i]);
289 is(seg.type + actual, test.expectedType + test.expected,
290 "Path segment for interpolation " +
291 (test.usesAddition ? "with addition " : "") +
292 " from " + test.from + " to " + test.to);
295 // Clear all the tests. We have tons of them attached to the DOM and refresh driver tick will
296 // go through them all by calling animation controller.
297 gSVG.remove();
299 SimpleTest.finish();
302 window.addEventListener("load", run);
303 ]]></script>
304 </body>
305 </html>