scripts/path-controller/toolsys/toolprop.js
import * as util from '../util/util.js';
import {Vector2, Vector3, Vector4, Quat, Matrix4} from '../util/vectormath.js';
import {ToolPropertyIF, PropTypes, PropFlags} from "./toolprop_abstract.js";
import nstructjs from '../util/struct.js';
export {PropTypes, PropFlags} from './toolprop_abstract.js';
export const NumberConstraintsBase = new Set([
'range', 'expRate', 'step', 'uiRange', 'baseUnit', 'displayUnit', 'stepIsRelative',
'slideSpeed'
]);
export const IntegerConstraints = new Set([
'radix'
].concat(util.list(NumberConstraintsBase)));
export const FloatConstrinats = new Set([
'decimalPlaces'
].concat(util.list(NumberConstraintsBase)));
export const NumberConstraints = new Set(util.list(IntegerConstraints).concat(util.list(FloatConstrinats)));
export const PropSubTypes = {
COLOR: 1
};
let first = (iter) => {
if (iter === undefined) {
return undefined;
}
if (!(Symbol.iterator in iter)) {
for (let item in iter) {
return item;
}
return undefined;
}
for (let item of iter) {
return item;
}
};
//set PropTypes to custom type integers
export function setPropTypes(types) {
for (let k in types) {
PropTypes[k] = types[k];
}
}
export let customPropertyTypes = [];
export let PropClasses = {};
let customPropTypeBase = 17;
let wordmap = {
sel : "select",
unsel: "deselect",
eid : "id",
props: "properties",
res : "resource",
};
export var defaultRadix = 10;
export var defaultDecimalPlaces = 4;
class OnceTag {
constructor(cb) {
this.cb = cb;
}
}
export class ToolProperty extends ToolPropertyIF {
constructor(type, subtype, apiname, uiname = "", description = "", flag = 0, icon = -1) {
super();
this.data = undefined;
if (type === undefined) {
type = this.constructor.PROP_TYPE_ID;
}
this.type = type;
this.subtype = subtype;
//is false if this property still has its default value,
//i.e. it hasn't been set by the user or anyone else
this.wasSet = false;
this.apiname = apiname;
this.uiname = uiname !== undefined ? uiname : apiname;
this.description = description;
this.flag = flag;
this.icon = icon;
this.icon2 = icon; //another icon, e.g. unchecked state
//remember to update NumberConstraintsBase et al when adding new number
//constraints
this.decimalPlaces = defaultDecimalPlaces;
this.radix = defaultRadix;
this.step = 0.05;
this.callbacks = {};
}
static internalRegister(cls) {
PropClasses[new cls().type] = cls;
}
static getClass(type) {
return PropClasses[type];
}
static setDefaultRadix(n) {
defaultRadix = n;
}
static setDefaultDecimalPlaces(n) {
defaultDecimalPlaces = n;
}
static makeUIName(name) {
let parts = [""];
let lastc = undefined;
let ischar = (c) => {
c = c.charCodeAt(0);
let upper = c >= "A".charCodeAt(0);
upper = upper && c <= "Z".charCodeAt(0);
let lower = c >= "a".charCodeAt(0);
lower = lower && c <= "z".charCodeAt(0);
return upper || lower;
}
for (let i = 0; i < name.length; i++) {
let c = name[i];
if (c === '_' || c === '-' || c === '$') {
lastc = c;
c = ' ';
parts.push('');
continue;
}
if (i > 0 && c === c.toUpperCase() && lastc !== lastc.toUpperCase()) {
if (ischar(c) && ischar(lastc)) {
parts.push('');
}
}
parts[parts.length - 1] += c;
lastc = c;
}
let subst = (word) => {
if (word in wordmap) {
return wordmap[word];
} else {
return word;
}
}
parts = parts
.filter(f => f.trim().length > 0)
.map(f => subst(f))
.map(f => f[0].toUpperCase() + f.slice(1, f.length).toLowerCase())
.join(" ").trim();
return parts;
}
static register(cls) {
cls.PROP_TYPE_ID = (1<<customPropTypeBase);
PropTypes[cls.name] = cls.PROP_TYPE_ID;
customPropTypeBase++;
customPropertyTypes.push(cls);
PropClasses[new cls().type] = cls;
return cls.PROP_TYPE_ID;
}
static calcRelativeStep(step, value, logBase = 1.5) {
value = Math.log(Math.abs(value) + 1.0)/Math.log(logBase);
value = Math.max(value, step);
this.report(util.termColor("STEP", "red"), value);
return value;
}
setDescription(s) {
this.description = s;
return this;
}
setUIName(s) {
this.uiname = s;
return this;
}
calcMemSize() {
function strlen(s) {
//length of string plus an assumed member pointer
return s !== undefined ? s.length + 8 : 8;
}
let tot = 0;
tot += strlen(this.apiname) + strlen(this.uiname);
tot += strlen(this.description);
tot += 11*8; //assumed member pointers
for (let k in this.callbacks) {
tot += 24;
}
return tot;
}
equals(b) {
throw new Error("implement me");
}
private() {
this.flag |= PropFlags.PRIVATE;
return this;
}
saveLastValue() {
this.flag |= PropFlags.SAVE_LAST_VALUE;
return this;
}
report() {
console.warn(...arguments);
}
_fire(type, arg1, arg2) {
if (this.callbacks[type] === undefined) {
return;
}
let stack = this.callbacks[type];
stack = stack.concat([]); //copy
for (let i = 0; i < stack.length; i++) {
let cb = stack[i];
if (cb instanceof OnceTag) {
let j = i;
//remove callback;
while (j < stack.length - 1) {
stack[j] = stack[j + 1];
j++;
}
stack[j] = undefined;
stack.length--;
i--;
cb.cb.call(this, arg1, arg2);
} else {
cb.call(this, arg1, arg2);
}
}
return this;
}
clearEventCallbacks() {
this.callbacks = {}
return this;
}
once(type, cb) {
if (this.callbacks[type] === undefined) {
this.callbacks[type] = [];
}
//check if cb is already in callback list inside a OnceTag
for (let cb2 of this.callbacks[type]) {
if (cb2 instanceof OnceTag && cb2.cb === cb) {
return;
}
}
cb = new OnceTag(cb);
this.callbacks[type].push(cb);
return this;
}
on(type, cb) {
if (this.callbacks[type] === undefined) {
this.callbacks[type] = [];
}
this.callbacks[type].push(cb);
return this;
}
off(type, cb) {
this.callbacks[type].remove(cb);
return this;
}
toJSON() {
return {
type : this.type,
subtype : this.subtype,
apiname : this.apiname,
uiname : this.uiname,
description: this.description,
flag : this.flag,
icon : this.icon,
data : this.data,
range : this.range,
uiRange : this.uiRange,
step : this.step
};
}
loadJSON(obj) {
this.type = obj.type;
this.subtype = obj.subtype;
this.apiname = obj.apiname;
this.uiname = obj.uiname;
this.description = obj.description;
this.flag = obj.flag;
this.icon = obj.icon;
this.data = obj.data;
return this;
}
getValue() {
return this.data;
}
setValue(val) {
if (this.constructor === ToolProperty) {
throw new Error("implement me!");
}
this.wasSet = true;
this._fire("change", val);
}
copyTo(b) {
b.apiname = this.apiname;
b.uiname = this.uiname;
b.description = this.description;
b.icon = this.icon;
b.icon2 = this.icon2;
b.baseUnit = this.baseUnit;
b.subtype = this.subtype;
b.displayUnit = this.displayUnit;
b.flag = this.flag;
for (let k in this.callbacks) {
b.callbacks[k] = this.callbacks[k];
}
}
copy() { //default copy method
let ret = new this.constructor();
this.copyTo(ret);
return ret;
}
setStep(step) {
this.step = step;
return this;
}
getStep(value = 1.0) {
if (this.stepIsRelative) {
return ToolProperty.calcRelativeStep(this.step, value);
} else {
return this.step;
}
}
setRelativeStep(step) {
this.step = step;
this.stepIsRelative = true;
}
setRange(min, max) {
if (min === undefined || max === undefined) {
throw new Error("min and/or max cannot be undefined");
}
this.range = [min, max];
return this;
}
noUnits() {
this.baseUnit = this.displayUnit = "none";
return this;
}
setBaseUnit(unit) {
this.baseUnit = unit;
return this;
}
setDisplayUnit(unit) {
this.displayUnit = unit;
return this;
}
setFlag(f, combine = false) {
this.flag = combine ? this.flag | f : f;
return this;
}
setUIRange(min, max) {
if (min === undefined || max === undefined) {
throw new Error("min and/or max cannot be undefined");
}
this.uiRange = [min, max];
return this;
}
setIcon(icon) {
this.icon = icon;
return this;
}
setIcon2(icon) {
this.icon2 = icon;
return this;
}
loadSTRUCT(reader) {
reader(this);
}
}
ToolProperty.STRUCT = `
ToolProperty {
type : int;
flag : int;
subtype : int;
icon : int;
icon2 : int;
baseUnit : string | ""+this.baseUnit;
displayUnit : string | ""+this.displayUnit;
range : array(float) | this.range ? this.range : [-1e17, 1e17];
uiRange : array(float) | this.range ? this.range : [-1e17, 1e17];
description : string;
stepIsRelative : bool;
step : float;
expRate : float;
radix : float;
decimalPlaces : int;
}
`;
nstructjs.register(ToolProperty);
window.ToolProperty = ToolProperty;
export class FloatArrayProperty extends ToolProperty {
constructor(value, apiname, uiname, description, flag, icon) {
super(PropTypes.FLOAT_ARRAY, undefined, apiname, uiname, description, flag, icon);
this.value = [];
if (value !== undefined) {
this.setValue(value);
}
}
[Symbol.iterator]() {
return this.value[Symbol.iterator]();
}
setValue(value) {
super.setValue();
if (value === undefined) {
throw new Error("value was undefined in FloatArrayProperty's setValue method");
}
this.value.length = 0;
for (let item of value) {
if (typeof item !== "number" && typeof item !== "boolean") {
console.log(value);
throw new Error("bad item for FloatArrayProperty " + item);
}
this.value.push(item);
}
}
push(item) {
if (typeof item !== "number" && typeof item !== "boolean") {
console.log(value);
throw new Error("bad item for FloatArrayProperty " + item);
}
this.value.push(item);
}
getValue() {
return this.value;
}
clear() {
this.value.length = 0;
return this;
}
}
FloatArrayProperty.STRUCT = nstructjs.inherit(FloatArrayProperty, ToolProperty) + `
value : array(float);
}`;
nstructjs.register(FloatArrayProperty);
export class StringProperty extends ToolProperty {
constructor(value, apiname, uiname, description, flag, icon) {
super(PropTypes.STRING, undefined, apiname, uiname, description, flag, icon);
this.multiLine = false;
if (value) {
this.setValue(value);
} else {
this.setValue("");
}
this.wasSet = false;
}
calcMemSize() {
return super.calcMemSize() + (this.data !== undefined ? this.data.length*4 : 0) + 8;
}
equals(b) {
return this.data === b.data;
}
copyTo(b) {
super.copyTo(b);
b.data = this.data;
b.multiLine = this.multiLine;
return this;
}
getValue() {
return this.data;
}
setValue(val) {
//fire events
super.setValue(val);
this.data = val;
}
}
StringProperty.STRUCT = nstructjs.inherit(StringProperty, ToolProperty) + `
data : string;
}
`;
nstructjs.register(StringProperty);
ToolProperty.internalRegister(StringProperty);
/*
let num_res = [
/([0-9]+)/,
/((0x)?[0-9a-fA-F]+(h?))/,
/([0-9]+\.[0-9]*)/,
/([0-9]*\.[0-9]+)/,
/(\.[0-9]+)/
];
//num_re = /([0-9]+\.[0-9]*)|([0-9]*\.[0-9]+)/
*/
export {isNumber} from '../../core/units.js';
/*
export function isNumber(f) {
if (f === "NaN" || (typeof f == "number" && isNaN(f))) {
return false;
}
f = ("" + f).trim();
let ok = false;
for (let re of num_res) {
let ret = f.match(re)
if (!ret) {
ok = false;
continue;
}
ok = ret[0].length === f.length;
if (ok) {
break;
}
}
return ok;
}*/
//window.isNumber = isNumber;
export class NumProperty extends ToolProperty {
constructor(type, value, apiname,
uiname, description, flag, icon) {
super(type, undefined, apiname, uiname, description, flag, icon);
this.data = 0;
this.range = [0, 0];
}
equals(b) {
return this.data == b.data;
}
loadSTRUCT(reader) {
reader(this);
super.loadSTRUCT(reader);
}
};
NumProperty.STRUCT = nstructjs.inherit(NumProperty, ToolProperty) + `
range : array(float);
data : float;
}
`;
export class _NumberPropertyBase extends ToolProperty {
constructor(type, value, apiname,
uiname, description, flag, icon) {
super(type, null, apiname, uiname, description, flag, icon);
this.data = 0.0;
//remember to update NumberConstraintsBase et al when adding new number
//constraints
/** controls roller slider rate */
this.slideSpeed = 1.0;
/** exponential rate, used by roller sliders */
this.expRate = 1.33;
this.step = 0.1;
this.stepIsRelative = false;
this.range = [-1e17, 1e17];
/** if undefined this.range will be used */
this.uiRange = undefined;
if (value !== undefined && value !== null) {
this.setValue(value);
this.wasSet = false;
}
}
get ui_range() {
this.report("NumberProperty.ui_range is deprecated");
return this.uiRange;
}
set ui_range(val) {
this.report("NumberProperty.ui_range is deprecated");
this.uiRange = val;
}
calcMemSize() {
return super.calcMemSize() + 8*8;
}
equals(b) {
return this.data === b.data;
}
toJSON() {
let json = super.toJSON();
json.data = this.data;
json.expRate = this.expRate;
return json;
}
loadJSON(obj) {
super.loadJSON(obj);
this.data = obj.data || this.data;
this.expRate = obj.expRate || this.expRate;
return this;
}
copyTo(b) {
super.copyTo(b);
b.displayUnit = this.displayUnit;
b.baseUnit = this.baseUnit;
b.expRate = this.expRate;
b.step = this.step;
b.range = this.range ? [this.range[0], this.range[1]] : undefined;
b.uiRange = this.uiRange ? [this.uiRange[0], this.uiRange[1]] : undefined;
b.slideSpeed = this.slideSpeed;
b.data = this.data;
}
setSlideSpeed(f) {
this.slideSpeed = f;
return this;
}
/*
* non-linear exponent for number sliders
* in roll mode
* */
setExpRate(exp) {
this.expRate = exp;
}
setValue(val) {
if (val === undefined || val === null) {
return;
}
if (typeof val !== "number") {
throw new Error("Invalid number " + val);
}
this.data = val;
super.setValue(val);
return this;
}
loadJSON(obj) {
super.loadJSON(obj);
let get = (key) => {
if (key in obj) {
this[key] = obj[key];
}
};
get("range");
get("step");
get("expRate");
get("ui_range");
return this;
}
};
_NumberPropertyBase.STRUCT = nstructjs.inherit(_NumberPropertyBase, ToolProperty) + `
range : array(float);
expRate : float;
data : float;
step : float;
slideSpeed : float;
}
`;
nstructjs.register(_NumberPropertyBase);
export class IntProperty extends _NumberPropertyBase {
constructor(value, apiname,
uiname, description, flag, icon) {
super(PropTypes.INT, value, apiname, uiname, description, flag, icon);
//remember to update NumberConstraintsBase et al when adding new number
//constraints
this.radix = 10;
}
setValue(val) {
super.setValue(Math.floor(val));
return this;
}
setRadix(radix) {
this.radix = radix;
}
toJSON() {
let json = super.toJSON();
json.data = this.data;
json.radix = this.radix;
return json;
}
loadJSON(obj) {
super.loadJSON(obj);
this.data = obj.data || this.data;
this.radix = obj.radix || this.radix;
return this;
}
loadSTRUCT(reader) {
reader(this);
super.loadSTRUCT(reader);
}
}
IntProperty.STRUCT = nstructjs.inherit(IntProperty, _NumberPropertyBase) + `
data : int;
}`;
nstructjs.register(IntProperty);
ToolProperty.internalRegister(IntProperty);
export class ReportProperty extends StringProperty {
constructor(value, apiname, uiname, description, flag, icon) {
super(value, apiname, uiname, description, flag, icon);
this.type = PropTypes.REPORT;
}
}
ReportProperty.STRUCT = nstructjs.inherit(ReportProperty, StringProperty) + `
}
`;
nstructjs.register(ReportProperty);
ToolProperty.internalRegister(ReportProperty);
export class BoolProperty extends ToolProperty {
constructor(value, apiname,
uiname, description, flag, icon) {
super(PropTypes.BOOL, undefined, apiname, uiname, description, flag, icon);
this.data = !!value;
}
equals(b) {
return this.data == b.data;
}
copyTo(b) {
super.copyTo(b);
b.data = this.data;
return this;
}
setValue(val) {
this.data = !!val;
super.setValue(val);
return this;
}
getValue() {
return this.data;
}
toJSON() {
let ret = super.toJSON();
return ret;
}
loadJSON(obj) {
super.loadJSON(obj);
return this;
}
}
ToolProperty.internalRegister(BoolProperty);
BoolProperty.STRUCT = nstructjs.inherit(BoolProperty, ToolProperty) + `
data : bool;
}
`;
nstructjs.register(BoolProperty);
export class FloatProperty extends _NumberPropertyBase {
constructor(value, apiname,
uiname, description, flag, icon) {
super(PropTypes.FLOAT, value, apiname, uiname, description, flag, icon);
//remember to update NumberConstraintsBase et al when adding new number
//constraints
this.decimalPlaces = 4;
}
setDecimalPlaces(n) {
this.decimalPlaces = n;
return this;
}
copyTo(b) {
super.copyTo(b);
b.data = this.data;
return this;
}
setValue(val) {
this.data = val;
//fire events
super.setValue(val);
return this;
}
toJSON() {
let json = super.toJSON();
json.data = this.data;
json.decimalPlaces = this.decimalPlaces;
return json;
}
loadJSON(obj) {
super.loadJSON(obj);
this.data = obj.data || this.data;
this.decimalPlaces = obj.decimalPlaces || this.decimalPlaces;
return this;
}
loadSTRUCT(reader) {
reader(this);
super.loadSTRUCT(reader);
}
}
ToolProperty.internalRegister(FloatProperty);
FloatProperty.STRUCT = nstructjs.inherit(FloatProperty, _NumberPropertyBase) + `
decimalPlaces : int;
data : float;
}
`;
nstructjs.register(FloatProperty);
export class EnumKeyPair {
constructor(key, val) {
this.key = "" + key;
this.val = "" + val;
this.key_is_int = typeof key === "number" || typeof key === "boolean";
this.val_is_int = typeof val === "number" || typeof val === "boolean";
}
loadSTRUCT(reader) {
reader(this);
if (this.val_is_int) {
this.val = parseInt(this.val);
}
if (this.key_is_int) {
this.key = parseInt(this.key);
}
}
}
EnumKeyPair.STRUCT = `
EnumKeyPair {
key : string;
val : string;
key_is_int : bool;
val_is_int : bool;
}
`
nstructjs.register(EnumKeyPair);
export class EnumProperty extends ToolProperty {
constructor(string_or_int, valid_values, apiname,
uiname, description, flag, icon) {
super(PropTypes.ENUM, undefined, apiname, uiname, description, flag, icon);
this.values = {};
this.keys = {};
this.ui_value_names = {};
this.descriptions = {};
if (valid_values === undefined) return this;
if (valid_values instanceof Array || valid_values instanceof String) {
for (var i = 0; i < valid_values.length; i++) {
this.values[valid_values[i]] = valid_values[i];
this.keys[valid_values[i]] = valid_values[i];
}
} else {
for (var k in valid_values) {
this.values[k] = valid_values[k];
this.keys[valid_values[k]] = k;
}
}
if (string_or_int === undefined) {
this.data = first(valid_values);
} else {
this.setValue(string_or_int);
}
for (var k in this.values) {
let uin = k.replace(/[_-]/g, " ").trim();
uin = uin.split(" ")
let uiname = ToolProperty.makeUIName(k);
this.ui_value_names[k] = uiname;
this.descriptions[k] = uiname;
}
this.iconmap = {};
this.iconmap2 = {};
this.wasSet = false;
}
calcHash(digest = new util.HashDigest()) {
for (let key in this.keys) {
digest.add(key);
digest.add(this.keys[key]);
}
return digest.get();
}
updateDefinition(enumdef_or_prop) {
let descriptions = this.descriptions;
let ui_value_names = this.ui_value_names;
this.values = {};
this.keys = {};
this.ui_value_names = {};
this.descriptions = {};
let enumdef;
if (enumdef_or_prop instanceof EnumProperty) {
enumdef = enumdef_or_prop.values;
} else {
enumdef = enumdef_or_prop;
}
for (let k in enumdef) {
let v = enumdef[k];
this.values[k] = v;
this.keys[v] = k;
}
if (enumdef_or_prop instanceof EnumProperty) {
let prop = enumdef_or_prop;
this.iconmap = Object.assign({}, prop.iconmap);
this.iconmap2 = Object.assign({}, prop.iconmap2);
this.ui_value_names = Object.assign({}, prop.ui_value_names);
this.descriptions = Object.assign({}, prop.descriptions);
} else {
for (let k in this.values) {
if (k in ui_value_names) {
this.ui_value_names[k] = ui_value_names[k];
} else {
this.ui_value_names[k] = ToolProperty.makeUIName(k);
}
if (k in descriptions) {
this.descriptions[k] = descriptions[k];
} else {
this.descriptions[k] = ToolProperty.makeUIName(k);
}
}
}
this._fire('metaChange', this);
return this;
}
calcMemSize() {
let tot = super.calcMemSize();
for (let k in this.values) {
tot += (k.length*4 + 16)*4;
}
if (this.descriptions) {
for (let k in this.descriptions) {
tot += (k.length + this.descriptions[k].length)*4;
}
}
return tot + 64;
}
equals(b) {
return this.getValue() === b.getValue();
}
addUINames(map) {
for (let k in map) {
this.ui_value_names[k] = map[k];
}
return this;
}
addDescriptions(map) {
for (let k in map) {
this.descriptions[k] = map[k];
}
return this;
}
addIcons2(iconmap2) {
if (this.iconmap2 === undefined) {
this.iconmap2 = {};
}
for (let k in iconmap2) {
this.iconmap2[k] = iconmap2[k];
}
return this;
}
addIcons(iconmap) {
if (this.iconmap === undefined) {
this.iconmap = {};
}
for (let k in iconmap) {
this.iconmap[k] = iconmap[k];
}
return this;
}
copyTo(p) {
super.copyTo(p);
p.data = this.data;
p.keys = Object.assign({}, this.keys);
p.values = Object.assign({}, this.values);
p.ui_value_names = this.ui_value_names;
p.update = this.update;
p.api_update = this.api_update;
p.iconmap = this.iconmap;
p.iconmap2 = this.iconmap2;
p.descriptions = this.descriptions;
return p;
}
copy() {
var p = new this.constructor("dummy", {"dummy": 0}, this.apiname, this.uiname, this.description, this.flag)
this.copyTo(p);
return p;
}
getValue() {
if (this.data in this.values)
return this.values[this.data];
else
return this.data;
}
setValue(val) {
if (!(val in this.values) && (val in this.keys))
val = this.keys[val];
if (!(val in this.values)) {
this.report("Invalid value for enum!", val, this.values);
return;
}
this.data = val;
//fire events
super.setValue(val);
return this;
}
_loadMap(obj) {
if (!obj) {
return {};
}
let ret = {};
for (let k of obj) {
ret[k.key] = k.val;
}
return ret;
}
_saveMap(obj) {
obj = obj === undefined ? {} : obj;
let ret = [];
for (let k in obj) {
ret.push(new EnumKeyPair(k, obj[k]));
}
return ret;
}
loadSTRUCT(reader) {
reader(this);
super.loadSTRUCT(reader);
this.keys = this._loadMap(this._keys);
this.values = this._loadMap(this._values);
this.ui_value_names = this._loadMap(this._ui_value_names);
this.iconmap = this._loadMap(this._iconmap);
this.iconmap2 = this._loadMap(this._iconmap2);
this.descriptions = this._loadMap(this._descriptions);
if (this.data_is_int) {
this.data = parseInt(this.data);
delete this.data_is_int;
} else if (this.data in this.keys) {
this.data = this.keys[this.data];
}
}
_is_data_int() {
return typeof this.data === "number";
}
}
ToolProperty.internalRegister(EnumProperty);
EnumProperty.STRUCT = nstructjs.inherit(EnumProperty, ToolProperty) + `
data : string | ""+this.data;
data_is_int : bool | this._is_data_int();
_keys : array(EnumKeyPair) | this._saveMap(this.keys) ;
_values : array(EnumKeyPair) | this._saveMap(this.values) ;
_ui_value_names : array(EnumKeyPair) | this._saveMap(this.ui_value_names) ;
_iconmap : array(EnumKeyPair) | this._saveMap(this.iconmap) ;
_iconmap2 : array(EnumKeyPair) | this._saveMap(this.iconmap2) ;
_descriptions : array(EnumKeyPair) | this._saveMap(this.descriptions) ;
}
`;
nstructjs.register(EnumProperty);
export class FlagProperty extends EnumProperty {
constructor(string, valid_values, apiname,
uiname, description, flag, icon) {
super(string, valid_values, apiname,
uiname, description, flag, icon);
this.type = PropTypes.FLAG;
this.wasSet = false;
}
setValue(bitmask) {
this.data = bitmask;
//do not trigger EnumProperty's setValue
ToolProperty.prototype.setValue.call(this, bitmask);
return this;
}
}
ToolProperty.internalRegister(FlagProperty);
FlagProperty.STRUCT = nstructjs.inherit(FlagProperty, EnumProperty) + `
}
`;
nstructjs.register(FlagProperty);
export class VecPropertyBase extends FloatProperty {
constructor(data, apiname, uiname, description) {
super(undefined, apiname, uiname, description);
this.hasUniformSlider = false;
}
calcMemSize() {
return super.calcMemSize() + this.data.length*8;
}
equals(b) {
return this.data.vectorDistance(b.data) < 0.00001;
}
uniformSlider(state = true) {
this.hasUniformSlider = state;
return this;
}
copyTo(b) {
super.copyTo(b);
b.hasUniformSlider = this.hasUniformSlider;
}
}
VecPropertyBase.STRUCT = nstructjs.inherit(VecPropertyBase, FloatProperty) + `
hasUniformSlider : bool;
}
`;
export class Vec2Property extends FloatProperty {
constructor(data, apiname, uiname, description) {
super(undefined, apiname, uiname, description);
this.type = PropTypes.VEC2;
this.data = new Vector2(data);
}
setValue(v) {
this.data.load(v);
//do not trigger parent classes's setValue
ToolProperty.prototype.setValue.call(this, v);
return this;
}
getValue() {
return this.data;
}
copyTo(b) {
let data = b.data;
super.copyTo(b);
b.data = data;
b.data.load(this.data);
}
}
Vec2Property.STRUCT = nstructjs.inherit(Vec2Property, VecPropertyBase) + `
data : vec2;
}
`;
nstructjs.register(Vec2Property);
ToolProperty.internalRegister(Vec2Property);
export class Vec3Property extends VecPropertyBase {
constructor(data, apiname, uiname, description) {
super(undefined, apiname, uiname, description);
this.type = PropTypes.VEC3;
this.data = new Vector3(data);
}
isColor() {
this.subtype = PropSubTypes.COLOR;
return this;
}
setValue(v) {
this.data.load(v);
//do not trigger parent classes's setValue
ToolProperty.prototype.setValue.call(this, v);
return this;
}
getValue() {
return this.data;
}
copyTo(b) {
let data = b.data;
super.copyTo(b);
b.data = data;
b.data.load(this.data);
}
}
Vec3Property.STRUCT = nstructjs.inherit(Vec3Property, VecPropertyBase) + `
data : vec3;
}
`;
nstructjs.register(Vec3Property);
ToolProperty.internalRegister(Vec3Property);
export class Vec4Property extends FloatProperty {
constructor(data, apiname, uiname, description) {
super(undefined, apiname, uiname, description);
this.type = PropTypes.VEC4;
this.data = new Vector4(data);
}
setValue(v, w = 1.0) {
this.data.load(v);
//do not trigger parent classes's setValue
ToolProperty.prototype.setValue.call(this, v);
if (v.length < 3) {
this.data[2] = 0.0;
}
if (v.length < 4) {
this.data[3] = w;
}
return this;
}
isColor() {
this.subtype = PropSubTypes.COLOR;
return this;
}
getValue() {
return this.data;
}
copyTo(b) {
let data = b.data;
super.copyTo(b);
b.data = data;
b.data.load(this.data);
}
}
Vec4Property.STRUCT = nstructjs.inherit(Vec4Property, VecPropertyBase) + `
data : vec4;
}
`;
nstructjs.register(Vec4Property);
ToolProperty.internalRegister(Vec4Property);
export class QuatProperty extends ToolProperty {
constructor(data, apiname, uiname, description) {
super(PropTypes.QUAT, undefined, apiname, uiname, description);
this.data = new Quat(data);
}
equals(b) {
return this.data.vectorDistance(b.data) < 0.00001;
}
setValue(v) {
this.data.load(v);
super.setValue(v);
return this;
}
getValue() {
return this.data;
}
copyTo(b) {
let data = b.data;
super.copyTo(b);
b.data = data;
b.data.load(this.data);
}
}
QuatProperty.STRUCT = nstructjs.inherit(QuatProperty, VecPropertyBase) + `
data : vec4;
}
`;
nstructjs.register(QuatProperty);
ToolProperty.internalRegister(QuatProperty);
export class Mat4Property extends ToolProperty {
constructor(data, apiname, uiname, description) {
super(PropTypes.MATRIX4, undefined, apiname, uiname, description);
this.data = new Matrix4(data);
}
calcMemSize() {
return super.calcMemSize() + 16*8 + 32;
}
equals(b) {
let m1 = this.data.$matrix;
let m2 = b.data.$matrix;
for (let i = 1; i <= 4; i++) {
for (let j = 1; j <= 4; j++) {
let key = `m${i}${j}`;
if (Math.abs(m1[key] - m2[key]) > 0.00001) {
return false;
}
}
}
return true;
}
setValue(v) {
this.data.load(v);
super.setValue(v);
return this;
}
getValue() {
return this.data;
}
copyTo(b) {
let data = b.data;
super.copyTo(b);
b.data = data;
b.data.load(this.data);
}
loadSTRUCT(reader) {
reader(this);
super.loadSTRUCT(reader);
}
}
Mat4Property.STRUCT = nstructjs.inherit(Mat4Property, FloatProperty) + `
data : mat4;
}
`;
nstructjs.register(Mat4Property);
ToolProperty.internalRegister(Mat4Property);
/**
* List of other tool props (all of one type)
*/
export class ListProperty extends ToolProperty {
/*
* Prop must be a ToolProperty subclass instance
* */
constructor(prop, list = [], uiname = "") {
super(PropTypes.PROPLIST);
this.uiname = uiname;
if (typeof prop == "number") {
prop = PropClasses[prop];
if (prop !== undefined) {
prop = new prop();
}
} else if (prop !== undefined) {
if (prop instanceof ToolProperty) {
prop = prop.copy();
} else {
prop = new prop();
}
}
this.prop = prop;
this.value = [];
if (list) {
for (let val of list) {
this.push(val);
}
}
this.wasSet = false;
}
get length() {
return this.value.length;
}
set length(val) {
this.value.length = val;
}
calcMemSize() {
let tot = super.calcMemSize();
let psize = this.prop ? this.prop.calcMemSize() + 8 : 8;
if (!this.prop && this.value.length > 0) {
psize = this.value[0].calcMemSize();
}
tot += psize*this.value.length + 8;
tot += 16;
return tot;
}
equals(b) {
let l1 = this.value ? this.value.length : 0;
let l2 = b.value ? b.value.length : 0;
if (l1 !== l2) {
return false;
}
for (let i = 0; i < l1; i++) {
let prop1 = this.value[i];
let prop2 = b.value[i];
let bad = prop1.constructor !== prop2.constructor;
bad = bad || !prop1.equals(prop2);
if (bad) {
return false;
}
}
return true;
}
copyTo(b) {
super.copyTo(b);
b.prop = this.prop.copy();
for (let prop of this.value) {
b.value.push(prop.copy());
}
return b;
}
copy() {
return this.copyTo(new ListProperty(this.prop.copy()));
}
push(item = undefined) {
if (item === undefined) {
item = this.prop.copy();
}
if (!(item instanceof ToolProperty)) {
let prop = this.prop.copy();
prop.setValue(item);
item = prop;
}
this.value.push(item);
return item;
}
clear() {
this.value.length = 0;
}
getListItem(i) {
return this.value[i].getValue();
}
setListItem(i, val) {
this.value[i].setValue(val);
}
setValue(value) {
this.clear();
for (let item of value) {
let prop = this.push();
if (typeof item !== "object") {
prop.setValue(item);
} else if (item instanceof prop.constructor) {
item.copyTo(prop);
} else {
this.report(item);
throw new Error("invalid value " + item);
}
}
super.setValue(value);
return this;
}
getValue() {
return this.value;
}
[Symbol.iterator]() {
let list = this.value;
return (function* () {
for (let item of list) {
yield item.getValue();
}
})();
}
}
ListProperty.STRUCT = nstructjs.inherit(ListProperty, ToolProperty) + `
prop : abstract(ToolProperty);
value : array(abstract(ToolProperty));
}`;
nstructjs.register(ListProperty);
ToolProperty.internalRegister(ListProperty);
//like FlagsProperty but uses strings
export class StringSetProperty extends ToolProperty {
constructor(value = undefined, definition = []) {
super(PropTypes.STRSET);
let values = [];
this.value = new util.set();
let def = definition;
if (Array.isArray(def) || def instanceof util.set || def instanceof Set) {
for (let item of def) {
values.push(item);
}
} else if (typeof def === "object") {
for (let k in def) {
values.push(k);
}
} else if (typeof def === "string") {
values.push(def);
}
this.values = {};
this.ui_value_names = {};
this.descriptions = {};
this.iconmap = {};
this.iconmap2 = {};
for (let v of values) {
this.values[v] = v;
let uiname = ToolProperty.makeUIName(v);
this.ui_value_names[v] = uiname;
}
if (value !== undefined) {
this.setValue(value);
}
this.wasSet = false;
}
calcMemSize() {
let tot = super.calcMemSize();
for (let k in this.values) {
tot += (k.length + 16)*5;
}
if (this.descriptions) {
for (let k in this.descriptions) {
tot += (k.length + this.descriptions[k].length + 8)*4;
}
}
return tot + 64;
}
equals(b) {
return this.value.equals(b.value);
}
/*
* Values can be a string, undefined/null, or a list/set/object-literal of strings.
* If destructive is true, then existing set will be cleared.
* */
setValue(values, destructive = true, soft_fail = true) {
let bad = typeof values !== "string";
bad = bad && typeof values !== "object";
bad = bad && values !== undefined && values !== null;
if (bad) {
if (soft_fail) {
this.report("Invalid argument to StringSetProperty.prototype.setValue() " + values);
return;
} else {
throw new Error("Invalid argument to StringSetProperty.prototype.setValue() " + values);
}
}
//handle undefined/null
if (!values) {
this.value.clear();
} else if (typeof values === "string") {
if (destructive)
this.value.clear();
if (!(values in this.values)) {
if (soft_fail) {
this.report(`"${values}" is not in this StringSetProperty`);
return;
} else {
throw new Error(`"${values}" is not in this StringSetProperty`);
}
}
this.value.add(values);
} else {
let data = [];
if (Array.isArray(values) || values instanceof util.set || values instanceof Set) {
for (let item of values) {
data.push(item);
}
} else { //object literal?
for (let k in values) {
data.push(k);
}
}
for (let item of data) {
if (!(item in this.values)) {
if (soft_fail) {
this.report(`"${item}" is not in this StringSetProperty`);
continue;
} else {
throw new Error(`"${item}" is not in this StringSetProperty`);
}
}
this.value.add(item);
}
}
super.setValue();
return this;
}
getValue() {
return this.value;
}
addIcons2(iconmap2) {
if (iconmap2 === undefined)
return;
for (let k in iconmap2) {
this.iconmap2[k] = iconmap2[k];
}
return this;
}
addIcons(iconmap) {
if (iconmap === undefined)
return;
for (let k in iconmap) {
this.iconmap[k] = iconmap[k];
}
return this;
}
addUINames(map) {
for (let k in map) {
this.ui_value_names[k] = map[k];
}
return this;
}
addDescriptions(map) {
for (let k in map) {
this.descriptions[k] = map[k];
}
return this;
}
copyTo(b) {
super.copyTo(b);
for (let val of this.value) {
b.value.add(val);
}
b.values = {};
for (let k in this.values) {
b.values[k] = this.values[k];
}
b.ui_value_names = {};
for (let k in this.ui_value_names) {
b.ui_value_names[k] = this.ui_value_names[k];
}
b.iconmap = {};
b.iconmap2 = {};
for (let k in this.iconmap) {
b.iconmap[k] = this.iconmap[k];
}
for (let k in this.iconmap2) {
b.iconmap2[k] = this.iconmap2[k];
}
b.descriptions = {};
for (let k in this.descriptions) {
b.descriptions[k] = this.descriptions[k];
}
}
loadSTRUCT(reader) {
reader(this);
let values = this.values;
this.values = {};
for (let s of values) {
this.values[s] = s;
}
this.value = new util.set(this.value);
}
}
StringSetProperty.STRUCT = nstructjs.inherit(StringSetProperty, ToolProperty) + `
value : iter(string);
values : iterkeys(string);
}`;
nstructjs.register(StringSetProperty);
ToolProperty.internalRegister(StringSetProperty);
import {Curve1D} from '../curve/curve1d.js';
export class Curve1DProperty extends ToolProperty {
constructor(curve, apiname, uiname, description, flag, icon) {
super(PropTypes.CURVE, undefined, apiname, uiname, description, flag, icon);
this.data = new Curve1D();
if (curve !== undefined) {
this.setValue(curve);
}
this.wasSet = false;
}
calcMemSize() {
//bleh, just return a largish block size
return 1024;
}
equals(b) {
}
getValue() {
return this.data;
}
evaluate(t) {
return this.data.evaluate(t);
}
setValue(curve) {
if (curve === undefined) {
return;
}
this.data.load(curve);
super.setValue(curve);
}
copyTo(b) {
super.copyTo(b);
b.setValue(this.data);
}
}
Curve1DProperty.STRUCT = nstructjs.inherit(Curve1DProperty, ToolProperty) + `
data : Curve1D;
}
`;
nstructjs.register(Curve1DProperty);
ToolProperty.internalRegister(Curve1DProperty);