uglymol
Version:
Macromolecular Viewer for Crystallographers
953 lines (783 loc) • 25.9 kB
JavaScript
// Copyright 2010-2023 Three.js Authors
// SPDX-License-Identifier: MIT
const _lut = [
'00', '01', '02', '03', '04', '05', '06', '07', '08', '09', '0a', '0b', '0c',
'0d', '0e', '0f', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19',
'1a', '1b', '1c', '1d', '1e', '1f', '20', '21', '22', '23', '24', '25', '26',
'27', '28', '29', '2a', '2b', '2c', '2d', '2e', '2f', '30', '31', '32', '33',
'34', '35', '36', '37', '38', '39', '3a', '3b', '3c', '3d', '3e', '3f', '40',
'41', '42', '43', '44', '45', '46', '47', '48', '49', '4a', '4b', '4c', '4d',
'4e', '4f', '50', '51', '52', '53', '54', '55', '56', '57', '58', '59', '5a',
'5b', '5c', '5d', '5e', '5f', '60', '61', '62', '63', '64', '65', '66', '67',
'68', '69', '6a', '6b', '6c', '6d', '6e', '6f', '70', '71', '72', '73', '74',
'75', '76', '77', '78', '79', '7a', '7b', '7c', '7d', '7e', '7f', '80', '81',
'82', '83', '84', '85', '86', '87', '88', '89', '8a', '8b', '8c', '8d', '8e',
'8f', '90', '91', '92', '93', '94', '95', '96', '97', '98', '99', '9a', '9b',
'9c', '9d', '9e', '9f', 'a0', 'a1', 'a2', 'a3', 'a4', 'a5', 'a6', 'a7', 'a8',
'a9', 'aa', 'ab', 'ac', 'ad', 'ae', 'af', 'b0', 'b1', 'b2', 'b3', 'b4', 'b5',
'b6', 'b7', 'b8', 'b9', 'ba', 'bb', 'bc', 'bd', 'be', 'bf', 'c0', 'c1', 'c2',
'c3', 'c4', 'c5', 'c6', 'c7', 'c8', 'c9', 'ca', 'cb', 'cc', 'cd', 'ce', 'cf',
'd0', 'd1', 'd2', 'd3', 'd4', 'd5', 'd6', 'd7', 'd8', 'd9', 'da', 'db', 'dc',
'dd', 'de', 'df', 'e0', 'e1', 'e2', 'e3', 'e4', 'e5', 'e6', 'e7', 'e8', 'e9',
'ea', 'eb', 'ec', 'ed', 'ee', 'ef', 'f0', 'f1', 'f2', 'f3', 'f4', 'f5', 'f6',
'f7', 'f8', 'f9', 'fa', 'fb', 'fc', 'fd', 'fe', 'ff'
];
// http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/21963136#21963136
function generateUUID() {
const d0 = (Math.random() * 0xffffffff) | 0;
const d1 = (Math.random() * 0xffffffff) | 0;
const d2 = (Math.random() * 0xffffffff) | 0;
const d3 = (Math.random() * 0xffffffff) | 0;
const uuid =
_lut[d0 & 0xff] + _lut[(d0 >> 8) & 0xff] +
_lut[(d0 >> 16) & 0xff] + _lut[(d0 >> 24) & 0xff] + '-' +
_lut[d1 & 0xff] + _lut[(d1 >> 8) & 0xff] + '-' +
_lut[((d1 >> 16) & 0x0f) | 0x40] + _lut[(d1 >> 24) & 0xff] + '-' +
_lut[(d2 & 0x3f) | 0x80] + _lut[(d2 >> 8) & 0xff] + '-' +
_lut[(d2 >> 16) & 0xff] + _lut[(d2 >> 24) & 0xff] + _lut[d3 & 0xff] +
_lut[(d3 >> 8) & 0xff] + _lut[(d3 >> 16) & 0xff] + _lut[(d3 >> 24) & 0xff];
// .toLowerCase() here flattens concatenated strings to save heap memory space.
return uuid.toLowerCase();
}
function clamp(value, min, max) {
return Math.max(min, Math.min(max, value));
}
// compute euclidean modulo of m % n
// https://en.wikipedia.org/wiki/Modulo_operation
function euclideanModulo(n, m) {
return ((n % m) + m) % m;
}
class Quaternion {
constructor(x = 0, y = 0, z = 0, w = 1) {
this._x = x;
this._y = y;
this._z = z;
this._w = w;
}
get x() {
return this._x;
}
get y() {
return this._y;
}
get z() {
return this._z;
}
get w() {
return this._w;
}
setFromAxisAngle(axis, angle) {
// http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm
// assumes axis is normalized
let halfAngle = angle / 2, s = Math.sin(halfAngle);
this._x = axis.x * s;
this._y = axis.y * s;
this._z = axis.z * s;
this._w = Math.cos(halfAngle);
return this;
}
setFromRotationMatrix(m) {
// http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm
// assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)
const te = m.elements,
m11 = te[0], m12 = te[4], m13 = te[8],
m21 = te[1], m22 = te[5], m23 = te[9],
m31 = te[2], m32 = te[6], m33 = te[10],
trace = m11 + m22 + m33;
if (trace > 0) {
const s = 0.5 / Math.sqrt(trace + 1.0);
this._w = 0.25 / s;
this._x = (m32 - m23) * s;
this._y = (m13 - m31) * s;
this._z = (m21 - m12) * s;
} else if (m11 > m22 && m11 > m33) {
const s = 2.0 * Math.sqrt(1.0 + m11 - m22 - m33);
this._w = (m32 - m23) / s;
this._x = 0.25 * s;
this._y = (m12 + m21) / s;
this._z = (m13 + m31) / s;
} else if (m22 > m33) {
const s = 2.0 * Math.sqrt(1.0 + m22 - m11 - m33);
this._w = (m13 - m31) / s;
this._x = (m12 + m21) / s;
this._y = 0.25 * s;
this._z = (m23 + m32) / s;
} else {
const s = 2.0 * Math.sqrt(1.0 + m33 - m11 - m22);
this._w = (m21 - m12) / s;
this._x = (m13 + m31) / s;
this._y = (m23 + m32) / s;
this._z = 0.25 * s;
}
return this;
}
setFromUnitVectors(vFrom, vTo) {
// assumes direction vectors vFrom and vTo are normalized
let r = vFrom.dot(vTo) + 1;
if (r < Number.EPSILON) {
// vFrom and vTo point in opposite directions
r = 0;
if (Math.abs(vFrom.x) > Math.abs(vFrom.z)) {
this._x = -vFrom.y;
this._y = vFrom.x;
this._z = 0;
this._w = r;
} else {
this._x = 0;
this._y = -vFrom.z;
this._z = vFrom.y;
this._w = r;
}
} else {
// crossVectors( vFrom, vTo ); // inlined to avoid cyclic dependency on Vector3
this._x = vFrom.y * vTo.z - vFrom.z * vTo.y;
this._y = vFrom.z * vTo.x - vFrom.x * vTo.z;
this._z = vFrom.x * vTo.y - vFrom.y * vTo.x;
this._w = r;
}
return this.normalize();
}
length() {
return Math.sqrt(this._x * this._x + this._y * this._y +
this._z * this._z + this._w * this._w);
}
normalize() {
let l = this.length();
if (l === 0) {
this._x = 0;
this._y = 0;
this._z = 0;
this._w = 1;
} else {
l = 1 / l;
this._x = this._x * l;
this._y = this._y * l;
this._z = this._z * l;
this._w = this._w * l;
}
return this;
}
}
class Vector3 {
constructor(x = 0, y = 0, z = 0) {
Vector3.prototype.isVector3 = true;
this.x = x;
this.y = y;
this.z = z;
}
set(x, y, z) {
if (z === undefined) z = this.z; // sprite.scale.set(x,y)
this.x = x;
this.y = y;
this.z = z;
return this;
}
clone() {
return new this.constructor(this.x, this.y, this.z);
}
copy(v) {
this.x = v.x;
this.y = v.y;
this.z = v.z;
return this;
}
add(v) {
this.x += v.x;
this.y += v.y;
this.z += v.z;
return this;
}
addVectors(a, b) {
this.x = a.x + b.x;
this.y = a.y + b.y;
this.z = a.z + b.z;
return this;
}
addScaledVector(v, s) {
this.x += v.x * s;
this.y += v.y * s;
this.z += v.z * s;
return this;
}
sub(v) {
this.x -= v.x;
this.y -= v.y;
this.z -= v.z;
return this;
}
subVectors(a, b) {
this.x = a.x - b.x;
this.y = a.y - b.y;
this.z = a.z - b.z;
return this;
}
multiplyScalar(scalar) {
this.x *= scalar;
this.y *= scalar;
this.z *= scalar;
return this;
}
applyMatrix4(m) {
const x = this.x, y = this.y, z = this.z;
const e = m.elements;
const w = 1 / (e[3] * x + e[7] * y + e[11] * z + e[15]);
this.x = (e[0] * x + e[4] * y + e[8] * z + e[12]) * w;
this.y = (e[1] * x + e[5] * y + e[9] * z + e[13]) * w;
this.z = (e[2] * x + e[6] * y + e[10] * z + e[14]) * w;
return this;
}
applyQuaternion(q) {
// quaternion q is assumed to have unit length
const vx = this.x, vy = this.y, vz = this.z;
const qx = q.x, qy = q.y, qz = q.z, qw = q.w;
// t = 2 * cross( q.xyz, v );
const tx = 2 * (qy * vz - qz * vy);
const ty = 2 * (qz * vx - qx * vz);
const tz = 2 * (qx * vy - qy * vx);
// v + q.w * t + cross( q.xyz, t );
this.x = vx + qw * tx + qy * tz - qz * ty;
this.y = vy + qw * ty + qz * tx - qx * tz;
this.z = vz + qw * tz + qx * ty - qy * tx;
return this;
}
unproject(camera) {
return this.applyMatrix4(camera.projectionMatrixInverse).applyMatrix4(camera.matrixWorld);
}
transformDirection(m) {
// input: THREE.Matrix4 affine matrix
// vector interpreted as a direction
const x = this.x, y = this.y, z = this.z;
const e = m.elements;
this.x = e[0] * x + e[4] * y + e[8] * z;
this.y = e[1] * x + e[5] * y + e[9] * z;
this.z = e[2] * x + e[6] * y + e[10] * z;
return this.normalize();
}
divideScalar(scalar) {
return this.multiplyScalar(1 / scalar);
}
dot(v) {
return this.x * v.x + this.y * v.y + this.z * v.z;
}
lengthSq() {
return this.x * this.x + this.y * this.y + this.z * this.z;
}
length() {
return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
}
normalize() {
return this.divideScalar(this.length() || 1);
}
setLength(length) {
return this.normalize().multiplyScalar(length);
}
lerp(v, alpha) {
this.x += (v.x - this.x) * alpha;
this.y += (v.y - this.y) * alpha;
this.z += (v.z - this.z) * alpha;
return this;
}
cross(v) {
return this.crossVectors(this, v);
}
crossVectors(a, b) {
const ax = a.x, ay = a.y, az = a.z;
const bx = b.x, by = b.y, bz = b.z;
this.x = ay * bz - az * by;
this.y = az * bx - ax * bz;
this.z = ax * by - ay * bx;
return this;
}
projectOnVector(v) {
const denominator = v.lengthSq();
if (denominator === 0) return this.set(0, 0, 0);
const scalar = v.dot(this) / denominator;
return this.copy(v).multiplyScalar(scalar);
}
projectOnPlane(planeNormal) {
_vector.copy(this).projectOnVector(planeNormal);
return this.sub(_vector);
}
distanceTo(v) {
return Math.sqrt(this.distanceToSquared(v));
}
distanceToSquared(v) {
const dx = this.x - v.x, dy = this.y - v.y, dz = this.z - v.z;
return dx * dx + dy * dy + dz * dz;
}
setFromMatrixPosition(m) {
const e = m.elements;
this.x = e[12];
this.y = e[13];
this.z = e[14];
return this;
}
setFromMatrixColumn(m, index) {
return this.fromArray(m.elements, index * 4);
}
equals(v) {
return v.x === this.x && v.y === this.y && v.z === this.z;
}
fromArray(array, offset = 0) {
this.x = array[offset];
this.y = array[offset + 1];
this.z = array[offset + 2];
return this;
}
}
const _vector = /*@__PURE__*/ new Vector3();
class Vector4 {
constructor(x = 0, y = 0, z = 0, w = 1) {
this.x = x;
this.y = y;
this.z = z;
this.w = w;
}
set(x, y, z, w) {
this.x = x;
this.y = y;
this.z = z;
this.w = w;
return this;
}
copy(v) {
this.x = v.x;
this.y = v.y;
this.z = v.z;
this.w = v.w !== undefined ? v.w : 1;
return this;
}
multiplyScalar(scalar) {
this.x *= scalar;
this.y *= scalar;
this.z *= scalar;
this.w *= scalar;
return this;
}
equals(v) {
return v.x === this.x && v.y === this.y && v.z === this.z && v.w === this.w;
}
}
class Matrix4 {
constructor() {
this.elements = [
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1,
];
}
set(n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44) {
const te = this.elements;
te[0] = n11; te[4] = n12; te[8] = n13; te[12] = n14;
te[1] = n21; te[5] = n22; te[9] = n23; te[13] = n24;
te[2] = n31; te[6] = n32; te[10] = n33; te[14] = n34;
te[3] = n41; te[7] = n42; te[11] = n43; te[15] = n44;
return this;
}
copy(m) {
const te = this.elements;
const me = m.elements;
te[0] = me[0];
te[1] = me[1];
te[2] = me[2];
te[3] = me[3];
te[4] = me[4];
te[5] = me[5];
te[6] = me[6];
te[7] = me[7];
te[8] = me[8];
te[9] = me[9];
te[10] = me[10];
te[11] = me[11];
te[12] = me[12];
te[13] = me[13];
te[14] = me[14];
te[15] = me[15];
return this;
}
makeRotationFromQuaternion(q) {
return this.compose(_zero, q, _one);
}
compose(position, quaternion, scale) {
const te = this.elements;
const x = quaternion._x, y = quaternion._y, z = quaternion._z, w = quaternion._w;
const x2 = x + x, y2 = y + y, z2 = z + z;
const xx = x * x2, xy = x * y2, xz = x * z2;
const yy = y * y2, yz = y * z2, zz = z * z2;
const wx = w * x2, wy = w * y2, wz = w * z2;
const sx = scale.x, sy = scale.y, sz = scale.z;
te[0] = (1 - (yy + zz)) * sx;
te[1] = (xy + wz) * sx;
te[2] = (xz - wy) * sx;
te[3] = 0;
te[4] = (xy - wz) * sy;
te[5] = (1 - (xx + zz)) * sy;
te[6] = (yz + wx) * sy;
te[7] = 0;
te[8] = (xz + wy) * sz;
te[9] = (yz - wx) * sz;
te[10] = (1 - (xx + yy)) * sz;
te[11] = 0;
te[12] = position.x;
te[13] = position.y;
te[14] = position.z;
te[15] = 1;
return this;
}
lookAt(eye, target, up) {
const te = this.elements;
_z.subVectors(eye, target);
if (_z.lengthSq() === 0) {
// eye and target are in the same position
_z.z = 1;
}
_z.normalize();
_x.crossVectors(up, _z);
if (_x.lengthSq() === 0) {
// up and z are parallel
if (Math.abs(up.z) === 1) {
_z.x += 0.0001;
} else {
_z.z += 0.0001;
}
_z.normalize();
_x.crossVectors(up, _z);
}
_x.normalize();
_y.crossVectors(_z, _x);
te[0] = _x.x; te[4] = _y.x; te[8] = _z.x;
te[1] = _x.y; te[5] = _y.y; te[9] = _z.y;
te[2] = _x.z; te[6] = _y.z; te[10] = _z.z;
return this;
}
multiplyMatrices(a, b) {
const ae = a.elements;
const be = b.elements;
const te = this.elements;
const a11 = ae[0], a12 = ae[4], a13 = ae[8], a14 = ae[12];
const a21 = ae[1], a22 = ae[5], a23 = ae[9], a24 = ae[13];
const a31 = ae[2], a32 = ae[6], a33 = ae[10], a34 = ae[14];
const a41 = ae[3], a42 = ae[7], a43 = ae[11], a44 = ae[15];
const b11 = be[0], b12 = be[4], b13 = be[8], b14 = be[12];
const b21 = be[1], b22 = be[5], b23 = be[9], b24 = be[13];
const b31 = be[2], b32 = be[6], b33 = be[10], b34 = be[14];
const b41 = be[3], b42 = be[7], b43 = be[11], b44 = be[15];
te[0] = a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41;
te[4] = a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42;
te[8] = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43;
te[12] = a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44;
te[1] = a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41;
te[5] = a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42;
te[9] = a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43;
te[13] = a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44;
te[2] = a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41;
te[6] = a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42;
te[10] = a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43;
te[14] = a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44;
te[3] = a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41;
te[7] = a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42;
te[11] = a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43;
te[15] = a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44;
return this;
}
setPosition(x, y, z) {
const te = this.elements;
if (x.isVector3) {
te[12] = x.x;
te[13] = x.y;
te[14] = x.z;
} else {
te[12] = x;
te[13] = y;
te[14] = z;
}
return this;
}
invert() {
// based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm
const te = this.elements,
n11 = te[0], n21 = te[1], n31 = te[2], n41 = te[3],
n12 = te[4], n22 = te[5], n32 = te[6], n42 = te[7],
n13 = te[8], n23 = te[9], n33 = te[10], n43 = te[11],
n14 = te[12], n24 = te[13], n34 = te[14], n44 = te[15],
t11 = n23 * n34 * n42 - n24 * n33 * n42 + n24 * n32 * n43 - n22 * n34 * n43 - n23 * n32 * n44 + n22 * n33 * n44,
t12 = n14 * n33 * n42 - n13 * n34 * n42 - n14 * n32 * n43 + n12 * n34 * n43 + n13 * n32 * n44 - n12 * n33 * n44,
t13 = n13 * n24 * n42 - n14 * n23 * n42 + n14 * n22 * n43 - n12 * n24 * n43 - n13 * n22 * n44 + n12 * n23 * n44,
t14 = n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34;
const det = n11 * t11 + n21 * t12 + n31 * t13 + n41 * t14;
if (det === 0) return this.set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
const detInv = 1 / det;
te[0] = t11 * detInv;
te[1] = (n24 * n33 * n41 - n23 * n34 * n41 - n24 * n31 * n43 + n21 * n34 * n43 + n23 * n31 * n44 - n21 * n33 * n44) * detInv;
te[2] = (n22 * n34 * n41 - n24 * n32 * n41 + n24 * n31 * n42 - n21 * n34 * n42 - n22 * n31 * n44 + n21 * n32 * n44) * detInv;
te[3] = (n23 * n32 * n41 - n22 * n33 * n41 - n23 * n31 * n42 + n21 * n33 * n42 + n22 * n31 * n43 - n21 * n32 * n43) * detInv;
te[4] = t12 * detInv;
te[5] = (n13 * n34 * n41 - n14 * n33 * n41 + n14 * n31 * n43 - n11 * n34 * n43 - n13 * n31 * n44 + n11 * n33 * n44) * detInv;
te[6] = (n14 * n32 * n41 - n12 * n34 * n41 - n14 * n31 * n42 + n11 * n34 * n42 + n12 * n31 * n44 - n11 * n32 * n44) * detInv;
te[7] = (n12 * n33 * n41 - n13 * n32 * n41 + n13 * n31 * n42 - n11 * n33 * n42 - n12 * n31 * n43 + n11 * n32 * n43) * detInv;
te[8] = t13 * detInv;
te[9] = (n14 * n23 * n41 - n13 * n24 * n41 - n14 * n21 * n43 + n11 * n24 * n43 + n13 * n21 * n44 - n11 * n23 * n44) * detInv;
te[10] = (n12 * n24 * n41 - n14 * n22 * n41 + n14 * n21 * n42 - n11 * n24 * n42 - n12 * n21 * n44 + n11 * n22 * n44) * detInv;
te[11] = (n13 * n22 * n41 - n12 * n23 * n41 - n13 * n21 * n42 + n11 * n23 * n42 + n12 * n21 * n43 - n11 * n22 * n43) * detInv;
te[12] = t14 * detInv;
te[13] = (n13 * n24 * n31 - n14 * n23 * n31 + n14 * n21 * n33 - n11 * n24 * n33 - n13 * n21 * n34 + n11 * n23 * n34) * detInv;
te[14] = (n14 * n22 * n31 - n12 * n24 * n31 - n14 * n21 * n32 + n11 * n24 * n32 + n12 * n21 * n34 - n11 * n22 * n34) * detInv;
te[15] = (n12 * n23 * n31 - n13 * n22 * n31 + n13 * n21 * n32 - n11 * n23 * n32 - n12 * n21 * n33 + n11 * n22 * n33) * detInv;
return this;
}
scale(v) {
const te = this.elements;
const x = v.x, y = v.y, z = v.z;
te[0] *= x; te[4] *= y; te[8] *= z;
te[1] *= x; te[5] *= y; te[9] *= z;
te[2] *= x; te[6] *= y; te[10] *= z;
te[3] *= x; te[7] *= y; te[11] *= z;
return this;
}
getMaxScaleOnAxis() {
const te = this.elements;
const scaleXSq = te[0] * te[0] + te[1] * te[1] + te[2] * te[2];
const scaleYSq = te[4] * te[4] + te[5] * te[5] + te[6] * te[6];
const scaleZSq = te[8] * te[8] + te[9] * te[9] + te[10] * te[10];
return Math.sqrt(Math.max(scaleXSq, scaleYSq, scaleZSq));
}
makeOrthographic(left, right, top, bottom, near, far) {
let te = this.elements;
let w = 1.0 / (right - left);
let h = 1.0 / (top - bottom);
let p = 1.0 / (far - near);
let x = (right + left) * w;
let y = (top + bottom) * h;
let z = (far + near) * p;
te[0] = 2 * w; te[4] = 0; te[8] = 0; te[12] = -x;
te[1] = 0; te[5] = 2 * h; te[9] = 0; te[13] = -y;
te[2] = 0; te[6] = 0; te[10] = - 2 * p; te[14] = -z;
te[3] = 0; te[7] = 0; te[11] = 0; te[15] = 1;
return this;
}
}
const _zero = /*@__PURE__*/ new Vector3(0, 0, 0);
const _one = /*@__PURE__*/ new Vector3(1, 1, 1);
const _x = /*@__PURE__*/ new Vector3();
const _y = /*@__PURE__*/ new Vector3();
const _z = /*@__PURE__*/ new Vector3();
function hue2rgb(p, q, t) {
if (t < 0) t += 1;
if (t > 1) t -= 1;
if (t < 1 / 6) return p + (q - p) * 6 * t;
if (t < 1 / 2) return q;
if (t < 2 / 3) return p + (q - p) * 6 * (2 / 3 - t);
return p;
}
class Color {
constructor(r, g, b) {
this.isColor = true;
this.r = 1;
this.g = 1;
this.b = 1;
return this.set(r, g, b);
}
set(r, g, b) {
if (g === undefined && b === undefined) {
// r is THREE.Color, hex or string
const value = r;
if (value && value.isColor) {
this.copy(value);
} else if (typeof value === 'number') {
this.setHex(value);
}
} else {
this.setRGB(r, g, b);
}
return this;
}
setHex(hex) {
hex = Math.floor(hex);
this.r = ((hex >> 16) & 255) / 255;
this.g = ((hex >> 8) & 255) / 255;
this.b = (hex & 255) / 255;
return this;
}
setRGB(r, g, b) {
this.r = r;
this.g = g;
this.b = b;
return this;
}
setHSL(h, s, l) {
// h,s,l ranges are in 0.0 - 1.0
h = euclideanModulo(h, 1);
s = clamp(s, 0, 1);
l = clamp(l, 0, 1);
if (s === 0) {
this.r = this.g = this.b = l;
} else {
const p = l <= 0.5 ? l * (1 + s) : l + s - l * s;
const q = 2 * l - p;
this.r = hue2rgb(q, p, h + 1 / 3);
this.g = hue2rgb(q, p, h);
this.b = hue2rgb(q, p, h - 1 / 3);
}
return this;
}
getHSL(target) {
// h,s,l ranges are in 0.0 - 1.0
const r = this.r, g = this.g, b = this.b;
const max = Math.max(r, g, b);
const min = Math.min(r, g, b);
let hue, saturation;
const lightness = (min + max) / 2.0;
if (min === max) {
hue = 0;
saturation = 0;
} else {
const delta = max - min;
saturation = lightness <= 0.5 ? delta / (max + min) : delta / (2 - max - min);
switch (max) {
case r:
hue = (g - b) / delta + (g < b ? 6 : 0);
break;
case g:
hue = (b - r) / delta + 2;
break;
case b:
hue = (r - g) / delta + 4;
break;
}
hue /= 6;
}
target.h = hue;
target.s = saturation;
target.l = lightness;
return target;
}
clone() {
return new this.constructor(this.r, this.g, this.b);
}
copy(color) {
this.r = color.r;
this.g = color.g;
this.b = color.b;
return this;
}
getHex() {
return ( this.r * 255 ) << 16 ^ ( this.g * 255 ) << 8 ^ ( this.b * 255 ) << 0;
}
getHexString() {
return ('000000' + this.getHex().toString(16)).slice(-6);
}
}
//const _vector is already defined above
const _segCenter = /*@__PURE__*/ new Vector3();
const _segDir = /*@__PURE__*/ new Vector3();
const _diff = /*@__PURE__*/ new Vector3();
class Ray {
constructor(origin = new Vector3(), direction = new Vector3(0, 0, -1)) {
this.origin = origin;
this.direction = direction;
}
copy(ray) {
this.origin.copy(ray.origin);
this.direction.copy(ray.direction);
return this;
}
distanceSqToPoint(point) {
const directionDistance = _vector.subVectors(point, this.origin).dot(this.direction);
// point behind the ray
if (directionDistance < 0) {
return this.origin.distanceToSquared(point);
}
_vector.copy(this.origin).addScaledVector(this.direction, directionDistance);
return _vector.distanceToSquared(point);
}
distanceSqToSegment(v0, v1, optionalPointOnRay, optionalPointOnSegment) {
// from https://github.com/pmjoniak/GeometricTools/blob/master/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);
const segExtent = v0.distanceTo(v1) * 0.5;
const a01 = -this.direction.dot(_segDir);
const b0 = _diff.dot(this.direction);
const b1 = -_diff.dot(_segDir);
const c = _diff.lengthSq();
const det = Math.abs(1 - a01 * a01);
let s0, s1, sqrDist, 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.
const 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.origin).addScaledVector(this.direction, s0);
}
if (optionalPointOnSegment) {
optionalPointOnSegment.copy(_segCenter).addScaledVector(_segDir, s1);
}
return sqrDist;
}
applyMatrix4(matrix4) {
this.origin.applyMatrix4(matrix4);
this.direction.transformDirection(matrix4);
return this;
}
}
export {
Quaternion,
Vector3,
Vector4,
Matrix4,
Color,
Ray,
generateUUID,
};