Commit 7af0b354 authored by Romain Loth's avatar Romain Loth

WIP: area select

parent 9e6a6545
......@@ -414,7 +414,7 @@ function draw1Circle(ctx , x , y , color) {
// --------------------
// new sigma.js: could be replaced by default _moveHandler with bindings ?
// => atm rewrote entire function with new values
function trackMouse(e) {
function circleTrackMouse(e) {
if(!shift_key) {
// $.doTimeout(300,function (){
// new sigma.js 2D mouse context
......@@ -426,17 +426,9 @@ function trackMouse(e) {
TW.partialGraph.renderers[0].container.offsetWidth,
TW.partialGraph.renderers[0].container.offsetHeight);
// testing with overNodes event
// cf. https://github.com/jacomyal/sigma.js/wiki/Events-API
if (e.type == "overNodes") {
x = e.data.captor.clientX
y = e.data.captor.clientY
}
// classic mousemove event or other similar events
else {
x = sigma.utils.getX(e);
y = sigma.utils.getY(e);
}
// classic mousemove event or other similar non-sigma events
x = sigma.utils.getX(e);
y = sigma.utils.getY(e);
// console.log("trackMouse mod: x", x, "y", y)
......@@ -446,46 +438,33 @@ function trackMouse(e) {
ctx.globalAlpha = 0.5;
ctx.beginPath();
// labels appear
// var nds = TW.partialGraph.graph.nodes()
// convert (TODO CHECK IN THIS CONTEXT)
var camCoords = TW.cam.cameraPosition(x,y)
// TODO replace by a hover binding (and POSS use quadtree zone)
var exactNodeset = circleGetAreaNodes(
camCoords.x,
camCoords.y
)
// TODO REFA UNCOMMENT pseudo hover
// labels appear
//
// if(TW.partialGraph.camera.ratio>showLabelsIfZoom){
// for(var i in nds){
// n=nds[i];
// if(n.hidden==false){
// distance = Math.sqrt(
// Math.pow((x-parseInt(n.displayX)),2) +
// Math.pow((y-parseInt(n.displayY)),2)
// );
// if(parseInt(distance)<=cursor_size) {
// n.forceLabel=true;
// } else {
// if(typeof(n.neighbour)!=="undefined") {
// if(!n.neighbour) n.forceLabel=false;
// } else n.forceLabel=false;
// }
// }
// }
// if(TW.partialGraph.forceatlas2 && TW.partialGraph.forceatlas2.count<=1) {
// TW.partialGraph.refresh({skipIndexation:true})
// }
// } else {
// for(var i in nds){
// n=nds[i];
// if(!n.hidden){
// n.forceLabel=false;
// if(typeof(n.neighbour)!=="undefined") {
// if(!n.neighbour) n.forceLabel=false;
// else n.forceLabel=true;
// } else n.forceLabel=false;
// }
// }
// if(TW.partialGraph.forceatlas2 && TW.partialGraph.forceatlas2.count<=1) {
// TW.partialGraph.refresh({skipIndexation:true})
// }
// if(TW.partialGraph.camera.ratio > showLabelsIfZoom){
// for n of exactNodeset
// n.forceLabel=true;
// }
// else {
// for(var i in exactNodeset){
// n.forceLabel=false;
// if(typeof(n.neighbour)!=="undefined") {
// if(!n.neighbour) n.forceLabel=false;
// else n.forceLabel=true;
// } else n.forceLabel=false;
// }
// if(TW.partialGraph.forceatlas2 && TW.partialGraph.forceatlas2.count<=1) {
// TW.partialGraph.refresh({skipIndexation:true})
// }
// }
ctx.arc(x, y, cursor_size, 0, Math.PI * 2, true);
//ctx.arc(TW.partialGraph._core.width/2, TW.partialGraph._core.height/2, 4, 0, 2 * Math.PI, true);/*todel*/
ctx.closePath();
......@@ -495,6 +474,105 @@ function trackMouse(e) {
}
}
// exact subset of nodes under circle
function circleGetAreaNodes(camX0, camY0) {
// leverage quadtree to get a neighborhood
var slightlyLargerNodeset = circleLocalSubset(
camX0,
camY0,
cursor_size * TW.cam.ratio // cursor_size to cam units
)
var exactNodeset = []
for(var i in slightlyLargerNodeset){
n = slightlyLargerNodeset[i];
if(n.hidden==false){
distance = Math.sqrt(
Math.pow((x-parseInt(n.displayX)),2) +
Math.pow((y-parseInt(n.displayY)),2)
);
if(parseInt(distance)<=cursor_size) {
exactNodeset.push.
} else {
if(typeof(n.neighbour)!=="undefined") {
if(!n.neighbour) n.forceLabel=false;
} else n.forceLabel=false;
}
}
}
if(TW.partialGraph.forceatlas2 && TW.partialGraph.forceatlas2.count<=1) {
TW.partialGraph.refresh({skipIndexation:true})
}
return exactNodeset
}
// returns set of nodes in the quad subzones around a square
// that is containing the circle centered on x0, y0
// (use case: reduce number of nodes before exact check)
function circleLocalSubset(camX0, camY0 , camRay) {
y
var P0 = {x:camX0, y:camY0}
// to use quadtree.area, we consider the square
// in which our circle is inscribed
// y-
//
// P1 x----------x P2
// | |
// <= x- | P0 | 2r x+ =>
// | |
// x----------x
//
// y+
var P1 = {x: P0.x - camRay , y: P0.y - camRay } // top left
var P2 = {x: P0.x + camRay , y: P0.y - camRay } // top right
var areaNodes = TW.cam.quadtree.area({
height: 2 * camRay,
x1:P1.x, y1:P1.y,
x2:P2.x, y2:P2.y
})
// neighborhood guaranteed a bit larger than the square
// console.log('got ',areaNodes.length, 'nodes:', areaNodes)
return areaNodes
}
// not used but useful to quickly make visible any nodes[]
function flashNodesArray (nodesArray) {
// for diagnostic
var minX = 1000000
var minY = 1000000
var maxX = 0
var maxY = 0
for (var j in nodesArray) {
var n = nodesArray[j]
if (minX > n.x) minX = n.x
if (minY > n.y) minY = n.y
if (maxX < n.x) maxX = n.x
if (maxY < n.y) maxY = n.y
n.size = 300
n.label = "> " + n.label + "< "
n.color = "yellow"
}
console.log("nodesArray encompassed by:", minX, minY,';', maxX, maxY)
TW.partialGraph.refresh()
}
// BASIC MODULARITY
// =================
// ProcessDivsFlags is for adding/removing features from TinawebJS
......
......@@ -14,15 +14,11 @@
display: none;
}
#graph-div {
background-color: #22e;
}
#graph-zone {
#sigma-contnr {
background-color: #44b;
}
#right-sidebar {
#sidebar {
background-color: #2e2;
}
......
......@@ -682,110 +682,85 @@ TinaWebJS = function ( sigmacanvas ) {
});
// new sigma.js: attempt to use sigma events bindings
// cf. https://github.com/jacomyal/sigma.js/wiki/Events-API
// cases:
// [DONE] - simple click, one node
// [TODO] - simple click, area
// 'click' - simple click, early event
// used for area (with global: cursor_size)
// 'clickNode'- simple click, second event if one node
// one node:
// POSS easy in new sigma.js:
// add doubleClick to select node + neighboors
// when circle area select
// ========================
// 1st event, even before we know if there are nodes
TW.partialGraph.bind('click', function(e) {
// console.log("sigma click event e", e)
// case with a selector circle cursor handled here
if (cursor_size > 0) {
// actual click position, but in graph coords
var x = e.data.x
var y = e.data.y
// convert
var camCoords = TW.cam.cameraPosition(x,y)
// retrieve area nodes, using indexed quadtree and global cursor_size
var circleNodes = circleGetAreaNodes(
camCoords.x,
camCoords.y
)
// TODO 1) use SelectorEngine prevsels iff 'Add'
// circleNodes += prevsels
// 2) show selection + do all related effects
SelInst.MultipleSelection2({nodes:circleNodes})
}
})
// when one node and normal click
// ===============================
TW.partialGraph.bind('clickNode', function(e) {
console.log("clickNode event node", e.data.node)
// console.log("clickNode event e", e.data.node)
// new sigma.js gives easy access to clicked node!
theNodeId = e.data.node.id
cancelSelection(false);
if (cursor_size == 0) {
// sigma already provided us the target
SelInst.MultipleSelection2({nodes:[theNodeId]})
}
// case with a selector circle cursor
else {
// cf TODO mousedown
}
// case with a selector circle cursor handled
// just before, at click event
})
// TODO re-connect area click
// TW.partialGraph.bind('click', function(e) {
// console.log("===click===");
// console.log("e", e);
// })
// TW.partialGraph.bind('downgraph upgraph', function(e) {
// console.log("===downgraph upgraph===");
// console.log("e", e);
// })
// --------------------------------------------fragment from v1.customized
// TW.partialGraph.bind('mousedown mouseup', function(e) {
//
// console.log("---------- event", e)
// var targeted = self.graph.nodes.filter(function(n) {
// return !!n['hover'];
// }).map(function(n) {
// return n.id;
// });
// console.log("---------- targeted by hover check loop", targeted)
// })
//
// self.dispatch(
// e['type'] == 'mousedown' ?
// 'downgraph' :
// 'upgraph'
// );
//
// if (targeted.length) {
// self.dispatch(
// e['type'] == 'mousedown' ?
// 'downnodes' :
// 'upnodes',
// targeted
// );
// }
// }
//
// FOLLOW UP in v1 customized:
// ===========================
// .mousedown(function(e) { // using SelectionEngine
// //left click!<- normal click
// if(e.which==1){
// console.warn('new sigma.js FIX event bindings for downgraph, upgraph')
// partialGraph.dispatchEvent(
// e['type'] == 'mousedown' ?
// 'downgraph' :
// 'upgraph'
// );
// var area = {}
//
//
// // new sigma.js: (x,y) from event -- TODO check if convert coords
// area.x1 = sigma.utils.getX(e);
// area.y1 = sigma.utils.getY(e);
//
// // old version
// // area.x1 = partialGraph._core.mousecaptor.mouseX;
// // area.y1 = partialGraph._core.mousecaptor.mouseY;
//
// var targeted = SelInst.SelectorEngine( {
// cursorsize:cursor_size,
// area:area,
// addvalue:checkBox,
// clicktype:"simple",
// prevsels:selections
// } )
// if(targeted.length>0) {
// cancelSelection(false);
// SelInst.MultipleSelection2( {nodes:targeted} )
// }
// partialGraph.refresh({skipIndexation:true});
// trackMouse(e);
// }
// });
// -------------------------------------------/fragment from v1.customized
// goTo (move/zoom) events
// -------------------------------------------fragment from v1.customized
// FOLLOW UP in v1 customized
// =========
// (last non-reimplemented bit that was using SelectorEngine)
//
// var targeted = SelInst.SelectorEngine( {
// cursorsize:cursor_size,
// area:area,
// addvalue:checkBox,
// clicktype:"simple",
// prevsels:selections
// } )
// if(targeted.length>0) {
// cancelSelection(false);
// SelInst.MultipleSelection2( {nodes:targeted} )
// }
// partialGraph.refresh({skipIndexation:true});
// trackMouse(e);
// -------------------------------------------/fragment from v1.customized
// for all goTo (move/zoom) events
var zoomTimeoutId = null
TW.cam.bind('coordinatesUpdated', function(e) {
// debounce
......@@ -796,12 +771,12 @@ TinaWebJS = function ( sigmacanvas ) {
}
// schedule next
zoomTimeoutId = window.setTimeout(
// make zoom slider cursor follow scroll
function(){
// make zoom slider cursor follow scroll
$("#zoomSlider").slider("value",1/TW.cam.ratio)
// console.log('auto cursor on val', 1/TW.cam.ratio , "( ratio:", TW.cam.ratio,")" )
},
250
500
)
})
......@@ -810,14 +785,13 @@ TinaWebJS = function ( sigmacanvas ) {
// ==========
$("#sigma-contnr")
.mousemove(function(event){
.mousemove(function(e){
if(!isUndef(partialGraph)) {
// when selector circle cursor
if(cursor_size>0) trackMouse(event);
// show/move selector circle cursor
if(cursor_size>0) circleTrackMouse(e);
}
})
// POSSible for the future: add tools to contextmenu
// .contextmenu(function(){
// return false;
......@@ -941,6 +915,8 @@ TinaWebJS = function ( sigmacanvas ) {
});
//Cursor Size slider
// + reindexation when size is settled (=> updates the quadtree)
var reindexTimeout = null
$("#unranged-value").freshslider({
step: 1,
min:cursor_size_min,
......@@ -950,9 +926,21 @@ TinaWebJS = function ( sigmacanvas ) {
// console.log("en cursorsize: "+value);
cursor_size=value;
if(cursor_size==0) partialGraph.refresh({skipIndexation:true});
// have reindex ready to go for when user stops moving slider
if (reindexTimeout) {
// (debounced)
clearTimeout(reindexTimeout)
reindexTimeout = null
}
reindexTimeout = setTimeout(function() {
TW.partialGraph.refresh({skipIndexation: false})
// =====
console.log("graph quadtree reindexed for cursor")
}, 500)
}
});
}
} // finish initListeners
};
......@@ -66,7 +66,7 @@ function changeType() {
}
}
TW.partialGraph.emptyGraph();
TW.partialGraph.graph.clear();
var nodes_2_colour = {}
var edges_2_colour = {}
......@@ -296,7 +296,7 @@ function changeLevel() {
}
var str_nextState = nextState.map(Number).join("|")
TW.partialGraph.emptyGraph();
TW.partialGraph.graph.clear();
var voisinage = {}
// Dictionaries of: selection+neighbors
......
......@@ -258,8 +258,11 @@ if(RES["OK"]) {
});
// shortcuts to the renderer and camera
TW.rend = TW.partialGraph.renderers[0]
TW.cam = TW.partialGraph.camera
TW.rend = TW.partialGraph.renderers[0]
// NB : camera positions are fix if the node is fixed => they only depend on layout
// renderer position depend on viewpoint/zoom (like ~ html absolute positions of the node in the div)
// useful
TW.partialGraph.nNodes = TW.partialGraph.graph.nodes().length
......
......@@ -399,7 +399,7 @@ function LevelButtonDisable( TF ){
function graphTagCloudElem(nodes) {
console.log("in graphTagCloudElem, nodae_id: "+nodes);
cancelSelection();
TW.partialGraph.emptyGraph();
TW.partialGraph.graph.clear();
var ndsids=[]
......@@ -603,7 +603,7 @@ function add1Elem(id) {
updateSearchLabels(id,TW.Nodes[id].label,TW.Nodes[id].type);
nodeslength++;
}
TW.partialGraph.addNode(id,anode);
TW.partialGraph.graph.addNode(anode);
return;
}
} else { // It's an edge!
......@@ -621,7 +621,7 @@ function add1Elem(id) {
weight: TW.Edges[id].weight
};
TW.partialGraph.addEdge(id , anedge.sourceID , anedge.targetID , anedge);
TW.partialGraph.graph.addEdge(anedge);
return;
}
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment