cables_dev/cables/src/core/core_op.js
import { Logger } from "cables-shared-client";
import { EventTarget } from "./eventtarget.js";
import { UTILS, cleanJson, shortId } from "./utils.js";
import { CONSTANTS } from "./constants.js";
import { Port } from "./core_port.js";
import { SwitchPort } from "./core_port_switch.js";
import { ValueSelectPort } from "./core_port_select.js";
import { MultiPort } from "./core_port_multi.js";
/**
* op the class of all operators
* @namespace external:CABLES#Op
* @hideconstructor
*/
/**
* @type {Object}
* @name attachments
* @instance
* @memberof Op
* @description access file attachments as String values
* @example
* // set shader source to attached files (files are called shader.vert / shader.frag)
* shader.setSource(attachments.shader_vert,attachments.shader_frag);
*/
const Ops = {};
const Op = function ()
{
EventTarget.apply(this);
this._log = new Logger("core_op");
this.data = {}; // UNUSED, DEPRECATED, only left in for backwards compatibility with userops
this.storage = {}; // op-specific data to be included in export
this.__objName = "";
this.portsOut = [];
this.portsIn = [];
this.portsInData = []; // original loaded patch data
this.opId = ""; // unique op id
this.uiAttribs = {};
this.enabled = true;
this.patch = arguments[0];
this._name = arguments[1];
this.preservedPortTitles = {};
this.preservedPortValues = {};
this.preservedPortLinks = {};
this._linkTimeRules = {
"needsLinkedToWork": [],
"needsParentOp": null
};
this.shouldWork = {};
this.hasUiErrors = false;
this._uiErrors = {};
this._hasAnimPort = false;
if (arguments[1])
{
this._shortOpName = CABLES.getShortOpName(arguments[1]);
this.getTitle();
}
this.id = arguments[2] || shortId(); // instance id
this.onAddPort = null;
this.onCreate = null;
this.onResize = null;
this.onLoaded = null;
this.onDelete = null;
this.onError = null;
this._instances = null;
/**
* overwrite this to prerender shader and meshes / will be called by op `loadingStatus`
* @function preRender
* @memberof Op
* @instance
*/
this.preRender = null;
/**
* overwrite this to initialize your op
* @function init
* @memberof Op
* @instance
*/
this.init = null;
Object.defineProperty(this, "name", {
get() { return this.getTitle(); },
set(v)
{
this.setTitle(v);
}
});
Object.defineProperty(this, "_objName", { set(on)
{
this.__objName = on; this._log = new Logger("op " + on);
} });
Object.defineProperty(this, "objName", { get() { return this.__objName; } });
Object.defineProperty(this, "shortName", { get() { return this._shortOpName; } });
if (this.initUi) this.initUi();
};
{
Op.prototype.clearUiAttrib = function (name)
{
const obj = {};
// obj.name = null;
this.uiAttrib(obj);
};
Op.prototype.require = function (name)
{
if (CABLES.platform && CABLES.StandaloneElectron && !CABLES.platform.frontendOptions.isStandalone)
this.setUiError("notstandalone", "This op will only work in cables standalone version", 3);
return null;
};
Op.prototype.checkMainloopExists = function ()
{
if (!CABLES.UI) return;
if (!this.patch.cgl.mainloopOp) this.setUiError("nomainloop", "patch should have a mainloop to use this op");
else this.setUiError("nomainloop", null);
};
Op.prototype.getTitle = function ()
{
if (!this.uiAttribs) return "nouiattribs" + this._name;
// if ((this.uiAttribs.title === undefined || this.uiAttribs.title === "") && this.objName.indexOf("Ops.Ui.") == -1)
// this.uiAttribs.title = this._shortOpName;
return this.uiAttribs.title || this._shortOpName;
};
Op.prototype.setTitle = function (title)
{
// this._log.log("settitle", title);
// this._log.log(
// (new Error()).stack
// );
if (title != this.getTitle()) this.uiAttr({ "title": title });
};
Op.prototype.setStorage = function (newAttribs)
{
if (!newAttribs) return;
this.storage = this.storage || {};
let changed = false;
for (const p in newAttribs)
{
if (this.storage[p] != newAttribs[p]) changed = true;
this.storage[p] = newAttribs[p];
}
if (changed) this.emitEvent("onStorageChange", newAttribs);
};
Op.prototype.isSubPatchOp = function ()
{
if (this.storage) return (this.storage.subPatchVer || 0);
};
const _setUiAttrib = function (newAttribs)
{
if (!newAttribs) return;
if (newAttribs.error || newAttribs.warning || newAttribs.hint)
{
this._log.warn("old ui error/warning attribute in " + this._name + ", use op.setUiError !", newAttribs);
}
if (typeof newAttribs != "object") this._log.error("op.uiAttrib attribs are not of type object");
if (!this.uiAttribs) this.uiAttribs = {};
let changed = false;
let emitMove = false;
if (
CABLES.UI &&
newAttribs.hasOwnProperty("translate") &&
(
!this.uiAttribs.translate ||
this.uiAttribs.translate.x != newAttribs.translate.x ||
this.uiAttribs.translate.y != newAttribs.translate.y
)) emitMove = true;
if (newAttribs.hasOwnProperty("title") && newAttribs.title != this.uiAttribs.title)
{
// const doEmitEvent = newAttribs.title != this.getTitle();
this.uiAttribs.title = newAttribs.title;
// if (doEmitEvent) this.emitEvent("onTitleChange", newAttribs.title);
changed = true;
// this.setTitle(newAttribs.title);
}
if (newAttribs.hasOwnProperty("disabled")) this.setEnabled(!newAttribs.disabled);
for (const p in newAttribs)
{
if (this.uiAttribs[p] != newAttribs[p]) changed = true;
this.uiAttribs[p] = newAttribs[p];
}
if (this.uiAttribs.hasOwnProperty("selected") && this.uiAttribs.selected == false) delete this.uiAttribs.selected;
if (changed)
{
this.emitEvent("onUiAttribsChange", newAttribs);
this.patch.emitEvent("onUiAttribsChange", this, newAttribs);
}
if (emitMove) this.emitEvent("move");
};
/**
* setUiAttrib
* possible values:
* <pre>
* warning - warning message - showing up in op parameter panel
* error - error message - showing up in op parameter panel
* extendTitle - op title extension, e.g. [ + ]
* </pre>
* @function setUiAttrib
* @param {Object} newAttribs, e.g. {"attrib":value}
* @memberof Op
* @instance
* @example
* op.setUiAttrib({"extendTitle":str});
*/
Op.prototype.setUiAttribs = Op.prototype.setUiAttrib = Op.prototype.uiAttr = _setUiAttrib;
Op.prototype.getName = function ()
{
if (this.uiAttribs.name) return this.uiAttribs.name;
return this._name;
};
Op.prototype.addOutPort = function (p)
{
p.direction = CONSTANTS.PORT.PORT_DIR_OUT;
p._op = this;
this.portsOut.push(p);
this.emitEvent("onPortAdd", p);
return p;
};
Op.prototype.hasDynamicPort = function ()
{
let i = 0;
for (i = 0; i < this.portsIn.length; i++)
{
if (this.portsIn[i].type == CONSTANTS.OP.OP_PORT_TYPE_DYNAMIC) return true;
if (this.portsIn[i].getName() == "dyn") return true;
}
for (i = 0; i < this.portsOut.length; i++)
{
if (this.portsOut[i].type == CONSTANTS.OP.OP_PORT_TYPE_DYNAMIC) return true;
if (this.portsOut[i].getName() == "dyn") return true;
}
return false;
};
Op.prototype.addInPort = function (p)
{
if (!(p instanceof Port)) throw new Error("parameter is not a port!");
p.direction = CONSTANTS.PORT.PORT_DIR_IN;
p._op = this;
this.portsIn.push(p);
this.emitEvent("onPortAdd", p);
return p;
};
/**
* create a trigger input port
* @function inTrigger
* @instance
* @memberof Op
* @param {String} name
* @return {Port} created port
*
*/
Op.prototype.inFunction = Op.prototype.inTrigger = function (name, v)
{
const p = this.addInPort(new Port(this, name, CONSTANTS.OP.OP_PORT_TYPE_FUNCTION));
if (v !== undefined) p.set(v);
return p;
};
/**
* create multiple UI trigger buttons
* @function inTriggerButton
* @memberof Op
* @instance
* @param {String} name
* @param {Array} names
* @return {Port} created port
*/
Op.prototype.inFunctionButton = Op.prototype.inTriggerButton = function (name, v)
{
const p = this.addInPort(
new Port(this, name, CONSTANTS.OP.OP_PORT_TYPE_FUNCTION, {
"display": "button"
})
);
if (v !== undefined) p.set(v);
return p;
};
Op.prototype.inFunctionButton = Op.prototype.inUiTriggerButtons = function (name, v)
{
const p = this.addInPort(
new Port(this, name, CONSTANTS.OP.OP_PORT_TYPE_FUNCTION, {
"display": "buttons"
})
);
if (v !== undefined) p.set(v);
return p;
};
/**
* create a number value input port
* @function inFloat
* @memberof Op
* @instance
* @param {String} name
* @param {Number} value
* @return {Port} created port
*/
Op.prototype.inValueFloat = Op.prototype.inValue = Op.prototype.inFloat = function (name, v)
{
const p = this.addInPort(new Port(this, name, CONSTANTS.OP.OP_PORT_TYPE_VALUE));
p.setInitialValue(v);
return p;
};
/**
* create a boolean input port, displayed as a checkbox
* @function inBool
* @instance
* @memberof Op
* @param {String} name
* @param {Boolean} value
* @return {Port} created port
*/
Op.prototype.inValueBool = Op.prototype.inBool = function (name, v)
{
const p = this.addInPort(
new Port(this, name, CONSTANTS.OP.OP_PORT_TYPE_NUMBER, {
"display": "bool"
})
);
if (v === true)v = 1;
if (v === false)v = 0;
p.setInitialValue(v);
return p;
};
Op.prototype.inMultiPort = function (name, type)
{
const p = new MultiPort(
this,
name,
type,
CONSTANTS.PORT.PORT_DIR_IN,
{
"addPort": true,
"hidePort": true
}
);
p.ignoreValueSerialize = true;
this.addInPort(p);
p.initPorts();
return p;
};
Op.prototype.outMultiPort = function (name, type, uiAttribsPort = {})
{
const p = new MultiPort(
this,
name,
type,
CONSTANTS.PORT.PORT_DIR_OUT,
{
"display": "multiport",
"hidePort": true
},
uiAttribsPort
);
p.ignoreValueSerialize = true;
this.addOutPort(p);
p.initPorts();
return p;
};
Op.prototype.inValueString = function (name, v)
{
const p = this.addInPort(
new Port(this, name, CONSTANTS.OP.OP_PORT_TYPE_VALUE, {
"type": "string"
})
);
p.value = "";
p.setInitialValue(v);
return p;
};
/**
* create a String value input port
* @function inString
* @instance
* @memberof Op
* @param {String} name
* @param {String} value default value
* @return {Port} created port
*/
Op.prototype.inString = function (name, v)
{
const p = this.addInPort(
new Port(this, name, CONSTANTS.OP.OP_PORT_TYPE_STRING, {
"type": "string"
})
);
v = v || "";
// p.value = v;
p.setInitialValue(v);
return p;
};
/**
* create a String value input port displayed as TextArea
* @function inValueText
* @instance
* @memberof Op
* @param {String} name
* @param {String} value default value
* @return {Port} created port
*/
Op.prototype.inValueText = function (name, v)
{
const p = this.addInPort(
new Port(this, name, CONSTANTS.OP.OP_PORT_TYPE_VALUE, {
"type": "string",
"display": "text"
})
);
p.value = "";
p.setInitialValue(v);
// if (v !== undefined)
// {
// p.set(v);
// p.defaultValue = v;
// }
return p;
};
Op.prototype.inTextarea = function (name, v)
{
const p = this.addInPort(
new Port(this, name, CONSTANTS.OP.OP_PORT_TYPE_STRING, {
"type": "string",
"display": "text"
})
);
p.value = "";
if (v !== undefined)
{
p.set(v);
p.defaultValue = v;
}
return p;
};
/**
* create a String value input port displayed as editor
* @function inStringEditor
* @instance
* @memberof Op
* @param {String} name
* @param {String} value default value
* @return {Port} created port
*/
// new string
Op.prototype.inStringEditor = function (name, v, syntax, hideFormatButton = true)
{
const p = this.addInPort(
new Port(this, name, CONSTANTS.OP.OP_PORT_TYPE_STRING, {
"type": "string",
"display": "editor",
"editShortcut": true,
"editorSyntax": syntax,
"hideFormatButton": hideFormatButton
}));
p.value = "";
if (v !== undefined)
{
p.set(v);
p.defaultValue = v;
}
return p;
};
// old
Op.prototype.inValueEditor = function (name, v, syntax, hideFormatButton = true)
{
const p = this.addInPort(
new Port(this, name, CONSTANTS.OP.OP_PORT_TYPE_NUMBER, {
"type": "string",
"display": "editor",
"editorSyntax": syntax,
"hideFormatButton": hideFormatButton
})
);
p.value = "";
if (v !== undefined)
{
p.set(v);
p.defaultValue = v;
}
return p;
};
/**
* create a string select box
* @function inDropDown
* @instance
* @memberof Op
* @param {String} name
* @param {Array} values
* @param {String} value default value
* @return {Port} created port
*/
Op.prototype.inValueSelect = Op.prototype.inDropDown = function (name, values, v, noindex)
{
let p = null;
if (!noindex)
{
const indexPort = new Port(this, name + " index", CONSTANTS.OP.OP_PORT_TYPE_NUMBER, {
"increment": "integer",
"hideParam": true
});
const n = this.addInPort(indexPort);
if (values) for (let i = 0; i < values.length; i++) values[i] = String(values[i]);
const valuePort = new ValueSelectPort(
this,
name,
CONSTANTS.OP.OP_PORT_TYPE_NUMBER,
{
"display": "dropdown",
"hidePort": true,
"type": "string",
"values": values
},
n
);
valuePort.indexPort = indexPort;
valuePort.on("change", (val, thePort) =>
{
if (!thePort.indexPort.isLinked() && thePort.uiAttribs.values)
{
const idx = thePort.uiAttribs.values.indexOf(val);
if (idx > -1) thePort.indexPort.set(idx);
}
});
indexPort.onLinkChanged = function ()
{
valuePort.setUiAttribs({ "greyout": indexPort.isLinked() });
};
p = this.addInPort(valuePort);
if (v !== undefined)
{
p.set(v);
const index = values.findIndex((item) => { return item == v; });
n.setValue(index);
p.defaultValue = v;
n.defaultValue = index;
}
}
else
{
const valuePort = new Port(this, name, CONSTANTS.OP.OP_PORT_TYPE_VALUE, {
"display": "dropdown",
"hidePort": true,
"type": "string",
values
});
p = this.addInPort(valuePort);
}
return p;
};
/**
* create a string switch box
* @function inSwitch
* @instance
* @memberof Op
* @param {String} name
* @param {Array} values
* @param {String} value default value
* @return {Port} created port
*/
Op.prototype.inSwitch = function (name, values, v, noindex)
{
let p = null;
if (!noindex)
{
if (!v)v = values[0];
const indexPort = new Port(this, name + " index", CONSTANTS.OP.OP_PORT_TYPE_VALUE, {
"increment": "integer",
"values": values,
"hideParam": true
});
const n = this.addInPort(indexPort);
if (values) for (let i = 0; i < values.length; i++) values[i] = String(values[i]);
const switchPort = new SwitchPort(
this,
name,
CONSTANTS.OP.OP_PORT_TYPE_STRING,
{
"display": "switch",
"hidePort": true,
"type": "string",
"values": values
},
n
);
switchPort.indexPort = indexPort;
switchPort.on("change", (val, thePort) =>
{
if (!thePort.indexPort.isLinked() && thePort.uiAttribs.values)
{
const idx = thePort.uiAttribs.values.indexOf(val);
if (idx > -1) thePort.indexPort.set(idx);
}
});
indexPort.onLinkChanged = function ()
{
switchPort.setUiAttribs({ "greyout": indexPort.isLinked() });
};
p = this.addInPort(switchPort);
if (v !== undefined)
{
p.set(v);
const index = values.findIndex((item) => { return item == v; });
n.setValue(index);
p.defaultValue = v;
n.defaultValue = index;
}
}
else
{
const switchPort = new Port(this, name, CONSTANTS.OP.OP_PORT_TYPE_STRING, {
"display": "switch",
"hidePort": true,
"type": "string",
"values": values
});
p = this.addInPort(switchPort);
}
return p;
};
/**
* create a integer input port
* @function inInt
* @instance
* @memberof Op
* @param {String} name
* @param {number} value default value
* @return {Port} created port
*/
Op.prototype.inValueInt = Op.prototype.inInt = function (name, v)
{
// old
const p = this.addInPort(
new Port(this, name, CONSTANTS.OP.OP_PORT_TYPE_VALUE, {
"increment": "integer"
})
);
if (v !== undefined)
{
p.set(v);
p.defaultValue = v;
}
return p;
};
/**
* create a file/URL input port
* @function inURL
* @instance
* @memberof Op
* @param {String} name
* @return {Port} created port
*/
Op.prototype.inFile = function (name, filter, v)
{
const p = this.addInPort(
new Port(this, name, CONSTANTS.OP.OP_PORT_TYPE_VALUE, {
"display": "file",
"type": "string",
"filter": filter
})
);
if (v !== undefined)
{
p.set(v);
p.defaultValue = v;
}
return p;
};
Op.prototype.inUrl = function (name, filter, v)
{
const p = this.addInPort(
new Port(this, name, CONSTANTS.OP.OP_PORT_TYPE_STRING, {
"display": "file",
"type": "string",
"filter": filter
})
);
if (v !== undefined)
{
p.set(v);
p.defaultValue = v;
}
return p;
};
/**
* create a texture input port
* @function inTexture
* @instance
* @memberof Op
* @param {String} name
* @return {Port} created port
*/
Op.prototype.inTexture = function (name, v)
{
const p = this.addInPort(
new Port(this, name, CONSTANTS.OP.OP_PORT_TYPE_OBJECT, {
"display": "texture",
"objType": "texture",
"preview": true
})
);
p.ignoreValueSerialize = true;
if (v !== undefined) p.set(v);
return p;
};
/**
* create a object input port
* @function inObject
* @instance
* @memberof Op
* @param {String} name
* @return {Port} created port
*/
Op.prototype.inObject = function (name, v, objType)
{
const p = this.addInPort(new Port(this, name, CONSTANTS.OP.OP_PORT_TYPE_OBJECT, { "objType": objType }));
p.ignoreValueSerialize = true;
if (v !== undefined) p.set(v);
return p;
};
Op.prototype.inGradient = function (name, v)
{
const p = this.addInPort(
new Port(this, name, CONSTANTS.OP.OP_PORT_TYPE_VALUE, {
"display": "gradient"
// "hidePort": true
})
);
if (v !== undefined) p.set(v);
return p;
};
Op.prototype.getPortVisibleIndex = function (p)
{
let ports = this.portsIn;
if (p.direction == CONSTANTS.PORT_DIR_OUT)ports = this.portsOut;
let index = 0;
for (let i = 0; i < ports.length; i++)
{
if (ports[i].uiAttribs.hidePort) continue;
index++;
if (ports[i] == p) return index;
}
};
/**
* create a array input port
* @function inArray
* @instance
* @memberof Op
* @param {String} name
* @return {Port} created port
*/
Op.prototype.inArray = function (name, v, stride)
{
if (!stride && CABLES.UTILS.isNumeric(v))stride = v;
const p = this.addInPort(new Port(this, name, CONSTANTS.OP.OP_PORT_TYPE_ARRAY, { "stride": stride }));
if (v !== undefined && (Array.isArray(v) || v == null)) p.set(v);
// if (v !== undefined) p.set(v);
return p;
};
/**
* create a value slider input port
* @function inFloatSlider
* @instance
* @memberof Op
* @param {String} name
* @param {number} defaultvalue
* @param {number} min
* @param {number} max
* @return {Port} created port
*/
Op.prototype.inValueSlider = Op.prototype.inFloatSlider = function (name, v, min, max)
{
const uiattribs = { "display": "range" };
if (min != undefined && max != undefined)
{
uiattribs.min = min;
uiattribs.max = max;
}
const p = this.addInPort(new Port(this, name, CONSTANTS.OP.OP_PORT_TYPE_VALUE, uiattribs));
if (v !== undefined)
{
p.set(v);
p.defaultValue = v;
}
return p;
};
/**
* create output trigger port
* @function outTrigger
* @instance
* @memberof Op
* @param {String} name
* @return {Port} created port
*/
Op.prototype.outFunction = Op.prototype.outTrigger = function (name, v)
{
// old
const p = this.addOutPort(new Port(this, name, CONSTANTS.OP.OP_PORT_TYPE_FUNCTION));
if (v !== undefined) p.set(v);
return p;
};
/**
* create output value port
* @function outNumber
* @instance
* @memberof Op
* @param {String} name
* @param {number} default value
* @return {Port} created port
*/
Op.prototype.outValue = Op.prototype.outNumber = function (name, v)
{
// old
const p = this.addOutPort(new Port(this, name, CONSTANTS.OP.OP_PORT_TYPE_VALUE));
if (v !== undefined) p.set(v);
return p;
};
/**
* deprecated create output boolean port
* @deprecated
* @function outBool
* @instance
* @memberof Op
* @param {String} name
* @return {Port} created port
*/
Op.prototype.outValueBool = Op.prototype.outBool = function (name, v)
{
// old: use outBoolNum
const p = this.addOutPort(
new Port(this, name, CONSTANTS.OP.OP_PORT_TYPE_VALUE, {
"display": "bool"
})
);
if (v !== undefined) p.set(v);
else p.set(0);
return p;
};
/**
* create output boolean port,value will be converted to 0 or 1
* @function outBoolNum
* @instance
* @memberof Op
* @param {String} name
* @return {Port} created port
*/
Op.prototype.outBoolNum = function (name, v)
{
const p = this.addOutPort(
new Port(this, name, CONSTANTS.OP.OP_PORT_TYPE_VALUE, {
"display": "boolnum"
})
);
p.set = function (b)
{
this.setValue(b ? 1 : 0);
// this._log.log("bool set", b, this.get());
}.bind(p);
if (v !== undefined) p.set(v);
else p.set(0);
return p;
};
/**
* create output string port
* @function outString
* @instance
* @memberof Op
* @param {String} name
* @return {Port} created port
*/
Op.prototype.outValueString = function (name, v)
{
const p = this.addOutPort(
new Port(this, name, CONSTANTS.OP.OP_PORT_TYPE_VALUE, {
"type": "string"
})
);
if (v !== undefined) p.set(v);
return p;
};
Op.prototype.outString = function (name, v)
{
const p = this.addOutPort(
new Port(this, name, CONSTANTS.OP.OP_PORT_TYPE_STRING, {
"type": "string"
})
);
if (v !== undefined) p.set(v);
else p.set("");
return p;
};
/**
* create output object port
* @function outObject
* @instance
* @memberof Op
* @param {String} name
* @return {Port} created port
*/
Op.prototype.outObject = function (name, v, objType)
{
const p = this.addOutPort(new Port(this, name, CONSTANTS.OP.OP_PORT_TYPE_OBJECT, { "objType": objType || null }));
p.set(v || null);
p.ignoreValueSerialize = true;
return p;
};
/**
* create output array port
* @function outArray
* @instance
* @memberof Op
* @param {String} name
* @return {Port} created port
*/
Op.prototype.outArray = function (name, v, stride)
{
if (!stride && CABLES.UTILS.isNumeric(v))stride = v;
const p = this.addOutPort(new Port(this, name, CONSTANTS.OP.OP_PORT_TYPE_ARRAY, { "stride": stride }));
if (v !== undefined && (Array.isArray(v) || v == null)) p.set(v);
p.ignoreValueSerialize = true;
return p;
};
/**
* create output texture port
* @function outTexture
* @instance
* @memberof Op
* @param {String} name
* @return {Port} created port
*/
Op.prototype.outTexture = function (name, v)
{
const p = this.addOutPort(
new Port(this, name, CONSTANTS.OP.OP_PORT_TYPE_OBJECT, {
"preview": true,
"objType": "texture",
"display": "texture"
})
);
if (v !== undefined) p.set(v || CGL.Texture.getEmptyTexture(this.patch.cgl));
p.ignoreValueSerialize = true;
return p;
};
Op.prototype.inDynamic = function (name, filter, options, v)
{
const p = new Port(this, name, CONSTANTS.OP.OP_PORT_TYPE_DYNAMIC, options);
p.shouldLink = function (p1, p2)
{
if (filter && UTILS.isArray(filter))
{
for (let i = 0; i < filter.length; i++)
{
if (p1 == this && p2.type === filter[i]) return true;
if (p2 == this && p1.type === filter[i]) return true;
}
return false; // types do not match
}
return true; // no filter set
};
this.addInPort(p);
if (v !== undefined)
{
p.set(v);
p.defaultValue = v;
}
return p;
};
Op.prototype.removeLinks = function ()
{
for (let i = 0; i < this.portsIn.length; i++) this.portsIn[i].removeLinks();
for (let i = 0; i < this.portsOut.length; i++) this.portsOut[i].removeLinks();
};
Op.prototype.getSerialized = function ()
{
const opObj = {};
if (this.opId) opObj.opId = this.opId;
if (this.patch.storeObjNames) opObj.objName = this.objName;
opObj.id = this.id;
opObj.uiAttribs = JSON.parse(JSON.stringify(this.uiAttribs)) || {};
if (this.storage && Object.keys(this.storage).length > 0) opObj.storage = JSON.parse(JSON.stringify(this.storage));
if (this.uiAttribs.hasOwnProperty("working") && this.uiAttribs.working == true) delete this.uiAttribs.working;
if (opObj.uiAttribs.hasOwnProperty("uierrors")) delete opObj.uiAttribs.uierrors;
if (opObj.uiAttribs.title === "") delete opObj.uiAttribs.title;
if (opObj.uiAttribs.color === null) delete opObj.uiAttribs.color;
if (opObj.uiAttribs.comment === null) delete opObj.uiAttribs.comment;
if (opObj.uiAttribs.title == this._shortOpName ||
(this.uiAttribs.title || "").toLowerCase() == this._shortOpName.toLowerCase()) delete opObj.uiAttribs.title;
opObj.portsIn = [];
opObj.portsOut = [];
for (let i = 0; i < this.portsIn.length; i++)
{
const s = this.portsIn[i].getSerialized();
if (s) opObj.portsIn.push(s);
}
for (let i = 0; i < this.portsOut.length; i++)
{
const s = this.portsOut[i].getSerialized();
if (s) opObj.portsOut.push(s);
}
if (opObj.portsIn.length == 0) delete opObj.portsIn;
if (opObj.portsOut.length == 0) delete opObj.portsOut;
cleanJson(opObj);
return opObj;
};
Op.prototype.getFirstOutPortByType = function (type)
{
for (const ipo in this.portsOut) if (this.portsOut[ipo].type == type) return this.portsOut[ipo];
};
Op.prototype.getFirstInPortByType = function (type)
{
for (const ipo in this.portsIn) if (this.portsIn[ipo].type == type) return this.portsIn[ipo];
};
/**
* return port by the name portName
* @function getPort
* @instance
* @memberof Op
* @param {String} portName
* @return {Port}
*/
Op.prototype.getPort = Op.prototype.getPortByName = function (name, lowerCase)
{
if (lowerCase)
{
for (let ipi = 0; ipi < this.portsIn.length; ipi++)
if (this.portsIn[ipi].getName().toLowerCase() == name || this.portsIn[ipi].id.toLowerCase() == name)
return this.portsIn[ipi];
for (let ipo = 0; ipo < this.portsOut.length; ipo++)
if (this.portsOut[ipo].getName().toLowerCase() == name || this.portsOut[ipo].id.toLowerCase() == name)
return this.portsOut[ipo];
}
else
{
for (let ipi = 0; ipi < this.portsIn.length; ipi++)
if (this.portsIn[ipi].getName() == name || this.portsIn[ipi].id == name)
return this.portsIn[ipi];
for (let ipo = 0; ipo < this.portsOut.length; ipo++)
if (this.portsOut[ipo].getName() == name || this.portsOut[ipo].id == name)
return this.portsOut[ipo];
}
};
/**
* return port by the name id
* @function getPortById
* @instance
* @memberof Op
* @param {String} id
* @return {Port}
*/
Op.prototype.getPortById = function (id)
{
for (let ipi = 0; ipi < this.portsIn.length; ipi++) if (this.portsIn[ipi].id == id) return this.portsIn[ipi];
for (let ipo = 0; ipo < this.portsOut.length; ipo++) if (this.portsOut[ipo].id == id) return this.portsOut[ipo];
};
Op.prototype.updateAnims = function ()
{
if (this._hasAnimPort)
for (let i = 0; i < this.portsIn.length; i++) this.portsIn[i].updateAnim();
};
Op.prototype.log = function ()
{
this._log.log(...arguments);
};
Op.prototype.error = Op.prototype.logError = function ()
{
// if (!this)
// {
// this._log.error("no this...!!!");
// debugger;
// return;
// }
// const initiator = "op " + this.objName;
// if (CABLES.UI && !CABLES.UI.logFilter.filterLog({ "initiator": initiator, "opInstId": this.id, "level": 2 }, ...arguments)) return;
// // if (this.patch.silent) return;
// const args = ["[op " + CABLES.getShortOpName(this.objName) + "]"];
// args.push.apply(args, arguments);
// Function.prototype.apply.apply(this._log.error, [console, args]);// eslint-disable-line
// if (window.gui) window.gui.emitEvent("opLogEvent", this.objName, "error", arguments);
this._log.error(...arguments);
};
Op.prototype.warn = Op.prototype.logWarn = function ()
{
this._log.warn(...arguments);
// const initiator = "op " + this.objName;
// if (CABLES.UI && !CABLES.UI.logFilter.filterLog({ "initiator": initiator, "opInstId": this.id, "level": 1 }, ...arguments)) return;
// // if (this.patch.silent) return;
// const args = ["[op " + CABLES.getShortOpName(this.objName) + "]"];
// args.push.apply(args, arguments);
// Function.prototype.apply.apply(this._log.warn, [console, args]);// eslint-disable-line
};
Op.prototype.verbose = Op.prototype.logVerbose = function ()
{
// const initiator = "op " + CABLES.getShortOpName(this.objName);
// if (CABLES.UI && !CABLES.UI.logFilter.filterLog({ "initiator": initiator, "opInstId": this.id, "level": 0 }, ...arguments)) return;
// if (!CABLES.UI && this.patch.silent) return;
// const args = ["[" + initiator + "]"];
// args.push.apply(args, arguments);
// Function.prototype.apply.apply(this._log.info, [console, args]);// eslint-disable-line
this._log.verbose(...arguments);
};
Op.prototype.profile = function (enable)
{
for (let ipi = 0; ipi < this.portsIn.length; ipi++)
{
this.portsIn[ipi]._onTriggered = this.portsIn[ipi]._onTriggeredProfiling;
this.portsIn[ipi].set = this.portsIn[ipi]._onSetProfiling;
}
};
Op.prototype.findParent = function (objName)
{
for (let ipi = 0; ipi < this.portsIn.length; ipi++)
{
if (this.portsIn[ipi].isLinked())
{
if (this.portsIn[ipi].links[0].portOut.parent.objName == objName)
return this.portsIn[ipi].links[0].portOut.parent;
let found = null;
found = this.portsIn[ipi].links[0].portOut.parent.findParent(objName);
if (found) return found;
}
}
return null;
};
// todo: check instancing stuff?
Op.prototype.cleanUp = function ()
{
if (this._instances)
{
for (let i = 0; i < this._instances.length; i++)
{
if (this._instances[i].onDelete) this._instances[i].onDelete();
}
this._instances.length = 0;
}
for (let i = 0; i < this.portsIn.length; i++)
{
this.portsIn[i].setAnimated(false);
}
if (this.onAnimFrame) this.patch.removeOnAnimFrame(this);
};
// todo: check instancing stuff?
Op.prototype.instanced = function (triggerPort)
{
return false;
// this._log.log("instanced", this.patch.instancing.numCycles());
// if (this.patch.instancing.numCycles() === 0) return false;
// let i = 0;
// let ipi = 0;
// if (!this._instances || this._instances.length != this.patch.instancing.numCycles())
// {
// if (!this._instances) this._instances = [];
// this._.log("creating instances of ", this.objName, this.patch.instancing.numCycles(), this._instances.length);
// this._instances.length = this.patch.instancing.numCycles();
// for (i = 0; i < this._instances.length; i++)
// {
// this._instances[i] = this.patch.createOp(this.objName, true);
// this._instances[i].instanced = function ()
// {
// return false;
// };
// this._instances[i].uiAttr(this.uiAttribs);
// for (let ipo = 0; ipo < this.portsOut.length; ipo++)
// {
// if (this.portsOut[ipo].type == CONSTANTS.OP.OP_PORT_TYPE_FUNCTION)
// {
// this._instances[i].getPortByName(this.portsOut[ipo].name).trigger = this.portsOut[ipo].trigger.bind(this.portsOut[ipo]);
// }
// }
// }
// for (ipi = 0; ipi < this.portsIn.length; ipi++)
// {
// this.portsIn[ipi].onChange = null;
// this.portsIn[ipi].onValueChanged = null;
// }
// }
// const theTriggerPort = null;
// for (ipi = 0; ipi < this.portsIn.length; ipi++)
// {
// if (
// this.portsIn[ipi].type == CONSTANTS.OP.OP_PORT_TYPE_VALUE ||
// this.portsIn[ipi].type == CONSTANTS.OP.OP_PORT_TYPE_ARRAY
// )
// {
// this._instances[this.patch.instancing.index()].portsIn[ipi].set(this.portsIn[ipi].get());
// }
// if (this.portsIn[ipi].type == CONSTANTS.OP.OP_PORT_TYPE_FUNCTION)
// {
// // if(this._instances[ this.patch.instancing.index() ].portsIn[ipi].name==triggerPort.name)
// // theTriggerPort=this._instances[ this.patch.instancing.index() ].portsIn[ipi];
// }
// }
// if (theTriggerPort) theTriggerPort.onTriggered();
// for (ipi = 0; ipi < this.portsOut.length; ipi++)
// {
// if (this.portsOut[ipi].type == CONSTANTS.OP.OP_PORT_TYPE_VALUE)
// {
// this.portsOut[ipi].set(this._instances[this.patch.instancing.index()].portsOut[ipi].get());
// }
// }
// return true;
};
// todo: check instancing stuff?
Op.prototype.initInstancable = function ()
{
// if(this.isInstanced)
// {
// this._log.log('cancel instancing');
// return;
// }
// this._instances=[];
// for(var ipi=0;ipi<this.portsIn.length;ipi++)
// {
// if(this.portsIn[ipi].type==CONSTANTS.OP.OP_PORT_TYPE_VALUE)
// {
//
// }
// if(this.portsIn[ipi].type==CONSTANTS.OP.OP_PORT_TYPE_FUNCTION)
// {
// // var piIndex=ipi;
// this.portsIn[ipi].onTriggered=function(piIndex)
// {
//
// var i=0;
// // this._log.log('trigger',this._instances.length);
//
// }.bind(this,ipi );
//
// }
// };
// this._instances=null;
};
Op.prototype.setValues = function (obj)
{
for (const i in obj)
{
const port = this.getPortByName(i);
if (port) port.set(obj[i]);
else this._log.warn("op.setValues: port not found:", i);
}
};
/**
* return true if op has this error message id
* @function hasUiError
* @instance
* @memberof Op
* @param {id} error id
* @returns {Boolean} - has id
*/
Op.prototype.hasUiError = function (id)
{
return this._uiErrors.hasOwnProperty(id) && this._uiErrors[id];
};
/**
* show op error message - set message to null to remove error message
* @function setUiError
* @instance
* @memberof Op
* @param {id} error id
* @param {txt} text message
* @param {level} level
*/
Op.prototype.setUiError = function (id, txt, level)
{
// overwritten in ui: core_extend_op
};
// todo: remove
Op.prototype.setError = function (id, txt)
{
this._log.warn("old error message op.error() - use op.setUiError()");
};
/**
* enable/disable op
* @function
* @instance
* @memberof Op
* @param {boolean}
*/
Op.prototype.setEnabled = function (b)
{
this.enabled = b;
this.emitEvent("onEnabledChange", b);
};
/**
* organize ports into a group
* @function
* @instance
* @memberof Op
* @param {String} name
* @param {Array} ports
*/
Op.prototype.setPortGroup = function (name, ports)
{
for (let i = 0; i < ports.length; i++)
{
if (ports[i])
if (ports[i].setUiAttribs) ports[i].setUiAttribs({ "group": name });
else
{
this._log.error("setPortGroup: invalid port!");
}
}
};
/**
* visually indicate ports that they are coordinate inputs
* @function
* @instance
* @memberof Op
* @param {Port} portX
* @param {Port} portY
* @param {Port} portZ
*/
Op.prototype.setUiAxisPorts = function (px, py, pz)
{
if (px) px.setUiAttribs({ "axis": "X" });
if (py) py.setUiAttribs({ "axis": "Y" });
if (pz) pz.setUiAttribs({ "axis": "Z" });
};
/**
* remove port from op
* @function removePort
* @instance
* @memberof Op
* @param {Port} port to remove
*/
Op.prototype.removePort = function (port)
{
for (let ipi = 0; ipi < this.portsIn.length; ipi++)
{
if (this.portsIn[ipi] == port)
{
this.portsIn.splice(ipi, 1);
this.emitEvent("onUiAttribsChange", {});
this.emitEvent("onPortRemoved", {});
return;
}
}
for (let ipi = 0; ipi < this.portsOut.length; ipi++)
{
if (this.portsOut[ipi] == port)
{
this.portsOut.splice(ipi, 1);
this.emitEvent("onUiAttribsChange", {});
this.emitEvent("onPortRemoved", {});
return;
}
}
};
Op.prototype._checkLinksNeededToWork = function () {};
/**
* show a warning of this op is not a child of parentOpName
* @function
* @instance
* @memberof Op
* @param {String} parentOpName
*/
Op.prototype.toWorkNeedsParent = function (parentOpName)
{
this._linkTimeRules.needsParentOp = parentOpName;
};
// /**
// * show a warning of this op is a child of parentOpName
// * @function
// * @instance
// * @memberof Op
// * @param {String} parentOpName
// */
Op.prototype.toWorkShouldNotBeChild = function (parentOpName, type)
{
if (!this.patch.isEditorMode()) return;
this._linkTimeRules.forbiddenParent = parentOpName;
if (type != undefined) this._linkTimeRules.forbiddenParentType = type;
};
/**
* show a small X to indicate op is not working when given ports are not linked
* @function
* @instance
* @memberof Op
* @param {Port} port1
* @param {Port} port2
* @param {Port} port3
*/
Op.prototype.toWorkPortsNeedToBeLinked = function ()
{
if (!this.patch.isEditorMode()) return;
for (let i = 0; i < arguments.length; i++)
if (this._linkTimeRules.needsLinkedToWork.indexOf(arguments[i]) == -1) this._linkTimeRules.needsLinkedToWork.push(arguments[i]);
};
Op.prototype.toWorkPortsNeedToBeLinkedReset = function ()
{
if (!this.patch.isEditorMode()) return;
this._linkTimeRules.needsLinkedToWork.length = 0;
if (this.checkLinkTimeWarnings) this.checkLinkTimeWarnings();
};
Op.prototype.initVarPorts = function ()
{
for (let i = 0; i < this.portsIn.length; i++)
{
if (this.portsIn[i].getVariableName()) this.portsIn[i].setVariable(this.portsIn[i].getVariableName());
}
};
/**
* refresh op parameters, if current op is selected
* @function
* @instance
* @memberof Op
*/
Op.prototype.refreshParams = function ()
{
if (this.patch && this.patch.isEditorMode() && this.isCurrentUiOp())
{
gui.opParams.show(this);
}
};
/**
* Returns true if op is selected and parameter are shown in the editor, can only return true if in editor/ui
* @function isCurrentUiOp
* @instance
* @memberof Op
* @returns {Boolean} - is current ui op
*/
Op.prototype.isCurrentUiOp = function ()
{
if (this.patch.isEditorMode()) return gui.patchView.isCurrentOp(this);
};
/**
* Implement to render 2d canvas based graphics from in an op
* @function renderVizLayer
* @instance
* @memberof Op
* @param {ctx} context of canvas 2d
* @param {Object} layer info
* @param {number} layer.x x position on canvas
* @param {number} layer.y y position on canvas
* @param {number} layer.width width of canvas
* @param {number} layer.height height of canvas
* @param {number} layer.scale current scaling of patchfield view
*/
Op.prototype.renderVizLayer = null; // optionaly defined in op instance
}
export { Op };