Commit de6b4496 authored by Romain Loth's avatar Romain Loth

harmonized types access + finish sliders and changeType

TW.ByType offers faster access to nodes by type + various clarifications with new typestring
parent a01d7b8f
...@@ -214,8 +214,8 @@ TW.conf = (function(TW){ ...@@ -214,8 +214,8 @@ TW.conf = (function(TW){
// if fa2Available, the auto-run config: // if fa2Available, the auto-run config:
TWConf.fa2Enabled= false; // fa2 auto-run at start and after graph modified ? TWConf.fa2Enabled= true; // fa2 auto-run at start and after graph modified ?
TWConf.fa2Milliseconds=10000; // duration of auto-run TWConf.fa2Milliseconds=5000; // duration of auto-run
TWConf.minNodesForAutoFA2 = 5 // graph size threshold to auto-run TWConf.minNodesForAutoFA2 = 5 // graph size threshold to auto-run
......
...@@ -314,6 +314,7 @@ function changeType(optionaltypeFlag) { ...@@ -314,6 +314,7 @@ function changeType(optionaltypeFlag) {
let typeFlag let typeFlag
let outgoing = TW.SystemState() let outgoing = TW.SystemState()
let oldTypeId = outgoing.activetypes.indexOf(true) let oldTypeId = outgoing.activetypes.indexOf(true)
let mixedState = (outgoing.activereltypes.length > 1)
let preservedNodes = {} let preservedNodes = {}
// 1 - make the targetTypes choices // 1 - make the targetTypes choices
...@@ -323,7 +324,6 @@ function changeType(optionaltypeFlag) { ...@@ -323,7 +324,6 @@ function changeType(optionaltypeFlag) {
else { else {
// "comeback" case: going back from mixed view to nodes0 view // "comeback" case: going back from mixed view to nodes0 view
// ---------- // ----------
let mixedState = (outgoing.activereltypes.length > 1)
if (mixedState) { if (mixedState) {
typeFlag = 0 typeFlag = 0
} }
...@@ -359,16 +359,31 @@ function changeType(optionaltypeFlag) { ...@@ -359,16 +359,31 @@ function changeType(optionaltypeFlag) {
// 3 - define the projected selection (sourceNids => corresponding opposites) // 3 - define the projected selection (sourceNids => corresponding opposites)
let sourceNids = outgoing.selectionNids let sourceNids = outgoing.selectionNids
if (!outgoing.level && ! sourceNids.length) { if (!outgoing.level && !sourceNids.length) {
// when local and no selection => all local graph is selection
sourceNids = TW.partialGraph.graph.nodes().map(function(n){return n.id}) sourceNids = TW.partialGraph.graph.nodes().map(function(n){return n.id})
} }
let targetNids = getNeighbors(sourceNids, 'XR') let targetNids = {}
if (!mixedState) targetNids = getNeighbors(sourceNids, 'XR')
else {
// in mixed state we need to separate those already tgt state from others
let alreadyOk = TW.partialGraph.graph.getNodesByType(typeFlag)
let alreadyOkLookup = {}
for (var i in alreadyOk) {alreadyOkLookup[alreadyOk[i]] = true}
let needTransition = []
for (var i in sourceNids) {
let nid = sourceNids[i]
console.log('nid', nid)
if (alreadyOkLookup[nid]) targetNids[nid] = true
else needTransition.push(nid)
}
targetNids = Object.assign(targetNids, getNeighbors(needTransition, 'XR'))
}
// 4 - define the nodes to be added // 4 - define the nodes to be added
let newNodes = {} let newNodes = {}
// when scope is "entire graph" => entire set by type // when scope is "entire graph" => entire sets by type
if (outgoing.level) { if (outgoing.level) {
for (let typeId in newActivetypes) { for (let typeId in newActivetypes) {
if (newActivetypes[typeId]) { if (newActivetypes[typeId]) {
...@@ -384,13 +399,17 @@ function changeType(optionaltypeFlag) { ...@@ -384,13 +399,17 @@ function changeType(optionaltypeFlag) {
} }
// console.log('newNodes', newNodes) // console.log('newNodes', newNodes)
// 5 - define the new selection
let newselsArr = []
if (outgoing.selectionNids.length) newselsArr = Object.keys(targetNids)
// 5 - effect the changes on nodes
// 6 - effect the changes on nodes
deselectNodes()
if (typeFlag != "all") { if (typeFlag != "all") {
TW.partialGraph.graph.clear() // a new start is faster except in "jutsu" TW.partialGraph.graph.clear() // a new start is faster except in "jutsu"
} }
else { else {
deselectNodes()
TW.partialGraph.graph.getNodesByType(oldTypeId).map( TW.partialGraph.graph.getNodesByType(oldTypeId).map(
function(nid){ preservedNodes[nid]=true } function(nid){ preservedNodes[nid]=true }
) )
...@@ -402,7 +421,7 @@ function changeType(optionaltypeFlag) { ...@@ -402,7 +421,7 @@ function changeType(optionaltypeFlag) {
} catch(e) {continue} } catch(e) {continue}
} }
// 6 - add the relations // 7 - add the relations
let newEdges = {} let newEdges = {}
let allNodes = TW.partialGraph let allNodes = TW.partialGraph
if (typeFlag != "all") allNodes = newNodes if (typeFlag != "all") allNodes = newNodes
...@@ -430,24 +449,32 @@ function changeType(optionaltypeFlag) { ...@@ -430,24 +449,32 @@ function changeType(optionaltypeFlag) {
} }
} }
// 7 - effect the changes on edges // 8 - effect the changes on edges
for (var eid in newEdges) { for (var eid in newEdges) {
try { try {
TW.partialGraph.graph.addEdge(newEdges[eid]) TW.partialGraph.graph.addEdge(newEdges[eid])
} catch(e) {continue} } catch(e) {continue}
} }
// 8 - refresh view and record the state // 9 - refresh view and record the state
TW.partialGraph.camera.goTo({x:0, y:0, ratio:1, angle: 0}) TW.partialGraph.camera.goTo({x:0, y:0, ratio:1, angle: 0})
TW.partialGraph.refresh() TW.partialGraph.refresh()
TW.pushGUIState({ TW.pushGUIState({
activetypes: newActivetypes, activetypes: newActivetypes,
activereltypes: newReltypes, activereltypes: newReltypes,
sels: Object.keys(targetNids) sels: newselsArr
// rels: added by MS2 (highlighted opposite- and same-side neighbours) // rels: added by MS2 (highlighted opposite- and same-side neighbours)
// possible: add it in an early way here and request that MS2 doesn't change state // possible: add it in an early way here and request that MS2 doesn't change state
}) })
// to recreate the new selection in the new type graph, if we had one before
// NB relies on new actypes so should be after pushState
if (newselsArr.length) {
TW.instance.selNgn.MultipleSelection2({nodes: newselsArr});
if (TW.conf.debug.logSelections)
console.log("selection transitive projection from",sourceNids, "to", newselsArr)
}
// update the color menu // update the color menu
TW.gui.handpickedcolor = false TW.gui.handpickedcolor = false
changeGraphAppearanceByFacets( getActivetypesNames() ) changeGraphAppearanceByFacets( getActivetypesNames() )
...@@ -465,9 +492,10 @@ function changeType(optionaltypeFlag) { ...@@ -465,9 +492,10 @@ function changeType(optionaltypeFlag) {
// the pool of available nodes of a given type // the pool of available nodes of a given type
function getNodesOfType (typeid){ function getNodesOfType (typeid){
let res = {} let res = {}
for (var nid in TW.Nodes) { if (TW.ByType[typeid]) {
let n = TW.Nodes[nid] for (var j in TW.ByType[typeid]) {
if (TW.catDict[n.type] == typeid) { let nid = TW.ByType[typeid][j]
let n = TW.Nodes[TW.ByType[typeid][j]]
res[nid] = n res[nid] = n
} }
} }
...@@ -500,6 +528,8 @@ function getNeighbors(sourceNids, relKey) { ...@@ -500,6 +528,8 @@ function getNeighbors(sourceNids, relKey) {
// v // v
// local selection SysSt = {level: false, activetypes:XY} // local selection SysSt = {level: false, activetypes:XY}
// //
// POSS: rewrite using .hidden instead of add/remove
//
function changeLevel() { function changeLevel() {
// show waiting cursor // show waiting cursor
TW.gui.elHtml.classList.add('waiting'); TW.gui.elHtml.classList.add('waiting');
...@@ -522,7 +552,6 @@ function changeLevel() { ...@@ -522,7 +552,6 @@ function changeLevel() {
// [true, true] <=> '1|1' // [true, true] <=> '1|1'
var activetypes = present.activetypes; var activetypes = present.activetypes;
var activetypesKey = activetypes.map(Number).join("|")
var activereltypes = present.activereltypes var activereltypes = present.activereltypes
TW.partialGraph.graph.clear(); TW.partialGraph.graph.clear();
...@@ -616,11 +645,9 @@ function changeLevel() { ...@@ -616,11 +645,9 @@ function changeLevel() {
callback: function() { callback: function() {
TW.gui.elHtml.classList.remove('waiting'); TW.gui.elHtml.classList.remove('waiting');
// when going local, it's nice to see the selected nodes rearrange // rearrange nodes in all cases (&& if fa2Enabled)
if (!futurelevel) {
sigma_utils.smartForceAtlas() sigma_utils.smartForceAtlas()
} }
}
}) })
},500 // cursor waiting },500 // cursor waiting
) )
...@@ -638,8 +665,8 @@ function changeLevel() { ...@@ -638,8 +665,8 @@ function changeLevel() {
function edgeSizesLookup(eTypeStrs, criterion) { function edgeSizesLookup(eTypeStrs, criterion) {
var edgeweis = {} var edgeweis = {}
for (let i in TW.edgeIds) { for (let eid in TW.Edges) {
let e = TW.partialGraph.graph.edges(TW.edgeIds[i]) let e = TW.partialGraph.graph.edges(eid)
for (var etype_i in eTypeStrs) { for (var etype_i in eTypeStrs) {
let eTypeStr = eTypeStrs[etype_i] let eTypeStr = eTypeStrs[etype_i]
......
...@@ -196,8 +196,8 @@ function SomeEffect( ValueclassCode ) { ...@@ -196,8 +196,8 @@ function SomeEffect( ValueclassCode ) {
// 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){ for(let nid in TW.Nodes) {
let n = TW.partialGraph.graph.nodes(TW.nodeIds[j]) let n = TW.partialGraph.graph.nodes(nid)
if (n) { if (n) {
n.label = TW.Nodes[n.id].label n.label = TW.Nodes[n.id].label
n.size = TW.Nodes[n.id].size n.size = TW.Nodes[n.id].size
......
...@@ -4,7 +4,8 @@ ...@@ -4,7 +4,8 @@
TW.Nodes = []; TW.Nodes = [];
TW.Edges = []; TW.Edges = [];
TW.Relations = {} // edges sorted by source/target type TW.ByType = {} // node ids sorted by nodetype id (0, 1)
TW.Relations = {} // edges sorted by source/target type id ("00", "11")
TW.Clusters = []; // "by value" facet index built in parseCustom TW.Clusters = []; // "by value" facet index built in parseCustom
TW.File = "" // remember the currently opened file TW.File = "" // remember the currently opened file
...@@ -377,8 +378,7 @@ function mainStartGraph(inFormat, inData, twInstance) { ...@@ -377,8 +378,7 @@ function mainStartGraph(inFormat, inData, twInstance) {
TW.Nodes = dicts.nodes; TW.Nodes = dicts.nodes;
TW.Edges = dicts.edges; TW.Edges = dicts.edges;
TW.nodeIds = Object.keys(dicts.nodes) // useful for loops TW.ByType = dicts.byType // useful for loops
TW.edgeIds = Object.keys(dicts.edges)
// in-place: pre-compute all color/unselected color/size properties // in-place: pre-compute all color/unselected color/size properties
prepareNodesRenderingProperties(TW.Nodes) prepareNodesRenderingProperties(TW.Nodes)
...@@ -388,7 +388,7 @@ function mainStartGraph(inFormat, inData, twInstance) { ...@@ -388,7 +388,7 @@ function mainStartGraph(inFormat, inData, twInstance) {
// main console info // main console info
// =================== // ===================
console.info(`== new graph ${TW.nodeIds.length} nodes (${TW.categories.length > 1 ? 'bipartite': 'monopartite'}), ${TW.edgeIds.length} edges ==`) console.info(`== new graph ${Object.keys(TW.Nodes).length} nodes (${TW.categories.length > 1 ? 'bipartite': 'monopartite'}), ${Object.keys(TW.Edges).length} edges ==`)
// a posteriori categories diagnostic // a posteriori categories diagnostic
// ---------------------------------- // ----------------------------------
...@@ -410,35 +410,6 @@ function mainStartGraph(inFormat, inData, twInstance) { ...@@ -410,35 +410,6 @@ function mainStartGraph(inFormat, inData, twInstance) {
TW.graphData = {nodes: [], edges: []} TW.graphData = {nodes: [], edges: []}
TW.graphData = sigma_utils.FillGraph( initialActivetypes , initialActivereltypes, TW.catDict , TW.Nodes , TW.Edges , TW.graphData ); TW.graphData = sigma_utils.FillGraph( initialActivetypes , initialActivereltypes, TW.catDict , TW.Nodes , TW.Edges , TW.graphData );
// // ----------- TEST stock parse gexf and use nodes to replace TW's ---------
// var gexfData = gexf.fetch('data/politoscope/ProgrammeDesCandidats.gexf')
//
// TW.graphData = sigmaTools.myGexfParserReplacement(
// gexfData.nodes,
// gexfData.edges
// )
// console.log ('ex in TW.graphData.nodes[0]', TW.graphData.nodes[0])
//
// // our holey id-indexed arrays
// TW.Nodes = {}
// TW.Edges = {}
// TW.nodeIds = []
// TW.edgeIds = []
// for (var j in TW.graphData.nodes) {
// var nid = TW.graphData.nodes[j].id
// TW.Nodes[nid] = TW.graphData.nodes[j]
// TW.nodeIds.push(nid)
// }
// for (var i in TW.graphData.edges) {
// var eid = TW.graphData.edges[i].id
// TW.Edges[eid] = TW.graphData.edges[i]
// TW.edgeIds.push(eid)
// }
//
//
// // -------------------------------------------------------------------------
if (TW.graphData.nodes.length == 0) console.error("empty graph") if (TW.graphData.nodes.length == 0) console.error("empty graph")
if (TW.graphData.edges.length == 0) console.error("no edges in graph") if (TW.graphData.edges.length == 0) console.error("no edges in graph")
......
...@@ -995,12 +995,15 @@ function dictfyJSON( data , categories ) { ...@@ -995,12 +995,15 @@ function dictfyJSON( data , categories ) {
var catDict = {} var catDict = {}
var catCount = {} var catCount = {}
for(var i in categories) catDict[categories[i]] = i;
var edges={}, nodes={} var edges={}, nodes={}, nodesByType={}
// NB old additional objects by type nodes1 and nodes2 not necessary // NB nodesByType lists arrays of ids per nodetype
// (can use TW.partialGraph.graph.nodesByTypeNSize faster custom index) // (equivalent to TW.partialGraph.graph.getNodesByType but on full nodeset)
for(var i in categories) {
catDict[categories[i]] = i
nodesByType[i] = []
}
// normalization, same as parseGexf // normalization, same as parseGexf
let minNodeSize = Infinity let minNodeSize = Infinity
...@@ -1050,7 +1053,9 @@ function dictfyJSON( data , categories ) { ...@@ -1050,7 +1053,9 @@ function dictfyJSON( data , categories ) {
if (!catCount[node.type]) catCount[node.type] = 0 if (!catCount[node.type]) catCount[node.type] = 0
catCount[node.type]++; catCount[node.type]++;
// record
nodes[node.id] = node; nodes[node.id] = node;
nodesByType[catDict[node.type]].push(node.id)
// creating a faceted index from node.attributes // creating a faceted index from node.attributes
if (TW.conf.scanClusters) { if (TW.conf.scanClusters) {
...@@ -1133,6 +1138,7 @@ function dictfyJSON( data , categories ) { ...@@ -1133,6 +1138,7 @@ function dictfyJSON( data , categories ) {
let resDict = {} let resDict = {}
resDict.catCount = catCount; resDict.catCount = catCount;
resDict.nodes = nodes; resDict.nodes = nodes;
resDict.byType = nodesByType;
resDict.edges = edges; resDict.edges = edges;
return resDict; return resDict;
......
...@@ -10,12 +10,6 @@ var SigmaUtils = function () { ...@@ -10,12 +10,6 @@ var SigmaUtils = function () {
// console.log("FillGraph nodes",nodes) // console.log("FillGraph nodes",nodes)
// console.log("FillGraph edges",edges) // console.log("FillGraph edges",edges)
// retrocompatibility -------------------------------- 8< -------------
if (!initialActivereltypes.length) {
initialActivereltypes = [initialActivetypes.map(Number).join("|")]
}
// ---------------------------------------------------- 8< -------------
let i = 0 let i = 0
for(var nid in nodes) { for(var nid in nodes) {
var n = nodes[nid]; var n = nodes[nid];
...@@ -676,8 +670,8 @@ function gradientColoring(daclass) { ...@@ -676,8 +670,8 @@ function gradientColoring(daclass) {
TW.gui.handpickedcolor = true TW.gui.handpickedcolor = true
var min_pow = 0; var min_pow = 0;
for(var j in TW.nodeIds) { for(var nid in TW.Nodes) {
var the_node = TW.Nodes[ TW.nodeIds[j] ] var the_node = TW.Nodes[ nid ]
var attval = the_node.attributes[daclass]; var attval = the_node.attributes[daclass];
if( !isNaN(parseFloat(attval)) ) { //is float if( !isNaN(parseFloat(attval)) ) { //is float
while(true) { while(true) {
...@@ -696,8 +690,8 @@ function gradientColoring(daclass) { ...@@ -696,8 +690,8 @@ function gradientColoring(daclass) {
var themult = Math.pow(10,min_pow); var themult = Math.pow(10,min_pow);
// console.log('themult', themult) // console.log('themult', themult)
for(var j in TW.nodeIds) { for(var nid in TW.Nodes) {
var the_node = TW.Nodes[ TW.nodeIds[j] ] var the_node = TW.Nodes[ nid ]
var attval = the_node.attributes[daclass]; var attval = the_node.attributes[daclass];
var attnumber = Number(attval); var attnumber = Number(attval);
if (isNaN(attnumber)) { if (isNaN(attnumber)) {
...@@ -706,7 +700,7 @@ function gradientColoring(daclass) { ...@@ -706,7 +700,7 @@ function gradientColoring(daclass) {
var round_number = Math.round( attnumber*themult ) ; var round_number = Math.round( attnumber*themult ) ;
NodeID_Val[TW.nodeIds[j]] = { "round":round_number , "real":attnumber }; NodeID_Val[nid] = { "round":round_number , "real":attnumber };
if (round_number<real_min) real_min = round_number; if (round_number<real_min) real_min = round_number;
if (round_number>real_max) real_max = round_number; if (round_number>real_max) real_max = round_number;
...@@ -788,9 +782,7 @@ function gradientColoring(daclass) { ...@@ -788,9 +782,7 @@ function gradientColoring(daclass) {
// Edge-colour: precompute alt_rgb by source-target node.alt_color combination // Edge-colour: precompute alt_rgb by source-target node.alt_color combination
function repaintEdges() { function repaintEdges() {
for (var i in TW.edgeIds) { for (var eid in TW.Edges) {
let eid = TW.edgeIds[i]
if (eid) { if (eid) {
let idPair = eid.split(';') let idPair = eid.split(';')
if (idPair.length != 2) { if (idPair.length != 2) {
...@@ -931,12 +923,12 @@ function clusterColoring(daclass) { ...@@ -931,12 +923,12 @@ function clusterColoring(daclass) {
} }
if (daclass=="clust_default") { if (daclass=="clust_default") {
for(var j in TW.nodeIds) { for(var nid in TW.Nodes) {
var original_node_color = TW.Nodes[ TW.nodeIds[j] ].color var original_node_color = TW.Nodes[ nid ].color
TW.partialGraph.graph.nodes(TW.nodeIds[j]).color = original_node_color TW.partialGraph.graph.nodes(nid).color = original_node_color
// reset the alt_color valflag // reset the alt_color valflag
TW.partialGraph.graph.nodes(TW.nodeIds[j]).customAttrs.alt_color = null TW.partialGraph.graph.nodes(nid).customAttrs.alt_color = null
} }
// reset the global state // reset the global state
...@@ -994,15 +986,15 @@ function clusterColoring(daclass) { ...@@ -994,15 +986,15 @@ function clusterColoring(daclass) {
} }
// fallback on old, slower strategy if scanClusters inactive // fallback on old, slower strategy if scanClusters inactive
else { else {
for(var j in TW.nodeIds) { for(var nid in TW.Nodes) {
var the_node = TW.partialGraph.graph.nodes(TW.nodeIds[j]) var the_node = TW.partialGraph.graph.nodes(nid)
if (the_node) { if (the_node) {
// POSS: use "hidden" in filters instead of remove/readd // POSS: use "hidden" in filters instead of remove/readd
// then this condition would be more useful here // then this condition would be more useful here
if (! the_node.hidden) { if (! the_node.hidden) {
var attval = ( !isUndef(the_node.attributes) && !isUndef(the_node.attributes[daclass]) )? the_node.attributes[daclass] : TW.partialGraph.graph.nodes(TW.nodeIds[j])[daclass]; var attval = ( !isUndef(the_node.attributes) && !isUndef(the_node.attributes[daclass]) )? the_node.attributes[daclass] : TW.partialGraph.graph.nodes(nid)[daclass];
let theColor let theColor
...@@ -1017,7 +1009,7 @@ function clusterColoring(daclass) { ...@@ -1017,7 +1009,7 @@ function clusterColoring(daclass) {
theColor = colList[ someRepresentativeInt ] theColor = colList[ someRepresentativeInt ]
} }
// TW.partialGraph.graph.nodes(TW.nodeIds[j]).color = theColor // TW.partialGraph.graph.nodes(nid).color = theColor
the_node.customAttrs.alt_color = theColor the_node.customAttrs.alt_color = theColor
the_node.customAttrs.altgrey_color = "rgba("+(hex2rgba(theColor).slice(0,3).join(','))+",0.4)" the_node.customAttrs.altgrey_color = "rgba("+(hex2rgba(theColor).slice(0,3).join(','))+",0.4)"
} }
......
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