import * as util from '../../util';
import * as math from '../../math';
let defaults = {
fit: true, padding: 30, startAngle: 3 / 2 * Math.PI, sweep: undefined, clockwise: true, equidistant: false, minNodeSpacing: 10, boundingBox: undefined, avoidOverlap: true, nodeDimensionsIncludeLabels: false, height: undefined, width: undefined, spacingFactor: undefined, concentric: function( node ){ return node.degree();
},
levelWidth: function( nodes ){ return nodes.maxDegree() / 4;
},
animate: false, animationDuration: 500, animationEasing: undefined, animateFilter: function ( node, i ){ return true; }, ready: undefined, stop: undefined, transform: function (node, position ){ return position; } };
function ConcentricLayout( options ){
this.options = util.extend( {}, defaults, options );
}
ConcentricLayout.prototype.run = function(){
let params = this.options;
let options = params;
let clockwise = options.counterclockwise !== undefined ? !options.counterclockwise : options.clockwise;
let cy = params.cy;
let eles = options.eles;
let nodes = eles.nodes().not( ':parent' );
let bb = math.makeBoundingBox( options.boundingBox ? options.boundingBox : {
x1: 0, y1: 0, w: cy.width(), h: cy.height()
} );
let center = {
x: bb.x1 + bb.w / 2,
y: bb.y1 + bb.h / 2
};
let nodeValues = []; let maxNodeSize = 0;
for( let i = 0; i < nodes.length; i++ ){
let node = nodes[ i ];
let value;
value = options.concentric( node );
nodeValues.push( {
value: value,
node: node
} );
node._private.scratch.concentric = value;
}
nodes.updateStyle();
for( let i = 0; i < nodes.length; i++ ){
let node = nodes[ i ];
let nbb = node.layoutDimensions( options );
maxNodeSize = Math.max( maxNodeSize, nbb.w, nbb.h );
}
nodeValues.sort( function( a, b ){
return b.value - a.value;
} );
let levelWidth = options.levelWidth( nodes );
let levels = [ [] ];
let currentLevel = levels[0];
for( let i = 0; i < nodeValues.length; i++ ){
let val = nodeValues[ i ];
if( currentLevel.length > 0 ){
let diff = Math.abs( currentLevel[0].value - val.value );
if( diff >= levelWidth ){
currentLevel = [];
levels.push( currentLevel );
}
}
currentLevel.push( val );
}
let minDist = maxNodeSize + options.minNodeSpacing;
if( !options.avoidOverlap ){ let firstLvlHasMulti = levels.length > 0 && levels[0].length > 1;
let maxR = ( Math.min( bb.w, bb.h ) / 2 - minDist );
let rStep = maxR / ( levels.length + firstLvlHasMulti ? 1 : 0 );
minDist = Math.min( minDist, rStep );
}
let r = 0;
for( let i = 0; i < levels.length; i++ ){
let level = levels[ i ];
let sweep = options.sweep === undefined ? 2 * Math.PI - 2 * Math.PI / level.length : options.sweep;
let dTheta = level.dTheta = sweep / ( Math.max( 1, level.length - 1 ) );
if( level.length > 1 && options.avoidOverlap ){ let dcos = Math.cos( dTheta ) - Math.cos( 0 );
let dsin = Math.sin( dTheta ) - Math.sin( 0 );
let rMin = Math.sqrt( minDist * minDist / ( dcos * dcos + dsin * dsin ) );
r = Math.max( rMin, r );
}
level.r = r;
r += minDist;
}
if( options.equidistant ){
let rDeltaMax = 0;
let r = 0;
for( let i = 0; i < levels.length; i++ ){
let level = levels[ i ];
let rDelta = level.r - r;
rDeltaMax = Math.max( rDeltaMax, rDelta );
}
r = 0;
for( let i = 0; i < levels.length; i++ ){
let level = levels[ i ];
if( i === 0 ){
r = level.r;
}
level.r = r;
r += rDeltaMax;
}
}
let pos = {}; for( let i = 0; i < levels.length; i++ ){
let level = levels[ i ];
let dTheta = level.dTheta;
let r = level.r;
for( let j = 0; j < level.length; j++ ){
let val = level[ j ];
let theta = options.startAngle + (clockwise ? 1 : -1) * dTheta * j;
let p = {
x: center.x + r * Math.cos( theta ),
y: center.y + r * Math.sin( theta )
};
pos[ val.node.id() ] = p;
}
}
eles.nodes().layoutPositions( this, options, function( ele ){
let id = ele.id();
return pos[ id ];
} );
return this; };
export default ConcentricLayout;