Home Reference Source

cables_dev/cables/src/core/core_link.js

  1. import { Events, Logger } from "cables-shared-client";
  2. import { Patch } from "./core_patch.js";
  3. import { Port } from "./core_port.js";
  4. /**
  5. * @namespace external:CABLES#Link
  6. * @description a link is a connection between two ops/ports -> one input and one output port
  7. * @hideconstructor
  8. * @class
  9. */
  10. export class Link extends Events
  11. {
  12. #log = new Logger("link");
  13. /**
  14. * @param {Patch} p
  15. */
  16. constructor(p)
  17. {
  18. super();
  19. this.id = CABLES.simpleId();
  20. /** @type {Port} */
  21. this.portIn = null;
  22. /** @type {Port} */
  23. this.portOut = null;
  24. /** @type {Patch} */
  25. this._patch = p;
  26. this.activityCounter = 0;
  27. this.ignoreInSerialize = false;
  28. }
  29. /**
  30. * @param {any} v
  31. */
  32. setValue(v)
  33. {
  34. if (v === undefined) this._setValue();
  35. else this.portIn.set(v);
  36. }
  37. activity()
  38. {
  39. this.activityCounter++;
  40. }
  41. _setValue()
  42. {
  43. if (!this.portOut)
  44. {
  45. this.remove();
  46. return;
  47. }
  48. const v = this.portOut.get();
  49. if (v == v) // NaN is the only JavaScript value that is treated as unequal to itself
  50. {
  51. if (this.portIn.type != Port.TYPE_FUNCTION) this.activity();
  52. if (this.portIn.get() !== v)
  53. {
  54. this.portIn.set(v);
  55. }
  56. else
  57. {
  58. if (this.portIn.changeAlways) this.portIn.set(v);
  59. if (this.portOut.forceRefChange) this.portIn.forceChange();
  60. }
  61. }
  62. }
  63. /**
  64. * @function getOtherPort
  65. * @memberof Link
  66. * @instance
  67. * @param {Port} p port
  68. * @description returns the port of the link, which is not port
  69. */
  70. getOtherPort(p)
  71. {
  72. if (p == this.portIn) return this.portOut;
  73. return this.portIn;
  74. }
  75. /**
  76. * @function remove
  77. * @memberof Link
  78. * @instance
  79. * @description unlink/remove this link from all ports
  80. */
  81. remove()
  82. {
  83. if (this.portIn) this.portIn.removeLink(this);
  84. if (this.portOut) this.portOut.removeLink(this);
  85. if (this._patch)
  86. {
  87. this._patch.emitEvent("onUnLink", this.portIn, this.portOut, this);
  88. }
  89. if (this.portIn && (this.portIn.type == Port.TYPE_OBJECT || this.portIn.type == Port.TYPE_ARRAY))
  90. {
  91. this.portIn.set(null);
  92. if (this.portIn.links.length > 0) this.portIn.set(this.portIn.links[0].getOtherPort(this.portIn).get());
  93. }
  94. if (this.portIn) this.portIn.op._checkLinksNeededToWork();
  95. if (this.portOut) this.portOut.op._checkLinksNeededToWork();
  96. this.portIn = null;
  97. this.portOut = null;
  98. this._patch = null;
  99. }
  100. /**
  101. * @function link
  102. * @memberof Link
  103. * @instance
  104. * @description link those two ports
  105. * @param {Port} p1 port1
  106. * @param {Port} p2 port2
  107. */
  108. link(p1, p2)
  109. {
  110. if (!Link.canLink(p1, p2))
  111. {
  112. this.#log.warn("[core_link] cannot link ports!", p1, p2);
  113. return false;
  114. }
  115. if (p1.direction == Port.DIR_IN)
  116. {
  117. this.portIn = p1;
  118. this.portOut = p2;
  119. }
  120. else
  121. {
  122. this.portIn = p2;
  123. this.portOut = p1;
  124. }
  125. p1.addLink(this);
  126. p2.addLink(this);
  127. this.setValue();
  128. p1.op._checkLinksNeededToWork();
  129. p2.op._checkLinksNeededToWork();
  130. }
  131. getSerialized()
  132. {
  133. /* minimalcore:start */
  134. const obj = {};
  135. obj.portIn = this.portIn.getName();
  136. obj.portOut = this.portOut.getName();
  137. obj.objIn = this.portIn.op.id;
  138. obj.objOut = this.portOut.op.id;
  139. return obj;
  140. /* minimalcore:end */
  141. }
  142. /**
  143. * return a text message with human readable reason if ports can not be linked, or can be
  144. *
  145. * @param {Port} p1 port1
  146. * @param {Port} p2 port2
  147. */
  148. static canLinkText(p1, p2)
  149. {
  150. /* minimalcore:start */
  151. if (p1.direction == p2.direction)
  152. {
  153. let txt = "(out)";
  154. if (p2.direction == Port.DIR_IN) txt = "(in)";
  155. return "can not link: same direction " + txt;
  156. }
  157. if (p1.op == p2.op) return "can not link: same op";
  158. if (p1.type != Port.TYPE_DYNAMIC && p2.type != Port.TYPE_DYNAMIC)
  159. {
  160. if (p1.type != p2.type) return "can not link: different type";
  161. }
  162. if (CABLES.UI && p1.type == Port.TYPE_OBJECT && p2.type == Port.TYPE_OBJECT)
  163. {
  164. if (p1.uiAttribs.objType && p2.uiAttribs.objType)
  165. if (p1.uiAttribs.objType != p2.uiAttribs.objType)
  166. return "incompatible objects";
  167. }
  168. if (!p1) return "can not link: port 1 invalid";
  169. if (!p2) return "can not link: port 2 invalid";
  170. if (p1.direction == Port.DIR_IN && p1.isAnimated()) return "can not link: is animated";
  171. if (p2.direction == Port.DIR_IN && p2.isAnimated()) return "can not link: is animated";
  172. if (p1.isLinkedTo(p2)) return "ports already linked";
  173. if ((p1.canLink && !p1.canLink(p2)) || (p2.canLink && !p2.canLink(p1))) return "Incompatible";
  174. return "can link";
  175. /* minimalcore:end */
  176. }
  177. /**
  178. * return true if ports can be linked
  179. *
  180. * @param {Port} p1 port1
  181. * @param {Port} p2 port2
  182. * @returns {Boolean}
  183. */
  184. static canLink(p1, p2)
  185. {
  186. /* minimalcore:start */
  187. if (!p1) return false;
  188. if (!p2) return false;
  189. if (p1.direction == Port.DIR_IN && p1.isAnimated()) return false;
  190. if (p2.direction == Port.DIR_IN && p2.isAnimated()) return false;
  191. if (p1.isHidden() || p2.isHidden()) return false;
  192. if (p1.isLinkedTo(p2)) return false;
  193. if (p1.direction == p2.direction) return false;
  194. if (CABLES.UI && p1.type == Port.TYPE_OBJECT && p2.type == Port.TYPE_OBJECT)
  195. {
  196. if (p1.uiAttribs.objType && p2.uiAttribs.objType)
  197. {
  198. if (p1.uiAttribs.objType.indexOf("sg_") == 0 && p2.uiAttribs.objType.indexOf("sg_") == 0) return true;
  199. if (p1.uiAttribs.objType != p2.uiAttribs.objType)
  200. return false;
  201. }
  202. }
  203. if (p1.type != p2.type && (p1.type != Port.TYPE_DYNAMIC && p2.type != Port.TYPE_DYNAMIC)) return false;
  204. if (p1.type == Port.TYPE_DYNAMIC || p2.type == Port.TYPE_DYNAMIC) return true;
  205. if (p1.op == p2.op) return false;
  206. if (p1.canLink && !p1.canLink(p2)) return false;
  207. if (p2.canLink && !p2.canLink(p1)) return false;
  208. /* minimalcore:end */
  209. return true;
  210. }
  211. }
  212. // --------------------------------------------