UNPKG

5.09 kBJavaScriptView Raw
1import * as util from '../util';
2import Promise from '../promise';
3import * as math from '../math';
4
5const getLayoutDimensionOptions = util.defaults({
6 nodeDimensionsIncludeLabels: false
7});
8
9let elesfn = ({
10 // Calculates and returns node dimensions { x, y } based on options given
11 layoutDimensions: function( options ){
12 options = getLayoutDimensionOptions( options );
13
14 let dims;
15
16 if( !this.takesUpSpace() ){
17 dims = { w: 0, h: 0 };
18 } else if( options.nodeDimensionsIncludeLabels ){
19 let bbDim = this.boundingBox();
20
21 dims = {
22 w: bbDim.w,
23 h: bbDim.h
24 };
25 } else {
26 dims = {
27 w: this.outerWidth(),
28 h: this.outerHeight()
29 };
30 }
31
32 // sanitise the dimensions for external layouts (avoid division by zero)
33 if( dims.w === 0 || dims.h === 0 ){
34 dims.w = dims.h = 1;
35 }
36
37 return dims;
38 },
39
40 // using standard layout options, apply position function (w/ or w/o animation)
41 layoutPositions: function( layout, options, fn ){
42 let nodes = this.nodes().filter(n => !n.isParent());
43 let cy = this.cy();
44 let layoutEles = options.eles; // nodes & edges
45 let getMemoizeKey = node => node.id();
46 let fnMem = util.memoize( fn, getMemoizeKey ); // memoized version of position function
47
48 layout.emit( { type: 'layoutstart', layout: layout } );
49
50 layout.animations = [];
51
52 let calculateSpacing = function( spacing, nodesBb, pos ){
53 let center = {
54 x: nodesBb.x1 + nodesBb.w / 2,
55 y: nodesBb.y1 + nodesBb.h / 2
56 };
57
58 let spacingVector = { // scale from center of bounding box (not necessarily 0,0)
59 x: (pos.x - center.x) * spacing,
60 y: (pos.y - center.y) * spacing
61 };
62
63 return {
64 x: center.x + spacingVector.x,
65 y: center.y + spacingVector.y
66 };
67 };
68
69 let useSpacingFactor = options.spacingFactor && options.spacingFactor !== 1;
70
71 let spacingBb = function(){
72 if( !useSpacingFactor ){ return null; }
73
74 let bb = math.makeBoundingBox();
75
76 for( let i = 0; i < nodes.length; i++ ){
77 let node = nodes[i];
78 let pos = fnMem( node, i );
79
80 math.expandBoundingBoxByPoint( bb, pos.x, pos.y );
81 }
82
83 return bb;
84 };
85
86 let bb = spacingBb();
87
88 let getFinalPos = util.memoize( function( node, i ){
89 let newPos = fnMem( node, i );
90
91 if( useSpacingFactor ){
92 let spacing = Math.abs( options.spacingFactor );
93
94 newPos = calculateSpacing( spacing, bb, newPos );
95 }
96
97 if( options.transform != null ){
98 newPos = options.transform( node, newPos );
99 }
100
101 return newPos;
102 }, getMemoizeKey );
103
104 if( options.animate ){
105 for( let i = 0; i < nodes.length; i++ ){
106 let node = nodes[ i ];
107 let newPos = getFinalPos( node, i );
108 let animateNode = options.animateFilter == null || options.animateFilter( node, i );
109
110 if( animateNode ){
111 let ani = node.animation( {
112 position: newPos,
113 duration: options.animationDuration,
114 easing: options.animationEasing
115 } );
116
117 layout.animations.push( ani );
118 } else {
119 node.position( newPos );
120 }
121
122 }
123
124 if( options.fit ){
125 let fitAni = cy.animation({
126 fit: {
127 boundingBox: layoutEles.boundingBoxAt( getFinalPos ),
128 padding: options.padding
129 },
130 duration: options.animationDuration,
131 easing: options.animationEasing
132 });
133
134 layout.animations.push( fitAni );
135 } else if( options.zoom !== undefined && options.pan !== undefined ){
136 let zoomPanAni = cy.animation({
137 zoom: options.zoom,
138 pan: options.pan,
139 duration: options.animationDuration,
140 easing: options.animationEasing
141 });
142
143 layout.animations.push( zoomPanAni );
144 }
145
146 layout.animations.forEach(ani => ani.play());
147
148 layout.one( 'layoutready', options.ready );
149 layout.emit( { type: 'layoutready', layout: layout } );
150
151 Promise.all( layout.animations.map(function( ani ){
152 return ani.promise();
153 }) ).then(function(){
154 layout.one( 'layoutstop', options.stop );
155 layout.emit( { type: 'layoutstop', layout: layout } );
156 });
157 } else {
158
159 nodes.positions( getFinalPos );
160
161 if( options.fit ){
162 cy.fit( options.eles, options.padding );
163 }
164
165 if( options.zoom != null ){
166 cy.zoom( options.zoom );
167 }
168
169 if( options.pan ){
170 cy.pan( options.pan );
171 }
172
173 layout.one( 'layoutready', options.ready );
174 layout.emit( { type: 'layoutready', layout: layout } );
175
176 layout.one( 'layoutstop', options.stop );
177 layout.emit( { type: 'layoutstop', layout: layout } );
178 }
179
180 return this; // chaining
181 },
182
183 layout: function( options ){
184 let cy = this.cy();
185
186 return cy.makeLayout( util.extend( {}, options, {
187 eles: this
188 } ) );
189 }
190
191});
192
193// aliases:
194elesfn.createLayout = elesfn.makeLayout = elesfn.layout;
195
196export default elesfn;