Skip to content

Commit 1d4a62e

Browse files
committed
draw dot edge long paths until next layout
1 parent 777aa7d commit 1d4a62e

File tree

6 files changed

+107
-21
lines changed

6 files changed

+107
-21
lines changed

README.md

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -52,18 +52,23 @@ In this project, the initial layout is performed with a cost based optimisation
5252
What is already available
5353
* Layout
5454
* stochastic cost driven centrality placement
55+
* cost based edge intersection avoidance
5556
* Nodes physical collisions, no overlap
5657
* Neighborhood interaction forces
5758
* step by step demo layout with colred cost samples
59+
* Grouping
60+
* group is a vertex with special edge relation "group"
61+
* allows all sorts of grouping, hierarchical and cross groups
62+
* Graph mutation to group and ungroup vertices
63+
* Update Graph
64+
* Drag and drop .graphml and .json in Graphson Format
5865
* SVG
5966
* shadows and light filters
6067
* inline html and css with classes
6168
* Mouse and Touch
6269
* Drag Node position with touch
6370
* Context menu (touch empty area, then second touch on Node)
6471
* Hover states with touch Node then touch empty area
65-
* Update Graph
66-
* Drag and drop .graphml and .json in Graphson Format
6772

6873
## Features Details
6974
* Node
@@ -84,9 +89,6 @@ What is already available
8489

8590
# Plan
8691
What is planned to be implemented in the Future
87-
* Grouping
88-
* Hierarchical
89-
* cross Grouping
9092
* Swap between :
9193
* proprties
9294
* nodes
@@ -96,12 +98,13 @@ What is planned to be implemented in the Future
9698
* color
9799
* light
98100
* filter effect
99-
* Update Graph
101+
* Graph Layout
102+
* use dot engine as a menu option
103+
* Graph Explore
100104
* Query Gremlin server
105+
* reference node / edge Gremlin query
101106
* Global hierarchical context menu
102107
* text input for queries
103-
* Update Graph
104-
* reference node / edge Gremlin query
105108
* Mouse and Touch
106109
* Pan zoom whole graph
107110
* Zoom vertices labels
@@ -110,6 +113,10 @@ What is planned to be implemented in the Future
110113
* Acceleration (wasm)
111114

112115
## Plan Details
116+
* move the whole group when gragging the group vertex
117+
* apply dot edge path (as long as no updates performed)
118+
* highlight all path on edge hover
119+
* multi points edge routing. In case an edge could shift to a certain extent in order to avoid intersection with vertices
113120
* configure sampling number (in config file)
114121
* polyline edges (complex concept, unclear and improvement might not be noticable)
115122
Style in js :

src/graph_io.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,17 @@ function import_dot_graph(dot){
268268
if(defined(dot_e.label)){
269269
edge.label = dot_e.label
270270
}
271+
let path = dot_e.pos.split(' ')
272+
if(path.length > 5){
273+
edge.dot_path = []
274+
path[0] = path[0].replace("e,","")
275+
path.forEach((point,id)=>{
276+
if(id!=0){
277+
let [x,y] = point.split(',')
278+
edge.dot_path.push({x:parseFloat(x),y:dot_height-parseFloat(y)})
279+
}
280+
})
281+
}
271282
return edge
272283
}
273284

src/layout.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,21 @@ function show_edges_if(v){
9797
}
9898
}
9999

