cables_dev/cables/src/core/core_link.js
import { CONSTANTS } from "./constants.js";
import { EventTarget } from "./eventtarget.js";
/**
* @namespace external:CABLES#Link
* @param {Object} scene The patch object
* @description a link is a connection between two ops/ports -> one input and one output port
* @hideconstructor
* @class
*/
const Link = function (scene)
{
EventTarget.apply(this);
this.id = CABLES.simpleId();
this.portIn = null;
this.portOut = null;
this.scene = scene; // todo: make private and rename to patch
this.activityCounter = 0;
this.ignoreInSerialize = false;
};
Link.prototype.setValue = function (v)
{
if (v === undefined) this._setValue();
else this.portIn.set(v);
};
Link.prototype.activity = function ()
{
this.activityCounter++;
// if(Date.now()-this.lastTime>100)
// {
// // this.lastTime=Date.now();
// // this.changesPerSecond=this.changesCounter*10;
// this.changesCounter=0;
// }
};
Link.prototype._setValue = function ()
{
if (!this.portOut)
{
this.remove();
return;
}
const v = this.portOut.get();
if (v == v) // NaN is the only JavaScript value that is treated as unequal to itself
{
if (this.portIn.type != CONSTANTS.OP.OP_PORT_TYPE_FUNCTION) this.activity();
if (this.portIn.get() !== v)
{
this.portIn.set(v);
}
else
{
if (this.portIn.changeAlways) this.portIn.set(v);
if (this.portOut.forceRefChange) this.portIn.forceChange();
}
}
};
/**
* @function getOtherPort
* @memberof Link
* @instance
* @param {Port} p port
* @description returns the port of the link, which is not port
*/
Link.prototype.getOtherPort = function (p)
{
if (p == this.portIn) return this.portOut;
return this.portIn;
};
/**
* @function remove
* @memberof Link
* @instance
* @description unlink/remove this link from all ports
*/
Link.prototype.remove = function ()
{
if (this.portIn) this.portIn.removeLink(this);
if (this.portOut) this.portOut.removeLink(this);
if (this.scene)
{
this.scene.emitEvent("onUnLink", this.portIn, this.portOut, this);
}
if (this.portIn && (this.portIn.type == CONSTANTS.OP.OP_PORT_TYPE_OBJECT || this.portIn.type == CONSTANTS.OP.OP_PORT_TYPE_ARRAY))
{
this.portIn.set(null);
if (this.portIn.links.length > 0) this.portIn.set(this.portIn.links[0].getOtherPort(this.portIn).get());
}
if (this.portIn) this.portIn.op._checkLinksNeededToWork();
if (this.portOut) this.portOut.op._checkLinksNeededToWork();
this.portIn = null;
this.portOut = null;
this.scene = null;
};
/**
* @function link
* @memberof Link
* @instance
* @description link those two ports
* @param {Port} p1 port1
* @param {Port} p2 port2
*/
Link.prototype.link = function (p1, p2)
{
if (!Link.canLink(p1, p2))
{
console.warn("[core_link] cannot link ports!", p1, p2);
return false;
}
if (p1.direction == CONSTANTS.PORT.PORT_DIR_IN)
{
this.portIn = p1;
this.portOut = p2;
}
else
{
this.portIn = p2;
this.portOut = p1;
}
p1.addLink(this);
p2.addLink(this);
this.setValue();
if (p1.onLink) p1.onLink(this);
if (p2.onLink) p2.onLink(this);
p1.op._checkLinksNeededToWork();
p2.op._checkLinksNeededToWork();
};
Link.prototype.getSerialized = function ()
{
const obj = {};
obj.portIn = this.portIn.getName();
obj.portOut = this.portOut.getName();
obj.objIn = this.portIn.op.id;
obj.objOut = this.portOut.op.id;
return obj;
};
// --------------------------------------------
/**
* @function canLinkText
* @memberof Link
* @instance
* @description return a text message with human readable reason if ports can not be linked, or can be
* @param {Port} p1 port1
* @param {Port} p2 port2
*/
Link.canLinkText = function (p1, p2)
{
if (p1.direction == p2.direction)
{
let txt = "(out)";
if (p2.direction == CONSTANTS.PORT.PORT_DIR_IN) txt = "(in)";
return "can not link: same direction " + txt;
}
if (p1.op == p2.op) return "can not link: same op";
if (p1.type != CONSTANTS.OP.OP_PORT_TYPE_DYNAMIC && p2.type != CONSTANTS.OP.OP_PORT_TYPE_DYNAMIC)
{
if (p1.type != p2.type) return "can not link: different type";
}
if (CABLES.UI && p1.type == CONSTANTS.OP.OP_PORT_TYPE_OBJECT && p2.type == CONSTANTS.OP.OP_PORT_TYPE_OBJECT)
{
if (p1.uiAttribs.objType && p2.uiAttribs.objType)
if (p1.uiAttribs.objType != p2.uiAttribs.objType)
return "incompatible objects";
}
if (!p1) return "can not link: port 1 invalid";
if (!p2) return "can not link: port 2 invalid";
if (p1.direction == CONSTANTS.PORT.PORT_DIR_IN && p1.isAnimated()) return "can not link: is animated";
if (p2.direction == CONSTANTS.PORT.PORT_DIR_IN && p2.isAnimated()) return "can not link: is animated";
// if(p1.direction==CABLES.CONSTANTS.PORT.PORT_DIR_IN && p1.links.length>0)return 'input port already busy';
// if(p2.direction==CABLES.CONSTANTS.PORT.PORT_DIR_IN && p2.links.length>0)return 'input port already busy';
if (p1.isLinkedTo(p2)) return "ports already linked";
if ((p1.canLink && !p1.canLink(p2)) || (p2.canLink && !p2.canLink(p1))) return "Incompatible";
return "can link";
};
/**
* @function canLink
* @memberof Link
* @instance
* @description return true if ports can be linked
* @param {Port} p1 port1
* @param {Port} p2 port2
* @returns {Boolean}
*/
Link.canLink = function (p1, p2)
{
if (!p1) return false;
if (!p2) return false;
if (p1.direction == CONSTANTS.PORT.PORT_DIR_IN && p1.isAnimated()) return false;
if (p2.direction == CONSTANTS.PORT.PORT_DIR_IN && p2.isAnimated()) return false;
if (p1.isHidden() || p2.isHidden()) return false;
if (p1.isLinkedTo(p2)) return false;
if (p1.direction == p2.direction) return false;
if (CABLES.UI && p1.type == CONSTANTS.OP.OP_PORT_TYPE_OBJECT && p2.type == CONSTANTS.OP.OP_PORT_TYPE_OBJECT)
{
if (p1.uiAttribs.objType && p2.uiAttribs.objType)
{
if (p1.uiAttribs.objType.indexOf("sg_") == 0 && p2.uiAttribs.objType.indexOf("sg_") == 0) return true;
if (p1.uiAttribs.objType != p2.uiAttribs.objType)
return false;
}
}
if (p1.type != p2.type && (p1.type != CONSTANTS.OP.OP_PORT_TYPE_DYNAMIC && p2.type != CONSTANTS.OP.OP_PORT_TYPE_DYNAMIC)) return false;
if (p1.type == CONSTANTS.OP.OP_PORT_TYPE_DYNAMIC || p2.type == CONSTANTS.OP.OP_PORT_TYPE_DYNAMIC) return true;
if (p1.op == p2.op) return false;
if (p1.canLink && !p1.canLink(p2)) return false;
if (p2.canLink && !p2.canLink(p1)) return false;
return true;
};
export { Link };