@ -49,11 +49,14 @@ class KamadaKawai {
// get the K Matrix
// get the K Matrix
this . _createK_matrix ( D_matrix ) ;
this . _createK_matrix ( D_matrix ) ;
// initial E Matrix
this . _createE_matrix ( ) ;
// calculate positions
// calculate positions
let threshold = 0.01 ;
let threshold = 0.01 ;
let innerThreshold = 1 ;
let innerThreshold = 1 ;
let iterations = 0 ;
let iterations = 0 ;
let maxIterations = Math . max ( 1000 , Math . min ( 10 * this . body . nodeIndices . length , 6000 ) ) ;
let maxIterations = Math . max ( 1000 , Math . min ( 10 * this . body . nodeIndices . length , 6000 ) ) ;
let maxInnerIterations = 5 ;
let maxInnerIterations = 5 ;
let maxEnergy = 1 e9 ;
let maxEnergy = 1 e9 ;
@ -64,10 +67,10 @@ class KamadaKawai {
[ highE_nodeId , maxEnergy , dE_dx , dE_dy ] = this . _getHighestEnergyNode ( ignoreClusters ) ;
[ highE_nodeId , maxEnergy , dE_dx , dE_dy ] = this . _getHighestEnergyNode ( ignoreClusters ) ;
delta_m = maxEnergy ;
delta_m = maxEnergy ;
subIterations = 0 ;
subIterations = 0 ;
while ( delta_m > innerThreshold && subIterations < maxInnerIterations ) {
while ( delta_m > innerThreshold && subIterations < maxInnerIterations ) {
subIterations += 1 ;
subIterations += 1 ;
this . _moveNode ( highE_nodeId , dE_dx , dE_dy ) ;
this . _moveNode ( highE_nodeId , dE_dx , dE_dy ) ;
[ delta_m , dE_dx , dE_dy ] = this . _getEnergy ( highE_nodeId ) ;
[ delta_m , dE_dx , dE_dy ] = this . _getEnergy ( highE_nodeId ) ;
}
}
}
}
}
}
@ -87,7 +90,7 @@ class KamadaKawai {
for ( let nodeIdx = 0 ; nodeIdx < nodesArray . length ; nodeIdx ++ ) {
for ( let nodeIdx = 0 ; nodeIdx < nodesArray . length ; nodeIdx ++ ) {
let m = nodesArray [ nodeIdx ] ;
let m = nodesArray [ nodeIdx ] ;
// by not evaluating nodes with predefined positions we should only move nodes that have no positions.
// by not evaluating nodes with predefined positions we should only move nodes that have no positions.
if ( ( nodes [ m ] . predefinedPosition === false || nodes [ m ] . isCluster === true && ignoreClusters === true ) || nodes [ m ] . options . fixed . x === true || nodes [ m ] . options . fixed . y === true ) {
if ( ( nodes [ m ] . predefinedPosition === false || nodes [ m ] . isCluster === true && ignoreClusters === true ) || nodes [ m ] . options . fixed . x === true || nodes [ m ] . options . fixed . y === true ) {
let [ delta_m , dE_dx , dE_dy ] = this . _getEnergy ( m ) ;
let [ delta_m , dE_dx , dE_dy ] = this . _getEnergy ( m ) ;
if ( maxEnergy < delta_m ) {
if ( maxEnergy < delta_m ) {
maxEnergy = delta_m ;
maxEnergy = delta_m ;
@ -108,24 +111,7 @@ class KamadaKawai {
* @ private
* @ private
* /
* /
_getEnergy ( m ) {
_getEnergy ( m ) {
let nodesArray = this . body . nodeIndices ;
let nodes = this . body . nodes ;
let x_m = nodes [ m ] . x ;
let y_m = nodes [ m ] . y ;
let dE_dx = 0 ;
let dE_dy = 0 ;
for ( let iIdx = 0 ; iIdx < nodesArray . length ; iIdx ++ ) {
let i = nodesArray [ iIdx ] ;
if ( i !== m ) {
let x_i = nodes [ i ] . x ;
let y_i = nodes [ i ] . y ;
let denominator = 1.0 / Math . sqrt ( Math . pow ( x_m - x_i , 2 ) + Math . pow ( y_m - y_i , 2 ) ) ;
dE_dx += this . K_matrix [ m ] [ i ] * ( ( x_m - x_i ) - this . L_matrix [ m ] [ i ] * ( x_m - x_i ) * denominator ) ;
dE_dy += this . K_matrix [ m ] [ i ] * ( ( y_m - y_i ) - this . L_matrix [ m ] [ i ] * ( y_m - y_i ) * denominator ) ;
}
}
let [ dE_dx , dE_dy ] = this . E_sums [ m ] ;
let delta_m = Math . sqrt ( Math . pow ( dE_dx , 2 ) + Math . pow ( dE_dy , 2 ) ) ;
let delta_m = Math . sqrt ( Math . pow ( dE_dx , 2 ) + Math . pow ( dE_dy , 2 ) ) ;
return [ delta_m , dE_dx , dE_dy ] ;
return [ delta_m , dE_dx , dE_dy ] ;
}
}
@ -147,15 +133,20 @@ class KamadaKawai {
let x_m = nodes [ m ] . x ;
let x_m = nodes [ m ] . x ;
let y_m = nodes [ m ] . y ;
let y_m = nodes [ m ] . y ;
let km = this . K_matrix [ m ] ;
let lm = this . L_matrix [ m ] ;
for ( let iIdx = 0 ; iIdx < nodesArray . length ; iIdx ++ ) {
for ( let iIdx = 0 ; iIdx < nodesArray . length ; iIdx ++ ) {
let i = nodesArray [ iIdx ] ;
let i = nodesArray [ iIdx ] ;
if ( i !== m ) {
if ( i !== m ) {
let x_i = nodes [ i ] . x ;
let x_i = nodes [ i ] . x ;
let y_i = nodes [ i ] . y ;
let y_i = nodes [ i ] . y ;
let kmat = km [ i ] ;
let lmat = lm [ i ] ;
let denominator = 1.0 / Math . pow ( Math . pow ( x_m - x_i , 2 ) + Math . pow ( y_m - y_i , 2 ) , 1.5 ) ;
let denominator = 1.0 / Math . pow ( Math . pow ( x_m - x_i , 2 ) + Math . pow ( y_m - y_i , 2 ) , 1.5 ) ;
d2E_dx2 += this . K_matrix [ m ] [ i ] * ( 1 - this . L_matrix [ m ] [ i ] * Math . pow ( y_m - y_i , 2 ) * denominator ) ;
d2E_dxdy += this . K_matrix [ m ] [ i ] * ( this . L_matrix [ m ] [ i ] * ( x_m - x_i ) * ( y_m - y_i ) * denominator ) ;
d2E_dy2 += this . K_matrix [ m ] [ i ] * ( 1 - this . L_matrix [ m ] [ i ] * Math . pow ( x_m - x_i , 2 ) * denominator ) ;
d2E_dx2 += kmat * ( 1 - lmat * Math . pow ( y_m - y_i , 2 ) * denominator ) ;
d2E_dxdy += kmat * ( lmat * ( x_m - x_i ) * ( y_m - y_i ) * denominator ) ;
d2E_dy2 += kmat * ( 1 - lmat * Math . pow ( x_m - x_i , 2 ) * denominator ) ;
}
}
}
}
// make the variable names easier to make the solving of the linear system easier to read
// make the variable names easier to make the solving of the linear system easier to read
@ -168,6 +159,9 @@ class KamadaKawai {
// move the node
// move the node
nodes [ m ] . x += dx ;
nodes [ m ] . x += dx ;
nodes [ m ] . y += dy ;
nodes [ m ] . y += dy ;
// Recalculate E_matrix (should be incremental)
this . _updateE_matrix ( m ) ;
}
}
@ -208,8 +202,82 @@ class KamadaKawai {
}
}
}
}
/ * *
* Create matrix with all energies between nodes
* @ private
* /
_createE_matrix ( ) {
let nodesArray = this . body . nodeIndices ;
let nodes = this . body . nodes ;
this . E_matrix = { } ;
this . E_sums = { } ;
for ( let mIdx = 0 ; mIdx < nodesArray . length ; mIdx ++ ) {
this . E_matrix [ nodesArray [ mIdx ] ] = [ ] ;
}
for ( let mIdx = 0 ; mIdx < nodesArray . length ; mIdx ++ ) {
let m = nodesArray [ mIdx ] ;
let x_m = nodes [ m ] . x ;
let y_m = nodes [ m ] . y ;
let dE_dx = 0 ;
let dE_dy = 0 ;
for ( let iIdx = mIdx ; iIdx < nodesArray . length ; iIdx ++ ) {
let i = nodesArray [ iIdx ] ;
if ( i !== m ) {
let x_i = nodes [ i ] . x ;
let y_i = nodes [ i ] . y ;
let denominator = 1.0 / Math . sqrt ( Math . pow ( x_m - x_i , 2 ) + Math . pow ( y_m - y_i , 2 ) ) ;
this . E_matrix [ m ] [ iIdx ] = [
this . K_matrix [ m ] [ i ] * ( ( x_m - x_i ) - this . L_matrix [ m ] [ i ] * ( x_m - x_i ) * denominator ) ,
this . K_matrix [ m ] [ i ] * ( ( y_m - y_i ) - this . L_matrix [ m ] [ i ] * ( y_m - y_i ) * denominator )
] ;
this . E_matrix [ i ] [ mIdx ] = this . E_matrix [ m ] [ iIdx ] ;
dE_dx += this . E_matrix [ m ] [ iIdx ] [ 0 ] ;
dE_dy += this . E_matrix [ m ] [ iIdx ] [ 1 ] ;
}
}
//Store sum
this . E_sums [ m ] = [ dE_dx , dE_dy ] ;
}
}
//Update method, just doing single column (rows are auto-updated) (update all sums)
_updateE_matrix ( m ) {
let nodesArray = this . body . nodeIndices ;
let nodes = this . body . nodes ;
let colm = this . E_matrix [ m ] ;
let kcolm = this . K_matrix [ m ] ;
let lcolm = this . L_matrix [ m ] ;
let x_m = nodes [ m ] . x ;
let y_m = nodes [ m ] . y ;
let dE_dx = 0 ;
let dE_dy = 0 ;
for ( let iIdx = 0 ; iIdx < nodesArray . length ; iIdx ++ ) {
let i = nodesArray [ iIdx ] ;
if ( i !== m ) {
//Keep old energy value for sum modification below
let cell = colm [ iIdx ] ;
let oldDx = cell [ 0 ] ;
let oldDy = cell [ 1 ] ;
//Calc new energy:
let x_i = nodes [ i ] . x ;
let y_i = nodes [ i ] . y ;
let denominator = 1.0 / Math . sqrt ( Math . pow ( x_m - x_i , 2 ) + Math . pow ( y_m - y_i , 2 ) ) ;
let dx = kcolm [ i ] * ( ( x_m - x_i ) - lcolm [ i ] * ( x_m - x_i ) * denominator ) ;
let dy = kcolm [ i ] * ( ( y_m - y_i ) - lcolm [ i ] * ( y_m - y_i ) * denominator ) ;
colm [ iIdx ] = [ dx , dy ] ;
dE_dx += dx ;
dE_dy += dy ;
//add new energy to sum of each column
let sum = this . E_sums [ i ] ;
sum [ 0 ] += ( dx - oldDx ) ;
sum [ 1 ] += ( dy - oldDy ) ;
}
}
//Store sum at -1 index
this . E_sums [ m ] = [ dE_dx , dE_dy ] ;
}
}
}
export default KamadaKawai ;
export default KamadaKawai ;