/**************************************************************************** * Copyright 2017 EPAM Systems * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ***************************************************************************/ function Vec2(x, y, z) { if (arguments.length == 0) { this.x = 0; this.y = 0; this.z = 0; } else if (arguments.length == 1) { this.x = parseFloat(x.x || 0); this.y = parseFloat(x.y || 0); this.z = parseFloat(x.z || 0); } else if (arguments.length == 2) { this.x = parseFloat(x || 0); this.y = parseFloat(y || 0); this.z = 0; } else if (arguments.length == 3) { this.x = parseFloat(x); this.y = parseFloat(y); this.z = parseFloat(z); } else { throw new Error('Vec2(): invalid arguments'); } } Vec2.ZERO = new Vec2(0, 0); Vec2.UNIT = new Vec2(1, 1); Vec2.segmentIntersection = function (a, b, c, d) { /* eslint-disable no-mixed-operators*/ var dc = (a.x - c.x) * (b.y - c.y) - (a.y - c.y) * (b.x - c.x); var dd = (a.x - d.x) * (b.y - d.y) - (a.y - d.y) * (b.x - d.x); var da = (c.x - a.x) * (d.y - a.y) - (c.y - a.y) * (d.x - a.x); var db = (c.x - b.x) * (d.y - b.y) - (c.y - b.y) * (d.x - b.x); /* eslint-enable no-mixed-operators*/ return dc * dd <= 0 && da * db <= 0; }; Vec2.prototype.length = function () { /* eslint-disable no-mixed-operators*/ return Math.sqrt(this.x * this.x + this.y * this.y); /* eslint-enable no-mixed-operators*/ }; Vec2.prototype.equals = function (v) { console.assert(!!v); return this.x == v.x && this.y == v.y; }; Vec2.prototype.add = function (v) { console.assert(!!v); return new Vec2(this.x + v.x, this.y + v.y, this.z + v.z); }; Vec2.prototype.add_ = function (v) { // eslint-disable-line no-underscore-dangle console.assert(!!v); this.x += v.x; this.y += v.y; this.z += v.z; }; Vec2.prototype.get_xy0 = function () { return new Vec2(this.x, this.y); }; Vec2.prototype.sub = function (v) { console.assert(!!v); return new Vec2(this.x - v.x, this.y - v.y, this.z - v.z); }; Vec2.prototype.scaled = function (s) { console.assert(s == 0 || !!s); return new Vec2(this.x * s, this.y * s, this.z * s); }; Vec2.prototype.negated = function () { return new Vec2(-this.x, -this.y, -this.z); }; Vec2.prototype.yComplement = function (y1) { y1 = y1 || 0; return new Vec2(this.x, y1 - this.y, this.z); }; Vec2.prototype.addScaled = function (v, f) { console.assert(!!v); console.assert(f == 0 || !!f); /* eslint-disable no-mixed-operators*/ return new Vec2(this.x + v.x * f, this.y + v.y * f, this.z + v.z * f); /* eslint-enable no-mixed-operators*/ }; Vec2.prototype.normalized = function () { return this.scaled(1 / this.length()); }; Vec2.prototype.normalize = function () { var l = this.length(); if (l < 0.000001) return false; this.x /= l; this.y /= l; return true; }; Vec2.prototype.turnLeft = function () { return new Vec2(-this.y, this.x, this.z); }; Vec2.prototype.coordStr = function () { return this.x.toString() + ' , ' + this.y.toString(); }; Vec2.prototype.toString = function () { return '(' + this.x.toFixed(2) + ',' + this.y.toFixed(2) + ')'; }; Vec2.dist = function (a, b) { console.assert(!!a); console.assert(!!b); return Vec2.diff(a, b).length(); }; Vec2.max = function (v1, v2) { console.assert(!!v1); console.assert(!!v2); return new Vec2(Math.max(v1.x, v2.x), Math.max(v1.y, v2.y), Math.max(v1.z, v2.z)); }; Vec2.min = function (v1, v2) { console.assert(!!v1); console.assert(!!v2); return new Vec2(Math.min(v1.x, v2.x), Math.min(v1.y, v2.y), Math.min(v1.z, v2.z)); }; Vec2.prototype.max = function (v) { console.assert(!!v); return new Vec2.max(this, v); // eslint-disable-line new-cap }; Vec2.prototype.min = function (v) { console.assert(!!v); return new Vec2.min(this, v); // eslint-disable-line new-cap }; Vec2.prototype.ceil = function () { return new Vec2(Math.ceil(this.x), Math.ceil(this.y), Math.ceil(this.z)); }; Vec2.prototype.floor = function () { return new Vec2(Math.floor(this.x), Math.floor(this.y), Math.floor(this.z)); }; Vec2.sum = function (v1, v2) { console.assert(!!v1); console.assert(!!v2); return new Vec2(v1.x + v2.x, v1.y + v2.y, v1.z + v2.z); }; Vec2.dot = function (v1, v2) { console.assert(!!v1); console.assert(!!v2); /* eslint-disable no-mixed-operators*/ return v1.x * v2.x + v1.y * v2.y; /* eslint-enable no-mixed-operators*/ }; Vec2.cross = function (v1, v2) { console.assert(!!v1); console.assert(!!v2); /* eslint-disable no-mixed-operators*/ return v1.x * v2.y - v1.y * v2.x; /* eslint-enable no-mixed-operators*/ }; Vec2.prototype.rotate = function (angle) { console.assert(angle == 0 || !!angle); var si = Math.sin(angle); var co = Math.cos(angle); return this.rotateSC(si, co); }; Vec2.prototype.rotateSC = function (si, co) { console.assert(si == 0 || !!si); console.assert(co == 0 || !!co); /* eslint-disable no-mixed-operators*/ return new Vec2(this.x * co - this.y * si, this.x * si + this.y * co, this.z); /* eslint-enable no-mixed-operators*/ }; Vec2.angle = function (v1, v2) { console.assert(!!v1); console.assert(!!v2); return Math.atan2(Vec2.cross(v1, v2), Vec2.dot(v1, v2)); }; Vec2.prototype.oxAngle = function () { return Math.atan2(this.y, this.x); }; Vec2.diff = function (v1, v2) { console.assert(!!v1); console.assert(!!v2); return new Vec2(v1.x - v2.x, v1.y - v2.y, v1.z - v2.z); }; // assume arguments v1, f1, v2, f2, v3, f3, etc. // where v[i] are vectors and f[i] are corresponding coefficients Vec2.lc = function () { var v = new Vec2(); for (var i = 0; i < arguments.length / 2; ++i) /* eslint-disable no-mixed-operators*/ v = v.addScaled(arguments[2 * i], arguments[2 * i + 1]); /* eslint-enable no-mixed-operators*/ return v; }; Vec2.lc2 = function (v1, f1, v2, f2) { console.assert(!!v1); console.assert(!!v2); console.assert(f1 == 0 || !!f1); console.assert(f2 == 0 || !!f2); /* eslint-disable no-mixed-operators*/ return new Vec2(v1.x * f1 + v2.x * f2, v1.y * f1 + v2.y * f2, v1.z * f1 + v2.z * f2); /* eslint-enable no-mixed-operators*/ }; Vec2.centre = function (v1, v2) { return new Vec2.lc2(v1, 0.5, v2, 0.5); // eslint-disable-line new-cap }; // find intersection of a ray and a box and // return the shift magnitude to avoid it Vec2.shiftRayBox = function (/* Vec2*/p, /* Vec2*/d, /* Box2Abs*/bb) { // eslint-disable-line max-statements console.assert(!!p); console.assert(!!d); console.assert(!!bb); // four corner points of the box var b = [bb.p0, new Vec2(bb.p1.x, bb.p0.y), bb.p1, new Vec2(bb.p0.x, bb.p1.y)]; var r = b.map(function (v) { return v.sub(p); }); // b relative to p d = d.normalized(); var rc = r.map(function (v) { return Vec2.cross(v, d); }); // cross prods var rd = r.map(function (v) { return Vec2.dot(v, d); }); // dot prods // find foremost points on the right and on the left of the ray var pid = -1, nid = -1; for (var i = 0; i < 4; ++i) { if (rc[i] > 0) { if (pid < 0 || rd[pid] < rd[i]) pid = i; } else if (nid < 0 || rd[nid] < rd[i]) { nid = i; } } if (nid < 0 || pid < 0) // no intersection, no shift return 0; // check the order var id0, id1; if (rd[pid] > rd[nid]) id0 = nid, id1 = pid; else id0 = pid, id1 = nid; // simple proportion to calculate the shift /* eslint-disable no-mixed-operators*/ return rd[id0] + Math.abs(rc[id0]) * (rd[id1] - rd[id0]) / (Math.abs(rc[id0]) + Math.abs(rc[id1])); /* eslint-enable no-mixed-operators*/ }; module.exports = Vec2;