Commit 36cc73af authored by Romain Loth's avatar Romain Loth

mini optimize render 2/2 + integrated neighbors

SystemState now integrates the currently highlighted neighbors and relations under .selectionRels property + with this we can optimize deselectNodes (replaces both highlightSelectedNodesand greyEverything and enhances cancelSelection)
parent 0a809b0d
...@@ -173,7 +173,7 @@ function RunLouvain() { ...@@ -173,7 +173,7 @@ function RunLouvain() {
function SomeEffect( ValueclassCode ) { function SomeEffect( ValueclassCode ) {
// console.debug("highlighting:", ValueclassCode ) // console.debug("highlighting:", ValueclassCode )
greyEverything(); deselectNodes(TW.SystemState())
TW.gui.selectionActive = true TW.gui.selectionActive = true
...@@ -191,65 +191,37 @@ function SomeEffect( ValueclassCode ) { ...@@ -191,65 +191,37 @@ function SomeEffect( ValueclassCode ) {
iClu=Number(raw[2]); iClu=Number(raw[2]);
// £TODO factorizable: always the same loop for neighbors this should be handled by a sub of MultipleSelection2
// get the active types code from current state (ie "1", "1|1", etc for TW.Relations lookup)
var activetypesKey = getActivetypesKey() var activetypesKey = getActivetypesKey()
// console.log( "\t"+activetypesKey)
// we have our precomputed idmaps for nodes_2_colour // we have our precomputed idmaps for nodes_2_colour
// ------------------------------------------------- // -------------------------------------------------
for (var k in TW.Clusters[nodeType][cluType].invIdx[iClu].nids) { for (var k in TW.Clusters[nodeType][cluType].invIdx[iClu].nids) {
var nid = TW.Clusters[nodeType][cluType].invIdx[iClu].nids[k] var nid = TW.Clusters[nodeType][cluType].invIdx[iClu].nids[k]
nodes_2_colour[nid] = true nodes_2_colour[nid] = true
}
for(var nid in nodes_2_colour) {
n = TW.partialGraph.graph.nodes(nid) n = TW.partialGraph.graph.nodes(nid)
if(n) { if(n) {
// new sigma js: we change only flags, rendering will adapt color accordingly
n.customAttrs['grey'] = false;
// highlight (like neighbors but with no selection)
n.customAttrs['highlight'] = true; n.customAttrs['highlight'] = true;
} }
if(TW.Relations[activetypesKey] && TW.Relations[activetypesKey][nid] ) {
neigh = TW.Relations[activetypesKey][nid]
if(neigh) {
for(j in neigh) {
tgt_nid = neigh[j]
if( !isUndef(nodes_2_colour[tgt_nid]) ) {
edges_2_colour[nid+";"+tgt_nid]=true;
edges_2_colour[tgt_nid+";"+nid]=true;
}
}
}
}
}
for(var eid in edges_2_colour) {
an_edge = TW.partialGraph.graph.edges(eid)
if(!isUndef(an_edge) && !an_edge.hidden){
// new sigma js: we change only flags, rendering will adapt color accordingly
an_edge.customAttrs['grey'] = 0;
an_edge.customAttrs['activeEdge'] = 1;
}
} }
TW.pushState({
sels:TW.Clusters[nodeType][cluType].invIdx[iClu].nids,
rels:{}
})
TW.partialGraph.refresh() TW.partialGraph.refresh()
} }
// some colorings cases also modify size and label // some colorings cases also modify size and label
function graphResetLabelsAndSizes(){ function graphResetLabelsAndSizes(){
for(let j in TW.nodeIds){
// NB could be also merged with greyEverything and highlightSelectedNodes(false) let n = TW.partialGraph.graph.nodes(TW.nodeIds[j])
cancelSelection(false, {'render':true, 'resetSizes':true, 'resetLabels': true}) if (n) {
n.label = TW.Nodes[n.id].label
n.size = TW.Nodes[n.id].size
}
}
} }
// @daclass: the name of a numeric/categorical attribute from node.attributes // @daclass: the name of a numeric/categorical attribute from node.attributes
// @groupingTicks: an optional threshold's array expressing ranges with their low/up bounds label and ref to matchin nodeIds // @groupingTicks: an optional threshold's array expressing ranges with their low/up bounds label and ref to matchin nodeIds
function set_ClustersLegend ( daclass, groupedByTicks ) { function set_ClustersLegend ( daclass, groupedByTicks ) {
......
...@@ -300,7 +300,7 @@ TW.conf = (function(TW){ ...@@ -300,7 +300,7 @@ TW.conf = (function(TW){
logFacets: false, // ...about parsing node attribute:value facets logFacets: false, // ...about parsing node attribute:value facets
logSettings: false, // ...about settings at Tina and Sigma init time logSettings: false, // ...about settings at Tina and Sigma init time
logStates: false, // ...about TW.states array logStates: false, // ...about TW.states array
logSelections: true logSelections: false
} }
......
...@@ -162,12 +162,8 @@ function SelectionEngine() { ...@@ -162,12 +162,8 @@ function SelectionEngine() {
console.log("nodes", args.nodes) console.log("nodes", args.nodes)
} }
// deselects only the active ones (based on SystemState())
console.warn('££TODO we could grey only active and neighbors if we kept neighbors') deselectNodes()
greyEverything();
var sameSideNeighbors = {}
var oppositeSideNeighbors = {}
// TW.SystemState() is the present graph state // TW.SystemState() is the present graph state
// eg // eg
...@@ -181,8 +177,10 @@ function SelectionEngine() { ...@@ -181,8 +177,10 @@ function SelectionEngine() {
var activetypesKey = getActivetypesKey() var activetypesKey = getActivetypesKey()
// Dictionaries of: selection+neighbors // Dictionaries of: selection+neighbors for the new state and updateRelatedNodesPanel
let selections = {} let selections = {}
let activeRelations = {}
activeRelations[activetypesKey] = {}
// targeted arg 'nodes' can be nid array or single nid // targeted arg 'nodes' can be nid array or single nid
var ndsids=[] var ndsids=[]
...@@ -195,6 +193,13 @@ function SelectionEngine() { ...@@ -195,6 +193,13 @@ function SelectionEngine() {
if(TW.Relations[activetypesKey] && TW.Relations[activetypesKey][srcnid] ) { if(TW.Relations[activetypesKey] && TW.Relations[activetypesKey][srcnid] ) {
var neighs = TW.Relations[activetypesKey][srcnid] var neighs = TW.Relations[activetypesKey][srcnid]
// for state cache
activeRelations[activetypesKey][srcnid] = {}
// shortcut
let sameSideNeighbors = activeRelations[activetypesKey][srcnid]
if(neighs) { if(neighs) {
for(var j in neighs) { for(var j in neighs) {
var tgtnid = neighs[j] var tgtnid = neighs[j]
...@@ -252,9 +257,16 @@ function SelectionEngine() { ...@@ -252,9 +257,16 @@ function SelectionEngine() {
// neighbors of the opposite type // neighbors of the opposite type
if(TW.Relations["1|1"]) { if(TW.Relations["1|1"]) {
activeRelations["1|1"] = {}
for(var srcnid in theSelection) { for(var srcnid in theSelection) {
var bipaNeighs = TW.Relations["1|1"][theSelection[srcnid]]; var bipaNeighs = TW.Relations["1|1"][theSelection[srcnid]];
activeRelations["1|1"][srcnid] = {}
// shortcut
let oppositeSideNeighbors = activeRelations["1|1"][srcnid]
for(var k in bipaNeighs) { for(var k in bipaNeighs) {
if (typeof oppositeSideNeighbors[bipaNeighs[k]] == "undefined") if (typeof oppositeSideNeighbors[bipaNeighs[k]] == "undefined")
oppositeSideNeighbors[bipaNeighs[k]] = 0; oppositeSideNeighbors[bipaNeighs[k]] = 0;
...@@ -267,10 +279,16 @@ function SelectionEngine() { ...@@ -267,10 +279,16 @@ function SelectionEngine() {
// Sort by descending value // Sort by descending value
// and rewrites dict[id]: val as array[order]: {key:id, value:val} // and rewrites dict[id]: val as array[order]: {key:id, value:val}
var oppos = ArraySortByValue(oppositeSideNeighbors, function(a,b){ let oppos = []
let same = []
if (activeRelations["1|1"]) {
oppos = ArraySortByValue(activeRelations["1|1"][srcnid], function(a,b){
return b-a return b-a
}); });
var same = ArraySortByValue(sameSideNeighbors, function(a,b){ }
same = ArraySortByValue(activeRelations[activetypesKey][srcnid], function(a,b){
return b-a return b-a
}); });
...@@ -281,7 +299,8 @@ function SelectionEngine() { ...@@ -281,7 +299,8 @@ function SelectionEngine() {
} }
// it's a new SystemState // it's a new SystemState
TW.pushState( { 'sels': theSelection, 'same': same, 'oppos': oppos } ) TW.pushState( { 'sels': theSelection,
'rels': activeRelations } )
// we send our "gotNodeSet" event // we send our "gotNodeSet" event
// (signal for plugins that a search-selection was done or a new hand picked selection) // (signal for plugins that a search-selection was done or a new hand picked selection)
......
...@@ -365,9 +365,8 @@ function changeType() { ...@@ -365,9 +365,8 @@ function changeType() {
TW.pushState({ TW.pushState({
activetypes: t1Activetypes, activetypes: t1Activetypes,
sels: newselsArr, sels: newselsArr,
oppos: [] // rels: added by MS2 (highlighted opposite- and same-side neighbours)
// possible: integrated highlighted opposite- and same-side neighbours from MS2 // possible: add it in an early way here and request that MS2 doesn't change state
// (var used to exist but wasn't filled and used consistently)
}) })
// to recreate the new selection in the new type graph, if we had one before // to recreate the new selection in the new type graph, if we had one before
...@@ -429,7 +428,7 @@ function changeLevel() { ...@@ -429,7 +428,7 @@ function changeLevel() {
var nodes_2_colour = {} var nodes_2_colour = {}
var edges_2_colour = {} var edges_2_colour = {}
// POSS: factorize with same strategy in MultipleSelection2 beginning // £TODO: factorize with same strategy in MultipleSelection2 beginning
for(var i in sels) { for(var i in sels) {
s = sels[i]; s = sels[i];
neigh = TW.Relations[activetypesKey][s] neigh = TW.Relations[activetypesKey][s]
...@@ -494,8 +493,8 @@ function changeLevel() { ...@@ -494,8 +493,8 @@ function changeLevel() {
if(sels.length>0) { if(sels.length>0) {
TW.instance.selNgn.MultipleSelection2({ TW.instance.selNgn.MultipleSelection2({
nodes:sels, nodes:sels,
nodesDict:nodes_2_colour, // nodesDict:nodes_2_colour,
edgesDict:edges_2_colour // edgesDict:edges_2_colour
}); });
TW.gui.selectionActive=true; TW.gui.selectionActive=true;
} }
......
...@@ -21,6 +21,7 @@ TW.initialSystemState = { ...@@ -21,6 +21,7 @@ TW.initialSystemState = {
activetypes: [], // <== filled from TW.categories activetypes: [], // <== filled from TW.categories
level: true, level: true,
selectionNids: [], // <== current selection !! selectionNids: [], // <== current selection !!
selectionRels: [], // <== current highlighted neighbors
LouvainFait: false, LouvainFait: false,
id: 0 // simple incremental stateid id: 0 // simple incremental stateid
} }
......
...@@ -10,7 +10,7 @@ TW.pushState = function( args ) { ...@@ -10,7 +10,7 @@ TW.pushState = function( args ) {
let lastState = TW.states.slice(-1)[0] // <=> TW.SystemState() let lastState = TW.states.slice(-1)[0] // <=> TW.SystemState()
if (TW.conf.debug.logSelections) console.log("setState args: ", args); if (TW.conf.debug.logStates) console.log("setState args: ", args);
// 1) we start from a copy // 1) we start from a copy
let newState = Object.assign({}, lastState) let newState = Object.assign({}, lastState)
...@@ -19,13 +19,12 @@ TW.pushState = function( args ) { ...@@ -19,13 +19,12 @@ TW.pushState = function( args ) {
newState.id ++ newState.id ++
// 2) we update it with provided args // 2) we update it with provided args
if (!isUndef(args.sels)) newState.selectionNids = args.sels;
if (!isUndef(args.activetypes)) newState.activetypes = args.activetypes if (!isUndef(args.activetypes)) newState.activetypes = args.activetypes
if (!isUndef(args.level)) newState.level = args.level; if (!isUndef(args.level)) newState.level = args.level;
if (!isUndef(args.sels)) newState.selectionNids = args.sels;
// neighbors (of both types) in a .neighborsNids[type] slot // neighbors (of any type) and their edges in an .selectionRels[type] slot
if(!isUndef(args.same)) newState.samesideSortdNeighs = args.same; if(!isUndef(args.rels)) newState.selectionRels = args.rels;
if(!isUndef(args.oppos)) newState.opposideSortdNeighs = args.oppos;
// POSS2: add filterSliders params to be able to recreate subsets at a given time // POSS2: add filterSliders params to be able to recreate subsets at a given time
...@@ -122,54 +121,19 @@ function cancelSelection (fromTagCloud, settings) { ...@@ -122,54 +121,19 @@ function cancelSelection (fromTagCloud, settings) {
if (TW.conf.debug.logSelections) { console.log("\t***in cancelSelection"); } if (TW.conf.debug.logSelections) { console.log("\t***in cancelSelection"); }
if (!settings) settings = {} if (!settings) settings = {}
highlightSelectedNodes(false); //Unselect the selected ones :D // SystemState effects
// -------------------
// clear the current state's selection and neighbors arrays // clear the current state's selection and neighbors arrays
deselectNodes(TW.SystemState()) //Unselect the selected ones :D
// new state // new state
TW.pushState({sels:[], oppos:[], same:[]}) TW.pushState({sels:[], rels:{}})
// GUI effects
// -----------
// global flag // global flag
TW.gui.selectionActive = false TW.gui.selectionActive = false
//Edges colors go back to normal
if (TW.partialGraph.settings('drawEdges')) {
for(let i in TW.edgeIds){
let e = TW.partialGraph.graph.edges(TW.edgeIds[i])
// console.log("cancelSelection: edge", e)
if (e) {
e.color = e.customAttrs['true_color'];
if (e.customAttrs.activeEdge) {
e.customAttrs.activeEdge = 0;
}
}
}
}
//Nodes colors go back to previous
// ££TODO partly duplicate effort with (de)highlightSelectedNodes and greyEverything
// => could be replaced by a (de)highlightSelectedAndNeighbors
// on smaller set (here entire nodeset!)
for(let j in TW.nodeIds){
let n = TW.partialGraph.graph.nodes(TW.nodeIds[j])
// console.log("cancelSelection: node", n)
if (n) {
n.customAttrs.active = 0
n.customAttrs.highlight = 0
n.customAttrs.forceLabel = 0
// some colorings cases also modify size and label
if (settings.resetLabels) {
n.label = TW.Nodes[n.id].label
}
if (settings.resetSizes) {
n.size = TW.Nodes[n.id].size
}
}
}
// hide all selection panels // hide all selection panels
if(fromTagCloud==false){ if(fromTagCloud==false){
$("#names").html(""); $("#names").html("");
...@@ -241,20 +205,62 @@ function swActual(aNodetype) { ...@@ -241,20 +205,62 @@ function swActual(aNodetype) {
function highlightSelectedNodes(flag){ // changes attributes of nodes and edges to remove active, highlight and activeEdge flags
let sels = TW.SystemState().selectionNids
// NB: "low-level" <=> by design, does NOT change the state, gui nor global flag
// but ought to be called by "scenario" functions that do
// fast because works on the subset of active nodes indicated in SystemState()
function deselectNodes(aSystemState){
if (isUndef(aSystemState)) aSystemState = TW.SystemState()
// active nodes
let sels = aSystemState.selectionNids
if (TW.conf.debug.logSelections) if (TW.conf.debug.logSelections)
console.log("\t***methods.js:highlightSelectedNodes(flag)"+flag+" sel:"+sels) console.log("deselecting using SystemState's lists")
for(let i in sels) { for(let i in sels) {
let nid = sels[i] let nid = sels[i]
TW.partialGraph.graph.nodes(nid).customAttrs.active = flag
// mark as unselected!
TW.partialGraph.graph.nodes(nid).customAttrs.active = 0
// for only case legend highlight...
TW.partialGraph.graph.nodes(nid).customAttrs.highlight = 0
}
// active relations
// (give us neighbors and edges to dehighlight/deactivate)
let rels = aSystemState.selectionRels
for (var reltyp in rels) {
for (var srcnid in rels[reltyp]) {
for (var tgtnid in rels[reltyp][srcnid]) {
let tgt = TW.partialGraph.graph.nodes(tgtnid)
if (tgt) {
tgt.customAttrs.highlight = 0
let eid1 = `${srcnid};${tgtnid}`
let eid2 = `${tgtnid};${srcnid}`
let e1 = TW.partialGraph.graph.edges(`${srcnid};${tgtnid}`)
if(e1) {
e1.customAttrs.activeEdge = 0
}
let e2 = TW.partialGraph.graph.edges(`${tgtnid};${srcnid}`)
if(e2) {
e2.customAttrs.activeEdge = 0
}
}
}
}
} }
} }
function manualForceLabel(nodeid, active, justHover) {
function manualForceLabel(nodeid, flagToSet, justHover) {
let nd = TW.partialGraph.graph.nodes(nodeid) let nd = TW.partialGraph.graph.nodes(nodeid)
nd.customAttrs.forceLabel = true nd.customAttrs.forceLabel = flagToSet
if (justHover) { if (justHover) {
// using single node redraw in hover layer (much faster ~ 0.5ms) // using single node redraw in hover layer (much faster ~ 0.5ms)
...@@ -350,9 +356,12 @@ function htmlfied_nodesatts(elems){ ...@@ -350,9 +356,12 @@ function htmlfied_nodesatts(elems){
function manualSelectNode ( nodeid ) { function manualSelectNode ( nodeid ) {
cancelSelection(false); // it was hovered but with no hover:out so we first remove hover effect
manualForceLabel(nodeid, false, true)
// and it's a new selection
TW.instance.selNgn.MultipleSelection2({nodes:[nodeid]}); TW.instance.selNgn.MultipleSelection2({nodes:[nodeid]});
// (MultipleSelection2 will do the re-rendering) // (MultipleSelection2 will do the re-rendering and push the new state)
} }
function htmlProportionalLabels(elems , limit, selectableFlag) { function htmlProportionalLabels(elems , limit, selectableFlag) {
...@@ -453,37 +462,6 @@ function LevelButtonDisable( TF ){ ...@@ -453,37 +462,6 @@ function LevelButtonDisable( TF ){
$('#changelevel').prop('disabled', TF); $('#changelevel').prop('disabled', TF);
} }
// edges greyish color for unselected, when we have a selection
// NB: we just change the flags, not the colors
// renderer will see the flags and handle the case accordingly
// ££TODO rendering optimization: reduce effort by looping only on previously selected and neighbors
// and having (!active && !highlight) tested instead of then useless grey flag
function greyEverything(){
for(var nid in TW.Nodes){
let n = TW.partialGraph.graph.nodes(nid)
if (n && !n.hidden) {
// normal case handled by node renderers
// will trigger defgrey_color if (!active && !highlight)
n.customAttrs.active = false
n.customAttrs.forceLabel = false;
n.customAttrs.highlight = false;
}
}
if (TW.partialGraph.settings('drawEdges')) {
for(var i in TW.edgeIds){
let e = TW.partialGraph.graph.edges(TW.edgeIds[i])
if (e && !e.hidden && e.customAttrs.activeEdge) {
e.customAttrs.activeEdge = 0
}
}
}
}
// Converts from read nodes (sigma.parseCustom ) // Converts from read nodes (sigma.parseCustom )
// Remarks: // Remarks:
...@@ -555,16 +533,6 @@ function prepareNodesRenderingProperties(nodesDict) { ...@@ -555,16 +533,6 @@ function prepareNodesRenderingProperties(nodesDict) {
} }
// POSS n.type: distinguish rendtype and twtype // POSS n.type: distinguish rendtype and twtype
// POSS flags like this
// // sigma's flag: hidden (not used)
// hidden: false,
// customFlags : {
// // our status flags
// active: false,
// highlight: false,
// forceLabel: false,
// }
} }
} }
......
...@@ -80,7 +80,7 @@ var SigmaUtils = function () { ...@@ -80,7 +80,7 @@ var SigmaUtils = function () {
(node.color || settings('defaultNodeColor')) : (node.color || settings('defaultNodeColor')) :
settings('defaultLabelColor') ; settings('defaultLabelColor') ;
// NB active is used in all TW selections // NB active is used in all TW selections
// forceLabel is used in cluster highlighting // forceLabel is used in manualForceLabel (fake hover)
// highlight is used for selection's neighbors or cluster highlighting // highlight is used for selection's neighbors or cluster highlighting
let X = node[prefix + 'x'] let X = node[prefix + 'x']
...@@ -308,7 +308,7 @@ var SigmaUtils = function () { ...@@ -308,7 +308,7 @@ var SigmaUtils = function () {
borderSize *= 1.3 borderSize *= 1.3
} }
// passive nodes should blend in the grey of twEdgeGreyColor // passive nodes should blend in the grey of twEdgeGreyColor
// cf settings_explorerjs, defgrey_color and greyEverything() // cf settings_explorerjs, defgrey_color and deselectNodes()
else { else {
if (! TW.gui.handpickedcolor) { if (! TW.gui.handpickedcolor) {
nodeColor = node.customAttrs.defgrey_color nodeColor = node.customAttrs.defgrey_color
......
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