2011-04-08 02:17:36 +04:00
|
|
|
<html xmlns="http://www.w3.org/1999/xhtml">
|
|
|
|
<!--
|
|
|
|
https://bugzilla.mozilla.org/show_bug.cgi?id=619498
|
|
|
|
-->
|
|
|
|
<head>
|
|
|
|
<title>Test interpolation between different path segment types</title>
|
2019-04-16 06:53:28 +03:00
|
|
|
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
2011-04-08 02:17:36 +04:00
|
|
|
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
|
|
|
</head>
|
|
|
|
<body>
|
|
|
|
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=619498">Mozilla Bug 619498</a>
|
|
|
|
<svg xmlns="http://www.w3.org/2000/svg" id="svg" visibility="hidden"
|
|
|
|
onload="this.pauseAnimations()"/>
|
2017-02-23 00:10:07 +03:00
|
|
|
<script type="application/javascript"><![CDATA[
|
2011-04-08 02:17:36 +04:00
|
|
|
SimpleTest.waitForExplicitFinish();
|
|
|
|
|
|
|
|
var gSVG = document.getElementById("svg");
|
|
|
|
|
|
|
|
// Array of all subtests to run. This is populated by addTest.
|
|
|
|
var gTests = [];
|
|
|
|
|
|
|
|
// Array of all path segment types.
|
|
|
|
var gTypes = "zMmLlCcQqAaHhVvSsTt".split("");
|
|
|
|
|
|
|
|
// Property names on the SVGPathSeg objects for the given segment type, in the
|
|
|
|
// order that they would appear in a path data string.
|
|
|
|
var gArgumentNames = {
|
|
|
|
Z: [],
|
2019-03-25 22:22:09 +03:00
|
|
|
M: ["x", "y"],
|
|
|
|
L: ["x", "y"],
|
|
|
|
C: ["x1", "y1", "x2", "y2", "x", "y"],
|
|
|
|
Q: ["x1", "y1", "x", "y"],
|
|
|
|
A: ["r1", "r2", "angle", "largeArcFlag", "sweepFlag", "x", "y"],
|
|
|
|
H: ["x"],
|
|
|
|
V: ["y"],
|
|
|
|
S: ["x2", "y2", "x", "y"],
|
|
|
|
T: ["x", "y"],
|
2011-04-08 02:17:36 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
// All of these prefixes leave the current point at 100,100. Some of them
|
|
|
|
// affect the implied control point if followed by a smooth quadratic or
|
|
|
|
// cubic segment, but no valid interpolations depend on those control points.
|
|
|
|
var gPrefixes = [
|
|
|
|
[1, "M100,100"],
|
|
|
|
[2, "M50,50 M100,100"],
|
|
|
|
[2, "M50,50 m50,50"],
|
|
|
|
[2, "M50,50 L100,100"],
|
|
|
|
[2, "M50,50 l50,50"],
|
|
|
|
[3, "M50,50 H100 V100"],
|
|
|
|
[3, "M50,50 h50 V100"],
|
|
|
|
[3, "M50,50 H100 v50"],
|
|
|
|
[2, "M50,50 A10,10,10,0,0,100,100"],
|
|
|
|
[2, "M50,50 a10,10,10,0,0,50,50"],
|
|
|
|
[4, "M50,50 l50,50 z m50,50"],
|
|
|
|
|
|
|
|
// These leave the quadratic implied control point at 125,125.
|
|
|
|
[2, "M50,50 Q75,75,100,100"],
|
|
|
|
[2, "M50,50 q25,25,50,50"],
|
|
|
|
[2, "M75,75 T100,100"],
|
|
|
|
[2, "M75,75 t25,25"],
|
|
|
|
[3, "M50,50 T62.5,62.5 t37.5,37.5"],
|
|
|
|
[3, "M50,50 T62.5,62.5 T100,100"],
|
|
|
|
[3, "M50,50 t12.5,12.5 t37.5,37.5"],
|
|
|
|
[3, "M50,50 t12.5,12.5 T100,100"],
|
|
|
|
[3, "M50,50 Q50,50,62.5,62.5 t37.5,37.5"],
|
|
|
|
[3, "M50,50 Q50,50,62.5,62.5 T100,100"],
|
|
|
|
[3, "M50,50 q0,0,12.5,12.5 t37.5,37.5"],
|
|
|
|
[3, "M50,50 q0,0,12.5,12.5 T100,100"],
|
|
|
|
|
|
|
|
// These leave the cubic implied control point at 125,125.
|
|
|
|
[2, "M50,50 C10,10,75,75,100,100"],
|
|
|
|
[2, "M50,50 c10,10,25,25,50,50"],
|
|
|
|
[2, "M50,50 S75,75,100,100"],
|
|
|
|
[2, "M50,50 s25,25,50,50"],
|
|
|
|
[3, "M50,50 S10,10,75,75 S75,75,100,100"],
|
|
|
|
[3, "M50,50 S10,10,75,75 s0,0,25,25"],
|
|
|
|
[3, "M50,50 s10,10,25,25 S75,75,100,100"],
|
|
|
|
[3, "M50,50 s10,10,25,25 s0,0,25,25"],
|
|
|
|
[3, "M50,50 C10,10,20,20,75,75 S75,75,100,100"],
|
|
|
|
[3, "M50,50 C10,10,20,20,75,75 s0,0,25,25"],
|
|
|
|
[3, "M50,50 c10,10,20,20,25,25 S75,75,100,100"],
|
2019-03-25 22:22:09 +03:00
|
|
|
[3, "M50,50 c10,10,20,20,25,25 s0,0,25,25"],
|
2011-04-08 02:17:36 +04:00
|
|
|
];
|
|
|
|
|
|
|
|
// These are all of the suffixes whose result is not dependent on whether the
|
|
|
|
// preceding segment types are quadratic or cubic types. Each entry is:
|
|
|
|
//
|
|
|
|
// "<fromType><toType>": [fromArguments,
|
|
|
|
// toArguments,
|
|
|
|
// expectedArguments,
|
|
|
|
// expectedArgumentsAdditive]
|
|
|
|
//
|
|
|
|
// As an example:
|
|
|
|
//
|
|
|
|
// "Mm": [[10, 20], [30, 40], [-30, -20], [-120, -100]]
|
|
|
|
//
|
|
|
|
// This will testing interpolating between "M10,20" and "m30,40". All of the
|
|
|
|
// these tests assume that the current point is left at 100,100. So the above
|
|
|
|
// entry represents two kinds of tests, one where additive and one not:
|
|
|
|
//
|
|
|
|
// <path d="... M10,20">
|
|
|
|
// <animate attributeName="d" from="... M10,20" to="... m30,40"/>
|
|
|
|
// </path>
|
|
|
|
//
|
|
|
|
// and
|
|
|
|
//
|
|
|
|
// <path d="... M10,20">
|
|
|
|
// <animate attributeName="d" from="... M10,20" to="... m30,40"
|
|
|
|
// additive="sum"/>
|
|
|
|
// </path>
|
|
|
|
//
|
|
|
|
// where the "..." is some prefix that leaves the current point at 100,100.
|
|
|
|
// Each of the suffixes here in gSuffixes will be paired with each of the
|
|
|
|
// prefixes in gPrefixes, all of which leave the current point at 100,100.
|
|
|
|
// (Thus the above two tests for interpolating between "M" and "m" will be
|
|
|
|
// performed many times, with different preceding commands.)
|
|
|
|
//
|
|
|
|
// The expected result of the non-additive test is "m-30,-20". Since the
|
|
|
|
// animation is from an absolute moveto to a relative moveto, we first
|
|
|
|
// convert the "M10,20" into its relative form, which is "m-90,-80" due to the
|
|
|
|
// current point being 100,100. Half way through the animation between
|
|
|
|
// "m-90,-80" and "m30,40" is thus "m-30,-20".
|
2019-03-25 22:22:09 +03:00
|
|
|
//
|
2011-04-08 02:17:36 +04:00
|
|
|
// The expected result of the additive test is "m-120,-100". We take the
|
|
|
|
// halfway value of the animation, "m-30,-20" and add it on to the underlying
|
|
|
|
// value. Since the underlying value "M10,20" is an absolute moveto, we first
|
|
|
|
// convert it to relative, "m-90,-80", and then add the "m-30,-20" to it,
|
|
|
|
// giving us the result "m-120,-100".
|
|
|
|
var gSuffixes = {
|
|
|
|
// Same path segment type, no conversion required.
|
|
|
|
MM: [[10, 20], [30, 40], [20, 30], [30, 50]],
|
|
|
|
mm: [[10, 20], [30, 40], [20, 30], [30, 50]],
|
|
|
|
LL: [[10, 20], [30, 40], [20, 30], [30, 50]],
|
|
|
|
ll: [[10, 20], [30, 40], [20, 30], [30, 50]],
|
|
|
|
CC: [[10, 20, 30, 40, 50, 60], [70, 80, 90, 100, 110, 120],
|
|
|
|
[40, 50, 60, 70, 80, 90], [50, 70, 90, 110, 130, 150]],
|
|
|
|
cc: [[10, 20, 30, 40, 50, 60], [70, 80, 90, 100, 110, 120],
|
|
|
|
[40, 50, 60, 70, 80, 90], [50, 70, 90, 110, 130, 150]],
|
|
|
|
QQ: [[10, 20, 30, 40], [50, 60, 70, 80], [30, 40, 50, 60], [40, 60, 80, 100]],
|
|
|
|
qq: [[10, 20, 30, 40], [50, 60, 70, 80], [30, 40, 50, 60], [40, 60, 80, 100]],
|
2011-11-02 23:15:32 +04:00
|
|
|
AA: [[10, 20, 30, 0, 0, 40, 50], [60, 70, 80, 0, 0, 90, 100],
|
|
|
|
[35, 45, 55, 0, 0, 65, 75], [45, 65, 85, 0, 0, 105, 125]],
|
|
|
|
aa: [[10, 20, 30, 0, 0, 40, 50], [60, 70, 80, 0, 0, 90, 100],
|
|
|
|
[35, 45, 55, 0, 0, 65, 75], [45, 65, 85, 0, 0, 105, 125]],
|
2011-04-08 02:17:36 +04:00
|
|
|
HH: [[10], [20], [15], [25]],
|
|
|
|
hh: [[10], [20], [15], [25]],
|
|
|
|
VV: [[10], [20], [15], [25]],
|
|
|
|
vv: [[10], [20], [15], [25]],
|
|
|
|
SS: [[10, 20, 30, 40], [50, 60, 70, 80], [30, 40, 50, 60], [40, 60, 80, 100]],
|
|
|
|
ss: [[10, 20, 30, 40], [50, 60, 70, 80], [30, 40, 50, 60], [40, 60, 80, 100]],
|
|
|
|
TT: [[10, 20], [30, 40], [20, 30], [30, 50]],
|
|
|
|
tt: [[10, 20], [30, 40], [20, 30], [30, 50]],
|
|
|
|
|
|
|
|
// Relative <-> absolute conversion.
|
|
|
|
Mm: [[10, 20], [30, 40], [-30, -20], [-120, -100]],
|
|
|
|
mM: [[10, 20], [30, 40], [70, 80], [180, 200]],
|
|
|
|
Ll: [[10, 20], [30, 40], [-30, -20], [-120, -100]],
|
|
|
|
lL: [[10, 20], [30, 40], [70, 80], [180, 200]],
|
|
|
|
Cc: [[10, 20, 30, 40, 50, 60], [70, 80, 90, 100, 110, 120],
|
|
|
|
[-10, 0, 10, 20, 30, 40], [-100, -80, -60, -40, -20, 0]],
|
|
|
|
cC: [[10, 20, 30, 40, 50, 60], [70, 80, 90, 100, 110, 120],
|
|
|
|
[90, 100, 110, 120, 130, 140], [200, 220, 240, 260, 280, 300]],
|
|
|
|
Qq: [[10, 20, 30, 40], [50, 60, 70, 80],
|
|
|
|
[-20, -10, 0, 10], [-110, -90, -70, -50]],
|
|
|
|
qQ: [[10, 20, 30, 40], [50, 60, 70, 80],
|
|
|
|
[80, 90, 100, 110], [190, 210, 230, 250]],
|
|
|
|
Aa: [[10, 20, 30, 0, 0, 40, 50], [60, 70, 80, 0, 0, 90, 100],
|
|
|
|
[35, 45, 55, 0, 0, 15, 25], [45, 65, 85, 0, 0, -45, -25]],
|
|
|
|
aA: [[10, 20, 30, 0, 0, 40, 50], [60, 70, 80, 0, 0, 90, 100],
|
|
|
|
[35, 45, 55, 0, 0, 115, 125], [45, 65, 85, 0, 0, 255, 275]],
|
|
|
|
Hh: [[10], [20], [-35], [-125]],
|
|
|
|
hH: [[10], [20], [65], [175]],
|
|
|
|
Vv: [[10], [20], [-35], [-125]],
|
|
|
|
vV: [[10], [20], [65], [175]],
|
2019-03-25 22:22:09 +03:00
|
|
|
Tt: [[10, 20], [30, 40], [-30, -20], [-120, -100]],
|
2011-04-08 02:17:36 +04:00
|
|
|
tT: [[10, 20], [30, 40], [70, 80], [180, 200]],
|
|
|
|
Ss: [[10, 20, 30, 40], [50, 60, 70, 80],
|
|
|
|
[-20, -10, 0, 10], [-110, -90, -70, -50]],
|
|
|
|
sS: [[10, 20, 30, 40], [50, 60, 70, 80],
|
2019-03-25 22:22:09 +03:00
|
|
|
[80, 90, 100, 110], [190, 210, 230, 250]],
|
2011-04-08 02:17:36 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
// Returns an array of property names that exist on an SVGPathSeg object
|
|
|
|
// corresponding to the given segment type, in the order that they would
|
|
|
|
// be present in a path data string.
|
2019-03-25 22:22:09 +03:00
|
|
|
function argumentNames(aType) {
|
2011-04-08 02:17:36 +04:00
|
|
|
return gArgumentNames[aType.toUpperCase()];
|
|
|
|
}
|
|
|
|
|
|
|
|
// Creates and returns a new element and sets some attributes on it.
|
2019-03-25 22:22:09 +03:00
|
|
|
function newElement(aNamespaceURI, aLocalName, aAttributes) {
|
2011-04-08 02:17:36 +04:00
|
|
|
var e = document.createElementNS(aNamespaceURI, aLocalName);
|
|
|
|
if (aAttributes) {
|
2016-08-12 05:05:49 +03:00
|
|
|
for (let [name, value] of Object.entries(aAttributes)) {
|
2011-04-08 02:17:36 +04:00
|
|
|
e.setAttribute(name, value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return e;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Creates and returns a new SVG element and sets some attributes on it.
|
2019-03-25 22:22:09 +03:00
|
|
|
function newSVGElement(aLocalName, aAttributes) {
|
2011-04-08 02:17:36 +04:00
|
|
|
return newElement("http://www.w3.org/2000/svg", aLocalName, aAttributes);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Creates a subtest and adds it to the document.
|
|
|
|
//
|
|
|
|
// * aPrefixLength/aPrefix the prefix to use
|
|
|
|
// * aFromType/aFromArguments the segment to interpolate from
|
|
|
|
// * aToType/aToArguments the segment to interpolate to
|
|
|
|
// * aExpectedType/aExpectedArguments the expected result of the interpolated
|
|
|
|
// segment half way through the animation
|
|
|
|
// duration
|
|
|
|
// * aAdditive whether the subtest is for an additive
|
|
|
|
// animation
|
|
|
|
function addTest(aPrefixLength, aPrefix, aFromType, aFromArguments,
|
|
|
|
aToType, aToArguments, aExpectedType, aExpectedArguments,
|
2019-03-25 22:22:09 +03:00
|
|
|
aAdditive) {
|
2011-04-08 02:17:36 +04:00
|
|
|
var fromPath = aPrefix + aFromType + aFromArguments,
|
|
|
|
toPath = aPrefix + aToType + aToArguments;
|
|
|
|
|
|
|
|
var path = newSVGElement("path", { d: fromPath });
|
|
|
|
var animate =
|
|
|
|
newSVGElement("animate", { attributeName: "d",
|
|
|
|
from: fromPath,
|
2019-03-25 22:22:27 +03:00
|
|
|
to: toPath,
|
|
|
|
dur: "8s",
|
|
|
|
additive: aAdditive ? "sum" : "replace" });
|
2011-04-08 02:17:36 +04:00
|
|
|
path.appendChild(animate);
|
|
|
|
gSVG.appendChild(path);
|
|
|
|
|
|
|
|
gTests.push({ element: path,
|
|
|
|
prefixLength: aPrefixLength,
|
|
|
|
from: fromPath,
|
|
|
|
to: toPath,
|
|
|
|
toType: aToType,
|
|
|
|
expectedType: aExpectedType,
|
|
|
|
expected: aExpectedArguments,
|
|
|
|
usesAddition: aAdditive });
|
|
|
|
}
|
|
|
|
|
|
|
|
// Generates an array of path segment arguments for the given type. aOffset
|
|
|
|
// is a number to add on to all non-Boolean segment arguments.
|
2019-03-25 22:22:09 +03:00
|
|
|
function generatePathSegmentArguments(aType, aOffset) {
|
2011-04-08 02:17:36 +04:00
|
|
|
var args = new Array(argumentNames(aType).length);
|
|
|
|
for (let i = 0; i < args.length; i++) {
|
|
|
|
args[i] = i * 10 + aOffset;
|
|
|
|
}
|
|
|
|
if (aType == "A" || aType == "a") {
|
|
|
|
args[3] = 0;
|
|
|
|
args[4] = 0;
|
|
|
|
}
|
|
|
|
return args;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns whether interpolating between the two given types is valid.
|
2019-03-25 22:22:09 +03:00
|
|
|
function isValidInterpolation(aFromType, aToType) {
|
2011-04-08 02:17:36 +04:00
|
|
|
return aFromType.toUpperCase() == aToType.toUpperCase();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Runs the test.
|
2019-03-25 22:22:09 +03:00
|
|
|
function run() {
|
2015-10-18 20:00:50 +03:00
|
|
|
for (let additive of [false, true]) {
|
2011-04-08 02:17:36 +04:00
|
|
|
let indexOfExpectedArguments = additive ? 3 : 2;
|
|
|
|
|
|
|
|
// Add subtests for each combination of prefix and suffix, and additive
|
|
|
|
// or not.
|
2016-08-12 05:05:49 +03:00
|
|
|
for (let [typePair, suffixEntry] of Object.entries(gSuffixes)) {
|
2011-04-08 02:17:36 +04:00
|
|
|
let fromType = typePair[0],
|
|
|
|
toType = typePair[1],
|
|
|
|
fromArguments = suffixEntry[0],
|
|
|
|
toArguments = suffixEntry[1],
|
|
|
|
expectedArguments = suffixEntry[indexOfExpectedArguments];
|
|
|
|
|
2015-10-18 20:00:50 +03:00
|
|
|
for (let prefixEntry of gPrefixes) {
|
2011-04-08 02:17:36 +04:00
|
|
|
let [prefixLength, prefix] = prefixEntry;
|
|
|
|
addTest(prefixLength, prefix, fromType, fromArguments,
|
2019-03-25 22:22:27 +03:00
|
|
|
toType, toArguments, toType, expectedArguments, additive);
|
2011-04-08 02:17:36 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-11-02 23:15:32 +04:00
|
|
|
// Test that differences in arc flag parameters cause the
|
|
|
|
// interpolation/addition not to occur.
|
|
|
|
addTest(1, "M100,100",
|
|
|
|
"A", [10, 20, 30, 0, 0, 40, 50],
|
|
|
|
"a", [60, 70, 80, 0, 1, 90, 100],
|
2019-03-25 22:22:27 +03:00
|
|
|
"a", [60, 70, 80, 0, 1, 90, 100], additive);
|
2011-11-02 23:15:32 +04:00
|
|
|
addTest(1, "M100,100",
|
|
|
|
"A", [10, 20, 30, 0, 0, 40, 50],
|
|
|
|
"a", [60, 70, 80, 1, 0, 90, 100],
|
2019-03-25 22:22:27 +03:00
|
|
|
"a", [60, 70, 80, 1, 0, 90, 100], additive);
|
2011-11-02 23:15:32 +04:00
|
|
|
|
2011-04-08 02:17:36 +04:00
|
|
|
// Test all pairs of segment types that cannot be interpolated between.
|
2015-10-18 20:00:50 +03:00
|
|
|
for (let fromType of gTypes) {
|
2011-04-08 02:17:36 +04:00
|
|
|
let fromArguments = generatePathSegmentArguments(fromType, 0);
|
2015-10-18 20:00:50 +03:00
|
|
|
for (let toType of gTypes) {
|
2011-04-08 02:17:36 +04:00
|
|
|
if (!isValidInterpolation(fromType, toType)) {
|
|
|
|
let toArguments = generatePathSegmentArguments(toType, 1000);
|
|
|
|
addTest(1, "M100,100", fromType, fromArguments,
|
2019-03-25 22:22:27 +03:00
|
|
|
toType, toArguments, toType, toArguments, additive);
|
2011-04-08 02:17:36 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Move the document time to half way through the animations.
|
|
|
|
gSVG.setCurrentTime(4);
|
|
|
|
|
|
|
|
// Inspect the results of each subtest.
|
2015-10-18 20:00:50 +03:00
|
|
|
for (let test of gTests) {
|
2011-04-08 02:17:36 +04:00
|
|
|
let list = test.element.animatedPathSegList;
|
|
|
|
is(list.numberOfItems, test.prefixLength + 1,
|
|
|
|
"Length of animatedPathSegList for interpolation " +
|
|
|
|
(test.usesAddition ? "with addition " : "") +
|
2019-03-25 22:22:27 +03:00
|
|
|
" from " + test.from + " to " + test.to);
|
2011-04-08 02:17:36 +04:00
|
|
|
|
|
|
|
let seg = list.getItem(list.numberOfItems - 1);
|
|
|
|
let propertyNames = argumentNames(test.expectedType);
|
|
|
|
|
|
|
|
let actual = [];
|
|
|
|
for (let i = 0; i < test.expected.length; i++) {
|
|
|
|
actual.push(+seg[propertyNames[i]]);
|
|
|
|
}
|
|
|
|
|
|
|
|
is(seg.pathSegTypeAsLetter + actual, test.expectedType + test.expected,
|
|
|
|
"Path segment for interpolation " +
|
|
|
|
(test.usesAddition ? "with addition " : "") +
|
|
|
|
" from " + test.from + " to " + test.to);
|
|
|
|
}
|
|
|
|
|
2016-11-08 15:05:45 +03:00
|
|
|
// Clear all the tests. We have tons of them attached to the DOM and refresh driver tick will
|
|
|
|
// go through them all by calling animation controller.
|
|
|
|
gSVG.remove();
|
|
|
|
|
2011-04-08 02:17:36 +04:00
|
|
|
SimpleTest.finish();
|
|
|
|
}
|
|
|
|
|
|
|
|
window.addEventListener("load", run);
|
|
|
|
]]></script>
|
|
|
|
</body>
|
|
|
|
</html>
|