Cleaner story tree in Panopticon

This commit is contained in:
Ruben van de Ven 2019-02-18 22:33:31 +01:00
parent 79424ccfbf
commit 8722203704
4 changed files with 77 additions and 9 deletions

View file

@ -162,6 +162,11 @@ img.icon {
width: 385px; width: 385px;
max-height: 100%; max-height: 100%;
overflow-y: auto; } overflow-y: auto; }
#story #msg .directions h3 {
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
margin-right: 5em; }
#story #msg .msg__info, #story #msg .directions > div { #story #msg .msg__info, #story #msg .directions > div {
padding: 10px; padding: 10px;
margin-bottom: 10px; margin-bottom: 10px;

View file

@ -70,7 +70,7 @@
<div id="btn-addMsg" class="btn">Create message</div> <div id="btn-addMsg" class="btn">Create message</div>
</div> </div>
<div id="msg"></div> <div id="msg"></div>
<svg id='graph' viewbox="0 0 1280 1024" <svg id='graph' viewbox="-640 -512 1280 1024"
preserveAspectRatio="xMidYMid"> preserveAspectRatio="xMidYMid">
<defs> <defs>
<marker markerHeight="8" markerWidth="8" refY="0" <marker markerHeight="8" markerWidth="8" refY="0"

View file

@ -447,7 +447,7 @@ class Graph {
crel( crel(
'h3', 'h3',
{'title': direction['@id']}, {'title': direction['@id']},
direction['source'] == msg ? `To ${direction['target']['@id']}`: `From ${direction['source']['@id']}` direction['source'] == msg ? `To ${direction['target']['text']}`: `From ${direction['source']['text']}`
), ),
crel('div', { crel('div', {
'class':'btn btn--delete', 'class':'btn btn--delete',
@ -982,6 +982,8 @@ class Graph {
document.getElementById('current_lang').appendChild(crel('span', { document.getElementById('current_lang').appendChild(crel('span', {
'class': 'flag-icon ' + this.language_code 'class': 'flag-icon ' + this.language_code
})); }));
this.distances = this.calculateDistancesFromStart();
// save state; // save state;
this.saveState(); this.saveState();
@ -1018,12 +1020,18 @@ class Graph {
build( isInit ) { build( isInit ) {
this.simulation = d3.forceSimulation( this.messages ) this.simulation = d3.forceSimulation( this.messages )
.force( "link", d3.forceLink( this.directions ).id( d => d['@id'] ) ) .force( "link", d3.forceLink( this.directions ).id( d => d['@id'] ).strength(0) )
.force( "charge", d3.forceManyBody().strength( 100 ) ) // .force( "charge", d3.forceManyBody().strength( 100 ) )
.force( "center", d3.forceCenter( this.width / 2, this.height / 2 ) ) // .force( "center", d3.forceCenter( this.width / 2, this.height / 2 ) )
.force( "collide", d3.forceCollide( this.nodeSize * 1.7 ) ) .force( "collide", d3.forceCollide( this.nodeSize * 2.3 ) )
.force( "forceX", d3.forceX(function(m){
let fx = panopticon.graph.distances[m['@id']] !== null ? panopticon.graph.distances[m['@id']] * panopticon.graph.nodeSize * 4 : 0
console.log('fx', m['@id'], panopticon.graph.distances[m['@id']], fx);
return fx;
}).strength(50))
.force( "forceY", d3.forceY(m => panopticon.graph.distances[m['@id']] !== null ? 0 : panopticon.graph.nodeSize * 3 ).strength(30))
; ;
this.simulation.velocityDecay(.99); this.simulation.velocityDecay(.98);
// Update existing nodes // Update existing nodes
let node = this.nodesG let node = this.nodesG
@ -1036,7 +1044,7 @@ class Graph {
let newNodeG = newNode.append( "g" ) let newNodeG = newNode.append( "g" )
.attr( 'id', d => d['@id'] ) .attr( 'id', d => d['@id'] )
.call( d3.drag( this.simulation ) ) // .call( d3.drag( this.simulation ) )
.on( 'click', function( d ) { .on( 'click', function( d ) {
this.clickMsg( d ); this.clickMsg( d );
}.bind( this ) ) }.bind( this ) )
@ -1162,10 +1170,56 @@ class Graph {
// this.simulation.restart(); // this.simulation.restart();
if ( typeof isInit != 'undefined' && isInit ) { if ( typeof isInit != 'undefined' && isInit ) {
for ( let i = 0, n = Math.ceil( Math.log( this.simulation.alphaMin() ) / Math.log( 1 - this.simulation.alphaDecay() ) ); i < n; ++i ) { for ( let i = 0, n = Math.ceil( Math.log( this.simulation.alphaMin() ) / Math.log( 1 - this.simulation.alphaDecay() ) ); i < n; ++i ) {
this.simulation.tick(); // this.simulation.tick();
} }
} }
return this.svg.node(); return this.svg.node();
} }
calculateDistancesFromStart() {
let starts = this.messages.filter( m => m.hasOwnProperty('start') && m['start'] == true);
if (starts.length < 1) {
console.error("No start set");
return;
}
let startMsg = starts[0];
//initiate distances
let distances = {};
for(let msg of this.messages) {
distances[msg['@id']] = msg === startMsg ? 0 : null;
}
let directionsPerMsg = {};
console.log("dir", this.directions);
for(let direction of this.directions) {
let from = typeof direction['source'] == "string" ? direction['source'] : direction['source']['@id'];
let to = typeof direction['target'] == "string" ? direction['target'] : direction['target']['@id'];
if(!directionsPerMsg.hasOwnProperty(from)) {
directionsPerMsg[from] = [];
}
directionsPerMsg[from].push(to);
}
let traverseMsg = function(msgId, depth) {
if(!directionsPerMsg.hasOwnProperty(msgId)) {
// end of trail
return;
}
for(let childMsgId of directionsPerMsg[msgId]) {
if(distances[childMsgId] === null || distances[childMsgId] > depth) {
distances[childMsgId] = depth;
traverseMsg(childMsgId, depth+1);
} else {
// apparently, there is a loop. Don't traverse it.
}
}
}
traverseMsg(startMsg['@id'], 1);
return distances;
}
} }

View file

@ -265,6 +265,15 @@ img.icon{
max-height:100%; max-height:100%;
overflow-y: auto; overflow-y: auto;
.directions{
h3{
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
margin-right: 5em;
}
}
.msg__info, .directions > div{ .msg__info, .directions > div{
padding: 10px; padding: 10px;
margin-bottom: 10px; margin-bottom: 10px;