@turbox3d/math
Version:
Large-scale graphics application math library
229 lines (228 loc) • 8.06 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.Ray = void 0;
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
var _Vector = require("./Vector3");
var _vector = new _Vector.Vector3();
var _segCenter = new _Vector.Vector3();
var _segDir = new _Vector.Vector3();
var _diff = new _Vector.Vector3();
var Ray = exports.Ray = /*#__PURE__*/function () {
function Ray(origin, direction) {
(0, _classCallCheck2["default"])(this, Ray);
this.origin = origin !== undefined ? origin : new _Vector.Vector3();
this.direction = direction !== undefined ? direction : new _Vector.Vector3(0, 0, -1);
}
return (0, _createClass2["default"])(Ray, [{
key: "set",
value: function set(origin, direction) {
this.origin.copy(origin);
this.direction.copy(direction);
return this;
}
}, {
key: "clone",
value: function clone() {
return new Ray().copy(this);
}
}, {
key: "copy",
value: function copy(ray) {
this.origin.copy(ray.origin);
this.direction.copy(ray.direction);
return this;
}
}, {
key: "at",
value: function at(t, target) {
return target.copy(this.direction).multiplyScalar(t).add(this.origin);
}
}, {
key: "lookAt",
value: function lookAt(v) {
this.direction.copy(v).sub(this.origin).normalize();
return this;
}
}, {
key: "recast",
value: function recast(t) {
this.origin.copy(this.at(t, _vector));
return this;
}
}, {
key: "closestPointToPoint",
value: function closestPointToPoint(point, target) {
target.subVectors(point, this.origin);
var directionDistance = target.dot(this.direction);
if (directionDistance < 0) {
return target.copy(this.origin);
}
return target.copy(this.direction).multiplyScalar(directionDistance).add(this.origin);
}
}, {
key: "distanceToPoint",
value: function distanceToPoint(point) {
return Math.sqrt(this.distanceSqToPoint(point));
}
}, {
key: "distanceSqToPoint",
value: function distanceSqToPoint(point) {
var directionDistance = _vector.subVectors(point, this.origin).dot(this.direction);
// point behind the ray
if (directionDistance < 0) {
return this.origin.distanceToSquared(point);
}
_vector.copy(this.direction).multiplyScalar(directionDistance).add(this.origin);
return _vector.distanceToSquared(point);
}
}, {
key: "distanceSqToSegment",
value: function distanceSqToSegment(v0, v1, optionalPointOnRay, optionalPointOnSegment) {
// from http://www.geometrictools.com/GTEngine/Include/Mathematics/GteDistRaySegment.h
// It returns the min distance between the ray and the segment
// defined by v0 and v1
// It can also set two optional targets :
// - The closest point on the ray
// - The closest point on the segment
_segCenter.copy(v0).add(v1).multiplyScalar(0.5);
_segDir.copy(v1).sub(v0).normalize();
_diff.copy(this.origin).sub(_segCenter);
var segExtent = v0.distanceTo(v1) * 0.5;
var a01 = -this.direction.dot(_segDir);
var b0 = _diff.dot(this.direction);
var b1 = -_diff.dot(_segDir);
var c = _diff.lengthSq;
var det = Math.abs(1 - a01 * a01);
var s0;
var s1;
var sqrDist;
var extDet;
if (det > 0) {
// The ray and segment are not parallel.
s0 = a01 * b1 - b0;
s1 = a01 * b0 - b1;
extDet = segExtent * det;
if (s0 >= 0) {
if (s1 >= -extDet) {
if (s1 <= extDet) {
// region 0
// Minimum at interior points of ray and segment.
var invDet = 1 / det;
s0 *= invDet;
s1 *= invDet;
sqrDist = s0 * (s0 + a01 * s1 + 2 * b0) + s1 * (a01 * s0 + s1 + 2 * b1) + c;
} else {
// region 1
s1 = segExtent;
s0 = Math.max(0, -(a01 * s1 + b0));
sqrDist = -s0 * s0 + s1 * (s1 + 2 * b1) + c;
}
} else {
// region 5
s1 = -segExtent;
s0 = Math.max(0, -(a01 * s1 + b0));
sqrDist = -s0 * s0 + s1 * (s1 + 2 * b1) + c;
}
} else if (s1 <= -extDet) {
// region 4
s0 = Math.max(0, -(-a01 * segExtent + b0));
s1 = s0 > 0 ? -segExtent : Math.min(Math.max(-segExtent, -b1), segExtent);
sqrDist = -s0 * s0 + s1 * (s1 + 2 * b1) + c;
} else if (s1 <= extDet) {
// region 3
s0 = 0;
s1 = Math.min(Math.max(-segExtent, -b1), segExtent);
sqrDist = s1 * (s1 + 2 * b1) + c;
} else {
// region 2
s0 = Math.max(0, -(a01 * segExtent + b0));
s1 = s0 > 0 ? segExtent : Math.min(Math.max(-segExtent, -b1), segExtent);
sqrDist = -s0 * s0 + s1 * (s1 + 2 * b1) + c;
}
} else {
// Ray and segment are parallel.
s1 = a01 > 0 ? -segExtent : segExtent;
s0 = Math.max(0, -(a01 * s1 + b0));
sqrDist = -s0 * s0 + s1 * (s1 + 2 * b1) + c;
}
if (optionalPointOnRay) {
optionalPointOnRay.copy(this.direction).multiplyScalar(s0).add(this.origin);
}
if (optionalPointOnSegment) {
optionalPointOnSegment.copy(_segDir).multiplyScalar(s1).add(_segCenter);
}
return sqrDist;
}
}, {
key: "intersectBox",
value: function intersectBox(box, target) {
var tmin;
var tmax;
var tymin;
var tymax;
var tzmin;
var tzmax;
var invdirx = 1 / this.direction.x;
var invdiry = 1 / this.direction.y;
var invdirz = 1 / this.direction.z;
var origin = this.origin;
if (invdirx >= 0) {
tmin = (box.min.x - origin.x) * invdirx;
tmax = (box.max.x - origin.x) * invdirx;
} else {
tmin = (box.max.x - origin.x) * invdirx;
tmax = (box.min.x - origin.x) * invdirx;
}
if (invdiry >= 0) {
tymin = (box.min.y - origin.y) * invdiry;
tymax = (box.max.y - origin.y) * invdiry;
} else {
tymin = (box.max.y - origin.y) * invdiry;
tymax = (box.min.y - origin.y) * invdiry;
}
if (tmin > tymax || tymin > tmax) return null;
// These lines also handle the case where tmin or tmax is NaN
// (result of 0 * Infinity). x !== x returns true if x is NaN
// eslint-disable-next-line no-self-compare
if (tymin > tmin || tmin !== tmin) tmin = tymin;
// eslint-disable-next-line no-self-compare
if (tymax < tmax || tmax !== tmax) tmax = tymax;
if (invdirz >= 0) {
tzmin = (box.min.z - origin.z) * invdirz;
tzmax = (box.max.z - origin.z) * invdirz;
} else {
tzmin = (box.max.z - origin.z) * invdirz;
tzmax = (box.min.z - origin.z) * invdirz;
}
if (tmin > tzmax || tzmin > tmax) return null;
// eslint-disable-next-line no-self-compare
if (tzmin > tmin || tmin !== tmin) tmin = tzmin;
// eslint-disable-next-line no-self-compare
if (tzmax < tmax || tmax !== tmax) tmax = tzmax;
// return point closest to the ray (positive side)
if (tmax < 0) return null;
return this.at(tmin >= 0 ? tmin : tmax, target);
}
}, {
key: "intersectsBox",
value: function intersectsBox(box) {
return this.intersectBox(box, _vector) !== null;
}
}, {
key: "applyMatrix4",
value: function applyMatrix4(matrix4) {
this.origin.applyMatrix4(matrix4);
this.direction.transformDirection(matrix4);
return this;
}
}, {
key: "equals",
value: function equals(ray) {
return ray.origin.equals(this.origin) && ray.direction.equals(this.direction);
}
}]);
}();