@ -28,32 +28,32 @@ const node_layout = {
shadowOpacity : 0.3 ,
} ;
const text _layout = {
fontSize : 22 ,
fontFamily : 'Roboto, Arial, sans-serif' ,
fill : 'black' ,
verticalAlign : 'middle' ,
align : 'center'
} ;
const node _text _layout = {
... text _layout ,
x : 1 - node _size ,
y : 2 - node _size ,
text : name ,
fontSize : 22 ,
width : 2 * node _size - 1 ,
height : 2 * node _size - 1 ,
fontFamily : 'Roboto' ,
fill : 'black' ,
verticalAlign : 'middle' ,
align : 'center'
} ;
const edge _layout = {
stroke : 'black' ,
strokeWidth : 3 ,
}
} ;
const edge _text _layout = {
fontSize : 22 ,
... text _layout ,
width : 2 * node _size ,
height : 2 * node _size ,
fontFamily : 'Roboto' ,
fill : 'black' ,
verticalAlign : 'middle' ,
align : 'center'
} ;
@ -103,10 +103,10 @@ function initialization() {
for ( var i = 0 ; i < 1000 ; i ++ ) {
force _directed _graph ( ) ;
}
edge _layer . show ( ) ;
node _layer . draw ( ) ;
}
initialization ( ) ;
//-----------------------------------------------------------------------------
@ -118,8 +118,8 @@ function sort(array) {
} )
}
function show _error ( msg ) {
let infobar = document . querySelector ( "#infobar" ) ;
function show _info ( msg , elem = "#errorbar" ) {
let infobar = document . querySelector ( elem ) ;
infobar . innerHTML = msg ;
infobar . classList . add ( "active" ) ;
window . setTimeout ( function ( ) {
@ -127,7 +127,6 @@ function show_error(msg) {
} , 4000 ) ;
}
//-----------------------------------------------------------------------------
//---------------------------draw methods--------------------------------------
//-----------------------------------------------------------------------------
@ -135,6 +134,7 @@ function draw_new_node(name, x, y) {
let group = new Konva . Group ( {
x : x ,
y : y ,
dragDistance : 5 ,
draggable : true ,
dragBoundFunc : function ( pos ) {
return {
@ -152,6 +152,7 @@ function draw_new_node(name, x, y) {
group . add ( box ) ;
group . add ( simpleText ) ;
group . on ( "click" , on _node _click ) ;
group . on ( 'tap' , on _node _click ) ;
group . on ( 'dragstart' , function ( ) {
group . attrs . dragging = true ;
} ) ;
@ -171,7 +172,6 @@ function draw_new_node(name, x, y) {
node _layer . draw ( ) ;
}
function draw _new _edge ( edge ) {
let start _node = stage . findOne ( '#' + edge [ "start" ] ) ;
let end _node = stage . findOne ( '#' + edge [ "end" ] ) ;
@ -185,7 +185,7 @@ function draw_new_edge(edge) {
let line = new Konva . Line ( edge _layout ) ;
let simpleText = new Konva . Text ( {
... edge _text _layout ,
text : weight
text : "" + weight
} ) ;
graph _copy [ edge [ "start" ] ] . push ( edge [ "end" ] ) ;
@ -236,7 +236,7 @@ function draw_selected_nodes() {
reset _node _style ( ) ;
reset _edge _style ( ) ;
for ( let id in selected ) {
selected [ id ] . children [ 0 ] . strokeWidth ( node _layout . strokeWidth + 1 ) ;
selected [ id ] . children [ 0 ] . strokeWidth ( node _layout . strokeWidth + 1 ) ;
selected [ id ] . children [ 0 ] . stroke ( "rgb(240, 141, 25)" ) ;
}
node _layer . draw ( ) ;
@ -262,6 +262,8 @@ function draw_path(path) {
path _elements [ id ] . children [ 0 ] . strokeWidth ( node _layout . strokeWidth + 1 ) ;
path _elements [ id ] . children [ 0 ] . stroke ( "rgb(240, 141, 25)" ) ;
}
node _layer . draw ( ) ;
edge _layer . draw ( ) ;
}
function on _node _click ( e ) {
@ -279,34 +281,73 @@ function on_node_click(e) {
draw _selected _nodes ( ) ;
} ;
function create _input ( x , y ) {
return new Promise ( function ( resolve , reject ) {
var container = document . createElement ( "div" ) ;
container . classList . add ( "background" ) ;
var input = document . createElement ( "input" ) ;
var clear = function ( ) {
container . remove ( ) ;
stage . listening ( true ) ;
focus = true ;
}
input . setAttribute ( "type" , "text" ) ;
input . style . left = x + "px" ;
input . style . top = y + "px" ;
canvas . appendChild ( container ) ;
container . appendChild ( input ) ;
input . focus ( ) ;
stage . listening ( false ) ;
focus = false ;
window . addEventListener ( "keyup" , function ( event ) {
if ( event . key === "Escape" ) {
reject ( ) ;
clear ( ) ;
}
} ) ;
input . addEventListener ( "keyup" , function ( event ) {
if ( event . key === "Enter" ) {
resolve ( input . value ) ;
clear ( ) ;
}
} ) ;
} ) ;
}
//-----------------------------------------------------------------------------
//---------------------------REST methods--------------------------------------
//-----------------------------------------------------------------------------
function post ( endpoint , data ) {
return fetch ( url + endpoint , {
method : 'POST' ,
body : JSON . stringify ( data ) ,
function rest _req ( method , endpoint , data ) {
let body _data = { }
if ( ! ! data ) {
body _data = { body : JSON . stringify ( data ) } ;
}
return fetch ( url + "api/" + endpoint , {
method : method ,
... body _data ,
headers : {
'Content-Type' : 'application/json'
}
} ) . then ( res => res . json ( ) )
}
//-----------------------------------------------------------------------------
function create _node ( ) {
if ( state != "create_node" ) {
state = "create_node" ;
// let mousePos = stage.getPointerPosition();
let pos = {
x : width / 2 ,
y : height / 2
x : ( Math . random ( ) - 0.5 ) * node _size * 4 + width / 2 ,
y : ( Math . random ( ) - 0.5 ) * node _size * 4 + height / 2
} ;
create _input ( pos . x , pos . y ) . then ( function ( text ) {
post ( "nodes" , {
rest _req ( 'POST' , "nodes" , {
name : text
} )
. then ( function ( res ) {
if ( res [ "error" ] ) {
show _error ( res [ "error" ] )
show _info ( res [ "error" ] )
} else {
draw _new _node ( text , pos . x , pos . y ) ;
}
@ -319,26 +360,22 @@ function create_node() {
function create _edge ( ) {
if ( selected . length < 2 ) {
show _error ( "Select at leas t 2 nodes." ) ;
show _info ( "Select 2 nodes." ) ;
return ;
}
let x = ( selected [ 0 ] . getX ( ) + selected [ 1 ] . getX ( ) ) / 2 ;
let y = ( selected [ 0 ] . getY ( ) + selected [ 1 ] . getY ( ) ) / 2 ;
create _input ( x , y ) . then ( function ( text ) {
post ( "edges" , {
rest _req ( 'POST' , "edges" , {
start : selected [ 0 ] . id ( ) ,
end : selected [ 1 ] . id ( ) ,
weight : text
} )
. then ( function ( res ) {
if ( res [ "error" ] ) {
show _error ( res [ "error" ] )
show _info ( res [ "error" ] )
} else {
draw _new _edge ( {
"start" : selected [ 0 ] . children [ 1 ] . text ( ) ,
"end" : selected [ 1 ] . children [ 1 ] . text ( ) ,
"weight" : text
} ) ;
draw _new _edge ( res ) ;
selected = [ ] ;
draw _selected _nodes ( ) ;
}
@ -348,57 +385,51 @@ function create_edge() {
} , function ( ) { } ) ;
}
function create _input ( x , y ) {
return new Promise ( function ( resolve , reject ) {
var container = document . createElement ( "div" ) ;
container . classList . add ( "background" ) ;
var input = document . createElement ( "input" ) ;
var clear = function ( ) {
container . remove ( ) ;
stage . listening ( true ) ;
focus = true ;
}
input . setAttribute ( "type" , "text" ) ;
input . style . left = x + "px" ;
input . style . top = y + "px" ;
canvas . appendChild ( container ) ;
container . appendChild ( input ) ;
input . focus ( ) ;
stage . listening ( false ) ;
focus = false ;
window . addEventListener ( "keyup" , function ( event ) {
if ( event . key === "Escape" ) {
reject ( ) ;
clear ( ) ;
}
} ) ;
input . addEventListener ( "keyup" , function ( event ) {
if ( event . key === "Enter" ) {
resolve ( input . value ) ;
clear ( ) ;
}
} ) ;
} ) ;
}
function find _path ( data ) {
if ( data . length < 2 ) {
show _error ( "Select at leas t 2 nodes." ) ;
show _info ( "Select 2 nodes." ) ;
return ;
}
console . log ( url + "paths/" + data [ 0 ] . attrs . id + "/" + data [ 1 ] . attrs . id ) ;
fetch ( url + "paths/" + data [ 0 ] . attrs . id + "/" + data [ 1 ] . attrs . id ) . then ( res => res . json ( ) )
rest _req ( "GET" , "paths/" + data [ 0 ] . attrs . id + "/" + data [ 1 ] . attrs . id , null )
. then ( function ( res ) {
if ( res [ "error" ] ) {
show _error ( res [ "error" ] ) ;
show _info ( res [ "error" ] ) ;
} else {
selected = [ ] ;
draw _path ( res [ "path" ] ) ;
show _info ( res [ "path" ] , "#infobar" ) ;
}
} )
. catch ( error => console . error ( 'Error:' , error ) ) ;
}
//-----------------------------------------------------------------------------
function save _graph ( ) {
fetch ( url + "save" ) . then ( res => res . json ( ) )
. then ( function ( res ) {
if ( res [ "error" ] ) {
show _info ( res [ "error" ] ) ;
} else {
console . log ( res ) ;
show _info ( res [ "info" ] , "#infobar" ) ;
}
} )
. catch ( error => console . error ( 'Error:' , error ) ) ;
}
function clear _graph ( ) {
fetch ( url + "clear" ) . then ( res => res . json ( ) )
. then ( function ( res ) {
if ( res [ "error" ] ) {
show _info ( res [ "error" ] ) ;
} else {
location . reload ( ) ;
}
} )
. catch ( error => console . error ( 'Error:' , error ) ) ;
}
//-----------------------------------------------------------------------------
//------------------------background methods-----------------------------------
@ -433,6 +464,9 @@ function toggle_auto_layout() {
function normalize ( vec ) {
let l = Math . sqrt ( vec [ 0 ] * * 2 + vec [ 1 ] * * 2 ) ;
if ( l == 0 ) {
return [ vec [ 0 ] , vec [ 1 ] ] ;
}
return [ vec [ 0 ] / l , vec [ 1 ] / l ] ;
}
@ -462,15 +496,20 @@ function force_directed_graph(spring_length = 300, step = 0.004) {
let x2 = konva _nodes [ jdx ] . getX ( ) ;
let y2 = konva _nodes [ jdx ] . getY ( ) ;
let dir = normalize ( [ x2 - x1 , y2 - y1 ] ) ;
let dist = ( ( x1 - x2 ) * * 2 + ( y1 - y2 ) * * 2 ) ;
let dist = Math . sqrt ( ( x1 - x2 ) * * 2 + ( y1 - y2 ) * * 2 ) ;
if ( dist == 0 ) {
continue ;
}
if ( in _edges ( konva _nodes [ idx ] , konva _nodes [ jdx ] ) ) {
let f = ( dist / spring _length ) ;
let f = ( dist * * 2 / spring _length ) ;
attr [ 0 ] += f * dir [ 0 ] ;
attr [ 1 ] += f * dir [ 1 ] ;
}
let f = ( spring _length * * 2 / Math . sqrt ( dist ) ) ;
rep [ 0 ] -= f * dir [ 0 ] ;
rep [ 1 ] -= f * dir [ 1 ] ;
if ( dist < width * 0.4 ) {
let f = ( spring _length * * 2 / dist ) ;
rep [ 0 ] -= f * dir [ 0 ] ;
rep [ 1 ] -= f * dir [ 1 ] ;
}
}
let new _x = x1 + step * ( rep [ 0 ] + attr [ 0 ] ) ;
let new _y = y1 + step * ( rep [ 1 ] + attr [ 1 ] ) ;
@ -492,11 +531,15 @@ function force_directed_graph(spring_length = 300, step = 0.004) {
var anim = new Konva . Animation ( function ( frame ) {
if ( auto _layout && focus ) {
const step = 0.00025 * frame . timeDiff ;
force _directed _graph ( 300 , step )
let spring _factor = Math . min ( ( konva _edges . length + konva _nodes . length - 3 ) / 20 , 1 ) ;
force _directed _graph ( 150 + ( 1 - spring _factor ) * 180 , step ) ;
}
} , node _layer ) ;
anim . start ( ) ;
// bug: this avoids glitches
window . onfocus = function ( ) { focus = true ; } ;
window . onblur = function ( ) { focus = false ; } ;
initialization ( ) ;