test_distance_of_transform.html (15459B)
1 <!doctype html> 2 <meta charset=utf-8> 3 <script src='/resources/testharness.js'></script> 4 <script src='/resources/testharnessreport.js'></script> 5 <script src='../testcommon.js'></script> 6 <div id='log'></div> 7 <script type='text/javascript'> 8 'use strict'; 9 10 // We don't have an official spec to define the distance between two transform 11 // lists, but we still need this for DevTools, so Gecko and Servo backend use 12 // the similar rules to define the distance. If there is a spec for it, we have 13 // to update this test file. 14 15 const EPSILON = 0.00001; 16 17 // |v| should be a unit vector (i.e. having length 1) 18 function getQuaternion(v, angle) { 19 return [ 20 v[0] * Math.sin(angle / 2.0), 21 v[1] * Math.sin(angle / 2.0), 22 v[2] * Math.sin(angle / 2.0), 23 Math.cos(angle / 2.0) 24 ]; 25 } 26 27 function computeRotateDistance(q1, q2) { 28 const dot = q1.reduce((sum, e, i) => sum + e * q2[i], 0); 29 return Math.acos(Math.min(Math.max(dot, -1.0), 1.0)) * 2.0; 30 } 31 32 function createMatrixFromArray(array) { 33 return (array.length === 16 ? 'matrix3d' : 'matrix') + `(${array.join()})`; 34 } 35 36 function rotate3dToMatrix(x, y, z, radian) { 37 var sc = Math.sin(radian / 2) * Math.cos(radian / 2); 38 var sq = Math.sin(radian / 2) * Math.sin(radian / 2); 39 40 // Normalize the vector. 41 var length = Math.sqrt(x*x + y*y + z*z); 42 x /= length; 43 y /= length; 44 z /= length; 45 46 return [ 47 1 - 2 * (y*y + z*z) * sq, 48 2 * (x * y * sq + z * sc), 49 2 * (x * z * sq - y * sc), 50 0, 51 2 * (x * y * sq - z * sc), 52 1 - 2 * (x*x + z*z) * sq, 53 2 * (y * z * sq + x * sc), 54 0, 55 2 * (x * z * sq + y * sc), 56 2 * (y * z * sq - x * sc), 57 1 - 2 * (x*x + y*y) * sq, 58 0, 59 0, 60 0, 61 0, 62 1 63 ]; 64 } 65 66 test(function(t) { 67 var target = addDiv(t); 68 var dist = getDistance(target, 'transform', 'none', 'none'); 69 assert_equals(dist, 0, 'distance of translate'); 70 }, 'Test distance of none and none'); 71 72 test(function(t) { 73 var target = addDiv(t); 74 var dist = getDistance(target, 'transform', 'translate(100px)', 'none'); 75 assert_equals(dist, 100, 'distance of translate'); 76 }, 'Test distance of translate function and none'); 77 78 test(function(t) { 79 var target = addDiv(t); 80 var dist = 81 getDistance(target, 'transform', 'translate(100px)', 'translate(200px)'); 82 assert_equals(dist, 200 - 100, 'distance of translate'); 83 }, 'Test distance of translate functions'); 84 85 test(function(t) { 86 var target = addDiv(t); 87 var dist = 88 getDistance(target, 'transform', 'translate3d(100px, 0, 50px)', 'none'); 89 assert_equals(dist, Math.sqrt(100 * 100 + 50 * 50), 90 'distance of translate3d'); 91 }, 'Test distance of translate3d function and none'); 92 93 test(function(t) { 94 var target = addDiv(t); 95 var dist = 96 getDistance(target, 'transform', 97 'translate3d(100px, 0, 50px)', 98 'translate3d(200px, 80px, 0)'); 99 assert_equals(dist, Math.sqrt(100 * 100 + 80 * 80 + 50 * 50), 100 'distance of translate'); 101 }, 'Test distance of translate3d functions'); 102 103 test(function(t) { 104 var target = addDiv(t); 105 var dist = getDistance(target, 'transform', 'scale(1.5)', 'none'); 106 assert_equals(dist, Math.sqrt(0.5 * 0.5 + 0.5 * 0.5), 'distance of scale'); 107 }, 'Test distance of scale function and none'); 108 109 test(function(t) { 110 var target = addDiv(t); 111 var dist = getDistance(target, 'transform', 'scale(1.5)', 'scale(2.0)'); 112 assert_equals(dist, Math.sqrt(0.5 * 0.5 + 0.5 * 0.5), 'distance of scale'); 113 }, 'Test distance of scale functions'); 114 115 test(function(t) { 116 var target = addDiv(t); 117 var dist = getDistance(target, 'transform', 118 'scale3d(1.5, 1.5, 1.5)', 119 'none'); 120 assert_equals(dist, 121 Math.sqrt(0.5 * 0.5 + 0.5 * 0.5 + 0.5 * 0.5), 122 'distance of scale3d'); 123 }, 'Test distance of scale3d function and none'); 124 125 test(function(t) { 126 var target = addDiv(t); 127 var dist = getDistance(target, 'transform', 128 'scale3d(1.5, 1.5, 1.5)', 129 'scale3d(2.0, 2.0, 1.0)'); 130 assert_equals(dist, 131 Math.sqrt(0.5 * 0.5 + 0.5 * 0.5 + 0.5 * 0.5), 132 'distance of scale3d'); 133 }, 'Test distance of scale3d functions'); 134 135 test(function(t) { 136 var target = addDiv(t); 137 var dist = 138 getDistance(target, 'transform', 'rotate(45deg)', 'rotate(90deg)'); 139 assert_approx_equals(dist, Math.PI / 2.0 - Math.PI / 4.0, EPSILON, 'distance of rotate'); 140 }, 'Test distance of rotate functions'); 141 142 test(function(t) { 143 var target = addDiv(t); 144 var dist = 145 getDistance(target, 'transform', 'rotate(45deg)', 'none'); 146 assert_approx_equals(dist, Math.PI / 4.0, EPSILON, 'distance of rotate'); 147 }, 'Test distance of rotate function and none'); 148 149 test(function(t) { 150 var target = addDiv(t); 151 var dist = getDistance(target, 'transform', 152 'rotate3d(0, 1, 0, 90deg)', 153 'none'); 154 assert_approx_equals(dist, Math.PI / 2, EPSILON, 'distance of rotate3d'); 155 }, 'Test distance of rotate3d function and none'); 156 157 test(function(t) { 158 var target = addDiv(t); 159 var dist = getDistance(target, 'transform', 160 'rotate3d(0, 0, 1, 90deg)', 161 'rotate3d(1, 0, 0, 90deg)'); 162 let q1 = getQuaternion([0, 0, 1], Math.PI / 2.0); 163 let q2 = getQuaternion([1, 0, 0], Math.PI / 2.0); 164 assert_approx_equals(dist, computeRotateDistance(q1, q2), EPSILON, 'distance of rotate3d'); 165 }, 'Test distance of rotate3d functions'); 166 167 test(function(t) { 168 var target = addDiv(t); 169 var dist = getDistance(target, 'transform', 170 'rotate3d(0, 0, 1, 90deg)', 171 'rotate3d(0, 0, 0, 90deg)'); 172 assert_approx_equals(dist, Math.PI / 2, EPSILON, 'distance of rotate3d'); 173 }, 'Test distance of rotate3d functions whose direction vector cannot be ' + 174 'normalized'); 175 176 test(function(t) { 177 var target = addDiv(t); 178 var dist = getDistance(target, 'transform', 'skew(1rad, 0.5rad)', 'none'); 179 assert_approx_equals(dist, Math.sqrt(1 * 1 + 0.5 * 0.5), EPSILON, 'distance of skew'); 180 }, 'Test distance of skew function and none'); 181 182 test(function(t) { 183 var target = addDiv(t); 184 var dist = getDistance(target, 'transform', 185 'skew(1rad, 0.5rad)', 186 'skew(-1rad, 0)'); 187 assert_approx_equals(dist, Math.sqrt(2 * 2 + 0.5 * 0.5), EPSILON, 'distance of skew'); 188 }, 'Test distance of skew functions'); 189 190 test(function(t) { 191 var target = addDiv(t); 192 var dist = getDistance(target, 'transform', 193 'perspective(128px)', 194 'none'); 195 assert_equals(dist, Infinity, 'distance of perspective'); 196 }, 'Test distance of perspective function and none'); 197 198 test(function(t) { 199 var target = addDiv(t); 200 // perspective(0) is treated as perspective(inf) because perspective length 201 // should be greater than or equal to zero. 202 var dist = getDistance(target, 'transform', 203 'perspective(128px)', 204 'perspective(0)'); 205 assert_equals(dist, 128, 'distance of perspective'); 206 }, 'Test distance of perspective function and an invalid perspective'); 207 208 test(function(t) { 209 var target = addDiv(t); 210 var dist = getDistance(target, 'transform', 211 'perspective(128px)', 212 'perspective(1024px)'); 213 assert_equals(dist, 1024 - 128, 'distance of perspective'); 214 }, 'Test distance of perspective functions'); 215 216 test(function(t) { 217 var target = addDiv(t); 218 var sin_30 = Math.sin(Math.PI / 6); 219 var cos_30 = Math.cos(Math.PI / 6); 220 // matrix => translate(100, 0) rotate(30deg). 221 var matrix = createMatrixFromArray([ cos_30, sin_30, 222 -sin_30, cos_30, 223 100, 0 ]); 224 var dist = getDistance(target, 'transform', matrix, 'none'); 225 assert_approx_equals(dist, 226 Math.sqrt(100 * 100 + (Math.PI / 6) * (Math.PI / 6)), 227 EPSILON, 228 'distance of matrix'); 229 }, 'Test distance of matrix function and none'); 230 231 test(function(t) { 232 var target = addDiv(t); 233 var sin_30 = Math.sin(Math.PI / 6); 234 var cos_30 = Math.cos(Math.PI / 6); 235 // matrix1 => translate(100, 0) rotate(30deg). 236 var matrix1 = createMatrixFromArray([ cos_30, sin_30, 237 -sin_30, cos_30, 238 100, 0 ]); 239 // matrix2 => translate(0, 100) scale(0.5). 240 var matrix2 = createMatrixFromArray([ 0.5, 0, 0, 0.5, 0, 100 ]); 241 var dist = getDistance(target, 'transform', matrix1, matrix2); 242 assert_approx_equals(dist, 243 Math.sqrt(100 * 100 + 100 * 100 + // translate 244 (Math.PI / 6) * (Math.PI / 6) + // rotate 245 0.5 * 0.5 + 0.5 * 0.5), // scale 246 EPSILON, 247 'distance of matrix'); 248 }, 'Test distance of matrix functions'); 249 250 test(function(t) { 251 var target = addDiv(t); 252 var matrix = createMatrixFromArray(rotate3dToMatrix(0, 1, 0, Math.PI / 6)); 253 var dist = getDistance(target, 'transform', matrix, 'none'); 254 assert_approx_equals(dist, Math.PI / 6, EPSILON, 'distance of matrix3d'); 255 }, 'Test distance of matrix3d function and none'); 256 257 test(function(t) { 258 var target = addDiv(t); 259 // matrix1 => rotate3d(0, 1, 0, 30deg). 260 var matrix1 = createMatrixFromArray(rotate3dToMatrix(0, 1, 0, Math.PI / 6)); 261 // matrix1 => translate3d(100, 0, 0) scale3d(0.5, 0.5, 0.5). 262 var matrix2 = createMatrixFromArray([ 0.5, 0, 0, 0, 263 0, 0.5, 0, 0, 264 0, 0, 0.5, 0, 265 100, 0, 0, 1 ]); 266 var dist = getDistance(target, 'transform', matrix1, matrix2); 267 assert_approx_equals(dist, 268 Math.sqrt(100 * 100 + // translate 269 0.5 * 0.5 * 3 + // scale 270 (Math.PI / 6) * (Math.PI / 6)), // rotate 271 EPSILON, 272 'distance of matrix'); 273 }, 'Test distance of matrix3d functions'); 274 275 test(function(t) { 276 var target = addDiv(t); 277 var cos_180 = Math.cos(Math.PI); 278 var sin_180 = Math.sin(Math.PI); 279 // matrix1 => translate3d(100px, 50px, -10px) skew(45deg). 280 var matrix1 = createMatrixFromArray([ 1, 0, 0, 0, 281 Math.tan(Math.PI/4.0), 1, 0, 0, 282 0, 0, 1, 0, 283 100, 50, -10, 1]); 284 // matrix2 => translate3d(1000px, 0, 0) rotate3d(1, 0, 0, 180deg). 285 var matrix2 = createMatrixFromArray([ 1, 0, 0, 0, 286 0, cos_180, sin_180, 0, 287 0, -sin_180, cos_180, 0, 288 1000, 0, 0, 1 ]); 289 var dist = getDistance(target, 'transform', matrix1, matrix2); 290 assert_approx_equals(dist, 291 Math.sqrt(900 * 900 + 50 * 50 + 10 * 10 + // translate 292 Math.PI * Math.PI + // rotate 293 (Math.PI / 4) * (Math.PI / 4)), // skew angle 294 EPSILON, 295 'distance of matrix'); 296 }, 'Test distance of matrix3d functions with skew factors'); 297 298 test(function(t) { 299 var target = addDiv(t); 300 var dist = 301 getDistance(target, 'transform', 302 'rotate(180deg) translate(1000px)', 303 'rotate(360deg) translate(0px)'); 304 assert_approx_equals(dist, Math.sqrt(1000 * 1000 + Math.PI * Math.PI), EPSILON, 305 'distance of transform lists'); 306 }, 'Test distance of transform lists'); 307 308 test(function(t) { 309 var target = addDiv(t); 310 var dist = getDistance(target, 'transform', 311 'translate(100px) rotate(180deg)', 312 'translate(50px) rotate(90deg) scale(5) skew(1rad)'); 313 assert_approx_equals(dist, 314 Math.sqrt(50 * 50 + 315 Math.PI / 2 * Math.PI / 2 + 316 4 * 4 * 2 + 317 1 * 1), 318 EPSILON, 319 'distance of transform lists'); 320 }, 'Test distance of transform lists where one has extra items'); 321 322 test(function(t) { 323 var target = addDiv(t); 324 var dist = getDistance(target, 'transform', 325 'translate(1000px) rotate3d(1, 0, 0, 180deg)', 326 'translate(1000px) scale3d(2.5, 0.5, 1)'); 327 assert_equals(dist, Math.sqrt(Math.PI * Math.PI + 1.5 * 1.5 + 0.5 * 0.5), 328 'distance of transform lists'); 329 }, 'Test distance of mismatched transform lists'); 330 331 test(function(t) { 332 var target = addDiv(t); 333 var dist = getDistance(target, 'transform', 334 'translate(100px) skew(1rad)', 335 'translate(1000px) rotate3d(0, 1, 0, -2rad)'); 336 assert_approx_equals(dist, 337 Math.sqrt(900 * 900 + 1 * 1 + 2 * 2), 338 EPSILON, 339 'distance of transform lists'); 340 }, 'Test distance of mismatched transform lists with skew function'); 341 342 343 // Individual transforms 344 test(function(t) { 345 var target = addDiv(t); 346 var dist = getDistance(target, 'translate', '50px', 'none'); 347 assert_equals(dist, Math.sqrt(50 * 50), 'distance of 2D translate and none'); 348 }, 'Test distance of 2D translate property with none'); 349 350 test(function(t) { 351 var target = addDiv(t); 352 var dist = getDistance(target, 'translate', '10px 30px', '50px'); 353 assert_equals(dist, Math.sqrt(40 * 40 + 30 * 30), 'distance of 2D translate'); 354 }, 'Test distance of 2D translate property'); 355 356 test(function(t) { 357 var target = addDiv(t); 358 var dist = getDistance(target, 'translate', '10px 30px 50px', '50px'); 359 assert_equals(dist, Math.sqrt(40 * 40 + 30 * 30 + 50 * 50), 360 'distance of 3D translate'); 361 }, 'Test distance of 3D translate property'); 362 363 test(function(t) { 364 var target = addDiv(t); 365 var dist = getDistance(target, 'scale', '2', 'none'); 366 assert_equals(dist, Math.sqrt(1 + 1), 'distance of 2D scale and none'); 367 }, 'Test distance of 2D scale property with none'); 368 369 test(function(t) { 370 var target = addDiv(t); 371 var dist = getDistance(target, 'scale', '3', '1 1'); 372 assert_equals(dist, Math.sqrt(2 * 2 + 2 * 2), 'distance of 2D scale'); 373 }, 'Test distance of 2D scale property'); 374 375 test(function(t) { 376 var target = addDiv(t); 377 var dist = getDistance(target, 'scale', '3 2 2', '1 1'); 378 assert_equals(dist, Math.sqrt(2 * 2 + 1 * 1 + 1 * 1), 379 'distance of 3D scale'); 380 }, 'Test distance of 3D scale property'); 381 382 test(function(t) { 383 var target = addDiv(t); 384 var dist = getDistance(target, 'rotate', '180deg', 'none'); 385 assert_equals(dist, Math.PI, 'distance of 2D rotate and none'); 386 }, 'Test distance of 2D rotate property with none'); 387 388 test(function(t) { 389 var target = addDiv(t); 390 var dist = getDistance(target, 'rotate', '180deg', '90deg'); 391 assert_equals(dist, Math.PI / 2.0, 'distance of 2D rotate'); 392 }, 'Test distance of 2D rotate property'); 393 394 test(function(t) { 395 var target = addDiv(t); 396 var dist = getDistance(target, 'rotate', 'z 90deg', 'x 90deg'); 397 let q1 = getQuaternion([0, 0, 1], Math.PI / 2.0); 398 let q2 = getQuaternion([1, 0, 0], Math.PI / 2.0); 399 assert_approx_equals(dist, computeRotateDistance(q1, q2), EPSILON, 400 'distance of 3D rotate'); 401 }, 'Test distance of 3D rotate property'); 402 403 </script> 404 </html>