mxEdgeStyle.js 42 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672
  1. /**
  2. * Copyright (c) 2006-2015, JGraph Ltd
  3. * Copyright (c) 2006-2015, Gaudenz Alder
  4. */
  5. var mxEdgeStyle =
  6. {
  7. /**
  8. * Class: mxEdgeStyle
  9. *
  10. * Provides various edge styles to be used as the values for
  11. * <mxConstants.STYLE_EDGE> in a cell style.
  12. *
  13. * Example:
  14. *
  15. * (code)
  16. * var style = stylesheet.getDefaultEdgeStyle();
  17. * style[mxConstants.STYLE_EDGE] = mxEdgeStyle.ElbowConnector;
  18. * (end)
  19. *
  20. * Sets the default edge style to <ElbowConnector>.
  21. *
  22. * Custom edge style:
  23. *
  24. * To write a custom edge style, a function must be added to the mxEdgeStyle
  25. * object as follows:
  26. *
  27. * (code)
  28. * mxEdgeStyle.MyStyle = function(state, source, target, points, result)
  29. * {
  30. * if (source != null && target != null)
  31. * {
  32. * var pt = new mxPoint(target.getCenterX(), source.getCenterY());
  33. *
  34. * if (mxUtils.contains(source, pt.x, pt.y))
  35. * {
  36. * pt.y = source.y + source.height;
  37. * }
  38. *
  39. * result.push(pt);
  40. * }
  41. * };
  42. * (end)
  43. *
  44. * In the above example, a right angle is created using a point on the
  45. * horizontal center of the target vertex and the vertical center of the source
  46. * vertex. The code checks if that point intersects the source vertex and makes
  47. * the edge straight if it does. The point is then added into the result array,
  48. * which acts as the return value of the function.
  49. *
  50. * The new edge style should then be registered in the <mxStyleRegistry> as follows:
  51. * (code)
  52. * mxStyleRegistry.putValue('myEdgeStyle', mxEdgeStyle.MyStyle);
  53. * (end)
  54. *
  55. * The custom edge style above can now be used in a specific edge as follows:
  56. *
  57. * (code)
  58. * model.setStyle(edge, 'edgeStyle=myEdgeStyle');
  59. * (end)
  60. *
  61. * Note that the key of the <mxStyleRegistry> entry for the function should
  62. * be used in string values, unless <mxGraphView.allowEval> is true, in
  63. * which case you can also use mxEdgeStyle.MyStyle for the value in the
  64. * cell style above.
  65. *
  66. * Or it can be used for all edges in the graph as follows:
  67. *
  68. * (code)
  69. * var style = graph.getStylesheet().getDefaultEdgeStyle();
  70. * style[mxConstants.STYLE_EDGE] = mxEdgeStyle.MyStyle;
  71. * (end)
  72. *
  73. * Note that the object can be used directly when programmatically setting
  74. * the value, but the key in the <mxStyleRegistry> should be used when
  75. * setting the value via a key, value pair in a cell style.
  76. *
  77. * Function: EntityRelation
  78. *
  79. * Implements an entity relation style for edges (as used in database
  80. * schema diagrams). At the time the function is called, the result
  81. * array contains a placeholder (null) for the first absolute point,
  82. * that is, the point where the edge and source terminal are connected.
  83. * The implementation of the style then adds all intermediate waypoints
  84. * except for the last point, that is, the connection point between the
  85. * edge and the target terminal. The first ant the last point in the
  86. * result array are then replaced with mxPoints that take into account
  87. * the terminal's perimeter and next point on the edge.
  88. *
  89. * Parameters:
  90. *
  91. * state - <mxCellState> that represents the edge to be updated.
  92. * source - <mxCellState> that represents the source terminal.
  93. * target - <mxCellState> that represents the target terminal.
  94. * points - List of relative control points.
  95. * result - Array of <mxPoints> that represent the actual points of the
  96. * edge.
  97. */
  98. EntityRelation: function(state, source, target, points, result)
  99. {
  100. var view = state.view;
  101. var graph = view.graph;
  102. var segment = mxUtils.getValue(state.style,
  103. mxConstants.STYLE_SEGMENT,
  104. mxConstants.ENTITY_SEGMENT) * view.scale;
  105. var pts = state.absolutePoints;
  106. var p0 = pts[0];
  107. var pe = pts[pts.length-1];
  108. var isSourceLeft = false;
  109. if (source != null)
  110. {
  111. var sourceGeometry = graph.getCellGeometry(source.cell);
  112. if (sourceGeometry.relative)
  113. {
  114. isSourceLeft = sourceGeometry.x <= 0.5;
  115. }
  116. else if (target != null)
  117. {
  118. isSourceLeft = ((pe != null) ? pe.x : target.x + target.width) < ((p0 != null) ? p0.x : source.x);
  119. }
  120. }
  121. if (p0 != null)
  122. {
  123. source = new mxCellState();
  124. source.x = p0.x;
  125. source.y = p0.y;
  126. }
  127. else if (source != null)
  128. {
  129. var constraint = mxUtils.getPortConstraints(source, state, true, mxConstants.DIRECTION_MASK_NONE);
  130. if (constraint != mxConstants.DIRECTION_MASK_NONE && constraint != mxConstants.DIRECTION_MASK_WEST +
  131. mxConstants.DIRECTION_MASK_EAST)
  132. {
  133. isSourceLeft = constraint == mxConstants.DIRECTION_MASK_WEST;
  134. }
  135. }
  136. else
  137. {
  138. return;
  139. }
  140. var isTargetLeft = true;
  141. if (target != null)
  142. {
  143. var targetGeometry = graph.getCellGeometry(target.cell);
  144. if (targetGeometry.relative)
  145. {
  146. isTargetLeft = targetGeometry.x <= 0.5;
  147. }
  148. else if (source != null)
  149. {
  150. isTargetLeft = ((p0 != null) ? p0.x : source.x + source.width) < ((pe != null) ? pe.x : target.x);
  151. }
  152. }
  153. if (pe != null)
  154. {
  155. target = new mxCellState();
  156. target.x = pe.x;
  157. target.y = pe.y;
  158. }
  159. else if (target != null)
  160. {
  161. var constraint = mxUtils.getPortConstraints(target, state, false, mxConstants.DIRECTION_MASK_NONE);
  162. if (constraint != mxConstants.DIRECTION_MASK_NONE && constraint != mxConstants.DIRECTION_MASK_WEST +
  163. mxConstants.DIRECTION_MASK_EAST)
  164. {
  165. isTargetLeft = constraint == mxConstants.DIRECTION_MASK_WEST;
  166. }
  167. }
  168. if (source != null && target != null)
  169. {
  170. var x0 = (isSourceLeft) ? source.x : source.x + source.width;
  171. var y0 = view.getRoutingCenterY(source);
  172. var xe = (isTargetLeft) ? target.x : target.x + target.width;
  173. var ye = view.getRoutingCenterY(target);
  174. var seg = segment;
  175. var dx = (isSourceLeft) ? -seg : seg;
  176. var dep = new mxPoint(x0 + dx, y0);
  177. dx = (isTargetLeft) ? -seg : seg;
  178. var arr = new mxPoint(xe + dx, ye);
  179. // Adds intermediate points if both go out on same side
  180. if (isSourceLeft == isTargetLeft)
  181. {
  182. var x = (isSourceLeft) ?
  183. Math.min(x0, xe)-segment :
  184. Math.max(x0, xe)+segment;
  185. result.push(new mxPoint(x, y0));
  186. result.push(new mxPoint(x, ye));
  187. }
  188. else if ((dep.x < arr.x) == isSourceLeft)
  189. {
  190. var midY = y0 + (ye - y0) / 2;
  191. result.push(dep);
  192. result.push(new mxPoint(dep.x, midY));
  193. result.push(new mxPoint(arr.x, midY));
  194. result.push(arr);
  195. }
  196. else
  197. {
  198. result.push(dep);
  199. result.push(arr);
  200. }
  201. }
  202. },
  203. /**
  204. * Function: Loop
  205. *
  206. * Implements a self-reference, aka. loop.
  207. */
  208. Loop: function(state, source, target, points, result)
  209. {
  210. var pts = state.absolutePoints;
  211. var p0 = pts[0];
  212. var pe = pts[pts.length-1];
  213. if (p0 != null && pe != null)
  214. {
  215. if (points != null && points.length > 0)
  216. {
  217. for (var i = 0; i < points.length; i++)
  218. {
  219. var pt = points[i];
  220. pt = state.view.transformControlPoint(state, pt);
  221. result.push(new mxPoint(pt.x, pt.y));
  222. }
  223. }
  224. return;
  225. }
  226. if (source != null)
  227. {
  228. var view = state.view;
  229. var graph = view.graph;
  230. var pt = (points != null && points.length > 0) ? points[0] : null;
  231. if (pt != null)
  232. {
  233. pt = view.transformControlPoint(state, pt);
  234. if (mxUtils.contains(source, pt.x, pt.y))
  235. {
  236. pt = null;
  237. }
  238. }
  239. var x = 0;
  240. var dx = 0;
  241. var y = 0;
  242. var dy = 0;
  243. var seg = mxUtils.getValue(state.style, mxConstants.STYLE_SEGMENT,
  244. graph.gridSize) * view.scale;
  245. var dir = mxUtils.getValue(state.style, mxConstants.STYLE_DIRECTION,
  246. mxConstants.DIRECTION_WEST);
  247. if (dir == mxConstants.DIRECTION_NORTH ||
  248. dir == mxConstants.DIRECTION_SOUTH)
  249. {
  250. x = view.getRoutingCenterX(source);
  251. dx = seg;
  252. }
  253. else
  254. {
  255. y = view.getRoutingCenterY(source);
  256. dy = seg;
  257. }
  258. if (pt == null ||
  259. pt.x < source.x ||
  260. pt.x > source.x + source.width)
  261. {
  262. if (pt != null)
  263. {
  264. x = pt.x;
  265. dy = Math.max(Math.abs(y - pt.y), dy);
  266. }
  267. else
  268. {
  269. if (dir == mxConstants.DIRECTION_NORTH)
  270. {
  271. y = source.y - 2 * dx;
  272. }
  273. else if (dir == mxConstants.DIRECTION_SOUTH)
  274. {
  275. y = source.y + source.height + 2 * dx;
  276. }
  277. else if (dir == mxConstants.DIRECTION_EAST)
  278. {
  279. x = source.x - 2 * dy;
  280. }
  281. else
  282. {
  283. x = source.x + source.width + 2 * dy;
  284. }
  285. }
  286. }
  287. else if (pt != null)
  288. {
  289. x = view.getRoutingCenterX(source);
  290. dx = Math.max(Math.abs(x - pt.x), dy);
  291. y = pt.y;
  292. dy = 0;
  293. }
  294. result.push(new mxPoint(x - dx, y - dy));
  295. result.push(new mxPoint(x + dx, y + dy));
  296. }
  297. },
  298. /**
  299. * Function: ElbowConnector
  300. *
  301. * Uses either <SideToSide> or <TopToBottom> depending on the horizontal
  302. * flag in the cell style. <SideToSide> is used if horizontal is true or
  303. * unspecified. See <EntityRelation> for a description of the
  304. * parameters.
  305. */
  306. ElbowConnector: function(state, source, target, points, result)
  307. {
  308. var pt = (points != null && points.length > 0) ? points[0] : null;
  309. var vertical = false;
  310. var horizontal = false;
  311. if (source != null && target != null)
  312. {
  313. if (pt != null)
  314. {
  315. var left = Math.min(source.x, target.x);
  316. var right = Math.max(source.x + source.width,
  317. target.x + target.width);
  318. var top = Math.min(source.y, target.y);
  319. var bottom = Math.max(source.y + source.height,
  320. target.y + target.height);
  321. pt = state.view.transformControlPoint(state, pt);
  322. vertical = pt.y < top || pt.y > bottom;
  323. horizontal = pt.x < left || pt.x > right;
  324. }
  325. else
  326. {
  327. var left = Math.max(source.x, target.x);
  328. var right = Math.min(source.x + source.width,
  329. target.x + target.width);
  330. vertical = left == right;
  331. if (!vertical)
  332. {
  333. var top = Math.max(source.y, target.y);
  334. var bottom = Math.min(source.y + source.height,
  335. target.y + target.height);
  336. horizontal = top == bottom;
  337. }
  338. }
  339. }
  340. if (!horizontal && (vertical ||
  341. state.style[mxConstants.STYLE_ELBOW] == mxConstants.ELBOW_VERTICAL))
  342. {
  343. mxEdgeStyle.TopToBottom(state, source, target, points, result);
  344. }
  345. else
  346. {
  347. mxEdgeStyle.SideToSide(state, source, target, points, result);
  348. }
  349. },
  350. /**
  351. * Function: SideToSide
  352. *
  353. * Implements a vertical elbow edge. See <EntityRelation> for a description
  354. * of the parameters.
  355. */
  356. SideToSide: function(state, source, target, points, result)
  357. {
  358. var view = state.view;
  359. var pt = (points != null && points.length > 0) ? points[0] : null;
  360. var pts = state.absolutePoints;
  361. var p0 = pts[0];
  362. var pe = pts[pts.length-1];
  363. if (pt != null)
  364. {
  365. pt = view.transformControlPoint(state, pt);
  366. }
  367. if (p0 != null)
  368. {
  369. source = new mxCellState();
  370. source.x = p0.x;
  371. source.y = p0.y;
  372. }
  373. if (pe != null)
  374. {
  375. target = new mxCellState();
  376. target.x = pe.x;
  377. target.y = pe.y;
  378. }
  379. if (source != null && target != null)
  380. {
  381. var l = Math.max(source.x, target.x);
  382. var r = Math.min(source.x + source.width,
  383. target.x + target.width);
  384. var x = (pt != null) ? pt.x : Math.round(r + (l - r) / 2);
  385. var y1 = view.getRoutingCenterY(source);
  386. var y2 = view.getRoutingCenterY(target);
  387. if (pt != null)
  388. {
  389. if (pt.y >= source.y && pt.y <= source.y + source.height)
  390. {
  391. y1 = pt.y;
  392. }
  393. if (pt.y >= target.y && pt.y <= target.y + target.height)
  394. {
  395. y2 = pt.y;
  396. }
  397. }
  398. if (!mxUtils.contains(target, x, y1) &&
  399. !mxUtils.contains(source, x, y1))
  400. {
  401. result.push(new mxPoint(x, y1));
  402. }
  403. if (!mxUtils.contains(target, x, y2) &&
  404. !mxUtils.contains(source, x, y2))
  405. {
  406. result.push(new mxPoint(x, y2));
  407. }
  408. if (result.length == 1)
  409. {
  410. if (pt != null)
  411. {
  412. if (!mxUtils.contains(target, x, pt.y) &&
  413. !mxUtils.contains(source, x, pt.y))
  414. {
  415. result.push(new mxPoint(x, pt.y));
  416. }
  417. }
  418. else
  419. {
  420. var t = Math.max(source.y, target.y);
  421. var b = Math.min(source.y + source.height,
  422. target.y + target.height);
  423. result.push(new mxPoint(x, t + (b - t) / 2));
  424. }
  425. }
  426. }
  427. },
  428. /**
  429. * Function: TopToBottom
  430. *
  431. * Implements a horizontal elbow edge. See <EntityRelation> for a
  432. * description of the parameters.
  433. */
  434. TopToBottom: function(state, source, target, points, result)
  435. {
  436. var view = state.view;
  437. var pt = (points != null && points.length > 0) ? points[0] : null;
  438. var pts = state.absolutePoints;
  439. var p0 = pts[0];
  440. var pe = pts[pts.length-1];
  441. if (pt != null)
  442. {
  443. pt = view.transformControlPoint(state, pt);
  444. }
  445. if (p0 != null)
  446. {
  447. source = new mxCellState();
  448. source.x = p0.x;
  449. source.y = p0.y;
  450. }
  451. if (pe != null)
  452. {
  453. target = new mxCellState();
  454. target.x = pe.x;
  455. target.y = pe.y;
  456. }
  457. if (source != null && target != null)
  458. {
  459. var t = Math.max(source.y, target.y);
  460. var b = Math.min(source.y + source.height,
  461. target.y + target.height);
  462. var x = view.getRoutingCenterX(source);
  463. if (pt != null &&
  464. pt.x >= source.x &&
  465. pt.x <= source.x + source.width)
  466. {
  467. x = pt.x;
  468. }
  469. var y = (pt != null) ? pt.y : Math.round(b + (t - b) / 2);
  470. if (!mxUtils.contains(target, x, y) &&
  471. !mxUtils.contains(source, x, y))
  472. {
  473. result.push(new mxPoint(x, y));
  474. }
  475. if (pt != null &&
  476. pt.x >= target.x &&
  477. pt.x <= target.x + target.width)
  478. {
  479. x = pt.x;
  480. }
  481. else
  482. {
  483. x = view.getRoutingCenterX(target);
  484. }
  485. if (!mxUtils.contains(target, x, y) &&
  486. !mxUtils.contains(source, x, y))
  487. {
  488. result.push(new mxPoint(x, y));
  489. }
  490. if (result.length == 1)
  491. {
  492. if (pt != null && result.length == 1)
  493. {
  494. if (!mxUtils.contains(target, pt.x, y) &&
  495. !mxUtils.contains(source, pt.x, y))
  496. {
  497. result.push(new mxPoint(pt.x, y));
  498. }
  499. }
  500. else
  501. {
  502. var l = Math.max(source.x, target.x);
  503. var r = Math.min(source.x + source.width,
  504. target.x + target.width);
  505. result.push(new mxPoint(l + (r - l) / 2, y));
  506. }
  507. }
  508. }
  509. },
  510. /**
  511. * Function: SegmentConnector
  512. *
  513. * Implements an orthogonal edge style. Use <mxEdgeSegmentHandler>
  514. * as an interactive handler for this style.
  515. *
  516. * state - <mxCellState> that represents the edge to be updated.
  517. * sourceScaled - <mxCellState> that represents the source terminal.
  518. * targetScaled - <mxCellState> that represents the target terminal.
  519. * controlHints - List of relative control points.
  520. * result - Array of <mxPoints> that represent the actual points of the
  521. * edge.
  522. */
  523. SegmentConnector: function(state, sourceScaled, targetScaled, controlHints, result)
  524. {
  525. // Creates array of all way- and terminalpoints
  526. var pts = mxEdgeStyle.scalePointArray(state.absolutePoints, state.view.scale);
  527. var source = mxEdgeStyle.scaleCellState(sourceScaled, state.view.scale);
  528. var target = mxEdgeStyle.scaleCellState(targetScaled, state.view.scale);
  529. var tol = 1;
  530. // Adds translated unscaled points for precise collision checks
  531. var tempPoints = [];
  532. function addPoint(pt)
  533. {
  534. tempPoints.push(pt);
  535. };
  536. // Whether the first segment outgoing from the source end is horizontal
  537. var lastPushed = (result.length > 0) ? result[0] : null;
  538. var horizontal = true;
  539. var hint = null;
  540. // Adds waypoints only if outside of tolerance
  541. function pushPoint(pt)
  542. {
  543. pt.x = Math.round(pt.x * state.view.scale * 10) / 10;
  544. pt.y = Math.round(pt.y * state.view.scale * 10) / 10;
  545. if (lastPushed == null || Math.abs(lastPushed.x - pt.x) >= tol || Math.abs(lastPushed.y - pt.y) >= Math.max(1, state.view.scale))
  546. {
  547. result.push(pt);
  548. lastPushed = pt;
  549. }
  550. return lastPushed;
  551. };
  552. // Adds the first point
  553. var pt = pts[0];
  554. if (pt == null && source != null)
  555. {
  556. pt = new mxPoint(state.view.getRoutingCenterX(source), state.view.getRoutingCenterY(source));
  557. }
  558. else if (pt != null)
  559. {
  560. pt = pt.clone();
  561. }
  562. var lastInx = pts.length - 1;
  563. // Adds the waypoints
  564. if (controlHints != null && controlHints.length > 0)
  565. {
  566. // Converts all hints and removes nulls
  567. var hints = [];
  568. for (var i = 0; i < controlHints.length; i++)
  569. {
  570. var tmp = state.view.transformControlPoint(state, controlHints[i], true);
  571. if (tmp != null)
  572. {
  573. hints.push(tmp);
  574. }
  575. }
  576. if (hints.length == 0)
  577. {
  578. return;
  579. }
  580. // Aligns source and target hint to fixed points
  581. if (pt != null && hints[0] != null)
  582. {
  583. if (Math.abs(hints[0].x - pt.x) < tol)
  584. {
  585. hints[0].x = pt.x;
  586. }
  587. if (Math.abs(hints[0].y - pt.y) < tol)
  588. {
  589. hints[0].y = pt.y;
  590. }
  591. }
  592. var pe = pts[lastInx];
  593. if (pe != null && hints[hints.length - 1] != null)
  594. {
  595. if (Math.abs(hints[hints.length - 1].x - pe.x) < tol)
  596. {
  597. hints[hints.length - 1].x = pe.x;
  598. }
  599. if (Math.abs(hints[hints.length - 1].y - pe.y) < tol)
  600. {
  601. hints[hints.length - 1].y = pe.y;
  602. }
  603. }
  604. hint = hints[0];
  605. var currentTerm = source;
  606. var currentPt = pts[0];
  607. var hozChan = false;
  608. var vertChan = false;
  609. var currentHint = hint;
  610. if (currentPt != null)
  611. {
  612. currentTerm = null;
  613. }
  614. // Check for alignment with fixed points and with channels
  615. // at source and target segments only
  616. for (var i = 0; i < 2; i++)
  617. {
  618. var fixedVertAlign = currentPt != null && currentPt.x == currentHint.x;
  619. var fixedHozAlign = currentPt != null && currentPt.y == currentHint.y;
  620. var inHozChan = currentTerm != null && (currentHint.y >= currentTerm.y &&
  621. currentHint.y <= currentTerm.y + currentTerm.height);
  622. var inVertChan = currentTerm != null && (currentHint.x >= currentTerm.x &&
  623. currentHint.x <= currentTerm.x + currentTerm.width);
  624. hozChan = fixedHozAlign || (currentPt == null && inHozChan);
  625. vertChan = fixedVertAlign || (currentPt == null && inVertChan);
  626. // If the current hint falls in both the hor and vert channels in the case
  627. // of a floating port, or if the hint is exactly co-incident with a
  628. // fixed point, ignore the source and try to work out the orientation
  629. // from the target end
  630. if (i==0 && ((hozChan && vertChan) || (fixedVertAlign && fixedHozAlign)))
  631. {
  632. }
  633. else
  634. {
  635. if (currentPt != null && (!fixedHozAlign && !fixedVertAlign) && (inHozChan || inVertChan))
  636. {
  637. horizontal = inHozChan ? false : true;
  638. break;
  639. }
  640. if (vertChan || hozChan)
  641. {
  642. horizontal = hozChan;
  643. if (i == 1)
  644. {
  645. // Work back from target end
  646. horizontal = hints.length % 2 == 0 ? hozChan : vertChan;
  647. }
  648. break;
  649. }
  650. }
  651. currentTerm = target;
  652. currentPt = pts[lastInx];
  653. if (currentPt != null)
  654. {
  655. currentTerm = null;
  656. }
  657. currentHint = hints[hints.length - 1];
  658. if (fixedVertAlign && fixedHozAlign)
  659. {
  660. hints = hints.slice(1);
  661. }
  662. }
  663. if (horizontal && ((pts[0] != null && pts[0].y != hint.y) ||
  664. (pts[0] == null && source != null &&
  665. (hint.y < source.y || hint.y > source.y + source.height))))
  666. {
  667. addPoint(new mxPoint(pt.x, hint.y));
  668. }
  669. else if (!horizontal && ((pts[0] != null && pts[0].x != hint.x) ||
  670. (pts[0] == null && source != null &&
  671. (hint.x < source.x || hint.x > source.x + source.width))))
  672. {
  673. addPoint(new mxPoint(hint.x, pt.y));
  674. }
  675. if (horizontal)
  676. {
  677. pt.y = hint.y;
  678. }
  679. else
  680. {
  681. pt.x = hint.x;
  682. }
  683. for (var i = 0; i < hints.length; i++)
  684. {
  685. horizontal = !horizontal;
  686. hint = hints[i];
  687. // mxLog.show();
  688. // mxLog.debug('hint', i, hint.x, hint.y);
  689. if (horizontal)
  690. {
  691. pt.y = hint.y;
  692. }
  693. else
  694. {
  695. pt.x = hint.x;
  696. }
  697. addPoint(pt.clone());
  698. }
  699. }
  700. else
  701. {
  702. hint = pt;
  703. // FIXME: First click in connect preview toggles orientation
  704. horizontal = true;
  705. }
  706. // Adds the last point
  707. pt = pts[lastInx];
  708. if (pt == null && target != null)
  709. {
  710. pt = new mxPoint(state.view.getRoutingCenterX(target), state.view.getRoutingCenterY(target));
  711. }
  712. if (pt != null)
  713. {
  714. if (hint != null)
  715. {
  716. if (horizontal && ((pts[lastInx] != null && pts[lastInx].y != hint.y) ||
  717. (pts[lastInx] == null && target != null &&
  718. (hint.y < target.y || hint.y > target.y + target.height))))
  719. {
  720. addPoint(new mxPoint(pt.x, hint.y));
  721. }
  722. else if (!horizontal && ((pts[lastInx] != null && pts[lastInx].x != hint.x) ||
  723. (pts[lastInx] == null && target != null &&
  724. (hint.x < target.x || hint.x > target.x + target.width))))
  725. {
  726. addPoint(new mxPoint(hint.x, pt.y));
  727. }
  728. }
  729. }
  730. // Removes bends inside the source terminal
  731. if (pts[0] == null && source != null)
  732. {
  733. while (tempPoints.length > 0 && tempPoints[0] != null &&
  734. mxUtils.contains(source, tempPoints[0].x, tempPoints[0].y))
  735. {
  736. tempPoints.splice(0, 1);
  737. }
  738. }
  739. // Removes bends inside the target terminal
  740. if (pts[lastInx] == null && target != null)
  741. {
  742. while (tempPoints.length > 0 && tempPoints[tempPoints.length - 1] != null &&
  743. mxUtils.contains(target, tempPoints[tempPoints.length - 1].x, tempPoints[tempPoints.length - 1].y))
  744. {
  745. tempPoints.splice(tempPoints.length - 1, 1);
  746. }
  747. }
  748. // Scales and smoothens edges
  749. for (var i = 0; i < tempPoints.length; i++)
  750. {
  751. pushPoint(tempPoints[i]);
  752. }
  753. // Removes last point if inside tolerance with end point
  754. if (pe != null && result[result.length - 1] != null &&
  755. Math.abs(pe.x - result[result.length - 1].x) <= tol &&
  756. Math.abs(pe.y - result[result.length - 1].y) <= tol)
  757. {
  758. result.splice(result.length - 1, 1);
  759. // Lines up second last point in result with end point
  760. if (result[result.length - 1] != null)
  761. {
  762. if (Math.abs(result[result.length - 1].x - pe.x) < tol)
  763. {
  764. result[result.length - 1].x = pe.x;
  765. }
  766. if (Math.abs(result[result.length - 1].y - pe.y) < tol)
  767. {
  768. result[result.length - 1].y = pe.y;
  769. }
  770. }
  771. }
  772. },
  773. orthBuffer: 10,
  774. orthPointsFallback: true,
  775. dirVectors: [ [ -1, 0 ],
  776. [ 0, -1 ], [ 1, 0 ], [ 0, 1 ], [ -1, 0 ], [ 0, -1 ], [ 1, 0 ] ],
  777. wayPoints1: [ [ 0, 0], [ 0, 0], [ 0, 0], [ 0, 0], [ 0, 0], [ 0, 0],
  778. [ 0, 0], [ 0, 0], [ 0, 0], [ 0, 0], [ 0, 0], [ 0, 0] ],
  779. routePatterns: [
  780. [ [ 513, 2308, 2081, 2562 ], [ 513, 1090, 514, 2184, 2114, 2561 ],
  781. [ 513, 1090, 514, 2564, 2184, 2562 ],
  782. [ 513, 2308, 2561, 1090, 514, 2568, 2308 ] ],
  783. [ [ 514, 1057, 513, 2308, 2081, 2562 ], [ 514, 2184, 2114, 2561 ],
  784. [ 514, 2184, 2562, 1057, 513, 2564, 2184 ],
  785. [ 514, 1057, 513, 2568, 2308, 2561 ] ],
  786. [ [ 1090, 514, 1057, 513, 2308, 2081, 2562 ], [ 2114, 2561 ],
  787. [ 1090, 2562, 1057, 513, 2564, 2184 ],
  788. [ 1090, 514, 1057, 513, 2308, 2561, 2568 ] ],
  789. [ [ 2081, 2562 ], [ 1057, 513, 1090, 514, 2184, 2114, 2561 ],
  790. [ 1057, 513, 1090, 514, 2184, 2562, 2564 ],
  791. [ 1057, 2561, 1090, 514, 2568, 2308 ] ] ],
  792. inlineRoutePatterns: [
  793. [ null, [ 2114, 2568 ], null, null ],
  794. [ null, [ 514, 2081, 2114, 2568 ] , null, null ],
  795. [ null, [ 2114, 2561 ], null, null ],
  796. [ [ 2081, 2562 ], [ 1057, 2114, 2568 ],
  797. [ 2184, 2562 ],
  798. null ] ],
  799. vertexSeperations: [],
  800. limits: [
  801. [ 0, 0, 0, 0, 0, 0, 0, 0, 0 ],
  802. [ 0, 0, 0, 0, 0, 0, 0, 0, 0 ] ],
  803. LEFT_MASK: 32,
  804. TOP_MASK: 64,
  805. RIGHT_MASK: 128,
  806. BOTTOM_MASK: 256,
  807. LEFT: 1,
  808. TOP: 2,
  809. RIGHT: 4,
  810. BOTTOM: 8,
  811. // TODO remove magic numbers
  812. SIDE_MASK: 480,
  813. //mxEdgeStyle.LEFT_MASK | mxEdgeStyle.TOP_MASK | mxEdgeStyle.RIGHT_MASK
  814. //| mxEdgeStyle.BOTTOM_MASK,
  815. CENTER_MASK: 512,
  816. SOURCE_MASK: 1024,
  817. TARGET_MASK: 2048,
  818. VERTEX_MASK: 3072,
  819. // mxEdgeStyle.SOURCE_MASK | mxEdgeStyle.TARGET_MASK,
  820. getJettySize: function(state, isSource)
  821. {
  822. var value = mxUtils.getValue(state.style, (isSource) ? mxConstants.STYLE_SOURCE_JETTY_SIZE :
  823. mxConstants.STYLE_TARGET_JETTY_SIZE, mxUtils.getValue(state.style,
  824. mxConstants.STYLE_JETTY_SIZE, mxEdgeStyle.orthBuffer));
  825. if (value == 'auto')
  826. {
  827. // Computes the automatic jetty size
  828. var type = mxUtils.getValue(state.style, (isSource) ? mxConstants.STYLE_STARTARROW : mxConstants.STYLE_ENDARROW, mxConstants.NONE);
  829. if (type != mxConstants.NONE)
  830. {
  831. var size = mxUtils.getNumber(state.style, (isSource) ? mxConstants.STYLE_STARTSIZE : mxConstants.STYLE_ENDSIZE, mxConstants.DEFAULT_MARKERSIZE);
  832. value = Math.max(2, Math.ceil((size + mxEdgeStyle.orthBuffer) / mxEdgeStyle.orthBuffer)) * mxEdgeStyle.orthBuffer;
  833. }
  834. else
  835. {
  836. value = 2 * mxEdgeStyle.orthBuffer;
  837. }
  838. }
  839. return value;
  840. },
  841. /**
  842. * Function: scalePointArray
  843. *
  844. * Scales an array of <mxPoint>
  845. *
  846. * Parameters:
  847. *
  848. * points - array of <mxPoint> to scale
  849. * scale - the scaling to divide by
  850. *
  851. */
  852. scalePointArray: function(points, scale)
  853. {
  854. var result = [];
  855. if (points != null)
  856. {
  857. for (var i = 0; i < points.length; i++)
  858. {
  859. if (points[i] != null)
  860. {
  861. var pt = new mxPoint(Math.round(points[i].x / scale * 10) / 10,
  862. Math.round(points[i].y / scale * 10) / 10);
  863. result[i] = pt;
  864. }
  865. else
  866. {
  867. result[i] = null;
  868. }
  869. }
  870. }
  871. else
  872. {
  873. result = null;
  874. }
  875. return result;
  876. },
  877. /**
  878. * Function: scaleCellState
  879. *
  880. * Scales an <mxCellState>
  881. *
  882. * Parameters:
  883. *
  884. * state - <mxCellState> to scale
  885. * scale - the scaling to divide by
  886. *
  887. */
  888. scaleCellState: function(state, scale)
  889. {
  890. var result = null;
  891. if (state != null)
  892. {
  893. result = state.clone();
  894. result.setRect(Math.round(state.x / scale * 10) / 10,
  895. Math.round(state.y / scale * 10) / 10,
  896. Math.round(state.width / scale * 10) / 10,
  897. Math.round(state.height / scale * 10) / 10);
  898. }
  899. else
  900. {
  901. result = null;
  902. }
  903. return result;
  904. },
  905. /**
  906. * Function: OrthConnector
  907. *
  908. * Implements a local orthogonal router between the given
  909. * cells.
  910. *
  911. * Parameters:
  912. *
  913. * state - <mxCellState> that represents the edge to be updated.
  914. * sourceScaled - <mxCellState> that represents the source terminal.
  915. * targetScaled - <mxCellState> that represents the target terminal.
  916. * controlHints - List of relative control points.
  917. * result - Array of <mxPoints> that represent the actual points of the
  918. * edge.
  919. *
  920. */
  921. OrthConnector: function(state, sourceScaled, targetScaled, controlHints, result)
  922. {
  923. var graph = state.view.graph;
  924. var sourceEdge = source == null ? false : graph.getModel().isEdge(source.cell);
  925. var targetEdge = target == null ? false : graph.getModel().isEdge(target.cell);
  926. var pts = mxEdgeStyle.scalePointArray(state.absolutePoints, state.view.scale);
  927. var source = mxEdgeStyle.scaleCellState(sourceScaled, state.view.scale);
  928. var target = mxEdgeStyle.scaleCellState(targetScaled, state.view.scale);
  929. var p0 = pts[0];
  930. var pe = pts[pts.length-1];
  931. var sourceX = source != null ? source.x : p0.x;
  932. var sourceY = source != null ? source.y : p0.y;
  933. var sourceWidth = source != null ? source.width : 1;
  934. var sourceHeight = source != null ? source.height : 1;
  935. var targetX = target != null ? target.x : pe.x;
  936. var targetY = target != null ? target.y : pe.y;
  937. var targetWidth = target != null ? target.width : 1;
  938. var targetHeight = target != null ? target.height : 1;
  939. var sourceBuffer = mxEdgeStyle.getJettySize(state, true);
  940. var targetBuffer = mxEdgeStyle.getJettySize(state, false);
  941. //console.log('sourceBuffer', sourceBuffer);
  942. //console.log('targetBuffer', targetBuffer);
  943. // Workaround for loop routing within buffer zone
  944. if (source != null && target == source)
  945. {
  946. targetBuffer = Math.max(sourceBuffer, targetBuffer);
  947. sourceBuffer = targetBuffer;
  948. }
  949. var totalBuffer = targetBuffer + sourceBuffer;
  950. // console.log('totalBuffer', totalBuffer);
  951. var tooShort = false;
  952. // Checks minimum distance for fixed points and falls back to segment connector
  953. if (p0 != null && pe != null)
  954. {
  955. var dx = pe.x - p0.x;
  956. var dy = pe.y - p0.y;
  957. tooShort = dx * dx + dy * dy < totalBuffer * totalBuffer;
  958. }
  959. if (tooShort || (mxEdgeStyle.orthPointsFallback && (controlHints != null &&
  960. controlHints.length > 0)) || sourceEdge || targetEdge)
  961. {
  962. mxEdgeStyle.SegmentConnector(state, sourceScaled, targetScaled, controlHints, result);
  963. return;
  964. }
  965. // Determine the side(s) of the source and target vertices
  966. // that the edge may connect to
  967. // portConstraint [source, target]
  968. var portConstraint = [mxConstants.DIRECTION_MASK_ALL, mxConstants.DIRECTION_MASK_ALL];
  969. var rotation = 0;
  970. if (source != null)
  971. {
  972. portConstraint[0] = mxUtils.getPortConstraints(source, state, true,
  973. mxConstants.DIRECTION_MASK_ALL);
  974. rotation = mxUtils.getValue(source.style, mxConstants.STYLE_ROTATION, 0);
  975. //console.log('source rotation', rotation);
  976. if (rotation != 0)
  977. {
  978. var newRect = mxUtils.getBoundingBox(new mxRectangle(sourceX, sourceY, sourceWidth, sourceHeight), rotation);
  979. sourceX = newRect.x;
  980. sourceY = newRect.y;
  981. sourceWidth = newRect.width;
  982. sourceHeight = newRect.height;
  983. }
  984. }
  985. if (target != null)
  986. {
  987. portConstraint[1] = mxUtils.getPortConstraints(target, state, false,
  988. mxConstants.DIRECTION_MASK_ALL);
  989. rotation = mxUtils.getValue(target.style, mxConstants.STYLE_ROTATION, 0);
  990. //console.log('target rotation', rotation);
  991. if (rotation != 0)
  992. {
  993. var newRect = mxUtils.getBoundingBox(new mxRectangle(targetX, targetY, targetWidth, targetHeight), rotation);
  994. targetX = newRect.x;
  995. targetY = newRect.y;
  996. targetWidth = newRect.width;
  997. targetHeight = newRect.height;
  998. }
  999. }
  1000. //console.log('source' , sourceX, sourceY, sourceWidth, sourceHeight);
  1001. //console.log('targetX' , targetX, targetY, targetWidth, targetHeight);
  1002. if (sourceWidth == 0 || sourceHeight == 0 ||
  1003. targetWidth == 0 || targetHeight == 0)
  1004. {
  1005. return;
  1006. }
  1007. var dir = [0, 0];
  1008. // Work out which faces of the vertices present against each other
  1009. // in a way that would allow a 3-segment connection if port constraints
  1010. // permitted.
  1011. // geo -> [source, target] [x, y, width, height]
  1012. var geo = [ [sourceX, sourceY, sourceWidth, sourceHeight] ,
  1013. [targetX, targetY, targetWidth, targetHeight] ];
  1014. var buffer = [sourceBuffer, targetBuffer];
  1015. for (var i = 0; i < 2; i++)
  1016. {
  1017. mxEdgeStyle.limits[i][1] = geo[i][0] - buffer[i];
  1018. mxEdgeStyle.limits[i][2] = geo[i][1] - buffer[i];
  1019. mxEdgeStyle.limits[i][4] = geo[i][0] + geo[i][2] + buffer[i];
  1020. mxEdgeStyle.limits[i][8] = geo[i][1] + geo[i][3] + buffer[i];
  1021. }
  1022. // Work out which quad the target is in
  1023. var sourceCenX = geo[0][0] + geo[0][2] / 2.0;
  1024. var sourceCenY = geo[0][1] + geo[0][3] / 2.0;
  1025. var targetCenX = geo[1][0] + geo[1][2] / 2.0;
  1026. var targetCenY = geo[1][1] + geo[1][3] / 2.0;
  1027. var dx = sourceCenX - targetCenX;
  1028. var dy = sourceCenY - targetCenY;
  1029. var quad = 0;
  1030. // 0 | 1
  1031. // -----
  1032. // 3 | 2
  1033. if (dx < 0)
  1034. {
  1035. if (dy < 0)
  1036. {
  1037. quad = 2;
  1038. }
  1039. else
  1040. {
  1041. quad = 1;
  1042. }
  1043. }
  1044. else
  1045. {
  1046. if (dy <= 0)
  1047. {
  1048. quad = 3;
  1049. // Special case on x = 0 and negative y
  1050. if (dx == 0)
  1051. {
  1052. quad = 2;
  1053. }
  1054. }
  1055. }
  1056. //console.log('quad', quad);
  1057. // Check for connection constraints
  1058. var currentTerm = null;
  1059. if (source != null)
  1060. {
  1061. currentTerm = p0;
  1062. }
  1063. var constraint = [ [0.5, 0.5] , [0.5, 0.5] ];
  1064. for (var i = 0; i < 2; i++)
  1065. {
  1066. if (currentTerm != null)
  1067. {
  1068. constraint[i][0] = (currentTerm.x - geo[i][0]) / geo[i][2];
  1069. if (Math.abs(currentTerm.x - geo[i][0]) <= 1)
  1070. {
  1071. dir[i] = mxConstants.DIRECTION_MASK_WEST;
  1072. }
  1073. else if (Math.abs(currentTerm.x - geo[i][0] - geo[i][2]) <= 1)
  1074. {
  1075. dir[i] = mxConstants.DIRECTION_MASK_EAST;
  1076. }
  1077. constraint[i][1] = (currentTerm.y - geo[i][1]) / geo[i][3];
  1078. if (Math.abs(currentTerm.y - geo[i][1]) <= 1)
  1079. {
  1080. dir[i] = mxConstants.DIRECTION_MASK_NORTH;
  1081. }
  1082. else if (Math.abs(currentTerm.y - geo[i][1] - geo[i][3]) <= 1)
  1083. {
  1084. dir[i] = mxConstants.DIRECTION_MASK_SOUTH;
  1085. }
  1086. }
  1087. currentTerm = null;
  1088. if (target != null)
  1089. {
  1090. currentTerm = pe;
  1091. }
  1092. }
  1093. var sourceTopDist = geo[0][1] - (geo[1][1] + geo[1][3]);
  1094. var sourceLeftDist = geo[0][0] - (geo[1][0] + geo[1][2]);
  1095. var sourceBottomDist = geo[1][1] - (geo[0][1] + geo[0][3]);
  1096. var sourceRightDist = geo[1][0] - (geo[0][0] + geo[0][2]);
  1097. mxEdgeStyle.vertexSeperations[1] = Math.max(sourceLeftDist - totalBuffer, 0);
  1098. mxEdgeStyle.vertexSeperations[2] = Math.max(sourceTopDist - totalBuffer, 0);
  1099. mxEdgeStyle.vertexSeperations[4] = Math.max(sourceBottomDist - totalBuffer, 0);
  1100. mxEdgeStyle.vertexSeperations[3] = Math.max(sourceRightDist - totalBuffer, 0);
  1101. //==============================================================
  1102. // Start of source and target direction determination
  1103. // Work through the preferred orientations by relative positioning
  1104. // of the vertices and list them in preferred and available order
  1105. var dirPref = [];
  1106. var horPref = [];
  1107. var vertPref = [];
  1108. horPref[0] = (sourceLeftDist >= sourceRightDist) ? mxConstants.DIRECTION_MASK_WEST
  1109. : mxConstants.DIRECTION_MASK_EAST;
  1110. vertPref[0] = (sourceTopDist >= sourceBottomDist) ? mxConstants.DIRECTION_MASK_NORTH
  1111. : mxConstants.DIRECTION_MASK_SOUTH;
  1112. horPref[1] = mxUtils.reversePortConstraints(horPref[0]);
  1113. vertPref[1] = mxUtils.reversePortConstraints(vertPref[0]);
  1114. var preferredHorizDist = sourceLeftDist >= sourceRightDist ? sourceLeftDist
  1115. : sourceRightDist;
  1116. var preferredVertDist = sourceTopDist >= sourceBottomDist ? sourceTopDist
  1117. : sourceBottomDist;
  1118. var prefOrdering = [ [0, 0] , [0, 0] ];
  1119. var preferredOrderSet = false;
  1120. // If the preferred port isn't available, switch it
  1121. for (var i = 0; i < 2; i++)
  1122. {
  1123. if (dir[i] != 0x0)
  1124. {
  1125. continue;
  1126. }
  1127. if ((horPref[i] & portConstraint[i]) == 0)
  1128. {
  1129. horPref[i] = mxUtils.reversePortConstraints(horPref[i]);
  1130. }
  1131. if ((vertPref[i] & portConstraint[i]) == 0)
  1132. {
  1133. vertPref[i] = mxUtils
  1134. .reversePortConstraints(vertPref[i]);
  1135. }
  1136. prefOrdering[i][0] = vertPref[i];
  1137. prefOrdering[i][1] = horPref[i];
  1138. }
  1139. if (preferredVertDist > 0
  1140. && preferredHorizDist > 0)
  1141. {
  1142. // Possibility of two segment edge connection
  1143. if (((horPref[0] & portConstraint[0]) > 0)
  1144. && ((vertPref[1] & portConstraint[1]) > 0))
  1145. {
  1146. prefOrdering[0][0] = horPref[0];
  1147. prefOrdering[0][1] = vertPref[0];
  1148. prefOrdering[1][0] = vertPref[1];
  1149. prefOrdering[1][1] = horPref[1];
  1150. preferredOrderSet = true;
  1151. }
  1152. else if (((vertPref[0] & portConstraint[0]) > 0)
  1153. && ((horPref[1] & portConstraint[1]) > 0))
  1154. {
  1155. prefOrdering[0][0] = vertPref[0];
  1156. prefOrdering[0][1] = horPref[0];
  1157. prefOrdering[1][0] = horPref[1];
  1158. prefOrdering[1][1] = vertPref[1];
  1159. preferredOrderSet = true;
  1160. }
  1161. }
  1162. if (preferredVertDist > 0 && !preferredOrderSet)
  1163. {
  1164. prefOrdering[0][0] = vertPref[0];
  1165. prefOrdering[0][1] = horPref[0];
  1166. prefOrdering[1][0] = vertPref[1];
  1167. prefOrdering[1][1] = horPref[1];
  1168. preferredOrderSet = true;
  1169. }
  1170. if (preferredHorizDist > 0 && !preferredOrderSet)
  1171. {
  1172. prefOrdering[0][0] = horPref[0];
  1173. prefOrdering[0][1] = vertPref[0];
  1174. prefOrdering[1][0] = horPref[1];
  1175. prefOrdering[1][1] = vertPref[1];
  1176. preferredOrderSet = true;
  1177. }
  1178. // The source and target prefs are now an ordered list of
  1179. // the preferred port selections
  1180. // If the list contains gaps, compact it
  1181. for (var i = 0; i < 2; i++)
  1182. {
  1183. if (dir[i] != 0x0)
  1184. {
  1185. continue;
  1186. }
  1187. if ((prefOrdering[i][0] & portConstraint[i]) == 0)
  1188. {
  1189. prefOrdering[i][0] = prefOrdering[i][1];
  1190. }
  1191. dirPref[i] = prefOrdering[i][0] & portConstraint[i];
  1192. dirPref[i] |= (prefOrdering[i][1] & portConstraint[i]) << 8;
  1193. dirPref[i] |= (prefOrdering[1 - i][i] & portConstraint[i]) << 16;
  1194. dirPref[i] |= (prefOrdering[1 - i][1 - i] & portConstraint[i]) << 24;
  1195. if ((dirPref[i] & 0xF) == 0)
  1196. {
  1197. dirPref[i] = dirPref[i] << 8;
  1198. }
  1199. if ((dirPref[i] & 0xF00) == 0)
  1200. {
  1201. dirPref[i] = (dirPref[i] & 0xF) | dirPref[i] >> 8;
  1202. }
  1203. if ((dirPref[i] & 0xF0000) == 0)
  1204. {
  1205. dirPref[i] = (dirPref[i] & 0xFFFF)
  1206. | ((dirPref[i] & 0xF000000) >> 8);
  1207. }
  1208. dir[i] = dirPref[i] & 0xF;
  1209. if (portConstraint[i] == mxConstants.DIRECTION_MASK_WEST
  1210. || portConstraint[i] == mxConstants.DIRECTION_MASK_NORTH
  1211. || portConstraint[i] == mxConstants.DIRECTION_MASK_EAST
  1212. || portConstraint[i] == mxConstants.DIRECTION_MASK_SOUTH)
  1213. {
  1214. dir[i] = portConstraint[i];
  1215. }
  1216. }
  1217. //==============================================================
  1218. // End of source and target direction determination
  1219. var sourceIndex = dir[0] == mxConstants.DIRECTION_MASK_EAST ? 3
  1220. : dir[0];
  1221. var targetIndex = dir[1] == mxConstants.DIRECTION_MASK_EAST ? 3
  1222. : dir[1];
  1223. sourceIndex -= quad;
  1224. targetIndex -= quad;
  1225. if (sourceIndex < 1)
  1226. {
  1227. sourceIndex += 4;
  1228. }
  1229. if (targetIndex < 1)
  1230. {
  1231. targetIndex += 4;
  1232. }
  1233. var routePattern = mxEdgeStyle.routePatterns[sourceIndex - 1][targetIndex - 1];
  1234. //console.log('routePattern', routePattern);
  1235. mxEdgeStyle.wayPoints1[0][0] = geo[0][0];
  1236. mxEdgeStyle.wayPoints1[0][1] = geo[0][1];
  1237. switch (dir[0])
  1238. {
  1239. case mxConstants.DIRECTION_MASK_WEST:
  1240. mxEdgeStyle.wayPoints1[0][0] -= sourceBuffer;
  1241. mxEdgeStyle.wayPoints1[0][1] += constraint[0][1] * geo[0][3];
  1242. break;
  1243. case mxConstants.DIRECTION_MASK_SOUTH:
  1244. mxEdgeStyle.wayPoints1[0][0] += constraint[0][0] * geo[0][2];
  1245. mxEdgeStyle.wayPoints1[0][1] += geo[0][3] + sourceBuffer;
  1246. break;
  1247. case mxConstants.DIRECTION_MASK_EAST:
  1248. mxEdgeStyle.wayPoints1[0][0] += geo[0][2] + sourceBuffer;
  1249. mxEdgeStyle.wayPoints1[0][1] += constraint[0][1] * geo[0][3];
  1250. break;
  1251. case mxConstants.DIRECTION_MASK_NORTH:
  1252. mxEdgeStyle.wayPoints1[0][0] += constraint[0][0] * geo[0][2];
  1253. mxEdgeStyle.wayPoints1[0][1] -= sourceBuffer;
  1254. break;
  1255. }
  1256. var currentIndex = 0;
  1257. // Orientation, 0 horizontal, 1 vertical
  1258. var lastOrientation = (dir[0] & (mxConstants.DIRECTION_MASK_EAST | mxConstants.DIRECTION_MASK_WEST)) > 0 ? 0
  1259. : 1;
  1260. var initialOrientation = lastOrientation;
  1261. var currentOrientation = 0;
  1262. for (var i = 0; i < routePattern.length; i++)
  1263. {
  1264. var nextDirection = routePattern[i] & 0xF;
  1265. // Rotate the index of this direction by the quad
  1266. // to get the real direction
  1267. var directionIndex = nextDirection == mxConstants.DIRECTION_MASK_EAST ? 3
  1268. : nextDirection;
  1269. directionIndex += quad;
  1270. if (directionIndex > 4)
  1271. {
  1272. directionIndex -= 4;
  1273. }
  1274. var direction = mxEdgeStyle.dirVectors[directionIndex - 1];
  1275. currentOrientation = (directionIndex % 2 > 0) ? 0 : 1;
  1276. // Only update the current index if the point moved
  1277. // in the direction of the current segment move,
  1278. // otherwise the same point is moved until there is
  1279. // a segment direction change
  1280. if (currentOrientation != lastOrientation)
  1281. {
  1282. currentIndex++;
  1283. // Copy the previous way point into the new one
  1284. // We can't base the new position on index - 1
  1285. // because sometime elbows turn out not to exist,
  1286. // then we'd have to rewind.
  1287. mxEdgeStyle.wayPoints1[currentIndex][0] = mxEdgeStyle.wayPoints1[currentIndex - 1][0];
  1288. mxEdgeStyle.wayPoints1[currentIndex][1] = mxEdgeStyle.wayPoints1[currentIndex - 1][1];
  1289. }
  1290. var tar = (routePattern[i] & mxEdgeStyle.TARGET_MASK) > 0;
  1291. var sou = (routePattern[i] & mxEdgeStyle.SOURCE_MASK) > 0;
  1292. var side = (routePattern[i] & mxEdgeStyle.SIDE_MASK) >> 5;
  1293. side = side << quad;
  1294. if (side > 0xF)
  1295. {
  1296. side = side >> 4;
  1297. }
  1298. var center = (routePattern[i] & mxEdgeStyle.CENTER_MASK) > 0;
  1299. if ((sou || tar) && side < 9)
  1300. {
  1301. var limit = 0;
  1302. var souTar = sou ? 0 : 1;
  1303. if (center && currentOrientation == 0)
  1304. {
  1305. limit = geo[souTar][0] + constraint[souTar][0] * geo[souTar][2];
  1306. }
  1307. else if (center)
  1308. {
  1309. limit = geo[souTar][1] + constraint[souTar][1] * geo[souTar][3];
  1310. }
  1311. else
  1312. {
  1313. limit = mxEdgeStyle.limits[souTar][side];
  1314. }
  1315. if (currentOrientation == 0)
  1316. {
  1317. var lastX = mxEdgeStyle.wayPoints1[currentIndex][0];
  1318. var deltaX = (limit - lastX) * direction[0];
  1319. if (deltaX > 0)
  1320. {
  1321. mxEdgeStyle.wayPoints1[currentIndex][0] += direction[0]
  1322. * deltaX;
  1323. }
  1324. }
  1325. else
  1326. {
  1327. var lastY = mxEdgeStyle.wayPoints1[currentIndex][1];
  1328. var deltaY = (limit - lastY) * direction[1];
  1329. if (deltaY > 0)
  1330. {
  1331. mxEdgeStyle.wayPoints1[currentIndex][1] += direction[1]
  1332. * deltaY;
  1333. }
  1334. }
  1335. }
  1336. else if (center)
  1337. {
  1338. // Which center we're travelling to depend on the current direction
  1339. mxEdgeStyle.wayPoints1[currentIndex][0] += direction[0]
  1340. * Math.abs(mxEdgeStyle.vertexSeperations[directionIndex] / 2);
  1341. mxEdgeStyle.wayPoints1[currentIndex][1] += direction[1]
  1342. * Math.abs(mxEdgeStyle.vertexSeperations[directionIndex] / 2);
  1343. }
  1344. if (currentIndex > 0
  1345. && mxEdgeStyle.wayPoints1[currentIndex][currentOrientation] == mxEdgeStyle.wayPoints1[currentIndex - 1][currentOrientation])
  1346. {
  1347. currentIndex--;
  1348. }
  1349. else
  1350. {
  1351. lastOrientation = currentOrientation;
  1352. }
  1353. }
  1354. for (var i = 0; i <= currentIndex; i++)
  1355. {
  1356. if (i == currentIndex)
  1357. {
  1358. // Last point can cause last segment to be in
  1359. // same direction as jetty/approach. If so,
  1360. // check the number of points is consistent
  1361. // with the relative orientation of source and target
  1362. // jx. Same orientation requires an even
  1363. // number of turns (points), different requires
  1364. // odd.
  1365. var targetOrientation = (dir[1] & (mxConstants.DIRECTION_MASK_EAST | mxConstants.DIRECTION_MASK_WEST)) > 0 ? 0
  1366. : 1;
  1367. var sameOrient = targetOrientation == initialOrientation ? 0 : 1;
  1368. // (currentIndex + 1) % 2 is 0 for even number of points,
  1369. // 1 for odd
  1370. if (sameOrient != (currentIndex + 1) % 2)
  1371. {
  1372. // The last point isn't required
  1373. break;
  1374. }
  1375. }
  1376. result.push(new mxPoint(Math.round(mxEdgeStyle.wayPoints1[i][0] * state.view.scale * 10) / 10,
  1377. Math.round(mxEdgeStyle.wayPoints1[i][1] * state.view.scale * 10) / 10));
  1378. }
  1379. //console.log(result);
  1380. // Removes duplicates
  1381. var index = 1;
  1382. while (index < result.length)
  1383. {
  1384. if (result[index - 1] == null || result[index] == null ||
  1385. result[index - 1].x != result[index].x ||
  1386. result[index - 1].y != result[index].y)
  1387. {
  1388. index++;
  1389. }
  1390. else
  1391. {
  1392. result.splice(index, 1);
  1393. }
  1394. }
  1395. },
  1396. getRoutePattern: function(dir, quad, dx, dy)
  1397. {
  1398. var sourceIndex = dir[0] == mxConstants.DIRECTION_MASK_EAST ? 3
  1399. : dir[0];
  1400. var targetIndex = dir[1] == mxConstants.DIRECTION_MASK_EAST ? 3
  1401. : dir[1];
  1402. sourceIndex -= quad;
  1403. targetIndex -= quad;
  1404. if (sourceIndex < 1)
  1405. {
  1406. sourceIndex += 4;
  1407. }
  1408. if (targetIndex < 1)
  1409. {
  1410. targetIndex += 4;
  1411. }
  1412. var result = routePatterns[sourceIndex - 1][targetIndex - 1];
  1413. if (dx == 0 || dy == 0)
  1414. {
  1415. if (inlineRoutePatterns[sourceIndex - 1][targetIndex - 1] != null)
  1416. {
  1417. result = inlineRoutePatterns[sourceIndex - 1][targetIndex - 1];
  1418. }
  1419. }
  1420. return result;
  1421. }
  1422. };