prosperon/scripts/base.js
2024-10-03 23:35:40 -05:00

1562 lines
35 KiB
JavaScript

Number.roman = {
M: 1000,
D: 500,
C: 100,
L: 50,
X: 10,
V: 5,
I: 1,
};
var convert = {};
convert.romanize = function (num) {
if (!+num) return false;
var digits = String(+num).split("");
var key = ["", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM", "", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC", "", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"];
var roman = "",
i = 3;
while (i--) roman = (key[+digits.pop() + i * 10] || "") + roman;
return Array(+digits.join("") + 1).join("M") + roman;
};
convert.deromanize = function (str) {
var str = str.toUpperCase();
var validator = /^M*(?:D?C{0,3}|C[MD])(?:L?X{0,3}|X[CL])(?:V?I{0,3}|I[XV])$/;
var token = /[MDLV]|C[MD]?|X[CL]?|I[XV]?/g;
var key = {
M: 1000,
CM: 900,
D: 500,
CD: 400,
C: 100,
XC: 90,
L: 50,
XL: 40,
X: 10,
IX: 9,
V: 5,
IV: 4,
I: 1,
};
var num = 0,
m;
if (!(str && validator.test(str))) return false;
while ((m = token.exec(str))) num += key[m[0]];
return num;
};
convert.buf2hex = function (buffer) {
// buffer is an ArrayBuffer
return [...new Uint8Array(buffer)].map(x => x.toString(16).padStart(2, "0")).join(" ");
};
/* Time values are always expressed in terms of real earth-seconds */
Object.assign(time, {
hour2minute() {
return this.hour / this.minute;
},
day2hour() {
return this.day / this.hour;
},
minute2second() {
return this.minute / this.second;
},
week2day() {
return this.week / this.day;
},
});
time.strparse = {
yyyy: "year",
mm: "month",
m: "month",
eee: "yday",
dd: "day",
d: "day",
v: "weekday",
hh: "hour",
h: "hour",
nn: "minute",
n: "minute",
ss: "second",
s: "second",
};
time.doc = {
doc: "Functions for manipulating time.",
second: "Earth-seconds in a second.",
minute: "Seconds in a minute.",
hour: "Seconds in an hour.",
day: "Seconds in a day.",
week: "Seconds in a week.",
weekdays: "Names of the days of the week.",
monthstr: "Full names of the months of the year.",
epoch: "Times are expressed in terms of day 0 at hms 0 of this year.",
now: "Get the time now.",
computer_zone: "Get the time zone of the running computer.",
computer_dst: "Return true if the computer is in daylight savings.",
yearsize: "Given a year, return the number of days in that year.",
monthdays: "Number of days in each month.",
fmt: "Default format for time.",
record: "Given a time, return an object with time fields.",
number: "Return the number representation of a given time.",
text: "Return a text formatted time.",
};
time.second = 1;
time.minute = 60;
time.hour = 3_600;
time.day = 86_400;
time.week = 604_800;
time.weekdays = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
time.monthstr = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
time.epoch = 1970;
time.isleap = function (year) {
return this.yearsize(year) === 366;
};
time.isleap.doc = "Return true if the given year is a leapyear.";
time.yearsize = function (y) {
if (y % 4 === 0 && (y % 100 != 0 || y % 400 === 0)) return 366;
return 365;
};
time.timecode = function (t, fps = 24) {
var s = Math.trunc(t);
t -= s;
return `${s}:${Math.trunc(fps * s)}`;
};
time.monthdays = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
time.zones = {};
time.zones["-12"] = "IDLW";
time.record = function (num, zone = this.computer_zone()) {
if (typeof num === "object") return num;
else if (typeof num === "number") {
var monthdays = this.monthdays.slice();
var rec = {
second: 0,
minute: 0,
hour: 0,
yday: 0,
year: 0,
zone: 0,
};
rec.zone = zone;
num += zone * this.hour;
var hms = num % this.day;
var day = parseInt(num / this.day);
if (hms < 0) {
hms += this.day;
day--;
}
rec.second = hms % this.minute;
var d1 = Math.floor(hms / this.minute);
rec.minute = d1 % this.minute;
rec.hour = Math.floor(d1 / this.minute);
/* addend%7 is 4 */
rec.weekday = (day + 4503599627370496 + 2) % 7;
var d1 = this.epoch;
if (day >= 0) for (d1 = this.epoch; day >= this.yearsize(d1); d1++) day -= this.yearsize(d1);
else for (d1 = this.epoch; day < 0; d1--) day += this.yearsize(d1 - 1);
rec.year = d1;
if (rec.year <= 0) rec.ce = "BC";
else rec.ce = "AD";
rec.yday = day;
if (this.yearsize(d1) === 366) monthdays[1] = 29;
var d0 = day;
for (d1 = 0; d0 >= monthdays[d1]; d1++) d0 -= monthdays[d1];
monthdays[1] = 28;
rec.day = d0 + 1;
rec.month = d1;
return rec;
}
};
time.number = function (rec) {
if (typeof rec === "number") return rec;
else if (typeof rec === "object") {
var c = 0;
var year = rec.year ? rec.year : 0;
var hour = rec.hour ? rec.hour : 0;
var minute = rec.minute ? rec.minute : 0;
var second = rec.second ? rec.second : 0;
var zone = rec.zone ? rec.zone : 0;
if (year > this.epoch) for (var i = this.epoch; i < year; i++) c += this.day * this.yearsize(i);
else if (year < this.epoch) {
for (var i = this.epoch - 1; i > year; i--) c += this.day * this.yearsize(i);
c += (this.yearsize(year) - yday - 1) * this.day;
c += (this.day2hour() - hour - 1) * this.hour;
c += (this.hour2minute() - minute - 1) * this.minute;
c += this.minute2second() - second;
c += zone * this.hour;
c *= -1;
return c;
}
c += second;
c += minute * this.minute;
c += hour * this.hour;
c += yday * this.day;
c -= zone * this.hour;
return c;
}
};
/* Time formatting
yyyy - year in a 4 digit field
y - as many digits as necessary
mm - month (1-12)
mB - month name
mb - abbreviated month name
dd - day (1-31)
d
c - if the year is <= 0, BC. Otherwise, AD.
hh - hour
h
nn - minutes
n
ss - seconds
s
v - day of the week (0-6)
vB - weekday name
vb - abbreviated weekday name
a - am/pm
z - zone, -12 to +11
*/
time.fmt = "vB mB d h:nn:ss TZz a y c";
/* If num is a number, converts to a rec first. */
time.text = function (num, fmt = this.fmt, zone) {
var rec = num;
if (typeof rec === "number") rec = time.record(num, zone);
zone = rec.zone;
if (fmt.match("a")) {
if (rec.hour >= 13) {
rec.hour -= 12;
fmt = fmt.replaceAll("a", "PM");
} else if (rec.hour === 12) fmt = fmt.replaceAll("a", "PM");
else if (rec.hour === 0) {
rec.hour = 12;
fmt = fmt.replaceAll("a", "AM");
} else fmt = fmt.replaceAll("a", "AM");
}
var year = rec.year > 0 ? rec.year : rec.year - 1;
if (fmt.match("c")) {
if (year < 0) {
year = Math.abs(year);
fmt = fmt.replaceAll("c", "BC");
} else fmt = fmt.replaceAll("c", "AD");
}
fmt = fmt.replaceAll("yyyy", year.toString().padStart(4, "0"));
fmt = fmt.replaceAll("y", year);
fmt = fmt.replaceAll("eee", rec.yday + 1);
fmt = fmt.replaceAll("dd", rec.day.toString().padStart(2, "0"));
fmt = fmt.replaceAll("d", rec.day);
fmt = fmt.replaceAll("hh", rec.hour.toString().padStart(2, "0"));
fmt = fmt.replaceAll("h", rec.hour);
fmt = fmt.replaceAll("nn", rec.minute.toString().padStart(2, "0"));
fmt = fmt.replaceAll("n", rec.minute);
fmt = fmt.replaceAll("ss", rec.second.toString().padStart(2, "0"));
fmt = fmt.replaceAll("s", rec.second);
fmt = fmt.replaceAll("z", zone >= 0 ? "+" + zone : zone);
fmt = fmt.replaceAll(/mm[^bB]/g, rec.month + 1);
fmt = fmt.replaceAll(/m[^bB]/g, rec.month + 1);
fmt = fmt.replaceAll(/v[^bB]/g, rec.weekday);
fmt = fmt.replaceAll("mb", this.monthstr[rec.month].slice(0, 3));
fmt = fmt.replaceAll("mB", this.monthstr[rec.month]);
fmt = fmt.replaceAll("vB", this.weekdays[rec.weekday]);
fmt = fmt.replaceAll("vb", this.weekdays[rec.weekday].slice(0, 3));
return fmt;
};
Object.methods = function (o) {
var m = [];
Object.keys(o).forEach(function (k) {
if (typeof o[k] === "function") m.push(k);
});
return m;
};
Object.methods.doc = "Retun an array of all functions an object has access to.";
Object.dig = function (obj, path, def = {}) {
var pp = path.split(".");
for (var i = 0; i < pp.length - 1; i++) {
obj = obj[pp[i]] = obj[pp[i]] || {};
}
obj[pp[pp.length - 1]] = def;
return def;
};
Object.rkeys = function (o) {
var keys = [];
Object.keys(o).forEach(function (key) {
keys.push(key);
if (Object.isObject(o[key])) keys.push(Object.rkeys(o[key]));
});
return keys;
};
Object.readonly = function (o, name, msg) {
var tmp = {};
var prop = Object.getOwnPropertyDescriptor(o, name);
if (!prop) {
console.error(`Attempted to make property ${name} readonly, but it doesn't exist on ${o}.`);
return;
}
Object.defineProperty(tmp, name, prop);
prop.get = function () {
return tmp[name];
};
prop.set = function () {
console.warn(`Attempted to set readonly property ${name}`);
};
Object.defineProperty(o, name, prop);
};
Object.mixin = function (target, source) {
if (typeof source !== "object") return target;
Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
return target;
};
Object.mix = function (...objs) {
var n = {};
for (var o of objs) Object.mixin(n, o);
return n;
};
Object.deepmixin = function (target, source) {
var o = source;
while (o !== Object.prototype) {
Object.mixin(target, o);
o = o.__proto__;
}
};
Object.deepfreeze = function (obj) {
for (var key in obj) {
if (typeof obj[key] === "object") Object.deepfreeze(obj[key]);
}
Object.freeze(obj);
};
/* Goes through each key and overwrites if it's present */
Object.dainty_assign = function (target, source) {
Object.keys(source).forEach(function (k) {
if (typeof source[k] === "function") return;
if (!(k in target)) return;
if (Array.isArray(source[k])) target[k] = deep_copy(source[k]);
else if (Object.isObject(source[k])) Object.dainty_assign(target[k], source[k]);
else target[k] = source[k];
});
};
Object.isObject = function (o) {
return o instanceof Object && !(o instanceof Array);
};
Object.setter_assign = function (target, source) {
for (var key in target) if (Object.isAccessor(target, key) && typeof source[key] !== "undefined") target[key] = source[key];
};
Object.containingKey = function (obj, prop) {
if (typeof obj !== "object") return undefined;
if (!(prop in obj)) return undefined;
var o = obj;
while (o.__proto__ && !Object.hasOwn(o, prop)) o = o.__proto__;
return o;
};
Object.access = function (obj, name) {
var dig = name.split(".");
for (var i of dig) {
obj = obj[i];
if (!obj) return undefined;
}
return obj;
};
Object.isAccessor = function (obj, prop) {
var o = Object.containingKey(obj, prop);
if (!o) return false;
var desc = Object.getOwnPropertyDescriptor(o, prop);
if (!desc) return false;
if (desc.get || desc.set) return true;
return false;
};
Object.mergekey = function (o1, o2, k) {
if (!o2) return;
if (typeof o2[k] === "object") {
if (Array.isArray(o2[k])) o1[k] = deep_copy(o2[k]);
else {
if (!o1[k]) o1[k] = {};
if (typeof o1[k] === "object") Object.merge(o1[k], o2[k]);
else o1[k] = o2[k];
}
} else o1[k] = o2[k];
};
/* Same as merge from Ruby */
/* Adds objs key by key to target */
Object.merge = function (target, ...objs) {
for (var obj of objs) for (var key of Object.keys(obj)) Object.mergekey(target, obj, key);
return target;
};
Object.totalmerge = function (target, ...objs) {
for (var obj of objs) for (var key in obj) Object.mergekey(target, obj, key);
return target;
};
/* Returns a new object with undefined, null, and empty values removed. */
Object.compact = function (obj) {};
Object.totalassign = function (to, from) {
for (var key in from) to[key] = from[key];
};
/* Prototypes out an object and assigns values */
Object.copy = function (proto, ...objs) {
var c = Object.create(proto);
for (var obj of objs) Object.mixin(c, obj);
return c;
};
/* OBJECT DEFININTIONS */
Object.defHidden = function (obj, prop) {
Object.defineProperty(obj, prop, { enumerable: false, writable: true });
};
Object.hide = function (obj, ...props) {
for (var prop of props) {
var p = Object.getOwnPropertyDescriptor(obj, prop);
if (!p) continue;
p.enumerable = false;
Object.defineProperty(obj, prop, p);
}
};
Object.enumerable = function (obj, val, ...props) {
for (var prop of props) {
p = Object.getOwnPropertyDescriptor(obj, prop);
if (!p) continue;
p.enumerable = val;
Object.defineProperty(obj, prop, p);
}
};
Object.unhide = function (obj, ...props) {
for (var prop of props) {
var p = Object.getOwnPropertyDescriptor(obj, prop);
if (!p) continue;
p.enumerable = true;
Object.defineProperty(obj, prop, p);
}
};
Object.defineProperty(Object.prototype, "obscure", {
value: function (name) {
Object.defineProperty(this, name, { enumerable: false });
},
});
Object.defineProperty(Object.prototype, "mixin", {
value: function (obj) {
if (typeof obj === "string") obj = use(obj);
if (obj) Object.mixin(this, obj);
},
});
Object.defineProperty(Object.prototype, "hasOwn", {
value: function (x) {
return this.hasOwnProperty(x);
},
});
Object.defineProperty(Object.prototype, "defn", {
value: function (name, val) {
Object.defineProperty(this, name, {
value: val,
writable: true,
configurable: true,
});
},
});
Object.defineProperty(Object.prototype, "nulldef", {
value: function (name, val) {
if (!this.hasOwnProperty(name)) this[name] = val;
},
});
Object.defineProperty(Object.prototype, "prop_obj", {
value: function () {
return JSON.parse(JSON.stringify(this));
},
});
/* defc 'define constant'. Defines a value that is not writable. */
Object.defineProperty(Object.prototype, "defc", {
value: function (name, val) {
Object.defineProperty(this, name, {
value: val,
writable: false,
enumerable: true,
configurable: false,
});
},
});
Object.defineProperty(Object.prototype, "stick", {
value: function (prop) {
Object.defineProperty(this, prop, { writable: false });
},
});
Object.defineProperty(Object.prototype, "harden", {
value: function (prop) {
Object.defineProperty(this, prop, {
writable: false,
configurable: false,
enumerable: false,
});
},
});
Object.defineProperty(Object.prototype, "deflock", {
value: function (prop) {
Object.defineProperty(this, prop, { configurable: false });
},
});
Object.defineProperty(Object.prototype, "forEach", {
value: function (fn) {
Object.values(this).forEach(fn);
},
});
Object.empty = function (obj) {
return Object.keys(obj).length === 0;
};
Object.defineProperty(Object.prototype, "nth", {
value: function (x) {
if (this.empty || x >= Object.keys(this).length) return null;
return this[Object.keys(this)[x]];
},
});
Object.defineProperty(Object.prototype, "filter", {
value: function (fn) {
return Object.values(this).filter(fn);
},
});
Object.defineProperty(Object.prototype, "push", {
value: function (val) {
var str = val.toString();
str = str.replaceAll(".", "_");
var n = 1;
var t = str;
while (Object.hasOwn(this, t)) {
t = str + n;
n++;
}
this[t] = val;
return t;
},
});
/* STRING DEFS */
Object.defineProperty(String.prototype, "next", {
value: function (char, from) {
if (!Array.isArray(char)) char = [char];
if (from > this.length - 1) return -1;
else if (!from) from = 0;
var find = this.slice(from).search(char[0]);
if (find === -1) return -1;
else return from + find;
var i = 0;
var c = this.charAt(from + i);
while (!char.includes(c)) {
i++;
if (from + i > this.length - 1) return -1;
c = this.charAt(from + i);
}
return from + i;
},
});
Object.defineProperty(String.prototype, "prev", {
value: function (char, from, count) {
if (from > this.length - 1) return -1;
else if (!from) from = this.length - 1;
if (!count) count = 0;
var find = this.slice(0, from).lastIndexOf(char);
while (count > 1) {
find = this.slice(0, find).lastIndexOf(char);
count--;
}
if (find === -1) return 0;
else return find;
},
});
Object.defineProperty(String.prototype, "shift", {
value: function (n) {
if (n === 0) return this.slice();
if (n > 0) return this.slice(n);
if (n < 0) return this.slice(0, this.length + n);
},
});
Object.defineProperty(String.prototype, "strip_ext", {
value: function () {
return this.tolast(".");
},
});
Object.defineProperty(String.prototype, "ext", {
value: function () {
return this.fromlast(".");
},
});
Object.defineProperty(String.prototype, "set_ext", {
value: function (val) {
return this.strip_ext() + val;
},
});
Object.defineProperty(String.prototype, "folder_same_name", {
value: function () {
var dirs = this.dir().split("/");
return dirs.last() === this.name();
},
});
Object.defineProperty(String.prototype, "up_path", {
value: function () {
var base = this.base();
var dirs = this.dir().split("/");
dirs.pop();
return dirs.join("/") + base;
},
});
Object.defineProperty(String.prototype, "fromlast", {
value: function (val) {
var idx = this.lastIndexOf(val);
if (idx === -1) return "";
return this.slice(idx + 1);
},
});
Object.defineProperty(String.prototype, "tofirst", {
value: function (val) {
var idx = this.indexOf(val);
if (idx === -1) return this.slice();
return this.slice(0, idx);
},
});
Object.defineProperty(String.prototype, "fromfirst", {
value: function (val) {
var idx = this.indexOf(val);
if (idx === -1) return this;
return this.slice(idx + val.length);
},
});
Object.defineProperty(String.prototype, "name", {
value: function () {
var idx = this.indexOf("/");
if (idx === -1) return this.tolast(".");
return this.fromlast("/").tolast(".");
},
});
Object.defineProperty(String.prototype, "set_name", {
value: function (name) {
var dir = this.dir();
return this.dir() + "/" + name + "." + this.ext();
},
});
Object.defineProperty(String.prototype, "base", {
value: function () {
return this.fromlast("/");
},
});
Object.defineProperty(String.prototype, "splice", {
value: function (index, str) {
return this.slice(0, index) + str + this.slice(index);
},
});
Object.defineProperty(String.prototype, "sub", {
value: function (index, str) {
return this.slice(0, index) + str + this.slice(index + str.length);
},
});
Object.defineProperty(String.prototype, "updir", {
value: function () {
if (this.lastIndexOf("/") === this.length - 1) return this.slice(0, this.length - 1);
var dir = (this + "/").dir();
return dir.dir();
},
});
Object.defineProperty(String.prototype, "trimchr", {
value: function (chars) {
return vector.trimchr(this, chars);
},
});
Object.defineProperty(String.prototype, "uc", {
value: function () {
return this.toUpperCase();
},
});
Object.defineProperty(String.prototype, "lc", {
value: function () {
return this.toLowerCase();
},
});
/* ARRAY DEFS */
Object.defineProperty(Array.prototype, "copy", {
value: function () {
var c = [];
this.forEach(function (x, i) {
c[i] = deep_copy(x);
});
return c;
},
});
Object.defineProperty(Array.prototype, "forFrom", {
value: function (n, fn) {
for (var i = n; i < this.length; i++) fn(this[i]);
},
});
Object.defineProperty(Array.prototype, "forTo", {
value: function (n, fn) {
for (var i = 0; i < n; i++) fn(this[i]);
},
});
Object.defineProperty(Array.prototype, "dofilter", {
value: function (fn) {
for (let i = 0; i < this.length; i++) {
if (!fn.call(this, this[i], i, this)) {
this.splice(i, 1);
i--;
}
}
return this;
},
});
Object.defineProperty(Array.prototype, "reversed", {
value: function () {
var c = this.slice();
return c.reverse();
},
});
Object.defineProperty(Array.prototype, "rotate", {
value: function (a) {
return Vector.rotate(this, a);
},
});
function make_swizz() {
function setelem(n) {
return {
get: function () {
return this[n];
},
set: function (x) {
this[n] = x;
},
};
}
function arrsetelem(str, n) {
Object.defineProperty(Array.prototype, str, setelem(n));
}
var arr_elems = ["x", "y", "z", "w"];
var quat_elems = ["i", "j", "k"];
var color_elems = ["r", "g", "b", "a"];
arr_elems.forEach(function (x, i) {
arrsetelem(x, i);
});
quat_elems.forEach(function (x, i) {
arrsetelem(x, i);
});
color_elems.forEach(function (x, i) {
arrsetelem(x, i);
});
var nums = [0, 1, 2, 3];
var swizz = [];
for (var i of nums) for (var j of nums) swizz.push([i, j]);
swizz.forEach(function (x) {
var str = "";
for (var i of x) str += arr_elems[i];
Object.defineProperty(Array.prototype, str, {
get() {
return [this[x[0]], this[x[1]]];
},
set(j) {
this[x[0]] = j[0];
this[x[1]] = j[1];
},
});
str = "";
for (var i of x) str += color_elems[i];
Object.defineProperty(Array.prototype, str, {
get() {
return [this[x[0]], this[x[1]]];
},
set(j) {
this[x[0]] = j[0];
this[x[1]] = j[1];
},
});
});
swizz = [];
for (var i of nums) for (var j of nums) for (var k of nums) swizz.push([i, j, k]);
swizz.forEach(function (x) {
var str = "";
for (var i of x) str += arr_elems[i];
Object.defineProperty(Array.prototype, str, {
get() {
return [this[x[0]], this[x[1]], this[x[2]]];
},
set(j) {
this[x[0]] = j[0];
this[x[1]] = j[1];
this[x[2]] = j[2];
},
});
str = "";
for (var i of x) str += color_elems[i];
Object.defineProperty(Array.prototype, str, {
get() {
return [this[x[0]], this[x[1]], this[x[2]]];
},
set(j) {
this[x[0]] = j[0];
this[x[1]] = j[1];
this[x[2]] = j[2];
},
});
});
swizz = [];
for (var i of nums) for (var j of nums) for (var k of nums) for (var w of nums) swizz.push([i, j, k, w]);
swizz.forEach(function (x) {
var str = "";
for (var i of x) str += arr_elems[i];
Object.defineProperty(Array.prototype, str, {
get() {
return [this[x[0]], this[x[1]], this[x[2]], this[x[3]]];
},
set(j) {
this[x[0]] = j[0];
this[x[1]] = j[1];
this[x[2]] = j[2];
this[x[3]] = j[3];
},
});
str = "";
for (var i of x) str += color_elems[i];
Object.defineProperty(Array.prototype, str, {
get() {
return [this[x[0]], this[x[1]], this[x[2]], this[x[3]]];
},
set(j) {
this[x[0]] = j[0];
this[x[1]] = j[1];
this[x[2]] = j[2];
this[x[3]] = j[3];
},
});
});
}
make_swizz();
Object.defineProperty(Array.prototype, "normalized", {
value: function () {
return vector.norm(this);
},
});
Object.defineProperty(Array.prototype, "newfirst", {
value: function (i) {
var c = this.slice();
if (i >= c.length) return c;
do {
c.push(c.shift());
i--;
} while (i > 0);
return c;
},
});
Object.defineProperty(Array.prototype, "doubleup", {
value: function (n) {
var c = [];
this.forEach(function (x) {
for (var i = 0; i < n; i++) c.push(x);
});
return c;
},
});
Object.defineProperty(Array.prototype, "mult", {
value: function (arr) {
var c = [];
for (var i = 0; i < this.length; i++) {
c[i] = this[i] * arr[i];
}
return c;
},
});
Object.defineProperty(Array.prototype, "apply", {
value: function (fn) {
this.forEach(function (x) {
x[fn].apply(x);
});
},
});
Object.defineProperty(Array.prototype, "sorted", {
value: function () {
return this.toSorted();
},
});
Object.defineProperty(Array.prototype, "equal", {
value: function (b) {
if (this.length !== b.length) return false;
if (b == null) return false;
if (this === b) return true;
return JSON.stringify(this.sorted()) === JSON.stringify(b.sorted());
for (var i = 0; i < this.length; i++) {
if (!this[i] === b[i]) return false;
}
return true;
},
});
Object.defineProperty(Array.prototype, "mapc", {
value: function (fn) {
return this.map(x => fn(x));
},
});
Object.defineProperty(Array.prototype, "mapvec", {
value: function (fn, b) {
return this.map((x, i) => fn(x, b[i]));
},
});
Object.defineProperty(Array.prototype, "remove", {
value: function (b) {
var idx = this.indexOf(b);
if (idx === -1) return false;
this.splice(idx, 1);
return true;
},
});
Object.defineProperty(Array.prototype, "set", {
value: function (b) {
if (this.length !== b.length) return;
b.forEach(function (val, i) {
this[i] = val;
}, this);
},
});
Object.defineProperty(Array.prototype, "flat", {
value: function () {
return [].concat.apply([], this);
},
});
/* Return true if array contains x */
Object.defineProperty(Array.prototype, "empty", {
get: function () {
return this.length === 0;
},
});
Object.defineProperty(Array.prototype, "push_unique", {
value: function (x) {
var inc = !this.includes(x);
if (inc) this.push(x);
return inc;
},
});
Object.defineProperty(Array.prototype, "unique", {
value: function () {
var c = [];
this.forEach(function (x) {
c.push_unique(x);
});
return c;
},
});
Object.defineProperty(Array.prototype, "unduped", {
value: function () {
return [...new Set(this)];
},
});
Object.defineProperty(Array.prototype, "findIndex", {
value: function (fn) {
var idx = -1;
this.every(function (x, i) {
if (fn(x)) {
idx = i;
return false;
}
return true;
});
return idx;
},
});
Object.defineProperty(Array.prototype, "find", {
value: function (fn) {
var ret;
this.every(function (x) {
if (fn(x)) {
ret = x;
return false;
}
return true;
});
return ret;
},
});
Object.defineProperty(Array.prototype, "search", {
value: function (val) {
for (var i = 0; i < this.length; i++) if (this[i] === val) return i;
return undefined;
},
});
Object.defineProperty(Array.prototype, "last", {
value: function () {
return this[this.length - 1];
},
});
Object.defineProperty(Array.prototype, "at", {
value: function (x) {
return x < 0 ? this[this.length + x] : this[x];
},
});
Object.defineProperty(Array.prototype, "wrapped", {
value: function (x) {
var c = this.slice(0, this.length);
for (var i = 0; i < x; i++) c.push(this[i]);
return c;
},
});
Object.defineProperty(Array.prototype, "wrap_idx", {
value: function (x) {
while (x >= this.length) {
x -= this.length;
}
return x;
},
});
Object.defineProperty(Array.prototype, "mirrored", {
value: function (x) {
var c = this.slice(0);
if (c.length <= 1) return c;
for (var i = c.length - 2; i >= 0; i--) c.push(c[i]);
return c;
},
});
Object.defineProperty(Array.prototype, "forEachRight", {
value: function(fn) {
for (var i = this.length-1; i >= 0; i--)
fn(this[i], i);
}
});
Math.lerp = vector.lerp;
Math.gcd = vector.gcd;
Math.lcm = vector.lcm;
Math.sum = vector.sum;
Math.mean = vector.mean;
Math.sigma = vector.sigma;
Math.median = vector.median;
vector.v2one = [1,1];
vector.v3one = [1,1,1];
vector.v2zero = [0,0];
vector.v3zero = [0,0,0];
Math.variance = function (series) {
var mean = Math.mean(series);
var vnce = 0;
for (var i = 0; i < series.length; i++) vnce += Math.pow(series[i] - mean, 2);
return vnce / series.length;
};
Math.ci = function (series) {
return (3 * Math.sigma(series)) / Math.sqrt(series.length);
};
Math.grab_from_points = function (pos, points, slop) {
var shortest = slop;
var idx = -1;
points.forEach(function (x, i) {
if (Vector.length(pos.sub(x)) < shortest) {
shortest = Vector.length(pos.sub(x));
idx = i;
}
});
return idx;
};
Math.nearest = function (n, incr) {
return Math.round(n / incr) * incr;
};
Math.places = function (n, digits) {
var div = Math.pow(10, digits);
return Math.round(n * div) / div;
};
Number.hex = function (n) {
var s = Math.floor(n).toString(16);
if (s.length === 1) s = "0" + s;
return s.uc();
};
Object.defineProperty(Object.prototype, "lerp", {
value: function (to, t) {
var self = this;
var obj = {};
Object.keys(self).forEach(function (key) {
obj[key] = self[key].lerp(to[key], t);
});
return obj;
},
});
/* MATH EXTENSIONS */
Object.defineProperty(Number.prototype, "lerp", {
value: function (to, t) {
return Math.lerp(this, to, t);
},
});
Object.defineProperty(Number.prototype, "clamp", {
value: function (from, to) {
return Math.clamp(this, from, to);
},
});
Math.clamp = vector.clamp;
Math.random_range = vector.random_range;
Math.rand_int = function (max = 9007199254740991) {
return Math.floor(Math.random() * max);
};
Math.snap = function (val, grid) {
if (!grid || grid === 1) return Math.round(val);
var rem = val % grid;
var d = val - rem;
var i = Math.round(rem / grid) * grid;
return d + i;
};
Math.angledist = vector.angledist;
Math.angledist.doc = "Find the shortest angle between two angles.";
Math.TAU = Math.PI * 2;
Math.deg2rad = function (deg) {
return deg * 0.0174533;
};
Math.rad2deg = function (rad) {
return rad / 0.0174533;
};
Math.turn2rad = function (x) {
return x * Math.TAU;
};
Math.rad2turn = function (x) {
return x / Math.TAU;
};
Math.turn2deg = function (x) {
return x * 360;
};
Math.deg2turn = function (x) {
return x / 360;
};
Math.randomint = function (max) {
return Math.clamp(Math.floor(Math.random() * max), 0, max - 1);
};
Math.variate = vector.variate;
/* BOUNDINGBOXES */
var bbox = {};
bbox.overlap = function (box1, box2) {
return box1.l > box2.l && box1.r < box2.r && box1.t < box2.t && box1.b > box2.b;
return box1.l > box2.r || box1.r < box2.l || box1.t > box2.b || box1.b < box2.t;
};
bbox.fromcwh = function (c, wh) {
return {
t: c.y + wh.y / 2,
b: c.y - wh.y / 2,
l: c.x - wh.x / 2,
r: c.x + wh.x / 2,
};
};
bbox.frompoints = function (points) {
var b = { t: 0, b: 0, l: 0, r: 0 };
points.forEach(function (x) {
if (x.y > b.t) b.t = x.y;
if (x.y < b.b) b.b = x.y;
if (x.x > b.r) b.r = x.x;
if (x.x < b.l) b.l = x.x;
});
return b;
};
bbox.topoints = function (bb) {
return [
[bb.l, bb.t],
[bb.r, bb.t],
[bb.r, bb.b],
[bb.l, bb.b],
];
};
bbox.tocwh = function (bb) {
if (!bb) return undefined;
var cwh = {};
var w = bb.r - bb.l;
var h = bb.t - bb.b;
cwh.wh = [w, h];
cwh.c = [bb.l + w / 2, bb.b + h / 2];
return cwh;
};
bbox.towh = function (bb) {
return [bb.r - bb.l, bb.t - bb.b];
};
bbox.pointin = function (bb, p) {
if (bb.t < p.y || bb.b > p.y || bb.l > p.x || bb.r < p.x) return false;
return true;
};
bbox.zero = function (bb) {
var newbb = Object.assign({}, bb);
newbb.r -= newbb.l;
newbb.t -= newbb.b;
newbb.b = 0;
newbb.l = 0;
return newbb;
};
bbox.move = function (bb, pos) {
var newbb = Object.assign({}, bb);
newbb.t += pos.y;
newbb.b += pos.y;
newbb.l += pos.x;
newbb.r += pos.x;
return newbb;
};
bbox.moveto = function (bb, pos) {
bb = bbox.zero(bb);
return bbox.move(bb, pos);
};
bbox.expand = function (oldbb, x) {
if (!oldbb || !x) return;
var bb = {};
Object.assign(bb, oldbb);
if (bb.t < x.t) bb.t = x.t;
if (bb.r < x.r) bb.r = x.r;
if (bb.b > x.b) bb.b = x.b;
if (bb.l > x.l) bb.l = x.l;
return bb;
};
bbox.blwh = function (bl, wh) {
return {
b: bl.y,
l: bl.x,
r: bl.x + wh.x,
t: bl.y + wh.y,
};
};
bbox.blwh.doc = "Bounding box from (bottom left, width height)";
bbox.fromobjs = function (objs) {
var bb = objs[0].boundingbox;
objs.forEach(function (obj) {
bb = bbox.expand(bb, obj.boundingbox);
});
return bb;
};
/* VECTORS */
var Vector = {};
Vector.length = vector.length;
Vector.norm = vector.norm;
Vector.project = vector.project;
Vector.dot = vector.dot;
Vector.random = function () {
var vec = [Math.random() - 0.5, Math.random() - 0.5];
return Vector.norm(vec);
};
Vector.angle_between = vector.angle_between;
Vector.rotate = vector.rotate;
vector.direction = function (from, to) {
return vector.norm(to.sub(from));
};
Vector.equal = function (v1, v2, tol) {
if (!tol) return v1.equal(v2);
var eql = true;
var c = v1.sub(v2);
c.forEach(function (x) {
if (!eql) return;
if (Math.abs(x) > tol) eql = false;
});
return eql;
};
Vector.reflect = function (vec, plane) {
var p = Vector.norm(plane);
return vec.sub(p.scale(2 * Vector.dot(vec, p)));
};
Vector.reflect_point = function (vec, point) {
return point.add(vec.sub(point).scale(-1));
};
/* POINT ASSISTANCE */
function points2cm(points) {
var x = 0;
var y = 0;
var n = points.length;
points.forEach(function (p) {
x = x + p[0];
y = y + p[1];
});
return [x / n, y / n];
}
Math.sortpointsccw = function (points) {
var cm = points2cm(points);
var cmpoints = points.map(function (x) {
return x.sub(cm);
});
var ccw = cmpoints.sort(function (a, b) {
var aatan = Math.atan2(a.y, a.x);
var batan = Math.atan2(b.y, b.x);
return aatan - batan;
});
return ccw.map(function (x) {
return x.add(cm);
});
};
var yaml = {};
yaml.tojson = function (yaml) {
// Replace key value pairs that are strings with quotation marks around them
yaml = yaml.replace(/(\w+):/g, '"$1":');
yaml = yaml.replace(/: ([\w\.\/]+)/g, ': "$1"'); // TODO: make this more general
yaml = yaml.split("\n");
var cont = {};
var cur = 0;
for (var i = 0; i < yaml.length; i++) {
var line = yaml[i];
var indent = line.search(/\S/);
if (indent > cur) {
if (line[indent] == "-") {
cont[indent] = "array";
yaml[i] = line.sub(indent, "[");
} else {
cont[indent] = "obj";
yaml[i] = line.sub(indent - 1, "{");
}
}
if (indent < cur) {
while (cur > indent) {
if (cont[cur] === "obj") yaml[i - 1] = yaml[i - 1] + "}";
else if (cont[cur] === "array") yaml[i - 1] = yaml[i - 1] + "]";
delete cont[cur];
cur--;
}
}
if (indent === cur) {
if (yaml[i][indent] === "-") yaml[i] = yaml[i].sub(indent, ",");
else yaml[i - 1] = yaml[i - 1] + ",";
}
cur = indent;
}
yaml = "{" + yaml.join("\n") + "}";
yaml = yaml.replace(/\s/g, "");
yaml = yaml.replace(/,}/g, "}");
yaml = yaml.replace(/,]/g, "]");
yaml = yaml.replace(/,"[^"]+"\:,/g, ",");
yaml = yaml.replace(/,"[^"]+"\:}/g, "}");
return yaml;
};
Math.sign = function (n) {
return n >= 0 ? 1 : -1;
};
var lodash = {};
lodash.get = function (obj, path, defValue) {
if (!path) return undefined;
// Check if path is string or array. Regex : ensure that we do not have '.' and brackets.
var pathArray = Array.isArray(path) ? path : path.match(/([^[.\]])+/g);
var result = pathArray.reduce((prevObj, key) => prevObj && prevObj[key], obj);
return result === undefined ? defValue : result;
};
return {
convert,
time,
Vector,
bbox,
yaml,
lodash,
};