Commit 01491368 authored by Romain Loth's avatar Romain Loth

fa2 reinit method for changeLevel

parent a108fb7a
...@@ -92,7 +92,7 @@ var desirableScholarSize=6; //Remember that all scholars have the same size! ...@@ -92,7 +92,7 @@ var desirableScholarSize=6; //Remember that all scholars have the same size!
* - "off": button doesn't exist, fa2 stopped forever * - "off": button doesn't exist, fa2 stopped forever
**/ var fa2enabled=false;//"off"; **/ var fa2enabled=false;//"off";
var stopcriteria = false; var stopcriteria = false;
var fa2milliseconds=10000; // for initial auto-run if fa2enabled and any var fa2milliseconds=5000; // for initial auto-run if fa2enabled and any
// subsequent auto-runs if graph modified // subsequent auto-runs if graph modified
// deprecated ? // deprecated ?
......
...@@ -283,126 +283,157 @@ function changeType() { ...@@ -283,126 +283,157 @@ function changeType() {
// and SysSt.level is aka swMacro // and SysSt.level is aka swMacro
function changeLevel() { function changeLevel() {
var present = TW.partialGraph.states.slice(-1)[0]; // Last // show waiting cursor
var past = TW.partialGraph.states.slice(-2)[0] // avant Last theHtml.classList.add('waiting');
var lastpos = TW.partialGraph.states.length-1;
var avantlastpos = lastpos-1; // let the waiting cursor appear
setTimeout(function() {
var level = present.level; var present = TW.partialGraph.states.slice(-1)[0]; // Last
var sels = present.selections;//[144, 384, 543]//TW.partialGraph.states.selections; var past = TW.partialGraph.states.slice(-2)[0] // avant Last
var catDict = present.categoriesDict; var lastpos = TW.partialGraph.states.length-1;
var avantlastpos = lastpos-1;
// type "grammar" var level = present.level;
// used to distinguish types in TW.Relations var sels = present.selections;//[144, 384, 543]//TW.partialGraph.states.selections;
var catDict = present.categoriesDict;
// types eg [true] <=> '1'
// [true, true] <=> '1|1'
// type "grammar"
var type_t0 = present.type; // used to distinguish types in TW.Relations
var str_type_t0 = type_t0.map(Number).join("|")
// types eg [true] <=> '1'
// [X|Y]-change (NOT operation over the received state [X\Y] ) // [true, true] <=> '1|1'
var type_t1 = []
for(var i in type_t0) type_t1[i] = !type_t0[i] var type_t0 = present.type;
var str_type_t1 = type_t1.map(Number).join("|") var str_type_t0 = type_t0.map(Number).join("|")
// removed typing 2nd part (binSumCats) not useful for changeLevel // [X|Y]-change (NOT operation over the received state [X\Y] )
var type_t1 = []
for(var i in type_t0) type_t1[i] = !type_t0[i]
TW.partialGraph.graph.clear(); var str_type_t1 = type_t1.map(Number).join("|")
var voisinage = {} TW.partialGraph.graph.clear();
// Dictionaries of: selection+neighbors
var nodes_2_colour = {} var voisinage = {}
var edges_2_colour = {} // Dictionaries of: selection+neighbors
var nodes_2_colour = {}
// POSS: factorize with same strategy in MultipleSelection2 beginning var edges_2_colour = {}
for(var i in sels) {
s = sels[i]; // POSS: factorize with same strategy in MultipleSelection2 beginning
neigh = TW.Relations[str_type_t0][s] for(var i in sels) {
if(neigh) { s = sels[i];
for(var j in neigh) { neigh = TW.Relations[str_type_t0][s]
t = neigh[j] if(neigh) {
nodes_2_colour[t]=false; for(var j in neigh) {
edges_2_colour[s+";"+t]=true; t = neigh[j]
edges_2_colour[t+";"+s]=true; nodes_2_colour[t]=false;
if( !selections[t] ) edges_2_colour[s+";"+t]=true;
voisinage[ Number(t) ] = true; edges_2_colour[t+";"+s]=true;
} if( !selections[t] )
} voisinage[ Number(t) ] = true;
} }
for(var i in sels) }
nodes_2_colour[sels[i]]=true; }
for(var i in sels)
nodes_2_colour[sels[i]]=true;
var futurelevel = []
if(present.level) { // [Change to Local] when level=Global(1) var futurelevel = []
for(var i in nodes_2_colour)
add1Elem(i) if(present.level) { // [Change to Local] when level=Global(1)
for(var i in edges_2_colour) for(var i in nodes_2_colour)
add1Elem(i) add1Elem(i)
for(var i in edges_2_colour)
// Adding intra-neighbors edges O(voisinage²) add1Elem(i)
voisinage = Object.keys(voisinage)
for(var i=0;i<voisinage.length;i++) { // Adding intra-neighbors edges O(voisinage²)
for(var j=1;j<voisinage.length;j++) { voisinage = Object.keys(voisinage)
if( voisinage[i]!=voisinage[j] ) { for(var i=0;i<voisinage.length;i++) {
// console.log( "\t" + voisinage[i] + " vs " + voisinage[j] ) for(var j=1;j<voisinage.length;j++) {
add1Elem( voisinage[i]+";"+voisinage[j] ) if( voisinage[i]!=voisinage[j] ) {
} // console.log( "\t" + voisinage[i] + " vs " + voisinage[j] )
add1Elem( voisinage[i]+";"+voisinage[j] )
} }
}
futurelevel = false; }
} else { // [Change to Global] when level=Local(0) }
for(var n in TW.Nodes) {
if(type_t0[catDict[TW.Nodes[n].type]]) futurelevel = false;
add1Elem(n) // Selection is unchanged, no need to call MultipleSelection2
}
for(var e in TW.Edges) {
if(TW.Edges[e].categ==str_type_t0) } else { // [Change to Global] when level=Local(0)
add1Elem(e)
// var t0 = performance.now()
for(var n in TW.Nodes) {
if(type_t0[catDict[TW.Nodes[n].type]])
// we add 1 by 1
add1Elem(n)
}
for(var e in TW.Edges) {
if(TW.Edges[e].categ==str_type_t0)
add1Elem(e)
}
// var t1 = performance.now()
futurelevel = true;
// console.log("returning to global took:", t1-t0)
// Nodes Selection now:
if(sels.length>0) {
var SelInst = new SelectionEngine();
SelInst.MultipleSelection2({
nodes:sels,
nodesDict:nodes_2_colour,
edgesDict:edges_2_colour
});
overNodes=true;
}
}
// console.log("enviroment changeLevel nodes_2_colour", nodes_2_colour)
TW.partialGraph.states[avantlastpos] = {};
TW.partialGraph.states[avantlastpos].level = present.level;
TW.partialGraph.states[avantlastpos].selections = present.selections;
TW.partialGraph.states[avantlastpos].type = present.type;
TW.partialGraph.states[avantlastpos].opposites = present.opposites;
TW.partialGraph.states[avantlastpos].categories = present.categories;//to_del
TW.partialGraph.states[avantlastpos].categoriesDict = present.categoriesDict;//to_del
TW.partialGraph.states[lastpos].setState({
type: present.type,
level: futurelevel,
sels: Object.keys(selections).map(Number),
oppos: []
})
TW.partialGraph.states[lastpos].categories = present.categories;//to_del
TW.partialGraph.states[lastpos].categoriesDict = catDict;//to_del
TW.partialGraph.camera.goTo({x:0, y:0, ratio:1.2, angle: 0})
TW.partialGraph.refresh()
// recreate FA2 nodes array after you change the nodes
reInitFa2({
useSoftMethod: !futurelevel,
callback: function() {
theHtml.classList.remove('waiting');
// when going local, it's nice to see the selected nodes rearrange
if (!futurelevel) {
TW.partialGraph.startForceAtlas2();
setTimeout(function(){
TW.partialGraph.stopForceAtlas2();
},
fa2milliseconds)
}
} }
futurelevel = true; })
} },500 // cursor waiting
)
// console.log("enviroment changeLevel nodes_2_colour", nodes_2_colour)
// Nodes Selection now:
if(sels.length>0) {
var SelInst = new SelectionEngine();
SelInst.MultipleSelection2({
nodesDict:nodes_2_colour,
edgesDict:edges_2_colour
});
overNodes=true;
}
TW.partialGraph.states[avantlastpos] = {};
TW.partialGraph.states[avantlastpos].level = present.level;
TW.partialGraph.states[avantlastpos].selections = present.selections;
TW.partialGraph.states[avantlastpos].type = present.type;
TW.partialGraph.states[avantlastpos].opposites = present.opposites;
TW.partialGraph.states[avantlastpos].categories = present.categories;//to_del
TW.partialGraph.states[avantlastpos].categoriesDict = present.categoriesDict;//to_del
TW.partialGraph.states[lastpos].setState({
type: present.type,
level: futurelevel,
sels: Object.keys(selections).map(Number),
oppos: []
})
TW.partialGraph.states[lastpos].categories = present.categories;//to_del
TW.partialGraph.states[lastpos].categoriesDict = catDict;//to_del
TW.partialGraph.camera.goTo({x:0, y:0, ratio:1.2, angle: 0})
TW.partialGraph.refresh()
// TODO fix Fa2 in this context
} }
//============================= </ NEW BUTTONS > =============================// //============================= </ NEW BUTTONS > =============================//
......
...@@ -309,6 +309,8 @@ if(RES["OK"]) { ...@@ -309,6 +309,8 @@ if(RES["OK"]) {
TW.nNodes = TW.partialGraph.graph.nodes().length TW.nNodes = TW.partialGraph.graph.nodes().length
TW.nEdges = TW.partialGraph.graph.edges().length TW.nEdges = TW.partialGraph.graph.edges().length
// POSS make it TW.states
TW.partialGraph.states = [] TW.partialGraph.states = []
TW.partialGraph.states[0] = false; TW.partialGraph.states[0] = false;
TW.partialGraph.states[1] = TW.SystemStates; TW.partialGraph.states[1] = TW.SystemStates;
...@@ -416,14 +418,13 @@ if(RES["OK"]) { ...@@ -416,14 +418,13 @@ if(RES["OK"]) {
} }
}).index(); }).index();
// config for any future forceAtlas2 calls TW.FA2Params = {
TW.partialGraph.configForceAtlas2({
// adapting speed ------------- // adapting speed -------------
slowDown: 1, slowDown: 1,
startingIterations: 5, startingIterations: 5,
iterationsPerRender: 3, iterationsPerRender: 3,
barnesHutOptimize: true, barnesHutOptimize: false,
barnesHutTheta: .5, // barnesHutTheta: .5,
// global behavior ----------- // global behavior -----------
linLogMode: true, linLogMode: true,
...@@ -438,7 +439,10 @@ if(RES["OK"]) { ...@@ -438,7 +439,10 @@ if(RES["OK"]) {
// (but rather not needed when data already shows topic-centered // (but rather not needed when data already shows topic-centered
// node groups and/nor when preferential attachment type of data) // node groups and/nor when preferential attachment type of data)
outboundAttractionDistribution: false outboundAttractionDistribution: false
}) }
// init FA2 for any future forceAtlas2 calls
TW.partialGraph.configForceAtlas2(TW.FA2Params)
// REFA new sigma.js // REFA new sigma.js
TW.partialGraph.camera.goTo({x:0, y:0, ratio:1.2, angle: 0}) TW.partialGraph.camera.goTo({x:0, y:0, ratio:1.2, angle: 0})
......
...@@ -808,3 +808,40 @@ function saveGraphIMG(){ ...@@ -808,3 +808,40 @@ function saveGraphIMG(){
var strData = edgesDiv.toDataURL("image/png"); var strData = edgesDiv.toDataURL("image/png");
document.location.href = strData.replace("image/png", strDownloadMime) document.location.href = strData.replace("image/png", strDownloadMime)
} }
// reInitFa2 : to call after changeType/changeLevel
// ------------------------------------------------
// sigma 1.2 FA2 supervisor is lazily inited at the
// first call (startForceAtlas2 or configForceAtlas2)
// but it keeps its own node index (as byteArray) and
// so needs to be recreated when nodes change
function reInitFa2 (params) {
if (!params) params = {}
if (params.useSoftMethod) {
// soft method: we just update FA2 internal index
// (is good enough if new nodes are subset of previous nodes)
TW.partialGraph.supervisor.graphToByteArrays()
// now cb
if (params.callback) {
params.callback()
}
}
else {
TW.partialGraph.killForceAtlas2()
// after 1s to let killForceAtlas2 finish
setTimeout ( function() {
// init FA2
TW.partialGraph.configForceAtlas2(TW.FA2Params)
// now cb
if (params.callback) {
params.callback()
}
}, 1000)
}
}
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