tor-browser

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

dom-matrix-2d.js (8830B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 "use strict";
      6 
      7 /**
      8 * Returns a matrix for the scaling given.
      9 * Calling `scale()` or `scale(1) returns a new identity matrix.
     10 *
     11 * @param {number} [sx = 1]
     12 *        the abscissa of the scaling vector.
     13 *        If unspecified, it will equal to `1`.
     14 * @param {number} [sy = sx]
     15 *        The ordinate of the scaling vector.
     16 *        If not present, its default value is `sx`, leading to a uniform scaling.
     17 * @return {Array}
     18 *         The new matrix.
     19 */
     20 const scale = (sx = 1, sy = sx) => [sx, 0, 0, 0, sy, 0, 0, 0, 1];
     21 exports.scale = scale;
     22 
     23 /**
     24 * Returns a matrix for the translation given.
     25 * Calling `translate()` or `translate(0) returns a new identity matrix.
     26 *
     27 * @param {number} [tx = 0]
     28 *        The abscissa of the translating vector.
     29 *        If unspecified, it will equal to `0`.
     30 * @param {number} [ty = tx]
     31 *        The ordinate of the translating vector.
     32 *        If unspecified, it will equal to `tx`.
     33 * @return {Array}
     34 *         The new matrix.
     35 */
     36 const translate = (tx = 0, ty = tx) => [1, 0, tx, 0, 1, ty, 0, 0, 1];
     37 exports.translate = translate;
     38 
     39 /**
     40 * Returns a matrix that reflects about the Y axis.  For example, the point (x1, y1) would
     41 * become (-x1, y1).
     42 *
     43 * @return {Array}
     44 *         The new matrix.
     45 */
     46 const reflectAboutY = () => [-1, 0, 0, 0, 1, 0, 0, 0, 1];
     47 exports.reflectAboutY = reflectAboutY;
     48 
     49 /**
     50 * Returns a matrix for the rotation given.
     51 * Calling `rotate()` or `rotate(0)` returns a new identity matrix.
     52 *
     53 * @param {number} [angle = 0]
     54 *        The angle, in radians, for which to return a corresponding rotation matrix.
     55 *        If unspecified, it will equal `0`.
     56 * @return {Array}
     57 *         The new matrix.
     58 */
     59 const rotate = (angle = 0) => {
     60  const cos = Math.cos(angle);
     61  const sin = Math.sin(angle);
     62 
     63  return [cos, sin, 0, -sin, cos, 0, 0, 0, 1];
     64 };
     65 exports.rotate = rotate;
     66 
     67 /**
     68 * Returns a new identity matrix.
     69 *
     70 * @return {Array}
     71 *         The new matrix.
     72 */
     73 const identity = () => [1, 0, 0, 0, 1, 0, 0, 0, 1];
     74 exports.identity = identity;
     75 
     76 /**
     77 * Multiplies two matrices and returns a new matrix with the result.
     78 *
     79 * @param {Array} M1
     80 *        The first operand.
     81 * @param {Array} M2
     82 *        The second operand.
     83 * @return {Array}
     84 *        The resulting matrix.
     85 */
     86 const multiply = (M1, M2) => {
     87  const c11 = M1[0] * M2[0] + M1[1] * M2[3] + M1[2] * M2[6];
     88  const c12 = M1[0] * M2[1] + M1[1] * M2[4] + M1[2] * M2[7];
     89  const c13 = M1[0] * M2[2] + M1[1] * M2[5] + M1[2] * M2[8];
     90 
     91  const c21 = M1[3] * M2[0] + M1[4] * M2[3] + M1[5] * M2[6];
     92  const c22 = M1[3] * M2[1] + M1[4] * M2[4] + M1[5] * M2[7];
     93  const c23 = M1[3] * M2[2] + M1[4] * M2[5] + M1[5] * M2[8];
     94 
     95  const c31 = M1[6] * M2[0] + M1[7] * M2[3] + M1[8] * M2[6];
     96  const c32 = M1[6] * M2[1] + M1[7] * M2[4] + M1[8] * M2[7];
     97  const c33 = M1[6] * M2[2] + M1[7] * M2[5] + M1[8] * M2[8];
     98 
     99  return [c11, c12, c13, c21, c22, c23, c31, c32, c33];
    100 };
    101 exports.multiply = multiply;
    102 
    103 /**
    104 * Applies the given matrix to a point.
    105 *
    106 * @param {Array} M
    107 *        The matrix to apply.
    108 * @param {Array} P
    109 *        The point's vector.
    110 * @return {Array}
    111 *        The resulting point's vector.
    112 */
    113 const apply = (M, P) => [
    114  M[0] * P[0] + M[1] * P[1] + M[2],
    115  M[3] * P[0] + M[4] * P[1] + M[5],
    116 ];
    117 exports.apply = apply;
    118 
    119 /**
    120 * Returns `true` if the given matrix is a identity matrix.
    121 *
    122 * @param {Array} M
    123 *        The matrix to check
    124 * @return {boolean}
    125 *        `true` if the matrix passed is a identity matrix, `false` otherwise.
    126 */
    127 const isIdentity = M =>
    128  M[0] === 1 &&
    129  M[1] === 0 &&
    130  M[2] === 0 &&
    131  M[3] === 0 &&
    132  M[4] === 1 &&
    133  M[5] === 0 &&
    134  M[6] === 0 &&
    135  M[7] === 0 &&
    136  M[8] === 1;
    137 exports.isIdentity = isIdentity;
    138 
    139 /**
    140 * Get the change of basis matrix and inverted change of basis matrix
    141 * for the coordinate system based on the two given vectors, as well as
    142 * the lengths of the two given vectors.
    143 *
    144 * @param {Array} u
    145 *        The first vector, serving as the "x axis" of the coordinate system.
    146 * @param {Array} v
    147 *        The second vector, serving as the "y axis" of the coordinate system.
    148 * @return {object}
    149 *        { basis, invertedBasis, uLength, vLength }
    150 *        basis and invertedBasis are the change of basis matrices. uLength and
    151 *        vLength are the lengths of u and v.
    152 */
    153 const getBasis = (u, v) => {
    154  const uLength = Math.abs(Math.sqrt(u[0] ** 2 + u[1] ** 2));
    155  const vLength = Math.abs(Math.sqrt(v[0] ** 2 + v[1] ** 2));
    156  const basis = [
    157    u[0] / uLength,
    158    v[0] / vLength,
    159    0,
    160    u[1] / uLength,
    161    v[1] / vLength,
    162    0,
    163    0,
    164    0,
    165    1,
    166  ];
    167  const determinant = 1 / (basis[0] * basis[4] - basis[1] * basis[3]);
    168  const invertedBasis = [
    169    basis[4] / determinant,
    170    -basis[1] / determinant,
    171    0,
    172    -basis[3] / determinant,
    173    basis[0] / determinant,
    174    0,
    175    0,
    176    0,
    177    1,
    178  ];
    179  return { basis, invertedBasis, uLength, vLength };
    180 };
    181 exports.getBasis = getBasis;
    182 
    183 /**
    184 * Convert the given matrix to a new coordinate system, based on the change of basis
    185 * matrix.
    186 *
    187 * @param {Array} M
    188 *        The matrix to convert
    189 * @param {Array} basis
    190 *        The change of basis matrix
    191 * @param {Array} invertedBasis
    192 *        The inverted change of basis matrix
    193 * @return {Array}
    194 *        The converted matrix.
    195 */
    196 const changeMatrixBase = (M, basis, invertedBasis) => {
    197  return multiply(invertedBasis, multiply(M, basis));
    198 };
    199 exports.changeMatrixBase = changeMatrixBase;
    200 
    201 /**
    202 * Returns the transformation matrix for the given node, relative to the ancestor passed
    203 * as second argument; considering the ancestor transformation too.
    204 * If no ancestor is specified, it will returns the transformation matrix relative to the
    205 * node's parent element.
    206 *
    207 * @param {DOMNode} node
    208 *        The node.
    209 * @param {DOMNode} ancestor
    210 *        The ancestor of the node given.
    211 * @return {Array}
    212 *        The transformation matrix.
    213 */
    214 function getNodeTransformationMatrix(node, ancestor = node.parentElement) {
    215  const { a, b, c, d, e, f } = ancestor
    216    .getTransformToParent()
    217    .multiply(node.getTransformToAncestor(ancestor));
    218 
    219  return [a, c, e, b, d, f, 0, 0, 1];
    220 }
    221 exports.getNodeTransformationMatrix = getNodeTransformationMatrix;
    222 
    223 /**
    224 * Returns the matrix to rotate, translate, and reflect (if needed) from the element's
    225 * top-left origin into the actual writing mode and text direction applied to the element.
    226 *
    227 * @param  {object} size
    228 *         An element's untransformed content `width` and `height` (excluding any margin,
    229 *         borders, or padding).
    230 * @param  {object} style
    231 *         The computed `writingMode` and `direction` properties for the element.
    232 * @return {Array}
    233 *         The matrix with adjustments for writing mode and text direction, if any.
    234 */
    235 function getWritingModeMatrix(size, style) {
    236  let currentMatrix = identity();
    237  const { width, height } = size;
    238  const { direction, writingMode } = style;
    239 
    240  switch (writingMode) {
    241    case "horizontal-tb":
    242      // This is the initial value. No further adjustment needed.
    243      break;
    244    case "vertical-rl":
    245      currentMatrix = multiply(translate(width, 0), rotate(-Math.PI / 2));
    246      break;
    247    case "vertical-lr":
    248      currentMatrix = multiply(reflectAboutY(), rotate(-Math.PI / 2));
    249      break;
    250    case "sideways-rl":
    251      currentMatrix = multiply(translate(width, 0), rotate(-Math.PI / 2));
    252      break;
    253    case "sideways-lr":
    254      currentMatrix = multiply(rotate(Math.PI / 2), translate(-height, 0));
    255      break;
    256    default:
    257      console.error(`Unexpected writing-mode: ${writingMode}`);
    258  }
    259 
    260  switch (direction) {
    261    case "ltr":
    262      // This is the initial value. No further adjustment needed.
    263      break;
    264    case "rtl": {
    265      let rowLength = width;
    266      if (writingMode != "horizontal-tb") {
    267        rowLength = height;
    268      }
    269      currentMatrix = multiply(currentMatrix, translate(rowLength, 0));
    270      currentMatrix = multiply(currentMatrix, reflectAboutY());
    271      break;
    272    }
    273    default:
    274      console.error(`Unexpected direction: ${direction}`);
    275  }
    276 
    277  return currentMatrix;
    278 }
    279 exports.getWritingModeMatrix = getWritingModeMatrix;
    280 
    281 /**
    282 * Convert from the matrix format used in this module:
    283 *   a, c, e,
    284 *   b, d, f,
    285 *   0, 0, 1
    286 * to the format used by the `matrix()` CSS transform function:
    287 *   a, b, c, d, e, f
    288 *
    289 * @param  {Array} M
    290 *         The matrix in this module's 9 element format.
    291 * @return {string}
    292 *         The matching 6 element CSS transform function.
    293 */
    294 function getCSSMatrixTransform(M) {
    295  const [a, c, e, b, d, f] = M;
    296  return `matrix(${a}, ${b}, ${c}, ${d}, ${e}, ${f})`;
    297 }
    298 exports.getCSSMatrixTransform = getCSSMatrixTransform;