simple-squiggle

A restricted subset of Squiggle
Log | Files | Refs | README

intersect.js (8576B)


      1 import { factory } from '../../utils/factory.js';
      2 var name = 'intersect';
      3 var dependencies = ['typed', 'config', 'abs', 'add', 'addScalar', 'matrix', 'multiply', 'multiplyScalar', 'divideScalar', 'subtract', 'smaller', 'equalScalar', 'flatten', 'isZero', 'isNumeric'];
      4 export var createIntersect = /* #__PURE__ */factory(name, dependencies, _ref => {
      5   var {
      6     typed,
      7     config,
      8     abs,
      9     add,
     10     addScalar,
     11     matrix,
     12     multiply,
     13     multiplyScalar,
     14     divideScalar,
     15     subtract,
     16     smaller,
     17     equalScalar,
     18     flatten,
     19     isZero,
     20     isNumeric
     21   } = _ref;
     22 
     23   /**
     24    * Calculates the point of intersection of two lines in two or three dimensions
     25    * and of a line and a plane in three dimensions. The inputs are in the form of
     26    * arrays or 1 dimensional matrices. The line intersection functions return null
     27    * if the lines do not meet.
     28    *
     29    * Note: Fill the plane coefficients as `x + y + z = c` and not as `x + y + z + c = 0`.
     30    *
     31    * Syntax:
     32    *
     33    *    math.intersect(endPoint1Line1, endPoint2Line1, endPoint1Line2, endPoint2Line2)
     34    *    math.intersect(endPoint1, endPoint2, planeCoefficients)
     35    *
     36    * Examples:
     37    *
     38    *    math.intersect([0, 0], [10, 10], [10, 0], [0, 10])              // Returns [5, 5]
     39    *    math.intersect([0, 0, 0], [10, 10, 0], [10, 0, 0], [0, 10, 0])  // Returns [5, 5, 0]
     40    *    math.intersect([1, 0, 1],  [4, -2, 2], [1, 1, 1, 6])            // Returns [7, -4, 3]
     41    *
     42    * @param  {Array | Matrix} w   Co-ordinates of first end-point of first line
     43    * @param  {Array | Matrix} x   Co-ordinates of second end-point of first line
     44    * @param  {Array | Matrix} y   Co-ordinates of first end-point of second line
     45    *                              OR Co-efficients of the plane's equation
     46    * @param  {Array | Matrix} z   Co-ordinates of second end-point of second line
     47    *                              OR undefined if the calculation is for line and plane
     48    * @return {Array}              Returns the point of intersection of lines/lines-planes
     49    */
     50   return typed('intersect', {
     51     'Array, Array, Array': _AAA,
     52     'Array, Array, Array, Array': _AAAA,
     53     'Matrix, Matrix, Matrix': function MatrixMatrixMatrix(x, y, plane) {
     54       var arr = _AAA(x.valueOf(), y.valueOf(), plane.valueOf());
     55 
     56       return arr === null ? null : matrix(arr);
     57     },
     58     'Matrix, Matrix, Matrix, Matrix': function MatrixMatrixMatrixMatrix(w, x, y, z) {
     59       // TODO: output matrix type should match input matrix type
     60       var arr = _AAAA(w.valueOf(), x.valueOf(), y.valueOf(), z.valueOf());
     61 
     62       return arr === null ? null : matrix(arr);
     63     }
     64   });
     65 
     66   function _AAA(x, y, plane) {
     67     x = _coerceArr(x);
     68     y = _coerceArr(y);
     69     plane = _coerceArr(plane);
     70 
     71     if (!_3d(x)) {
     72       throw new TypeError('Array with 3 numbers or BigNumbers expected for first argument');
     73     }
     74 
     75     if (!_3d(y)) {
     76       throw new TypeError('Array with 3 numbers or BigNumbers expected for second argument');
     77     }
     78 
     79     if (!_4d(plane)) {
     80       throw new TypeError('Array with 4 numbers expected as third argument');
     81     }
     82 
     83     return _intersectLinePlane(x[0], x[1], x[2], y[0], y[1], y[2], plane[0], plane[1], plane[2], plane[3]);
     84   }
     85 
     86   function _AAAA(w, x, y, z) {
     87     w = _coerceArr(w);
     88     x = _coerceArr(x);
     89     y = _coerceArr(y);
     90     z = _coerceArr(z);
     91 
     92     if (w.length === 2) {
     93       if (!_2d(w)) {
     94         throw new TypeError('Array with 2 numbers or BigNumbers expected for first argument');
     95       }
     96 
     97       if (!_2d(x)) {
     98         throw new TypeError('Array with 2 numbers or BigNumbers expected for second argument');
     99       }
    100 
    101       if (!_2d(y)) {
    102         throw new TypeError('Array with 2 numbers or BigNumbers expected for third argument');
    103       }
    104 
    105       if (!_2d(z)) {
    106         throw new TypeError('Array with 2 numbers or BigNumbers expected for fourth argument');
    107       }
    108 
    109       return _intersect2d(w, x, y, z);
    110     } else if (w.length === 3) {
    111       if (!_3d(w)) {
    112         throw new TypeError('Array with 3 numbers or BigNumbers expected for first argument');
    113       }
    114 
    115       if (!_3d(x)) {
    116         throw new TypeError('Array with 3 numbers or BigNumbers expected for second argument');
    117       }
    118 
    119       if (!_3d(y)) {
    120         throw new TypeError('Array with 3 numbers or BigNumbers expected for third argument');
    121       }
    122 
    123       if (!_3d(z)) {
    124         throw new TypeError('Array with 3 numbers or BigNumbers expected for fourth argument');
    125       }
    126 
    127       return _intersect3d(w[0], w[1], w[2], x[0], x[1], x[2], y[0], y[1], y[2], z[0], z[1], z[2]);
    128     } else {
    129       throw new TypeError('Arrays with two or thee dimensional points expected');
    130     }
    131   }
    132   /** Coerce row and column 2-dim arrays to 1-dim array */
    133 
    134 
    135   function _coerceArr(arr) {
    136     // row matrix
    137     if (arr.length === 1) return arr[0]; // column matrix
    138 
    139     if (arr.length > 1 && Array.isArray(arr[0])) {
    140       if (arr.every(el => Array.isArray(el) && el.length === 1)) return flatten(arr);
    141     }
    142 
    143     return arr;
    144   }
    145 
    146   function _2d(x) {
    147     return x.length === 2 && isNumeric(x[0]) && isNumeric(x[1]);
    148   }
    149 
    150   function _3d(x) {
    151     return x.length === 3 && isNumeric(x[0]) && isNumeric(x[1]) && isNumeric(x[2]);
    152   }
    153 
    154   function _4d(x) {
    155     return x.length === 4 && isNumeric(x[0]) && isNumeric(x[1]) && isNumeric(x[2]) && isNumeric(x[3]);
    156   }
    157 
    158   function _intersect2d(p1a, p1b, p2a, p2b) {
    159     var o1 = p1a;
    160     var o2 = p2a;
    161     var d1 = subtract(o1, p1b);
    162     var d2 = subtract(o2, p2b);
    163     var det = subtract(multiplyScalar(d1[0], d2[1]), multiplyScalar(d2[0], d1[1]));
    164     if (isZero(det)) return null;
    165 
    166     if (smaller(abs(det), config.epsilon)) {
    167       return null;
    168     }
    169 
    170     var d20o11 = multiplyScalar(d2[0], o1[1]);
    171     var d21o10 = multiplyScalar(d2[1], o1[0]);
    172     var d20o21 = multiplyScalar(d2[0], o2[1]);
    173     var d21o20 = multiplyScalar(d2[1], o2[0]);
    174     var t = divideScalar(addScalar(subtract(subtract(d20o11, d21o10), d20o21), d21o20), det);
    175     return add(multiply(d1, t), o1);
    176   }
    177 
    178   function _intersect3dHelper(a, b, c, d, e, f, g, h, i, j, k, l) {
    179     // (a - b)*(c - d) + (e - f)*(g - h) + (i - j)*(k - l)
    180     var add1 = multiplyScalar(subtract(a, b), subtract(c, d));
    181     var add2 = multiplyScalar(subtract(e, f), subtract(g, h));
    182     var add3 = multiplyScalar(subtract(i, j), subtract(k, l));
    183     return addScalar(addScalar(add1, add2), add3);
    184   }
    185 
    186   function _intersect3d(x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4) {
    187     var d1343 = _intersect3dHelper(x1, x3, x4, x3, y1, y3, y4, y3, z1, z3, z4, z3);
    188 
    189     var d4321 = _intersect3dHelper(x4, x3, x2, x1, y4, y3, y2, y1, z4, z3, z2, z1);
    190 
    191     var d1321 = _intersect3dHelper(x1, x3, x2, x1, y1, y3, y2, y1, z1, z3, z2, z1);
    192 
    193     var d4343 = _intersect3dHelper(x4, x3, x4, x3, y4, y3, y4, y3, z4, z3, z4, z3);
    194 
    195     var d2121 = _intersect3dHelper(x2, x1, x2, x1, y2, y1, y2, y1, z2, z1, z2, z1);
    196 
    197     var numerator = subtract(multiplyScalar(d1343, d4321), multiplyScalar(d1321, d4343));
    198     var denominator = subtract(multiplyScalar(d2121, d4343), multiplyScalar(d4321, d4321));
    199     if (isZero(denominator)) return null;
    200     var ta = divideScalar(numerator, denominator);
    201     var tb = divideScalar(addScalar(d1343, multiplyScalar(ta, d4321)), d4343);
    202     var pax = addScalar(x1, multiplyScalar(ta, subtract(x2, x1)));
    203     var pay = addScalar(y1, multiplyScalar(ta, subtract(y2, y1)));
    204     var paz = addScalar(z1, multiplyScalar(ta, subtract(z2, z1)));
    205     var pbx = addScalar(x3, multiplyScalar(tb, subtract(x4, x3)));
    206     var pby = addScalar(y3, multiplyScalar(tb, subtract(y4, y3)));
    207     var pbz = addScalar(z3, multiplyScalar(tb, subtract(z4, z3)));
    208 
    209     if (equalScalar(pax, pbx) && equalScalar(pay, pby) && equalScalar(paz, pbz)) {
    210       return [pax, pay, paz];
    211     } else {
    212       return null;
    213     }
    214   }
    215 
    216   function _intersectLinePlane(x1, y1, z1, x2, y2, z2, x, y, z, c) {
    217     var x1x = multiplyScalar(x1, x);
    218     var x2x = multiplyScalar(x2, x);
    219     var y1y = multiplyScalar(y1, y);
    220     var y2y = multiplyScalar(y2, y);
    221     var z1z = multiplyScalar(z1, z);
    222     var z2z = multiplyScalar(z2, z);
    223     var numerator = subtract(subtract(subtract(c, x1x), y1y), z1z);
    224     var denominator = subtract(subtract(subtract(addScalar(addScalar(x2x, y2y), z2z), x1x), y1y), z1z);
    225     var t = divideScalar(numerator, denominator);
    226     var px = addScalar(x1, multiplyScalar(t, subtract(x2, x1)));
    227     var py = addScalar(y1, multiplyScalar(t, subtract(y2, y1)));
    228     var pz = addScalar(z1, multiplyScalar(t, subtract(z2, z1)));
    229     return [px, py, pz]; // TODO: Add cases when line is parallel to the plane:
    230     //       (a) no intersection,
    231     //       (b) line contained in plane
    232   }
    233 });