CroppingConnectionDockingSpec.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446
  1. import {
  2. bootstrapDiagram,
  3. getDiagramJS,
  4. inject
  5. } from 'test/TestHelper';
  6. import {
  7. append as svgAppend,
  8. attr as svgAttr,
  9. create as svgCreate
  10. } from 'tiny-svg';
  11. import CroppingConnectionDocking from 'lib/layout/CroppingConnectionDocking';
  12. var layoutModule = {
  13. connectionDocking: [ 'type', CroppingConnectionDocking ]
  14. };
  15. function mid(shape) {
  16. return {
  17. x: shape.x + shape.width / 2,
  18. y: shape.y + shape.height / 2
  19. };
  20. }
  21. function visualizeExpected(canvas, point) {
  22. var circle = svgCreate('circle');
  23. svgAttr(circle, {
  24. dx: point.x,
  25. dy: point.y,
  26. r: 4
  27. });
  28. svgAppend(canvas._svg, circle);
  29. return circle;
  30. }
  31. function visualizeActual(canvas, point) {
  32. var circle = svgCreate('circle');
  33. svgAttr(circle, {
  34. dx: point.x,
  35. dy: point.y,
  36. r: 4,
  37. fill: 'orange',
  38. 'stroke': 'black',
  39. 'stroke-width': '1px',
  40. 'shapeRendering': 'crisp-edges'
  41. });
  42. svgAppend(canvas._svg, circle);
  43. return circle;
  44. }
  45. function expectDockingPoint(connection, shape, expected) {
  46. return getDiagramJS().invoke(function(canvas, connectionDocking) {
  47. var cropStart = shape === connection.source;
  48. var dockingPoint = connectionDocking.getDockingPoint(connection, shape, cropStart);
  49. visualizeExpected(canvas, expected.actual);
  50. visualizeActual(canvas, dockingPoint.actual);
  51. expect(dockingPoint).to.eql(expected);
  52. });
  53. }
  54. function expectCropping(connection, expectedWaypoints) {
  55. return getDiagramJS().invoke(function(canvas, connectionDocking) {
  56. var croppedWaypoints = connectionDocking.getCroppedWaypoints(connection);
  57. expectedWaypoints.forEach(function(p) {
  58. visualizeExpected(canvas, p);
  59. });
  60. croppedWaypoints.forEach(function(p) {
  61. visualizeActual(canvas, p);
  62. });
  63. expect(croppedWaypoints).to.eql(expectedWaypoints);
  64. });
  65. }
  66. describe('features/layout/CroppingConnectionDocking', function() {
  67. beforeEach(bootstrapDiagram({ modules: [ layoutModule ] }));
  68. describe('basics', function() {
  69. var topLeftShape,
  70. bottomRightShape,
  71. bottomLeftShape,
  72. topLeft_bottomLeftConnection,
  73. bottomLeft_bottomRightConnection,
  74. topLeft_bottomRightConnection,
  75. topLeft_bottomRightFreeStyleConnection,
  76. backAndForthConnection,
  77. unconnectedConnection;
  78. beforeEach(inject(function(canvas) {
  79. topLeftShape = canvas.addShape({
  80. id: 's-topLeft',
  81. x: 100, y: 100,
  82. width: 100, height: 100
  83. });
  84. bottomLeftShape = canvas.addShape({
  85. id: 's-bottomLeft',
  86. x: 100, y: 400,
  87. width: 100, height: 100
  88. });
  89. bottomRightShape = canvas.addShape({
  90. id: 's-bottomRight',
  91. x: 400, y: 400,
  92. width: 100, height: 100
  93. });
  94. function createConnection(id, startShape, endShape) {
  95. return canvas.addConnection({
  96. id: id,
  97. waypoints: [ mid(startShape), mid(endShape) ],
  98. source: startShape,
  99. target: endShape
  100. });
  101. }
  102. topLeft_bottomLeftConnection = createConnection('c-topLeft-bottomLeft', topLeftShape, bottomLeftShape);
  103. topLeft_bottomRightConnection = createConnection('c-topLeft-bottomRight', topLeftShape, bottomRightShape);
  104. bottomLeft_bottomRightConnection = createConnection('c-bottomLeft-bottomRight', bottomLeftShape, bottomRightShape);
  105. topLeft_bottomRightFreeStyleConnection = canvas.addConnection({
  106. id: 'c-freestyle',
  107. waypoints: [
  108. mid(topLeftShape),
  109. { x: 250, y: 250 },
  110. { x: 350, y: 250 },
  111. { x: 350, y: 350 },
  112. mid(bottomRightShape)
  113. ],
  114. source: topLeftShape,
  115. target: bottomRightShape
  116. });
  117. backAndForthConnection = canvas.addConnection({
  118. id: 'c-backandforth',
  119. waypoints: [
  120. mid(topLeftShape),
  121. { x: 300, y: 150 },
  122. { x: 300, y: 200 },
  123. { x: 190, y: 170 },
  124. { x: 400, y: 300 },
  125. mid(bottomRightShape)
  126. ],
  127. source: topLeftShape,
  128. target: bottomRightShape
  129. });
  130. unconnectedConnection = canvas.addConnection({
  131. id: 'c-unconnected',
  132. waypoints: [ { x: 130, y: 210 }, { x: 130, y: 390 } ],
  133. source: topLeftShape,
  134. target: bottomLeftShape
  135. });
  136. }));
  137. describe('#getDockingPoint', function() {
  138. it('should get topLeft -> bottomLeft source docking', inject(function(connectionDocking, canvas) {
  139. // vertical source docking
  140. expectDockingPoint(topLeft_bottomLeftConnection, topLeft_bottomLeftConnection.source, {
  141. point : { x: 150, y: 150 },
  142. actual : { x : 150, y : 200 },
  143. idx : 0
  144. });
  145. }));
  146. it('should get topLeft -> bottomLeft target docking', inject(function(connectionDocking, canvas) {
  147. // vertical target docking
  148. expectDockingPoint(topLeft_bottomLeftConnection, topLeft_bottomLeftConnection.target, {
  149. point : { x : 150, y : 450 },
  150. actual : { x : 150, y : 400 },
  151. idx : 1
  152. });
  153. }));
  154. it('should get bottomLeft -> bottomRight source docking', inject(function(connectionDocking, canvas) {
  155. // horizontal source docking
  156. expectDockingPoint(bottomLeft_bottomRightConnection, bottomLeft_bottomRightConnection.source, {
  157. point : { x : 150, y : 450 },
  158. actual : { x : 200, y : 450 },
  159. idx : 0
  160. });
  161. }));
  162. it('should get bottomLeft -> bottomRight target docking', inject(function(connectionDocking, canvas) {
  163. // vertical target docking
  164. expectDockingPoint(bottomLeft_bottomRightConnection, bottomLeft_bottomRightConnection.target, {
  165. point : { x : 450, y : 450 },
  166. actual : { x : 400, y : 450 },
  167. idx : 1
  168. });
  169. }));
  170. it('should get topLeft -> bottomRight source docking', inject(function(connectionDocking, canvas) {
  171. // diagonal source docking
  172. expectDockingPoint(topLeft_bottomRightConnection, topLeft_bottomRightConnection.source, {
  173. point : { x : 150, y : 150 },
  174. actual : { x : 200, y : 200 },
  175. idx : 0
  176. });
  177. }));
  178. it('should get topLeft -> bottomRight target docking', inject(function(connectionDocking, canvas) {
  179. // vertical target docking
  180. expectDockingPoint(topLeft_bottomRightConnection, topLeft_bottomRightConnection.target, {
  181. point : { x : 450, y : 450 },
  182. actual : { x : 400, y : 400 },
  183. idx : 1
  184. });
  185. }));
  186. it('should take shape x,y from shape', inject(function(connectionDocking, canvas) {
  187. var shape = topLeft_bottomRightConnection.target;
  188. // simulate position update
  189. shape.x += 20;
  190. shape.y += 20;
  191. // vertical target docking
  192. expectDockingPoint(topLeft_bottomRightConnection, shape, {
  193. point : { x : 450, y : 450 },
  194. actual : { x : 420, y : 420 },
  195. idx : 1
  196. });
  197. }));
  198. it('should fallback if no intersection', inject(function(connectionDocking, canvas) {
  199. // non intersecting (source)
  200. expectDockingPoint(unconnectedConnection, unconnectedConnection.source, {
  201. point : unconnectedConnection.waypoints[0],
  202. actual : unconnectedConnection.waypoints[0],
  203. idx : 0
  204. });
  205. // non intersecting (target)
  206. expectDockingPoint(unconnectedConnection, unconnectedConnection.target, {
  207. point : unconnectedConnection.waypoints[1],
  208. actual : unconnectedConnection.waypoints[1],
  209. idx : 1
  210. });
  211. }));
  212. });
  213. describe('#getCroppedWaypoints', function() {
  214. it('should crop topLeft -> bottomLeft connection', inject(function(connectionDocking) {
  215. // vertical connection
  216. expectCropping(topLeft_bottomLeftConnection, [
  217. { x: 150, y: 200, original: topLeft_bottomLeftConnection.waypoints[0] },
  218. { x: 150, y: 400, original: topLeft_bottomLeftConnection.waypoints[1] }
  219. ]);
  220. }));
  221. it('should crop bottomLeft -> bottomRight connection', inject(function(connectionDocking) {
  222. expectCropping(bottomLeft_bottomRightConnection, [
  223. { x: 200, y: 450, original: bottomLeft_bottomRightConnection.waypoints[0] },
  224. { x: 400, y: 450, original: bottomLeft_bottomRightConnection.waypoints[1] }
  225. ]);
  226. }));
  227. it('should crop topLeft -> bottomRight connection', inject(function(connectionDocking) {
  228. expectCropping(topLeft_bottomRightConnection, [
  229. { x: 200, y: 200, original: topLeft_bottomRightConnection.waypoints[0] },
  230. { x: 400, y: 400, original: topLeft_bottomRightConnection.waypoints[1] }
  231. ]);
  232. }));
  233. it('should crop backAndForth connection', inject(function(connectionDocking) {
  234. expectCropping(backAndForthConnection, [
  235. { x: 200, y: 150, original: backAndForthConnection.waypoints[0] },
  236. backAndForthConnection.waypoints[1],
  237. backAndForthConnection.waypoints[2],
  238. backAndForthConnection.waypoints[3],
  239. backAndForthConnection.waypoints[4],
  240. { x: 433, y: 400, original: backAndForthConnection.waypoints[5] }
  241. ]);
  242. }));
  243. it('should crop topLeft -> bottomRightFreeStyle connection', inject(function(connectionDocking) {
  244. expectCropping(topLeft_bottomRightFreeStyleConnection, [
  245. { x: 200, y: 200, original: topLeft_bottomRightFreeStyleConnection.waypoints[0] },
  246. topLeft_bottomRightFreeStyleConnection.waypoints[1],
  247. topLeft_bottomRightFreeStyleConnection.waypoints[2],
  248. topLeft_bottomRightFreeStyleConnection.waypoints[3],
  249. { x: 400, y: 400, original: topLeft_bottomRightFreeStyleConnection.waypoints[4] }
  250. ]);
  251. }));
  252. it('should crop unconnected connection', inject(function(connectionDocking) {
  253. // unconnected connection
  254. expectCropping(unconnectedConnection, [
  255. { x: 130, y: 210, original: unconnectedConnection.waypoints[0] },
  256. { x: 130, y: 390, original: unconnectedConnection.waypoints[1] }
  257. ]);
  258. }));
  259. });
  260. });
  261. describe('multiple intersections', function() {
  262. var shapeA, shapeB, shapeC,
  263. diagonalConnection,
  264. verticalConnection;
  265. beforeEach(inject(function(canvas) {
  266. shapeA = canvas.addShape({
  267. id: 'shapeA',
  268. x: 100, y: 100,
  269. width: 100, height: 100
  270. });
  271. shapeB = canvas.addShape({
  272. id: 'shapeB',
  273. x: 300, y: 200,
  274. width: 100, height: 100
  275. });
  276. shapeC = canvas.addShape({
  277. id: 'shapeC',
  278. x: 100, y: 300,
  279. width: 100, height: 100
  280. });
  281. diagonalConnection = canvas.addConnection({
  282. id: 'diagonalConnection',
  283. waypoints: [
  284. { x: 100, y: 150 },
  285. { x: 400, y: 250 }
  286. ],
  287. source: shapeA,
  288. target: shapeB
  289. });
  290. verticalConnection = canvas.addConnection({
  291. id: 'verticalConnection',
  292. waypoints: [
  293. { x: 150, y: 150 },
  294. { x: 150, y: 50 },
  295. { x: 150, y: 500 },
  296. { x: 150, y: 350 }
  297. ],
  298. source: shapeA,
  299. target: shapeC
  300. });
  301. }));
  302. describe('#getDockingPoint', function() {
  303. it('should dock diagonalConnection start', inject(function(connectionDocking) {
  304. expectDockingPoint(diagonalConnection, shapeA, {
  305. point: { x: 100, y: 150 },
  306. actual: { x : 200, y : 183 },
  307. idx: 0
  308. });
  309. }));
  310. it('should dock diagonalConnection end', inject(function(connectionDocking) {
  311. expectDockingPoint(diagonalConnection, shapeB, {
  312. point: { x: 400, y: 250 },
  313. actual : { x : 300, y : 217 },
  314. idx : 1
  315. });
  316. }));
  317. it('should dock verticalConnection start', inject(function(connectionDocking) {
  318. expectDockingPoint(verticalConnection, shapeA, {
  319. point: { x: 150, y: 150 },
  320. actual: { x: 150, y: 100 },
  321. idx: 0
  322. });
  323. }));
  324. it('should dock verticalConnection end', inject(function(connectionDocking) {
  325. expectDockingPoint(verticalConnection, shapeC, {
  326. point: { x: 150, y: 350 },
  327. actual: { x: 150, y: 400 },
  328. idx : 3
  329. });
  330. }));
  331. });
  332. });
  333. });