tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

test_pathAnimInterpolation.xhtml (11511B)


      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();
     16 
     17 var gSVG = document.getElementById("svg");
     18 
     19 // Array of all subtests to run.  This is populated by addTest.
     20 var gTests = [];
     21 
     22 // Array of all path segment types.
     23 var gTypes = "ZMmLlCcQqAaHhVvSsTt".split("");
     24 
     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"],
     38 };
     39 
     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"],
     55 
     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"],
     69 
     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"],
     83 ];
     84 
     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:
     87 //
     88 //   "<fromType><toType>": [fromArguments,
     89 //                          toArguments,
     90 //                          expectedArguments,
     91 //                          expectedArgumentsAdditive]
     92 //
     93 // As an example:
     94 //
     95 //   "Mm": [[10, 20], [30, 40], [-30, -20], [-120, -100]]
     96 //
     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:
    100 //
    101 //   <path d="... M10,20">
    102 //     <animate attributeName="d" from="... M10,20" to="... m30,40"/>
    103 //   </path>
    104 //
    105 // and
    106 //
    107 //   <path d="... M10,20">
    108 //     <animate attributeName="d" from="... M10,20" to="... m30,40"
    109 //              additive="sum"/>
    110 //   </path>
    111 //
    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.)
    117 //
    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".
    123 //
    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]],
    142 
    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]],
    157 };
    158 
    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()];
    164 }
    165 
    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);
    172    }
    173  }
    174  return e;
    175 }
    176 
    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);
    180 }
    181 
    182 // Creates a subtest and adds it to the document.
    183 //
    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;
    197 
    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);
    207 
    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 });
    216 }
    217 
    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;
    224  }
    225  if (aType == "A" || aType == "a") {
    226    args[3] = 0;
    227    args[4] = 0;
    228  }
    229  return args;
    230 }
    231 
    232 // Returns whether interpolating between the two given types is valid.
    233 function isValidInterpolation(aFromType, aToType) {
    234  return aFromType.toUpperCase() == aToType.toUpperCase();
    235 }
    236 
    237 // Runs the test.
    238 function run() {
    239  for (let additive of [false, true]) {
    240    let indexOfExpectedArguments = additive ? 3 : 2;
    241 
    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];
    250 
    251      for (let prefixEntry of gPrefixes) {
    252        let [prefixLength, prefix] = prefixEntry;
    253        addTest(prefixLength, prefix, fromType, fromArguments,
    254                toType, toArguments, toType, expectedArguments, additive);
    255      }
    256    }
    257 
    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);
    266        }
    267      }
    268    }
    269  }
    270 
    271  // Move the document time to half way through the animations.
    272  gSVG.setCurrentTime(4);
    273 
    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);
    281 
    282    let seg = list.at(-1);
    283 
    284    let actual = [];
    285    for (let i = 0; i < test.expected.length; i++) {
    286      actual.push(seg.values[i]);
    287    }
    288 
    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);
    293  }
    294 
    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();
    298 
    299  SimpleTest.finish();
    300 }
    301 
    302 window.addEventListener("load", run);
    303 ]]></script>
    304 </body>
    305 </html>