Commit 028ce16c authored by Romain Loth's avatar Romain Loth

optimize coloring legends

display empty bins if samerange, show unrounded values on range thresh hover, normalize empty bin treatment, write exemplar colors directly in TW.Clusters etc
parent 1d4e221c
...@@ -264,19 +264,19 @@ function set_ClustersLegend ( daclass, groupedByTicks ) { ...@@ -264,19 +264,19 @@ function set_ClustersLegend ( daclass, groupedByTicks ) {
// get a sample node color for each bin/class // get a sample node color for each bin/class
var nMatchedNodes = legendInfo[l]['nids'].length var nMatchedNodes = legendInfo[l]['nids'].length
if (nMatchedNodes) { let theColor = legendInfo[l].col || "#111" // black if empty
var midNid = legendInfo[l]['nids'][Math.floor(3*nMatchedNodes/4)]
var exampleColor
if (TW.gui.handpickedcolor) { // create the legend item
exampleColor = TW.partialGraph.graph.nodes(midNid).customAttrs.alt_color var preparedLabel = legendInfo[l]['labl']
if (preparedLabel == '_non_numeric_') {
if (!nMatchedNodes) {
continue // we skip "trash" category if empty
} }
else { else {
exampleColor = TW.partialGraph.graph.nodes(midNid).color preparedLabel = "not numeric"
}
} }
// create the legend item
var preparedLabel = legendInfo[l]['labl']
// we add a title to cluster classes by ranking their nodes and taking k best labels // we add a title to cluster classes by ranking their nodes and taking k best labels
if (TW.conf.facetOptions[daclass] && TW.conf.facetOptions[daclass].col == 'cluster') { if (TW.conf.facetOptions[daclass] && TW.conf.facetOptions[daclass].col == 'cluster') {
...@@ -326,13 +326,12 @@ function set_ClustersLegend ( daclass, groupedByTicks ) { ...@@ -326,13 +326,12 @@ function set_ClustersLegend ( daclass, groupedByTicks ) {
// all-in-one argument for SomeEffect // all-in-one argument for SomeEffect
var valueclassId = `${curType}::${daclass}::${l}` var valueclassId = `${curType}::${daclass}::${l}`
var colorBg = `<span style="background:${exampleColor};"></span>` var colorBg = `<span class="lgdcol" style="background:${theColor};"></span>`
LegendDiv += `<li onclick='SomeEffect("${valueclassId}")'>` LegendDiv += `<li onclick='SomeEffect("${valueclassId}")'>`
LegendDiv += colorBg + preparedLabel LegendDiv += colorBg + preparedLabel
LegendDiv += "</li>\n" LegendDiv += "</li>\n"
} }
}
LegendDiv += ' </ul>' LegendDiv += ' </ul>'
LegendDiv += ' </div>' LegendDiv += ' </div>'
......
...@@ -48,6 +48,7 @@ ...@@ -48,6 +48,7 @@
color:#250587; color:#250587;
margin: 7px; margin: 7px;
font-size:120%; font-size:120%;
cursor: default;
} }
.my-legend .legend-title { .my-legend .legend-title {
...@@ -70,7 +71,7 @@ ...@@ -70,7 +71,7 @@
line-height: 16px; line-height: 16px;
margin-bottom: 2px; margin-bottom: 2px;
} }
.my-legend ul.legend-labels li span { .my-legend ul.legend-labels li span.lgdcol {
display: block; display: block;
float: left; float: left;
height: 16px; height: 16px;
...@@ -79,6 +80,9 @@ ...@@ -79,6 +80,9 @@
margin-left: 0; margin-left: 0;
border: 1px solid #999; border: 1px solid #999;
} }
.my-legend span.thresh {
display: inline;
}
.my-legend .legend-source { .my-legend .legend-source {
font-size: 70%; font-size: 70%;
color: #999; color: #999;
...@@ -89,6 +93,7 @@ ...@@ -89,6 +93,7 @@
} }
/* ZOOMBAR */ /* ZOOMBAR */
#ctlzoom { #ctlzoom {
...@@ -322,7 +327,7 @@ ...@@ -322,7 +327,7 @@
margin-bottom: 0; margin-bottom: 0;
} }
.my-legend ul.legend-labels li span { .my-legend ul.legend-labels li span.lgdcol {
height: 11px; height: 11px;
width: 11px; width: 11px;
border: 1px solid #999; border: 1px solid #999;
......
...@@ -416,6 +416,8 @@ function facetsBinning (valuesIdx) { ...@@ -416,6 +416,8 @@ function facetsBinning (valuesIdx) {
} }
// NB these ticks are *minimums* so we stop one step *before* vMax // NB these ticks are *minimums* so we stop one step *before* vMax
// and simply include it in last interval // and simply include it in last interval
// console.warn(`samerange nBins:${nBins}, n distinct:${workingVals.length} => got n ticks:${legendRefTicks.length}`)
} }
else if (binningMode == 'samepop') { else if (binningMode == 'samepop') {
...@@ -545,14 +547,12 @@ function facetsBinning (valuesIdx) { ...@@ -545,14 +547,12 @@ function facetsBinning (valuesIdx) {
bracket = ']' bracket = ']'
} }
newTick.labl = `[${labLowThres} ; ${labHiThres}${bracket} (${newTick.nids.length})` newTick.labl = `[<span title="${lowThres}">${labLowThres}</span> ; <span title="${hiThres}">${labHiThres}</span>${bracket} (${newTick.nids.length})`
newTick.fullLabl = `${cat}||${at}||[${labLowThres} ; ${labHiThres}${bracket} (${newTick.nids.length})` // newTick.fullLabl = `${cat}||${at}||[${lowThres} ; ${hiThres}${bracket} (${newTick.nids.length})`
// save these bins as the cluster index (aka faceting) // faceting: save these bins as the cluster index (even if empty)
if (newTick.nids.length) {
facetIdx[cat][at].invIdx.push(newTick) facetIdx[cat][at].invIdx.push(newTick)
} }
}
// finally add the 'trash' category with any non_numeric vals // finally add the 'trash' category with any non_numeric vals
facetIdx[cat][at].invIdx.push({ facetIdx[cat][at].invIdx.push({
......
...@@ -645,9 +645,8 @@ function edgeInfos(anEdge) { ...@@ -645,9 +645,8 @@ function edgeInfos(anEdge) {
function gradientColoring(daclass) { function gradientColoring(daclass) {
// £TODO group as an option of cancelSelection to avoid 2 loops cancelSelection(false); // loops only on selected
cancelSelection(false); graphResetLabelsAndSizes() // full loop
graphResetLabelsAndSizes()
TW.gui.handpickedcolor = true TW.gui.handpickedcolor = true
...@@ -672,8 +671,6 @@ function gradientColoring(daclass) { ...@@ -672,8 +671,6 @@ 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)
// £TODO should use TW.Clusters here
for(var j in TW.nodeIds) { for(var j in TW.nodeIds) {
var the_node = TW.Nodes[ TW.nodeIds[j] ] var the_node = TW.Nodes[ TW.nodeIds[j] ]
var attval = the_node.attributes[daclass]; var attval = the_node.attributes[daclass];
...@@ -732,6 +729,28 @@ function gradientColoring(daclass) { ...@@ -732,6 +729,28 @@ function gradientColoring(daclass) {
// Edge precompute alt_rgb by new source-target nodes-colours combination // Edge precompute alt_rgb by new source-target nodes-colours combination
repaintEdges() repaintEdges()
// remember in clusters
let bins = TW.Clusters[getActivetypesNames()[0]][daclass]
if (bins && bins.invIdx) {
for (var i in bins.invIdx) {
if (bins.invIdx[i].labl != '_non_numeric_') {
let nidList = bins.invIdx[i]['nids']
if (nidList.length) {
// we take an exemplar in the range, further than middle
// (result optically more representative than with 1/2 of len)
let aNid = nidList[Math.floor(3*nidList.length/4)]
bins.invIdx[i].col = TW.partialGraph.graph.nodes(aNid).color
}
else {
bins.invIdx[i].col = "#111" // empty bin
}
}
else {
bins.invIdx[i].col = "#bbb" // non numeric values bin
}
}
}
// NB legend will group different possible values using // NB legend will group different possible values using
// precomputed ticks from TW.Clusters.terms[daclass] // precomputed ticks from TW.Clusters.terms[daclass]
set_ClustersLegend ( daclass) set_ClustersLegend ( daclass)
...@@ -814,9 +833,8 @@ function heatmapColoring(daclass) { ...@@ -814,9 +833,8 @@ function heatmapColoring(daclass) {
binColors = getHeatmapColors(nColors) binColors = getHeatmapColors(nColors)
// let's go // let's go
// £TODO group as an option of cancelSelection to avoid 2 loops cancelSelection(false); // loops only on selected
cancelSelection(false); graphResetLabelsAndSizes() // full loop
graphResetLabelsAndSizes()
// global flag // global flag
TW.gui.handpickedcolor = true TW.gui.handpickedcolor = true
...@@ -825,33 +843,41 @@ function heatmapColoring(daclass) { ...@@ -825,33 +843,41 @@ function heatmapColoring(daclass) {
for (var k in tickThresholds) { for (var k in tickThresholds) {
// console.debug('tick infos', tickThresholds[k]) // console.debug('tick infos', tickThresholds[k])
// ex: {labl: "terms||growth_rate||[0 ; 0.583]", nids: Array(99), range: [0 ; 0.583210]}
let theColor
// skip grouped NaN values case => grey // skip grouped NaN values case => grey
if (tickThresholds[k].labl == '_non_numeric_') { if (tickThresholds[k].labl == '_non_numeric_') {
continue theColor = '#bbb'
}
else {
theColor = binColors[k]
} }
// ex: {labl: "terms||growth_rate||[0 ; 0.583]", nids: Array(99), range: [0 ; 0.583210]} if (tickThresholds[k].nids.length) {
let rgbColStr = hex2rgba(binColors[k]).slice(0,3).join(',')
// color the referred nodes // color the referred nodes
for (var j in tickThresholds[k].nids) { for (var j in tickThresholds[k].nids) {
let n = TW.partialGraph.graph.nodes(tickThresholds[k].nids[j]) let n = TW.partialGraph.graph.nodes(tickThresholds[k].nids[j])
if (n) {
n.customAttrs.alt_color = binColors[k] n.customAttrs.alt_color = binColors[k]
n.customAttrs.altgrey_color = "rgba("+(hex2rgba(binColors[k]).slice(0,3).join(','))+",0.4)" n.customAttrs.altgrey_color = "rgba("+rgbColStr+",0.4)"
var originalLabel = TW.Nodes[n.id].label var originalLabel = TW.Nodes[n.id].label
if (doModifyLabel) { if (doModifyLabel) {
var valSt = n.attributes[daclass] var valSt = n.attributes[daclass]
n.label = `(${valSt}) ${originalLabel}` n.label = `(${valSt}) ${originalLabel}`
} }
else {
n.label = originalLabel
} }
} }
} }
// remember
tickThresholds[k].col = theColor
}
// Edge precompute alt_rgb by new source-target nodes-colours combination // Edge precompute alt_rgb by new source-target nodes-colours combination
repaintEdges() repaintEdges()
...@@ -871,19 +897,18 @@ function clusterColoring(daclass) { ...@@ -871,19 +897,18 @@ function clusterColoring(daclass) {
console.log(" = = = = = = = = = = = = = = = = = ") console.log(" = = = = = = = = = = = = = = = = = ")
console.log("") console.log("")
// £TODO group as an option of cancelSelection to avoid 2 loops cancelSelection(false); // now loops only on selected
cancelSelection(false); graphResetLabelsAndSizes() // full loop
graphResetLabelsAndSizes()
// louvain needs preparation // louvain needs preparation
if(daclass=="clust_louvain") { if(daclass=="clust_louvain") {
if(!TW.states.slice(-1)[0].LouvainFait) { if(!TW.SystemState().LouvainFait) {
try { try {
RunLouvain() RunLouvain()
TW.states.slice(-1)[0].LouvainFait = true TW.SystemState().LouvainFait = true
} }
catch(e) { catch(e) {
TW.states.slice(-1)[0].LouvainFait = false TW.SystemState().LouvainFait = false
console.warn("skipped error on louvain, falling back to default colors") console.warn("skipped error on louvain, falling back to default colors")
daclass == 'clust_default' daclass == 'clust_default'
} }
...@@ -915,6 +940,40 @@ function clusterColoring(daclass) { ...@@ -915,6 +940,40 @@ function clusterColoring(daclass) {
let nColors = TW.gui.colorList.length let nColors = TW.gui.colorList.length
let facets = TW.Clusters[getActivetypesNames()[0]][daclass]
if (facets && facets.invIdx) {
for (var i in facets.invIdx) {
let valGroup = facets.invIdx[i]
let theColor
if (valGroup.labl == "_non_numeric_") {
theColor == '#bbb'
}
else if (valGroup.val) {
theColor = colList[ valGroup.val ]
}
else if (valGroup.range) {
let someRepresentativeInt = stringToSomeInt(valGroup.range) % nColors
theColor = colList[ someRepresentativeInt ]
}
if (valGroup.nids.length) {
let rgbColStr = hex2rgba(theColor).slice(0,3).join(',')
for (let j in valGroup.nids) {
let theNode = TW.partialGraph.graph.nodes(valGroup.nids[j])
if (theNode) {
theNode.customAttrs.alt_color = theColor
theNode.customAttrs.altgrey_color = "rgba("+rgbColStr+",0.4)"
}
}
}
// rembember in TW.Clusters
valGroup.col = theColor
}
}
// fallback on old, slower strategy if scanClusters inactive
else {
for(var j in TW.nodeIds) { for(var j in TW.nodeIds) {
var the_node = TW.partialGraph.graph.nodes(TW.nodeIds[j]) var the_node = TW.partialGraph.graph.nodes(TW.nodeIds[j])
...@@ -944,6 +1003,8 @@ function clusterColoring(daclass) { ...@@ -944,6 +1003,8 @@ function clusterColoring(daclass) {
} }
} }
} }
}
// set the global state // set the global state
TW.gui.handpickedcolor = true TW.gui.handpickedcolor = true
} }
......
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