UNPKG

7.96 kBJavaScriptView Raw
1'use strict'
2
3function factory (type, config, load, typed) {
4 const abs = load(require('../arithmetic/abs'))
5 const add = load(require('../arithmetic/add'))
6 const addScalar = load(require('../arithmetic/addScalar'))
7 const matrix = load(require('../../type/matrix/function/matrix'))
8 const multiply = load(require('../arithmetic/multiply'))
9 const multiplyScalar = load(require('../arithmetic/multiplyScalar'))
10 const divideScalar = load(require('../arithmetic/divideScalar'))
11 const subtract = load(require('../arithmetic/subtract'))
12 const smaller = load(require('../relational/smaller'))
13 const equalScalar = load(require('../relational/equalScalar'))
14
15 /**
16 * Calculates the point of intersection of two lines in two or three dimensions
17 * and of a line and a plane in three dimensions. The inputs are in the form of
18 * arrays or 1 dimensional matrices. The line intersection functions return null
19 * if the lines do not meet.
20 *
21 * Note: Fill the plane coefficients as `x + y + z = c` and not as `x + y + z + c = 0`.
22 *
23 * Syntax:
24 *
25 * math.intersect(endPoint1Line1, endPoint2Line1, endPoint1Line2, endPoint2Line2)
26 * math.intersect(endPoint1, endPoint2, planeCoefficients)
27 *
28 * Examples:
29 *
30 * math.intersect([0, 0], [10, 10], [10, 0], [0, 10]) // Returns [5, 5]
31 * math.intersect([0, 0, 0], [10, 10, 0], [10, 0, 0], [0, 10, 0]) // Returns [5, 5, 0]
32 * math.intersect([1, 0, 1], [4, -2, 2], [1, 1, 1, 6]) // Returns [7, -4, 3]
33 *
34 * @param {Array | Matrix} w Co-ordinates of first end-point of first line
35 * @param {Array | Matrix} x Co-ordinates of second end-point of first line
36 * @param {Array | Matrix} y Co-ordinates of first end-point of second line
37 * OR Co-efficients of the plane's equation
38 * @param {Array | Matrix} z Co-ordinates of second end-point of second line
39 * OR null if the calculation is for line and plane
40 * @return {Array} Returns the point of intersection of lines/lines-planes
41 */
42 const intersect = typed('intersect', {
43 'Array, Array, Array': function (x, y, plane) {
44 if (!_3d(x)) { throw new TypeError('Array with 3 numbers or BigNumbers expected for first argument') }
45 if (!_3d(y)) { throw new TypeError('Array with 3 numbers or BigNumbers expected for second argument') }
46 if (!_4d(plane)) { throw new TypeError('Array with 4 numbers expected as third argument') }
47
48 return _intersectLinePlane(x[0], x[1], x[2], y[0], y[1], y[2], plane[0], plane[1], plane[2], plane[3])
49 },
50
51 'Array, Array, Array, Array': function (w, x, y, z) {
52 if (w.length === 2) {
53 if (!_2d(w)) { throw new TypeError('Array with 2 numbers or BigNumbers expected for first argument') }
54 if (!_2d(x)) { throw new TypeError('Array with 2 numbers or BigNumbers expected for second argument') }
55 if (!_2d(y)) { throw new TypeError('Array with 2 numbers or BigNumbers expected for third argument') }
56 if (!_2d(z)) { throw new TypeError('Array with 2 numbers or BigNumbers expected for fourth argument') }
57
58 return _intersect2d(w, x, y, z)
59 } else if (w.length === 3) {
60 if (!_3d(w)) { throw new TypeError('Array with 3 numbers or BigNumbers expected for first argument') }
61 if (!_3d(x)) { throw new TypeError('Array with 3 numbers or BigNumbers expected for second argument') }
62 if (!_3d(y)) { throw new TypeError('Array with 3 numbers or BigNumbers expected for third argument') }
63 if (!_3d(z)) { throw new TypeError('Array with 3 numbers or BigNumbers expected for fourth argument') }
64
65 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])
66 } else {
67 throw new TypeError('Arrays with two or thee dimensional points expected')
68 }
69 },
70
71 'Matrix, Matrix, Matrix': function (x, y, plane) {
72 return matrix(intersect(x.valueOf(), y.valueOf(), plane.valueOf()))
73 },
74
75 'Matrix, Matrix, Matrix, Matrix': function (w, x, y, z) {
76 // TODO: output matrix type should match input matrix type
77 return matrix(intersect(w.valueOf(), x.valueOf(), y.valueOf(), z.valueOf()))
78 }
79 })
80
81 function _isNumber (a) {
82 // intersect supports numbers and bignumbers
83 return (typeof a === 'number' || type.isBigNumber(a))
84 }
85
86 function _2d (x) {
87 return x.length === 2 && _isNumber(x[0]) && _isNumber(x[1])
88 }
89
90 function _3d (x) {
91 return x.length === 3 && _isNumber(x[0]) && _isNumber(x[1]) && _isNumber(x[2])
92 }
93
94 function _4d (x) {
95 return x.length === 4 && _isNumber(x[0]) && _isNumber(x[1]) && _isNumber(x[2]) && _isNumber(x[3])
96 }
97
98 function _intersect2d (p1a, p1b, p2a, p2b) {
99 const o1 = p1a
100 const o2 = p2a
101 const d1 = subtract(o1, p1b)
102 const d2 = subtract(o2, p2b)
103 const det = subtract(multiplyScalar(d1[0], d2[1]), multiplyScalar(d2[0], d1[1]))
104 if (smaller(abs(det), config.epsilon)) {
105 return null
106 }
107 const d20o11 = multiplyScalar(d2[0], o1[1])
108 const d21o10 = multiplyScalar(d2[1], o1[0])
109 const d20o21 = multiplyScalar(d2[0], o2[1])
110 const d21o20 = multiplyScalar(d2[1], o2[0])
111 const t = divideScalar(addScalar(subtract(subtract(d20o11, d21o10), d20o21), d21o20), det)
112 return add(multiply(d1, t), o1)
113 }
114
115 function _intersect3dHelper (a, b, c, d, e, f, g, h, i, j, k, l) {
116 // (a - b)*(c - d) + (e - f)*(g - h) + (i - j)*(k - l)
117 const add1 = multiplyScalar(subtract(a, b), subtract(c, d))
118 const add2 = multiplyScalar(subtract(e, f), subtract(g, h))
119 const add3 = multiplyScalar(subtract(i, j), subtract(k, l))
120 return addScalar(addScalar(add1, add2), add3)
121 }
122
123 function _intersect3d (x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4) {
124 const d1343 = _intersect3dHelper(x1, x3, x4, x3, y1, y3, y4, y3, z1, z3, z4, z3)
125 const d4321 = _intersect3dHelper(x4, x3, x2, x1, y4, y3, y2, y1, z4, z3, z2, z1)
126 const d1321 = _intersect3dHelper(x1, x3, x2, x1, y1, y3, y2, y1, z1, z3, z2, z1)
127 const d4343 = _intersect3dHelper(x4, x3, x4, x3, y4, y3, y4, y3, z4, z3, z4, z3)
128 const d2121 = _intersect3dHelper(x2, x1, x2, x1, y2, y1, y2, y1, z2, z1, z2, z1)
129 const ta = divideScalar(
130 subtract(multiplyScalar(d1343, d4321), multiplyScalar(d1321, d4343)),
131 subtract(multiplyScalar(d2121, d4343), multiplyScalar(d4321, d4321)))
132 const tb = divideScalar(addScalar(d1343, multiplyScalar(ta, d4321)), d4343)
133
134 const pax = addScalar(x1, multiplyScalar(ta, subtract(x2, x1)))
135 const pay = addScalar(y1, multiplyScalar(ta, subtract(y2, y1)))
136 const paz = addScalar(z1, multiplyScalar(ta, subtract(z2, z1)))
137 const pbx = addScalar(x3, multiplyScalar(tb, subtract(x4, x3)))
138 const pby = addScalar(y3, multiplyScalar(tb, subtract(y4, y3)))
139 const pbz = addScalar(z3, multiplyScalar(tb, subtract(z4, z3)))
140 if (equalScalar(pax, pbx) && equalScalar(pay, pby) && equalScalar(paz, pbz)) {
141 return [pax, pay, paz]
142 } else {
143 return null
144 }
145 }
146
147 function _intersectLinePlane (x1, y1, z1, x2, y2, z2, x, y, z, c) {
148 const x1x = multiplyScalar(x1, x)
149 const x2x = multiplyScalar(x2, x)
150 const y1y = multiplyScalar(y1, y)
151 const y2y = multiplyScalar(y2, y)
152 const z1z = multiplyScalar(z1, z)
153 const z2z = multiplyScalar(z2, z)
154 const t = divideScalar(
155 subtract(subtract(subtract(c, x1x), y1y), z1z),
156 subtract(subtract(subtract(addScalar(addScalar(x2x, y2y), z2z), x1x), y1y), z1z))
157 const px = addScalar(x1, multiplyScalar(t, subtract(x2, x1)))
158 const py = addScalar(y1, multiplyScalar(t, subtract(y2, y1)))
159 const pz = addScalar(z1, multiplyScalar(t, subtract(z2, z1)))
160 return [px, py, pz]
161 // TODO: Add cases when line is parallel to the plane:
162 // (a) no intersection,
163 // (b) line contained in plane
164 }
165
166 return intersect
167}
168
169exports.name = 'intersect'
170exports.factory = factory