mxEdgeSegmentHandler.js 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415
  1. /**
  2. * Copyright (c) 2006-2015, JGraph Ltd
  3. * Copyright (c) 2006-2015, Gaudenz Alder
  4. */
  5. function mxEdgeSegmentHandler(state)
  6. {
  7. mxEdgeHandler.call(this, state);
  8. };
  9. /**
  10. * Extends mxEdgeHandler.
  11. */
  12. mxUtils.extend(mxEdgeSegmentHandler, mxElbowEdgeHandler);
  13. /**
  14. * Function: getCurrentPoints
  15. *
  16. * Returns the current absolute points.
  17. */
  18. mxEdgeSegmentHandler.prototype.getCurrentPoints = function()
  19. {
  20. var pts = this.state.absolutePoints;
  21. if (pts != null)
  22. {
  23. // Special case for straight edges where we add a virtual middle handle for moving the edge
  24. var tol = Math.max(1, this.graph.view.scale);
  25. if (pts.length == 2 || (pts.length == 3 &&
  26. (Math.abs(pts[0].x - pts[1].x) < tol && Math.abs(pts[1].x - pts[2].x) < tol ||
  27. Math.abs(pts[0].y - pts[1].y) < tol && Math.abs(pts[1].y - pts[2].y) < tol)))
  28. {
  29. var cx = pts[0].x + (pts[pts.length - 1].x - pts[0].x) / 2;
  30. var cy = pts[0].y + (pts[pts.length - 1].y - pts[0].y) / 2;
  31. pts = [pts[0], new mxPoint(cx, cy), new mxPoint(cx, cy), pts[pts.length - 1]];
  32. }
  33. }
  34. return pts;
  35. };
  36. /**
  37. * Function: getPreviewPoints
  38. *
  39. * Updates the given preview state taking into account the state of the constraint handler.
  40. */
  41. mxEdgeSegmentHandler.prototype.getPreviewPoints = function(point)
  42. {
  43. if (this.isSource || this.isTarget)
  44. {
  45. return mxElbowEdgeHandler.prototype.getPreviewPoints.apply(this, arguments);
  46. }
  47. else
  48. {
  49. var pts = this.getCurrentPoints();
  50. var last = this.convertPoint(pts[0].clone(), false);
  51. point = this.convertPoint(point.clone(), false);
  52. var result = [];
  53. for (var i = 1; i < pts.length; i++)
  54. {
  55. var pt = this.convertPoint(pts[i].clone(), false);
  56. if (i == this.index)
  57. {
  58. if (Math.round(last.x - pt.x) == 0)
  59. {
  60. last.x = point.x;
  61. pt.x = point.x;
  62. }
  63. if (Math.round(last.y - pt.y) == 0)
  64. {
  65. last.y = point.y;
  66. pt.y = point.y;
  67. }
  68. }
  69. if (i < pts.length - 1)
  70. {
  71. result.push(pt);
  72. }
  73. last = pt;
  74. }
  75. // Replaces single point that intersects with source or target
  76. if (result.length == 1)
  77. {
  78. var source = this.state.getVisibleTerminalState(true);
  79. var target = this.state.getVisibleTerminalState(false);
  80. var scale = this.state.view.getScale();
  81. var tr = this.state.view.getTranslate();
  82. var x = result[0].x * scale + tr.x;
  83. var y = result[0].y * scale + tr.y;
  84. if ((source != null && mxUtils.contains(source, x, y)) ||
  85. (target != null && mxUtils.contains(target, x, y)))
  86. {
  87. result = [point, point];
  88. }
  89. }
  90. return result;
  91. }
  92. };
  93. /**
  94. * Function: updatePreviewState
  95. *
  96. * Overridden to perform optimization of the edge style result.
  97. */
  98. mxEdgeSegmentHandler.prototype.updatePreviewState = function(edge, point, terminalState, me)
  99. {
  100. mxEdgeHandler.prototype.updatePreviewState.apply(this, arguments);
  101. // Checks and corrects preview by running edge style again
  102. if (!this.isSource && !this.isTarget)
  103. {
  104. point = this.convertPoint(point.clone(), false);
  105. var pts = edge.absolutePoints;
  106. var pt0 = pts[0];
  107. var pt1 = pts[1];
  108. var result = [];
  109. for (var i = 2; i < pts.length; i++)
  110. {
  111. var pt2 = pts[i];
  112. // Merges adjacent segments only if more than 2 to allow for straight edges
  113. if ((Math.round(pt0.x - pt1.x) != 0 || Math.round(pt1.x - pt2.x) != 0) &&
  114. (Math.round(pt0.y - pt1.y) != 0 || Math.round(pt1.y - pt2.y) != 0))
  115. {
  116. result.push(this.convertPoint(pt1.clone(), false));
  117. }
  118. pt0 = pt1;
  119. pt1 = pt2;
  120. }
  121. var source = this.state.getVisibleTerminalState(true);
  122. var target = this.state.getVisibleTerminalState(false);
  123. var rpts = this.state.absolutePoints;
  124. // A straight line is represented by 3 handles
  125. if (result.length == 0 && (Math.round(pts[0].x - pts[pts.length - 1].x) == 0 ||
  126. Math.round(pts[0].y - pts[pts.length - 1].y) == 0))
  127. {
  128. result = [point, point];
  129. }
  130. // Handles special case of transitions from straight vertical to routed
  131. else if (pts.length == 5 && result.length == 2 && source != null && target != null &&
  132. rpts != null && Math.round(rpts[0].x - rpts[rpts.length - 1].x) == 0)
  133. {
  134. var view = this.graph.getView();
  135. var scale = view.getScale();
  136. var tr = view.getTranslate();
  137. var y0 = view.getRoutingCenterY(source) / scale - tr.y;
  138. // Use fixed connection point y-coordinate if one exists
  139. var sc = this.graph.getConnectionConstraint(edge, source, true);
  140. if (sc != null)
  141. {
  142. var pt = this.graph.getConnectionPoint(source, sc);
  143. if (pt != null)
  144. {
  145. this.convertPoint(pt, false);
  146. y0 = pt.y;
  147. }
  148. }
  149. var ye = view.getRoutingCenterY(target) / scale - tr.y;
  150. // Use fixed connection point y-coordinate if one exists
  151. var tc = this.graph.getConnectionConstraint(edge, target, false);
  152. if (tc)
  153. {
  154. var pt = this.graph.getConnectionPoint(target, tc);
  155. if (pt != null)
  156. {
  157. this.convertPoint(pt, false);
  158. ye = pt.y;
  159. }
  160. }
  161. result = [new mxPoint(point.x, y0), new mxPoint(point.x, ye)];
  162. }
  163. this.points = result;
  164. // LATER: Check if points and result are different
  165. edge.view.updateFixedTerminalPoints(edge, source, target);
  166. edge.view.updatePoints(edge, this.points, source, target);
  167. edge.view.updateFloatingTerminalPoints(edge, source, target);
  168. }
  169. };
  170. /**
  171. * Overriden to merge edge segments.
  172. */
  173. mxEdgeSegmentHandler.prototype.connect = function(edge, terminal, isSource, isClone, me)
  174. {
  175. var model = this.graph.getModel();
  176. var geo = model.getGeometry(edge);
  177. var result = null;
  178. // Merges adjacent edge segments
  179. if (geo != null && geo.points != null && geo.points.length > 0)
  180. {
  181. var pts = this.abspoints;
  182. var pt0 = pts[0];
  183. var pt1 = pts[1];
  184. result = [];
  185. for (var i = 2; i < pts.length; i++)
  186. {
  187. var pt2 = pts[i];
  188. // Merges adjacent segments only if more than 2 to allow for straight edges
  189. if ((Math.round(pt0.x - pt1.x) != 0 || Math.round(pt1.x - pt2.x) != 0) &&
  190. (Math.round(pt0.y - pt1.y) != 0 || Math.round(pt1.y - pt2.y) != 0))
  191. {
  192. result.push(this.convertPoint(pt1.clone(), false));
  193. }
  194. pt0 = pt1;
  195. pt1 = pt2;
  196. }
  197. }
  198. model.beginUpdate();
  199. try
  200. {
  201. if (result != null)
  202. {
  203. var geo = model.getGeometry(edge);
  204. if (geo != null)
  205. {
  206. geo = geo.clone();
  207. geo.points = result;
  208. model.setGeometry(edge, geo);
  209. }
  210. }
  211. edge = mxEdgeHandler.prototype.connect.apply(this, arguments);
  212. }
  213. finally
  214. {
  215. model.endUpdate();
  216. }
  217. return edge;
  218. };
  219. /**
  220. * Function: getTooltipForNode
  221. *
  222. * Returns no tooltips.
  223. */
  224. mxEdgeSegmentHandler.prototype.getTooltipForNode = function(node)
  225. {
  226. return null;
  227. };
  228. /**
  229. * Function: start
  230. *
  231. * Starts the handling of the mouse gesture.
  232. */
  233. mxEdgeSegmentHandler.prototype.start = function(x, y, index)
  234. {
  235. mxEdgeHandler.prototype.start.apply(this, arguments);
  236. if (this.bends != null && this.bends[index] != null &&
  237. !this.isSource && !this.isTarget)
  238. {
  239. mxUtils.setOpacity(this.bends[index].node, 100);
  240. }
  241. };
  242. /**
  243. * Function: createBends
  244. *
  245. * Adds custom bends for the center of each segment.
  246. */
  247. mxEdgeSegmentHandler.prototype.createBends = function()
  248. {
  249. var bends = [];
  250. // Source
  251. var bend = this.createHandleShape(0);
  252. this.initBend(bend);
  253. bend.setCursor(mxConstants.CURSOR_TERMINAL_HANDLE);
  254. bends.push(bend);
  255. var pts = this.getCurrentPoints();
  256. // Waypoints (segment handles)
  257. if (this.graph.isCellBendable(this.state.cell))
  258. {
  259. if (this.points == null)
  260. {
  261. this.points = [];
  262. }
  263. for (var i = 0; i < pts.length - 1; i++)
  264. {
  265. bend = this.createVirtualBend();
  266. bends.push(bend);
  267. var horizontal = Math.round(pts[i].x - pts[i + 1].x) == 0;
  268. // Special case where dy is 0 as well
  269. if (Math.round(pts[i].y - pts[i + 1].y) == 0 && i < pts.length - 2)
  270. {
  271. horizontal = Math.round(pts[i].x - pts[i + 2].x) == 0;
  272. }
  273. bend.setCursor((horizontal) ? 'col-resize' : 'row-resize');
  274. this.points.push(new mxPoint(0,0));
  275. }
  276. }
  277. // Target
  278. var bend = this.createHandleShape(pts.length);
  279. this.initBend(bend);
  280. bend.setCursor(mxConstants.CURSOR_TERMINAL_HANDLE);
  281. bends.push(bend);
  282. return bends;
  283. };
  284. /**
  285. * Function: redraw
  286. *
  287. * Overridden to invoke <refresh> before the redraw.
  288. */
  289. mxEdgeSegmentHandler.prototype.redraw = function()
  290. {
  291. this.refresh();
  292. mxEdgeHandler.prototype.redraw.apply(this, arguments);
  293. };
  294. /**
  295. * Function: redrawInnerBends
  296. *
  297. * Updates the position of the custom bends.
  298. */
  299. mxEdgeSegmentHandler.prototype.redrawInnerBends = function(p0, pe)
  300. {
  301. if (this.graph.isCellBendable(this.state.cell))
  302. {
  303. var pts = this.getCurrentPoints();
  304. if (pts != null && pts.length > 1)
  305. {
  306. var straight = false;
  307. // Puts handle in the center of straight edges
  308. if (pts.length == 4 && Math.round(pts[1].x - pts[2].x) == 0 && Math.round(pts[1].y - pts[2].y) == 0)
  309. {
  310. straight = true;
  311. if (Math.round(pts[0].y - pts[pts.length - 1].y) == 0)
  312. {
  313. var cx = pts[0].x + (pts[pts.length - 1].x - pts[0].x) / 2;
  314. pts[1] = new mxPoint(cx, pts[1].y);
  315. pts[2] = new mxPoint(cx, pts[2].y);
  316. }
  317. else
  318. {
  319. var cy = pts[0].y + (pts[pts.length - 1].y - pts[0].y) / 2;
  320. pts[1] = new mxPoint(pts[1].x, cy);
  321. pts[2] = new mxPoint(pts[2].x, cy);
  322. }
  323. }
  324. for (var i = 0; i < pts.length - 1; i++)
  325. {
  326. if (this.bends[i + 1] != null)
  327. {
  328. var p0 = pts[i];
  329. var pe = pts[i + 1];
  330. var pt = new mxPoint(p0.x + (pe.x - p0.x) / 2, p0.y + (pe.y - p0.y) / 2);
  331. var b = this.bends[i + 1].bounds;
  332. this.bends[i + 1].bounds = new mxRectangle(Math.floor(pt.x - b.width / 2),
  333. Math.floor(pt.y - b.height / 2), b.width, b.height);
  334. this.bends[i + 1].redraw();
  335. if (this.manageLabelHandle)
  336. {
  337. this.checkLabelHandle(this.bends[i + 1].bounds);
  338. }
  339. }
  340. }
  341. if (straight)
  342. {
  343. mxUtils.setOpacity(this.bends[1].node, this.virtualBendOpacity);
  344. mxUtils.setOpacity(this.bends[3].node, this.virtualBendOpacity);
  345. }
  346. }
  347. }
  348. };
  349. __mxOutput.mxEdgeSegmentHandler = typeof mxEdgeSegmentHandler !== 'undefined' ? mxEdgeSegmentHandler : undefined;