Skip to content

Commit ee8bd71

Browse files
committed
svg action menu ready
1 parent 637fc5d commit ee8bd71

File tree

7 files changed

+203
-34
lines changed

7 files changed

+203
-34
lines changed

README.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ https://networkgraphs.github.io/graphysics/
77
# Features
88

99
# Features details
10+
* Update Graph
11+
* startup Demo
1012
* stochastic centrality placement
1113
* Mouse actions
1214
* highlight neighbors
@@ -16,7 +18,6 @@ https://networkgraphs.github.io/graphysics/
1618

1719
# Features Plan
1820
* Update Graph
19-
* startup Demo
2021
* Right click and select samples
2122
* Drag and drop .graphml .Graphson
2223
* Query Gremlin server
@@ -44,3 +45,13 @@ https://networkgraphs.github.io/graphysics/
4445

4546
# References
4647
Follow project up from [NetworkGraphs/graph2d](https://github.com/NetworkGraphs/graph2d)
48+
## SVG CSS
49+
* http://tinkerpop.apache.org/docs/3.4.4/dev/io/#graphson
50+
* [SVG w3.org](https://www.w3.org/TR/SVG/Overview.html)
51+
* [SVG Filters](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/filter)
52+
* [SVG Animations](https://svgwg.org/specs/animations/)
53+
* [CSS Filters](https://developer.mozilla.org/en-US/docs/Web/CSS/filter)
54+
55+
## Graph javasctipt libraries
56+
* [Cytoscape](https://js.cytoscape.org/) : Graph theory (network) library for visualisation and analysis
57+
* [Sigma.js](http://sigmajs.org/) : Sigma is a JavaScript library dedicated to graph drawing. It makes easy to publish networks on Web pages, and allows developers to integrate network exploration in rich Web applications.

config.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
{
22
"default_graph_file":"./data/GraphSON_blueprints.json",
33
"render":{
4-
"font":"20px Arial",
5-
"margin":3
4+
"font":"16px Verdana",
5+
"v_margin":5,
6+
"h_margin":10
67
},
78
"github":{
89
"image":"./media/github.png",

libs/svg_utils.js

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {defined,html} from "./web-js-utils.js"
1+
import {defined,html,html_tag} from "./web-js-utils.js"
22

33
class Svg{
44
constructor(svg_element){
@@ -33,6 +33,58 @@ class Svg{
3333
);
3434
}
3535

36+
pie_dash(parent,x,y,radius_start,radius_stop,angle_start,angle_stop){
37+
const circ_rad = (radius_start+radius_stop) / 2
38+
const stroke_width = (radius_stop - radius_start)
39+
const circ_length = 2 * Math.PI * circ_rad
40+
const dash_length = (angle_stop - angle_start) * circ_length
41+
const dash_start = angle_start * circ_length
42+
return html_tag(parent,"circle",/*html*/`
43+
<circle cx=${x} cy=${y} r="${circ_rad}"
44+
stroke-dasharray="${dash_length} ${circ_length}"
45+
stroke-dashoffset="${dash_start}"
46+
stroke-width="${stroke_width}"
47+
stroke="rgba(0,100,0,0.5)"
48+
fill="rgba(0,0,0,0)" />
49+
`);
50+
}
51+
52+
/**
53+
*
54+
* @param {*} parent : parent SVG element
55+
* @param {*} x : x coordinates of the pie arcs center
56+
* @param {*} y : y coordinates of the pie arcs center
57+
* @param {*} radius_start : small radius
58+
* @param {*} radius_stop : big radius
59+
* @param {*} angle_start : beginning in a scale of [0,1] for the whole 360° circle
60+
* @param {*} angle_stop : stop in a scale of [0,1] for the whole 360° circle
61+
* @param {*} color : filling color
62+
*/
63+
pie(parent,x,y,radius_start,radius_stop,angle_start,angle_stop,margin){
64+
const s_marg = margin * radius_stop / radius_start
65+
const small_start_x = x + radius_start * Math.cos((angle_start + s_marg) * 2 * Math.PI)
66+
const small_start_y = y + radius_start * Math.sin((angle_start + s_marg) * 2 * Math.PI)
67+
const small_stop_x = x + radius_start * Math.cos((angle_stop - s_marg) * 2 * Math.PI)
68+
const small_stop_y = y + radius_start * Math.sin((angle_stop - s_marg) * 2 * Math.PI)
69+
70+
const big_start_x = x + radius_stop * Math.cos((angle_start + margin) * 2 * Math.PI)
71+
const big_start_y = y + radius_stop * Math.sin((angle_start + margin) * 2 * Math.PI)
72+
const big_stop_x = x + radius_stop * Math.cos((angle_stop - margin) * 2 * Math.PI)
73+
const big_stop_y = y + radius_stop * Math.sin((angle_stop - margin) * 2 * Math.PI)
74+
75+
//(rx ry x-axis-rotation large-arc-flag sweep-flag x y)
76+
return html_tag(parent,"path",/*html*/`
77+
<path d=" M ${small_start_x} ${small_start_y}
78+
A ${radius_start},${radius_start} 0,0,1 ${small_stop_x},${small_stop_y}
79+
L ${big_stop_x} ${big_stop_y}
80+
A ${radius_stop},${radius_stop} 0,0,0 ${big_start_x},${big_start_y}
81+
L ${small_start_x} ${small_start_y}
82+
Z
83+
"
84+
stroke-width="0" />
85+
`)
86+
}
87+
3688
circle_p_id(parent,x,y,id){
3789
//
3890
return html(parent,

libs/web-js-utils.js

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ function htmls(parent,html_text){
3737
}
3838

3939
function html_tag(parent,tagName,html_text){
40-
console.warn("html_tag() deprecated, replace with html()")
4140
parent.insertAdjacentHTML("beforeend",html_text);
4241
let elements = parent.getElementsByTagName(tagName);
4342
let res_svg = elements[elements.length-1];
@@ -96,6 +95,19 @@ function clear(element){
9695
})
9796
}
9897

98+
function remove_sheet(sheet){
99+
let bkp_sheets = document.adoptedStyleSheets
100+
document.adoptedStyleSheets = []
101+
for(let i=0;i<bkp_sheets.length-1;i++){
102+
if(bkp_sheets[i] != sheet){
103+
document.adoptedStyleSheets = [...document.adoptedStyleSheets,bkp_sheets[i]];
104+
}
105+
}
106+
}
107+
108+
function add_sheet(sheet){
109+
document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
110+
}
99111

100112

101113
export{
@@ -113,5 +125,7 @@ export{
113125
css,
114126
html_tag,
115127
htmls,
116-
clear
128+
clear,
129+
add_sheet,
130+
remove_sheet
117131
}

src/menu.js

Lines changed: 77 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,106 @@
1-
import {html} from "../libs/web-js-utils.js"
1+
import {html,html_tag,add_sheet,remove_sheet,send} from "../libs/web-js-utils.js"
22
import {event} from "./utils.js"
3+
import {Svg} from "../libs/svg_utils.js"
4+
5+
let utl = new Svg()
36

47
let svg = null;
58
let menu_svg = null;
69
let vertex = null;
710

811
let state = {active:false}
12+
let sheet = null
913

10-
function onMouseLeave(e){
14+
/**
15+
*
16+
* @param {*} rel : relative position [0,1] around the 360° circle
17+
* @param {*} radius
18+
*/
19+
function angle_pos(rel,radius){
20+
let angle = rel * 2 * Math.PI
21+
let x = radius * Math.cos(angle)
22+
let y = radius * Math.sin(angle)
23+
return [x,y]
24+
}
25+
26+
function remove(){
1127
if(state.active){
1228
svg.removeChild(menu_svg)
1329
console.log("menu over")
1430
state.active = false
1531
event("vertex_menu",{type:"end"})
32+
remove_sheet(sheet)
33+
//can't remove sheet : document.adoptedStyleSheets.splice(document.adoptedStyleSheets.indexOf(sheet))
34+
}
35+
}
36+
37+
function onMouseLeave(e){
38+
remove()
39+
}
40+
41+
function onMouseDown(e){
42+
if(e.buttons == 2){
43+
remove()
1644
}
1745
}
1846

1947
function onContext(e){
20-
e.preventDefault();
21-
e.stopPropagation();
48+
e.preventDefault();
49+
e.stopPropagation();
2250
}
2351

2452
class Menu{
2553
call(Params){
2654
vertex = Params.v
2755
svg = Params.svg
28-
console.log(`menu called from ${vertex.label}`)
29-
let [x,y] = [vertex.viewBox.x,vertex.viewBox.y]
30-
menu_svg = html(svg,/*html*/`<circle class="menu" cx="${x}" cy="${y}" r="60" fill="rgba(120,160,200,0.5)"/>`)
56+
let [x,y] = [Params.x,Params.y]
57+
menu_svg = html(svg,/*html*/`<g id="g_menu"/>`)
58+
html_tag(menu_svg,"circle",/*html*/`
59+
<circle class="svg_menu" cx="${x}" cy="${y}" r="10" fill="rgba(120,160,200,0.5)" >
60+
<animate attributeName="r" begin="0s" values="10;80" keyTimes="0;1" calcMode="spline" keySplines="0 .75 .25 1" dur="300ms" repeatCount="1" fill="freeze"/>
61+
</circle>`)
62+
menu_svg.getElementsByTagName("animate")[0].beginElement()
3163
state.active = true
3264
menu_svg.addEventListener( 'mouseleave', onMouseLeave, false );
65+
menu_svg.addEventListener( 'mousedown', onMouseDown, false );
3366
menu_svg.addEventListener( 'contextmenu', onContext, false );
34-
}
67+
68+
const pie_radius_start = 20
69+
const pie_radius_end = 70
70+
const len = Params.actions.length
71+
Params.actions.forEach((a,i)=>{
72+
const start = i / len
73+
const stop = (i+1) / len
74+
const margin = 0.01
75+
setTimeout(()=>{
76+
let pie = utl.pie(menu_svg,x,y,pie_radius_start,pie_radius_end,start,stop,margin)
77+
pie.setAttribute("data-name",a)
78+
pie.classList.add("pie_element")
79+
pie.addEventListener( 'click', (e)=>{send("menu_action",{type:e.target.getAttribute("data-name")} )}, false );
80+
let [tx,ty] = angle_pos((start+stop)/2,(pie_radius_start+pie_radius_end)/2)
81+
html(menu_svg,/*html*/`<text x="${x+tx}" y="${y+ty}" class="m_text" dominant-baseline="middle" text-anchor="middle" style="pointer-events:none">${a}</text>`)
82+
},200+i*50)
83+
})
84+
sheet = new CSSStyleSheet()
85+
sheet.insertRule(/*css*/`
86+
path.pie_element{
87+
fill:hsl(140, 80%, 35%)
88+
}`)
89+
sheet.insertRule(/*css*/`
90+
path.pie_element:hover{
91+
fill:hsl(140, 80%, 45%)
92+
}`)
93+
sheet.insertRule(/*css*/`
94+
path.pie_element:hover:active{
95+
fill:hsl(140, 80%, 65%)
96+
}`)
97+
sheet.insertRule(/*css*/`
98+
text.m_text{
99+
font: bold 12px Verdana, Helvetica, Arial, sans-serif;
100+
color:hsl(240, 80%, 65%)
101+
}`)
102+
add_sheet(sheet)
103+
}
35104
}
36105

37106
export{Menu};

src/mouse.js

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,20 @@ import {event} from "./utils.js"
33
let state ={over_vertex:false,id:0,coord:{x:0,y:0},offset:{x:0,y:0},dragging:false,acting:false,menu:false};
44

55
function onContext(e){
6-
if(e.target.tagName == "rect"){
7-
e.preventDefault();
8-
e.stopPropagation();
9-
}
6+
e.preventDefault();
7+
e.stopPropagation();
108
}
119

1210
function onMousePan(e){
13-
const is_vetex = e.target.classList.contains("vertex")
11+
const is_vertex = e.target.classList.contains("vertex")
1412
let dx = e.clientX - state.coord.x;
1513
let dy = e.clientY - state.coord.y;
1614
let vdx = e.offsetX - state.offset.x;
1715
let vdy = e.offsetY - state.offset.y;
1816
if(e.type == "mousemove"){
1917
//console.log(state)
2018
if(!state.dragging && !state.menu){//then no update for the hover state machine
21-
if(is_vetex){
19+
if(is_vertex){
2220
if(!state.over_vertex){
2321
state.id = e.target.id
2422
state.over_vertex = true
@@ -40,13 +38,20 @@ function onMousePan(e){
4038
if((e.buttons == 1) && state.dragging){
4139
event("vertex_drag",{type:"move",tx:vdx,ty:vdy})
4240
}
41+
if(!is_vertex && (e.buttons != 1) && (e.buttons == 2) && !state.menu){
42+
if(state.over_vertex){
43+
state.over_vertex = false
44+
event("vertex_hover",{type:"exit",id:state.id})
45+
}
46+
}
4347
}else if(e.type == "mousedown"){
44-
if((e.buttons == 1) && is_vetex){
48+
if((e.buttons == 1) && is_vertex){
4549
state.dragging = true
4650
state.id = e.target.id
4751
event("vertex_drag",{type:"start",id:state.id})
48-
}else if((e.buttons == 2) && is_vetex){
52+
}else if((e.buttons == 2) && is_vertex){
4953
state.menu = true
54+
state.id = e.target.id
5055
event("vertex_menu",{type:"start",id:state.id})
5156
}
5257
}else if(e.type == "mouseup"){

0 commit comments

Comments
 (0)