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