798 lines
24 KiB
JavaScript
798 lines
24 KiB
JavaScript
// Helper.js
|
|
//
|
|
// Created by Milad Nazeri on 2018-06-19
|
|
// Added to by Robin Wilson 2018-08-24
|
|
//
|
|
// Copyright 2018 High Fidelity, Inc.
|
|
//
|
|
// Distributed under the Apache License, Version 2.0.
|
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
|
//
|
|
// Util Library for Common Tasks
|
|
|
|
// DEPENDENCIES
|
|
|
|
// From Luis Vector Library
|
|
(function(){
|
|
var Vector3 = new (function() {
|
|
var self = this;
|
|
this.EPSILON = 0.000001;
|
|
this.EPSILON_SQUARED = self.EPSILON * self.EPSILON;
|
|
this.PI = 3.14159265358979;
|
|
this.ALMOST_ONE= 1.0 - self.EPSILON;
|
|
this.PI_OVER_TWO = 1.57079632679490;
|
|
|
|
this.cross = function(A, B) {
|
|
return {x: (A.y * B.z - A.z * B.y), y: (A.z * B.x - A.x * B.z), z: (A.x * B.y - A.y * B.x)};
|
|
};
|
|
this.distance = function(A, B) {
|
|
return Math.sqrt((A.x - B.x) * (A.x - B.x) + (A.y - B.y) * (A.y - B.y) + (A.z - B.z) * (A.z - B.z));
|
|
};
|
|
this.dot = function(A, B) {
|
|
return A.x * B.x + A.y * B.y + A.z * B.z;
|
|
};
|
|
this.length = function(V) {
|
|
return Math.sqrt(V.x * V.x + V.y * V.y + V.z * V.z);
|
|
};
|
|
this.subtract = function(A, B) {
|
|
return {x: (A.x - B.x), y: (A.y - B.y), z: (A.z - B.z)};
|
|
};
|
|
this.sum = function(A, B) {
|
|
return {x: (A.x + B.x), y: (A.y + B.y), z: (A.z + B.z)};
|
|
};
|
|
this.multiply = function(V, scale) {
|
|
return {x: scale * V.x, y: scale * V.y, z: scale * V.z};
|
|
};
|
|
this.normalize = function(V) {
|
|
var L2 = V.x*V.x + V.y*V.y + V.z*V.z;
|
|
if (L2 < self.EPSILON_SQUARED) {
|
|
return {x: V.x, y: V.y, z: V.z};
|
|
}
|
|
var invL = 1.0/Math.sqrt(L2);
|
|
return {x: invL * V.x, y: invL * V.y, z: invL * V.z};
|
|
};
|
|
this.multiplyQbyV = function(Q,V) {
|
|
var num = Q.x * 2.0;
|
|
var num2 = Q.y * 2.0;
|
|
var num3 = Q.z * 2.0;
|
|
var num4 = Q.x * num;
|
|
var num5 = Q.y * num2;
|
|
var num6 = Q.z * num3;
|
|
var num7 = Q.x * num2;
|
|
var num8 = Q.x * num3;
|
|
var num9 = Q.y * num3;
|
|
var num10 = Q.w * num;
|
|
var num11 = Q.w * num2;
|
|
var num12 = Q.w * num3;
|
|
var result = {x: 0, y: 0, z: 0};
|
|
result.x = (1.0 - (num5 + num6)) * V.x + (num7 - num12) * V.y + (num8 + num11) * V.z;
|
|
result.y = (num7 + num12) * V.x + (1.0 - (num4 + num6)) * V.y + (num9 - num10) * V.z;
|
|
result.z = (num8 - num11) * V.x + (num9 + num10) * V.y + (1.0 - (num4 + num5)) * V.z;
|
|
return result;
|
|
};
|
|
})();
|
|
|
|
var Quaternion = new (function() {
|
|
var self = this;
|
|
|
|
this.IDENTITY = function() {
|
|
return {x:0, y:0, z:0, w:1};
|
|
};
|
|
|
|
this.multiply = function(Q, R) {
|
|
// from this page:
|
|
// http://mathworld.wolfram.com/Quaternion.html
|
|
return {
|
|
w: Q.w * R.w - Q.x * R.x - Q.y * R.y - Q.z * R.z,
|
|
x: Q.w * R.x + Q.x * R.w + Q.y * R.z - Q.z * R.y,
|
|
y: Q.w * R.y - Q.x * R.z + Q.y * R.w + Q.z * R.x,
|
|
z: Q.w * R.z + Q.x * R.y - Q.y * R.x + Q.z * R.w};
|
|
};
|
|
|
|
this.angleAxis = function(angle, axis) {
|
|
var s = Math.sin(0.5 * angle);
|
|
return {w: Math.cos(0.5 * angle),x: s * axis.x, y: s * axis.y, z: s * axis.z};
|
|
};
|
|
|
|
this.inverse = function(Q) {
|
|
return {w: -Q.w, x: Q.x, y: Q.y, z: Q.z};
|
|
};
|
|
|
|
this.rotationBetween = function(orig, dest) {
|
|
var v1 = Vector3.normalize(orig);
|
|
var v2 = Vector3.normalize(dest);
|
|
var cosTheta = Vector3.dot(v1, v2);
|
|
var rotationAxis;
|
|
if(cosTheta >= 1 - Vector3.EPSILON){
|
|
return self.IDENTITY();
|
|
}
|
|
|
|
if(cosTheta < -1 + Vector3.EPSILON)
|
|
{
|
|
// special case when vectors in opposite directions :
|
|
// there is no "ideal" rotation axis
|
|
// So guess one; any will do as long as it's perpendicular to start
|
|
// This implementation favors a rotation around the Up axis (Y),
|
|
// since it's often what you want to do.
|
|
rotationAxis = Vector3.cross({x: 0, y: 0, z: 1}, v1);
|
|
if(Vector3.length(rotationAxis) < Vector3.EPSILON) { // bad luck, they were parallel, try again!
|
|
rotationAxis = Vector3.cross({x:1, y:0, z:0}, v1);
|
|
}
|
|
rotationAxis = Vector3.normalize(rotationAxis);
|
|
return self.angleAxis(Vector3.PI, rotationAxis);
|
|
}
|
|
// Implementation from Stan Melax's Game Programming Gems 1 article
|
|
rotationAxis = Vector3.cross(v1, v2);
|
|
|
|
var s = Math.sqrt((1 + cosTheta) * 2);
|
|
var invs = 1 / s;
|
|
|
|
return {
|
|
w: s * 0.5,
|
|
x: rotationAxis.x * invs,
|
|
y: rotationAxis.y * invs,
|
|
z: rotationAxis.z * invs,
|
|
}
|
|
}
|
|
})();
|
|
Script.registerValue("VEC3", Vector3);
|
|
Script.registerValue("QUAT", Quaternion);
|
|
})();
|
|
|
|
// Avatar
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// Place something in front of your avatar
|
|
function inFrontOf(distance, position, orientation) {
|
|
return Vec3.sum(position || MyAvatar.position,
|
|
Vec3.multiply(distance, Quat.getForward(orientation || MyAvatar.orientation)));
|
|
}
|
|
|
|
// Color
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// Mix between two colors
|
|
function colorMix(colorA, colorB, mix) {
|
|
var result = {};
|
|
for (var key in colorA) {
|
|
result[key] = (colorA[key] * (1 - mix)) + (colorB[key] * mix);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
// Going from hsl color space to RGB
|
|
function hslToRgb(hsl) {
|
|
var r, g, b;
|
|
if (hsl.s == 0) {
|
|
r = g = b = hsl.l; // achromatic
|
|
} else {
|
|
var hue2rgb = 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) * (2 / 3 - t) * 6;
|
|
return p;
|
|
}
|
|
|
|
var q = hsl.l < 0.5 ? hsl.l * (1 + hsl.s) : hsl.l + hsl.s - hsl.l * hsl.s;
|
|
var p = 2 * hsl.l - q;
|
|
r = hue2rgb(p, q, hsl.h + 1 / 3);
|
|
g = hue2rgb(p, q, hsl.h);
|
|
b = hue2rgb(p, q, hsl.h - 1 / 3);
|
|
}
|
|
|
|
return {
|
|
red: Math.round(r * 255),
|
|
green: Math.round(g * 255),
|
|
blue: Math.round(b * 255)
|
|
};
|
|
}
|
|
|
|
// Debug
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// Formating an object in debug log for overlay windows
|
|
function formatObj(obj) {
|
|
var formatedOBj = {};
|
|
for (var key in obj) {
|
|
if (typeof obj[key] === "number") {
|
|
formatedOBj[key] = obj[key].toFixed(3);
|
|
}
|
|
if (typeof obj[key] === "object") {
|
|
formatedOBj[key] = formatObj(obj[key]);
|
|
}
|
|
if (typeof obj[key] === "string") {
|
|
formatedOBj[key] === obj[key];
|
|
}
|
|
}
|
|
return formatedOBj;
|
|
}
|
|
|
|
// Custom log functions for groups and custom debouncing
|
|
function log(configGroup) {
|
|
var deBounceGroup = {};
|
|
var deBounceCheck = function(oldTime, newTime, bounceTime) {
|
|
if (newTime - oldTime > bounceTime) {
|
|
return true;
|
|
}
|
|
return false;
|
|
};
|
|
return function (group, title, value, bounce) {
|
|
|
|
if (configGroup[group]) {
|
|
var printString = arguments.length === 2 || value === null
|
|
? group + " :: " + title
|
|
: group + " :: " + title + " :: " + JSON.stringify(value);
|
|
if (bounce) {
|
|
var key = group+title+value+bounce;
|
|
|
|
if (!deBounceGroup[key]) {
|
|
deBounceGroup[key] = Date.now();
|
|
console.log(printString);
|
|
} else {
|
|
if (deBounceCheck(deBounceGroup[key], Date.now(), bounce)) {
|
|
deBounceGroup[key] = Date.now();
|
|
console.log(printString);
|
|
} else {
|
|
return;
|
|
}
|
|
}
|
|
} else {
|
|
console.log(printString);
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
function makeColor(red, green, blue) {
|
|
var obj = {};
|
|
obj.red = red;
|
|
obj.green = green;
|
|
obj.blue = blue;
|
|
return obj;
|
|
}
|
|
|
|
// Entities
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// Get props of particular name
|
|
function getNameProps(name, position, radius) {
|
|
position = position || MyAvatar.position;
|
|
radius = radius || 20;
|
|
var ents = Entities.findEntitiesByName(name, position, radius)[0];
|
|
if (ents) {
|
|
return [ents, Entities.getEntityProperties(ents)];
|
|
}
|
|
}
|
|
|
|
// Get props
|
|
function getProps(id, props) {
|
|
if (props) {
|
|
return Entities.getEntityProperties(id, props);
|
|
} else {
|
|
return Entities.getEntityProperties(id);
|
|
}
|
|
}
|
|
|
|
// Get only userData for an entity
|
|
function getUserData(id, defaultObject, cb) {
|
|
defaultObject = defaultObject || {};
|
|
var userData = Entities.getEntityProperties(id, ["userData"]).userData;
|
|
var parsedData = defaultObject;
|
|
try {
|
|
parsedData = JSON.parse(userData);
|
|
if (cb) {
|
|
cb(parsedData);
|
|
}
|
|
return parsedData;
|
|
} catch (e) {
|
|
return parsedData;
|
|
}
|
|
}
|
|
|
|
// Search for Children of an entity and then run a callback after they are found
|
|
function searchForChildren(parentID, names, callback, timeoutMs, outputPrint) {
|
|
// Map from name to entity ID for the children that have been found
|
|
var foundEntities = {};
|
|
var foundAllEntities = false;
|
|
for (var i = 0; i < names.length; ++i) {
|
|
foundEntities[names[i]] = null;
|
|
}
|
|
|
|
const CHECK_EVERY_MS = 500;
|
|
const maxChecks = Math.ceil(timeoutMs / CHECK_EVERY_MS);
|
|
|
|
var check = 0;
|
|
var intervalID = Script.setInterval(function() {
|
|
check++;
|
|
|
|
var childrenIDs = Entities.getChildrenIDs(parentID);
|
|
if (outputPrint) {
|
|
print("\tNumber of children:", childrenIDs.length);
|
|
print("\check:", check);
|
|
|
|
}
|
|
for (var i = 0; i < childrenIDs.length; ++i) {
|
|
|
|
|
|
var id = childrenIDs[i];
|
|
var name = Entities.getEntityProperties(id, 'name').name;
|
|
var idx = names.indexOf(name);
|
|
if (idx > -1) {
|
|
foundEntities[name] = id;
|
|
print(name, id);
|
|
names.splice(idx, 1);
|
|
childrenIDs.splice(i, 1)
|
|
}
|
|
}
|
|
|
|
if (names.length === 0 || check >= maxChecks) {
|
|
if (outputPrint) {
|
|
print("names: " + JSON.stringify(names));
|
|
}
|
|
if (names.length > 0) {
|
|
callback(foundEntities, foundAllEntities, names);
|
|
} else {
|
|
foundAllEntities = true;
|
|
callback(foundEntities, foundAllEntities);
|
|
}
|
|
Script.clearInterval(intervalID);
|
|
}
|
|
}, CHECK_EVERY_MS);
|
|
}
|
|
|
|
// Search for a list of entity names and then run a callback after they are found
|
|
function searchForEntityNames(names, position, callback, timeoutMs, outputPrint) {
|
|
var foundEntities = {};
|
|
names.forEach(function(name) {
|
|
foundEntities[name] = null;
|
|
})
|
|
|
|
const CHECK_EVERY_MS = 500;
|
|
const maxChecks = Math.ceil(timeoutMs / CHECK_EVERY_MS);
|
|
|
|
var check = 0;
|
|
var intervalID = Script.setInterval(function() {
|
|
check++;
|
|
|
|
names.forEach(function(name, index) {
|
|
var ents = Entities.findEntitiesByName(name, position, 50);
|
|
if (ents.length === 1) {
|
|
foundEntities[name] = ents[0];
|
|
if (outputPrint) {
|
|
print(name, ents[0]);
|
|
}
|
|
names.splice(index, 1);
|
|
}
|
|
|
|
})
|
|
|
|
if (names.length === 0 || check >= maxChecks) {
|
|
Script.clearInterval(intervalID);
|
|
callback(foundEntities);
|
|
}
|
|
}, CHECK_EVERY_MS)
|
|
}
|
|
|
|
// Update userData with an object
|
|
function updateUserData(id, userData) {
|
|
var stringified = JSON.stringify(userData);
|
|
var props = { userData: stringified};
|
|
Entities.editEntity(id, props);
|
|
}
|
|
|
|
// Functional
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// Return back true or false for debouncing
|
|
function debounce() {
|
|
var date = Date.now();
|
|
return function(timeToPass) {
|
|
var dateTest = Date.now();
|
|
var timePassed = dateTest-date;
|
|
|
|
if (timePassed > timeToPass) {
|
|
date = Date.now();
|
|
return true;
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
};
|
|
}
|
|
|
|
// Fire every n count
|
|
function fireEvery() {
|
|
var currentCount = 0;
|
|
return function(steps) {
|
|
if (currentCount >= steps) {
|
|
currentCount = 0;
|
|
return true;
|
|
} else {
|
|
currentCount++;
|
|
return false;
|
|
}
|
|
};
|
|
}
|
|
|
|
// HTTP
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// Encode params for get request helper
|
|
function encodeURLParams (params) {
|
|
var paramPairs = [];
|
|
for (var key in params) {
|
|
paramPairs.push(key + "=" + params[key]);
|
|
}
|
|
return paramPairs.join("&");
|
|
}
|
|
|
|
// Math
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// Get an orientation that is in front of you but to the closet axis
|
|
function axisAlignedOrientation(orientation) {
|
|
if (!Math.sign) {
|
|
Math.sign = function(x) {
|
|
return ((x > 0) - (x < 0)) || +x;
|
|
};
|
|
}
|
|
|
|
var rotation = MyAvatar.orientation;
|
|
var getForward = Quat.getForward(rotation);
|
|
|
|
var sign = {
|
|
x: Math.sign(getForward.x),
|
|
y: Math.sign(getForward.y),
|
|
z: Math.sign(getForward.z)
|
|
};
|
|
|
|
var newObj = {
|
|
x: Math.abs(getForward.x),
|
|
y: Math.abs(getForward.y),
|
|
z: Math.abs(getForward.z)
|
|
};
|
|
|
|
var keys = Object.keys(newObj);
|
|
|
|
function getLargest(obj) {
|
|
var largestKey = "x";
|
|
keys.forEach(function (key) {
|
|
if (newObj[largestKey] < newObj[key]) {
|
|
largestKey = key;
|
|
}
|
|
});
|
|
return largestKey;
|
|
}
|
|
|
|
var largestKey = getLargest(newObj);
|
|
keys.splice(keys.indexOf(largestKey), 1);
|
|
|
|
var finalObj = {};
|
|
finalObj[largestKey] = sign[largestKey];
|
|
keys.forEach(function (key) {
|
|
finalObj[key] = 0;
|
|
});
|
|
var finalRotation = Quat.fromVec3Degrees(finalObj);
|
|
|
|
return [finalRotation, finalObj];
|
|
}
|
|
|
|
// Check if a point is in an axis aligned space
|
|
function checkIfIn(currentPosition, minMaxObj, margin) {
|
|
margin = margin || 0.05;
|
|
return (
|
|
(currentPosition.x >= minMaxObj.xMin - margin && currentPosition.x <= minMaxObj.xMax + margin) &&
|
|
(currentPosition.y >= minMaxObj.yMin - margin && currentPosition.y <= minMaxObj.yMax + margin) &&
|
|
(currentPosition.z >= minMaxObj.zMin - margin && currentPosition.z <= minMaxObj.zMax + margin)
|
|
);
|
|
}
|
|
|
|
// Check if a point is in a non axis aligned space
|
|
function checkIfInNonAligned(pointToCheck, position, orientation, minMaxObj, margin) {
|
|
var worldOffset = VEC3.subtract(pointToCheck, position),
|
|
pointToCheck = VEC3.multiplyQbyV(QUAT.inverse(orientation), worldOffset);
|
|
margin = margin || 0.03;
|
|
|
|
return (
|
|
(pointToCheck.x >= minMaxObj.xMin - margin && pointToCheck.x <= minMaxObj.xMax + margin) &&
|
|
(pointToCheck.y >= minMaxObj.yMin - margin && pointToCheck.y <= minMaxObj.yMax + margin) &&
|
|
(pointToCheck.z >= minMaxObj.zMin - margin && pointToCheck.z <= minMaxObj.zMax + margin)
|
|
);
|
|
}
|
|
|
|
// Clamp a value by a min and max
|
|
function clamp(min, max, num) {
|
|
return Math.min(Math.max(num, min), max);
|
|
}
|
|
|
|
// Find the area below you
|
|
function findSurfaceBelowPosition (pos) {
|
|
var result = Entities.findRayIntersection({
|
|
origin: pos,
|
|
direction: { x: 0.0, y: -1.0, z: 0.0 }
|
|
}, true);
|
|
if (result.intersects) {
|
|
return result.intersection;
|
|
}
|
|
return pos;
|
|
}
|
|
|
|
// Get a value between 2 ranges
|
|
function lerp(InputLow, InputHigh, OutputLow, OutputHigh, Input) {
|
|
return ((Input - InputLow) / (InputHigh - InputLow)) * (OutputHigh - OutputLow) + OutputLow;
|
|
}
|
|
|
|
// Get the dimension that is the largest
|
|
function largestAxisVec(dimensions) {
|
|
var dimensionArray = [];
|
|
for (var key in dimensions) {
|
|
dimensionArray.push(dimensions[key]);
|
|
}
|
|
return Math.max.apply(null, dimensionArray);
|
|
}
|
|
|
|
// Make an object of min and max
|
|
function makeMinMax(dimensions, position) {
|
|
var minMaxObj = {
|
|
xMin: position.x - dimensions.x / 2,
|
|
xMax: position.x + dimensions.x / 2,
|
|
yMin: position.y - dimensions.y / 2,
|
|
yMax: position.y + dimensions.y / 2,
|
|
zMin: position.z - dimensions.z / 2,
|
|
zMax: position.z + dimensions.z / 2
|
|
};
|
|
|
|
return minMaxObj;
|
|
}
|
|
|
|
|
|
// Make an object of min and max based off the origin
|
|
function makeOriginMinMax(dimensions) {
|
|
var minMaxObj = {
|
|
xMin: 0 - dimensions.x / 2,
|
|
xMax: 0 + dimensions.x / 2,
|
|
yMin: 0 - dimensions.y / 2,
|
|
yMax: 0 + dimensions.y / 2,
|
|
zMin: 0 - dimensions.z / 2,
|
|
zMax: 0 + dimensions.z / 2
|
|
};
|
|
|
|
return minMaxObj;
|
|
}
|
|
|
|
// Smoothing Low Pass Filter
|
|
function smoothing(initialValue, smoothingAmount) {
|
|
var smoothed = initialValue;
|
|
var smoothing = smoothingAmount;
|
|
var lastUpdate = new Date;
|
|
return function smoothedValue( newValue ) {
|
|
var now = new Date;
|
|
var elapsedTime = now - lastUpdate;
|
|
smoothed += elapsedTime * ( newValue - smoothed ) / smoothing;
|
|
lastUpdate = now;
|
|
return smoothed;
|
|
};
|
|
}
|
|
|
|
// Smooth a range
|
|
function smoothRange(range, smoothingAmount, smoothFunction) {
|
|
var smoothing = smoothFunction;
|
|
var x = range.x;
|
|
var y = range.y;
|
|
var z = range.z;
|
|
var smoothedx = smoothing(x, smoothingAmount);
|
|
var smoothedy = smoothing(y, smoothingAmount);
|
|
var smoothedz = smoothing(z, smoothingAmount);
|
|
return function (newRange) {
|
|
var smoothRange = {};
|
|
smoothRange.x = smoothedx(newRange.x);
|
|
smoothRange.y = smoothedy(newRange.y);
|
|
smoothRange.z = smoothedz(newRange.z);
|
|
return smoothRange;
|
|
};
|
|
}
|
|
|
|
// V Vector libary that is object oriented #WIP
|
|
function V(x, y, z) {
|
|
if (arguments.length === 0) {
|
|
this.x = 0;
|
|
this.y = 0;
|
|
this.z = 0;
|
|
}
|
|
if (arguments.length === 1) {
|
|
this.x = x;
|
|
this.y = x;
|
|
this.z = x;
|
|
}
|
|
if (arguments.length === 3) {
|
|
this.x = x;
|
|
this.y = y;
|
|
this.z = z;
|
|
}
|
|
}
|
|
|
|
V.prototype = {
|
|
add: function(vector) {
|
|
var returnVector = {};
|
|
returnVector.x = this.x + vector.x;
|
|
returnVector.y = this.y + vector.y;
|
|
returnVector.z = this.z + vector.z;
|
|
return returnVector;
|
|
},
|
|
cross: function(vector) {
|
|
var returnVector = {};
|
|
returnVector.x = this.y * vector.z - this.z * vector.y;
|
|
returnVector.y = this.z * vector.x - this.x * vector.z;
|
|
returnVector.z = this.x * vector.y - this.y * vector.x;
|
|
return returnVector;
|
|
},
|
|
dot: function(vector) {
|
|
return (
|
|
this.x * vector.x +
|
|
this.y * vector.y +
|
|
this.z * vector.z
|
|
);
|
|
},
|
|
length: function() {
|
|
return Math.sqrt(
|
|
this.x * this.x +
|
|
this.y * this.y +
|
|
this.z * this.z
|
|
)
|
|
},
|
|
multiply: function(scalar) {
|
|
var returnVector = {};
|
|
returnVector.x = this.x * scalar;
|
|
returnVector.y = this.y * scalar;
|
|
returnVector.z = this.z * scalar;
|
|
return returnVector;
|
|
},
|
|
normalize: function() {
|
|
var len = this.length();
|
|
if (len > 0) {
|
|
var invLen = 1 / len;
|
|
this.x *= invLen;
|
|
this.y *= invLen;
|
|
this.z *= invLen;
|
|
}
|
|
return this;
|
|
},
|
|
subtract: function(vector) {
|
|
var returnVector = {};
|
|
returnVector.x = this.x - vector.x;
|
|
returnVector.y = this.y - vector.y;
|
|
returnVector.z = this.z - vector.z;
|
|
return returnVector;
|
|
}
|
|
};
|
|
|
|
// Make a quick vector (V should replace this)
|
|
function vec(x, y, z) {
|
|
var obj = {};
|
|
obj.x = x;
|
|
obj.y = y;
|
|
obj.z = z;
|
|
return obj;
|
|
}
|
|
|
|
// Quick distance true/false return
|
|
function withinDistance(vec1, vec2, distance) {
|
|
var vecDistance = Vec3.distance(vec1,vec2);
|
|
return vecDistance <= distance
|
|
? true
|
|
: false;
|
|
}
|
|
|
|
// Where on the range is a point
|
|
function whereOnRange(currentPosition, minMax) {
|
|
var whereOnRange = {
|
|
x: 0,
|
|
y: 0,
|
|
z: 0
|
|
};
|
|
for (var key in whereOnRange) {
|
|
var minKey = key + "Min";
|
|
var maxKey = key + "Max";
|
|
var min = minMax[minKey];
|
|
var max = minMax[maxKey];
|
|
var maxMinusMin = max - min;
|
|
var currentMinusMin = currentPosition[key] - min;
|
|
var normalizedTotal = currentMinusMin / maxMinusMin;
|
|
whereOnRange[key] = normalizedTotal;
|
|
}
|
|
return whereOnRange;
|
|
}
|
|
|
|
// Scripts
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// Helps with managing cache busting
|
|
function cacheBuster(debug, baseName, scriptName) {
|
|
if (debug) {
|
|
return baseName + scriptName + "?" + Date.now();
|
|
} else {
|
|
return baseName + scriptName;
|
|
}
|
|
}
|
|
|
|
// Object
|
|
// ----------------------------------------------------------------------------
|
|
function combineProperties(obj1, obj2) {
|
|
// obj1 overrrides obj2 props
|
|
var newObject = {};
|
|
|
|
for(var key2 in obj2) {
|
|
newObject[key2] = obj2[key2];
|
|
}
|
|
|
|
for(var key1 in obj1) {
|
|
newObject[key1] = obj1[key1];
|
|
}
|
|
|
|
return newObject;
|
|
}
|
|
|
|
// Export
|
|
// ----------------------------------------------------------------------------
|
|
|
|
module.exports = {
|
|
Avatar: {
|
|
inFrontOf: inFrontOf
|
|
},
|
|
Color: {
|
|
colorMix: colorMix,
|
|
hslToRgb: hslToRgb,
|
|
makeColor: makeColor
|
|
},
|
|
Debug: {
|
|
formatObj: formatObj,
|
|
log: log,
|
|
LOG_ENTER: "Log_Enter",
|
|
LOG_UPDATE: "Log_Update",
|
|
LOG_ERROR: "Log_Error",
|
|
LOG_VALUE: "Log_Value",
|
|
LOG_VALUE_EZ: "Log_Value_EZ",
|
|
LOG_ARCHIVE: "Log_Archive"
|
|
},
|
|
Entity: {
|
|
getNameProps: getNameProps,
|
|
getProps: getProps,
|
|
getUserData: getUserData,
|
|
searchForChildren: searchForChildren,
|
|
searchForEntityNames: searchForEntityNames,
|
|
updateUserData: updateUserData
|
|
},
|
|
Functional: {
|
|
debounce: debounce,
|
|
fireEvery: fireEvery
|
|
},
|
|
HTTP: {
|
|
encodeURLParams: encodeURLParams
|
|
},
|
|
Maths: {
|
|
axisAlignedOrientation: axisAlignedOrientation,
|
|
checkIfIn: checkIfIn,
|
|
checkIfInNonAligned: checkIfInNonAligned,
|
|
clamp: clamp,
|
|
findSurfaceBelowPosition: findSurfaceBelowPosition,
|
|
fireEvery: fireEvery,
|
|
largestAxisVec: largestAxisVec,
|
|
lerp: lerp,
|
|
makeMinMax: makeMinMax,
|
|
makeOriginMinMax: makeOriginMinMax,
|
|
smoothing: smoothing,
|
|
smoothRange: smoothRange,
|
|
V: V,
|
|
vec: vec,
|
|
withinDistance: withinDistance,
|
|
whereOnRange: whereOnRange
|
|
},
|
|
Scripts: {
|
|
cacheBuster: cacheBuster
|
|
},
|
|
Object: {
|
|
combineProperties: combineProperties
|
|
}
|
|
};
|