100+
101+
function clear_dot(g){
102+
for(let [eid,e] of Object.entries(g.edges)){
103+
if(defined(e.dot_path)){
104+
e.svg.path.classList.add("d_arrow")
105+
e.svg.path.classList.remove("dot")
106+
delete e.dot_path
107+
console.log(e)
108+
}
109+
}
110+
}
111+
100112
class Layout{
101113
async centrals_first(g,params){
114+
clear_dot(g)
102115
let central_order = degree_centrality(g.vertices)
103116
let already_placed = []
104117
remove_add_pinned(g,central_order,already_placed)
@@ -129,6 +142,7 @@ class Layout{
129142
}
130143

131144
async propagate_neighbors(g,params){
145+
clear_dot(g)
132146
let neighbors_order = neighbors_centrality(g.vertices,params.v)
133147
//neighbors_order.forEach((n)=>{console.log(n.label)})
134148
let already_placed = []

src/mouse.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ function get_pointers_default(e,t_id){
3535

3636
function onMousePan(e){
3737
const is_vertex = e.target.classList.contains("vertex")
38+
const is_edge = e.target.classList.contains("edge")
3839
let pointe_1 = defined(e.buttons)?(e.buttons == 1):(e.touches.length == 1)
3940
let pointer_2 = defined(e.buttons)?(e.buttons == 2):(e.touches.length == 2)
4041
let t_id = pointer_2?1:0
@@ -66,6 +67,26 @@ function onMousePan(e){
6667
state.over_vertex = false
6768
}
6869
}
70+
if(is_edge){
71+
if(!state.over_edge){
72+
state.id = e.target.id
73+
state.over_edge = true
74+
event("edge_hover",{type:"enter",id:state.id,x:pointer_x,y:pointer_y})
75+
}else{
76+
if(e.target.id != state.id){
77+
event("edge_hover",{type:"exit",id:state.id})//exit old
78+
state.id = e.target.id
79+
event("edge_hover",{type:"enter",id:state.id,x:pointer_x,y:pointer_y})//enter new
80+
}else{
81+
event("edge_hover",{type:"move",id:state.id,x:pointer_x,y:pointer_y})
82+
}
83+
}
84+
}else{
85+
if(state.over_edge){
86+
event("edge_hover",{type:"exit",id:state.id})
87+
state.over_edge = false
88+
}
89+
}
6990
}
7091
if(pointe_1 && state.dragging){
7192
//console.log(e)

src/render.js

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,11 @@ let menu_v = null;
1717
let attraction = false;
1818
let f_hover = {main:null,pointLight:null,dropShadow:null}
1919

20-
const select_color = "hsl(140, 80%, 50%)"
21-
const default_color = "hsl(140, 80%, 90%)"
22-
const darken_color = "hsl(140, 80%, 33%)"
20+
const select_color = "hsl(140, 80%, 50%)"
21+
const default_color = "hsl(140, 80%, 90%)"
22+
const edge_color = "hsl(130, 30%, 60%)"
23+
const edge_select_color = "hsl(100, 80%, 60%)"
24+
const darken_color = "hsl(140, 80%, 33%)"
2325

2426

2527

@@ -33,12 +35,8 @@ function onVertexMenuAction(e){
3335
e.detail.v.svg.shape.classList.remove("pinned")
3436
menu.update_action("unpin","pin")
3537
}else if(e.detail.action == "group"){
36-
e.detail.v.grouped = true
37-
e.detail.v.svg.shape.classList.add("grouped")
3838
menu.update_action("group","ungroup")
3939
}else if(e.detail.action == "ungroup"){
40-
e.detail.v.grouped = false
41-
e.detail.v.svg.shape.classList.remove("grouped")
4240
menu.update_action("ungroup","group")
4341
}
4442
}
@@ -56,7 +54,7 @@ function onVertexContextMenu(e){
5654
menu_v = g.vertices[e.detail.id]
5755
let vb = menu_v.viewBox
5856
let pin_name = menu_v.pinned?"unpin":"pin"
59-
let group_name = menu_v.grouped?"ungroup":"group"
57+
let group_name = menu_v.group.used?"ungroup":"group"
6058
let buttons = menu.call({svg:svg,menu:"Vertex",x:vb.x,y:vb.y,actions:["attract","layout",pin_name,group_name]})
6159
let buttonMenuclick = (e)=>{send("menu_action",{menu:"Vertex",type:"click",action:e.target.getAttribute("data-name"),v:menu_v} )}
6260
buttons[pin_name].addEventListener( 'click', buttonMenuclick, false );
@@ -129,6 +127,12 @@ function onVertexHover(e){
129127
f_hover.dropShadow.setAttribute("dy",ly/5)
130128
}
131129
}
130+
function onEdgeHover(e){
131+
const edge = g.edges[e.detail.id]
132+
if(e.detail.type != "move"){
133+
console.log(`hover over edge (${edge.outV.label},${edge.inV.label}) => ${e.detail.type}`)
134+
}
135+
}
132136

133137
function onVertexDrag(e){
134138
if(e.detail.type == "start"){
@@ -144,6 +148,7 @@ class Render{
144148
init(graph_data){
145149
g = graph_data
146150
window.addEventListener( 'vertex_hover', onVertexHover, false );
151+
window.addEventListener( 'edge_hover', onEdgeHover, false );
147152
window.addEventListener( 'vertex_drag', onVertexDrag, false );
148153
window.addEventListener( 'context_menu', onContextMenu, false );
149154
window.addEventListener( 'menu_action', onMenuAction, false );
@@ -195,12 +200,25 @@ class Render{
195200
}`);
196201
this.sheet.insertRule(/*css*/`
197202
.edge.hover {
198-
stroke: ${select_color}
203+
stroke: ${edge_select_color};
204+
fill: ${edge_select_color};
205+
}`);
206+
this.sheet.insertRule(/*css*/`
207+
.edge.dot {
208+
stroke-width:2;
209+
stroke: ${edge_color};
210+
fill:rgba(0,0,0,0)
211+
}`);
212+
this.sheet.insertRule(/*css*/`
213+
.edge.dot.hover {
214+
stroke: ${edge_select_color};
215+
fill:rgba(0,0,0,0)
199216
}`);
200217
this.sheet.insertRule(/*css*/`
201218
.edge.default {
202-
stroke: ${default_color};
203-
fill: ${darken_color}
219+
stroke-width:2;
220+
stroke: ${edge_color};
221+
fill: ${edge_color}
204222
}`);
205223
document.adoptedStyleSheets = [...document.adoptedStyleSheets, this.sheet];
206224
}

src/render/edge_render.js

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,15 @@ function path_circle(center,r){
6464
a ${r},${r} 0,1,0 ${-2*r},0`
6565
}
6666

67+
function dot_line(e){
68+
let path = `M ${e.outV.viewBox.x},${e.outV.viewBox.y} `
69+
e.dot_path.forEach((point)=>{
70+
path += `L ${point.x},${point.y} `
71+
})
72+
path += `L ${e.inV.viewBox.x},${e.inV.viewBox.y}`
73+
return path
74+
}
75+
6776
function line_arrow(e){
6877
if(e.multi.used){
6978
let p1 = e.outV.viewBox//temporary fill with default
@@ -118,7 +127,11 @@ class Edge{
118127
let s_width = (1+e.weight*5)
119128
let svg = {}
120129
svg.group = html(top_svg,/*html*/`<g id="edge_${e.label}"/>`)
121-
svg.path = html(svg.group,/*html*/`<path class="edge path default d_arrow" d="${line_arrow(e)}" stroke-width="2" />`)
130+
if(e.dot_path){
131+
svg.path = html(svg.group,/*html*/`<path id="${e.id}" class="edge path default dot" d="${dot_line(e)}" />`)
132+
}else{
133+
svg.path = html(svg.group,/*html*/`<path id="${e.id}" class="edge path default d_arrow" d="${line_arrow(e)}" />`)
134+
}
122135
if(defined(e.label)){
123136
svg.textpath = html(svg.group,/*html*/`<path id="e_p_${e.id}" d="${text_path(e)}" visibility="hidden" />`)
124137
svg.text = html(svg.group,/*html*/` <text class="e_text" class="edge text" >
@@ -136,7 +149,9 @@ class Edge{
136149
e.svg.group.setAttribute("visibility","visible")
137150
}
138151
update(e){
139-
if(e.svg.path.classList.contains("d_arrow")){
152+
if(e.dot_path){
153+
e.svg.path.setAttribute("d",dot_line(e))
154+
}else if(e.svg.path.classList.contains("d_arrow")){
140155
e.svg.path.setAttribute("d",line_arrow(e))
141156
}else if(e.svg.path.classList.contains("d_line")){
142157
e.svg.path.setAttribute("d",line_simple(e))

0 commit comments

Comments
 (0)