|
import * as util from '../../util'; |
|
import * as math from '../../math'; |
|
import * as is from '../../is'; |
|
|
|
let defaults = { |
|
fit: true, |
|
padding: 30, |
|
boundingBox: undefined, |
|
avoidOverlap: true, |
|
nodeDimensionsIncludeLabels: false, |
|
spacingFactor: undefined, |
|
radius: undefined, |
|
startAngle: 3 / 2 * Math.PI, |
|
sweep: undefined, |
|
clockwise: true, |
|
sort: undefined, |
|
animate: false, |
|
animationDuration: 500, |
|
animationEasing: undefined, |
|
animateFilter: function ( node, i ){ return true; }, |
|
ready: undefined, |
|
stop: undefined, |
|
transform: function (node, position ){ return position; } |
|
|
|
}; |
|
|
|
function CircleLayout( options ){ |
|
this.options = util.extend( {}, defaults, options ); |
|
} |
|
|
|
CircleLayout.prototype.run = function(){ |
|
let params = this.options; |
|
let options = params; |
|
|
|
let cy = params.cy; |
|
let eles = options.eles; |
|
|
|
let clockwise = options.counterclockwise !== undefined ? !options.counterclockwise : options.clockwise; |
|
|
|
let nodes = eles.nodes().not( ':parent' ); |
|
|
|
if( options.sort ){ |
|
nodes = nodes.sort( options.sort ); |
|
} |
|
|
|
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 sweep = options.sweep === undefined ? 2 * Math.PI - 2 * Math.PI / nodes.length : options.sweep; |
|
let dTheta = sweep / ( Math.max( 1, nodes.length - 1 ) ); |
|
let r; |
|
|
|
let minDistance = 0; |
|
for( let i = 0; i < nodes.length; i++ ){ |
|
let n = nodes[ i ]; |
|
let nbb = n.layoutDimensions( options ); |
|
let w = nbb.w; |
|
let h = nbb.h; |
|
|
|
minDistance = Math.max( minDistance, w, h ); |
|
} |
|
|
|
if( is.number( options.radius ) ){ |
|
r = options.radius; |
|
} else if( nodes.length <= 1 ){ |
|
r = 0; |
|
} else { |
|
r = Math.min( bb.h, bb.w ) / 2 - minDistance; |
|
} |
|
|
|
|
|
if( nodes.length > 1 && options.avoidOverlap ){ |
|
minDistance *= 1.75; |
|
|
|
let dcos = Math.cos( dTheta ) - Math.cos( 0 ); |
|
let dsin = Math.sin( dTheta ) - Math.sin( 0 ); |
|
let rMin = Math.sqrt( minDistance * minDistance / ( dcos * dcos + dsin * dsin ) ); |
|
r = Math.max( rMin, r ); |
|
} |
|
|
|
let getPos = function( ele, i ){ |
|
let theta = options.startAngle + i * dTheta * ( clockwise ? 1 : -1 ); |
|
|
|
let rx = r * Math.cos( theta ); |
|
let ry = r * Math.sin( theta ); |
|
let pos = { |
|
x: center.x + rx, |
|
y: center.y + ry |
|
}; |
|
|
|
return pos; |
|
}; |
|
|
|
eles.nodes().layoutPositions( this, options, getPos ); |
|
|
|
return this; |
|
}; |
|
|
|
export default CircleLayout; |
|
|