Cleaner story tree in Panopticon
This commit is contained in:
parent
79424ccfbf
commit
8722203704
4 changed files with 77 additions and 9 deletions
|
@ -162,6 +162,11 @@ img.icon {
|
|||
width: 385px;
|
||||
max-height: 100%;
|
||||
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 {
|
||||
padding: 10px;
|
||||
margin-bottom: 10px;
|
||||
|
|
|
@ -70,7 +70,7 @@
|
|||
<div id="btn-addMsg" class="btn">Create message</div>
|
||||
</div>
|
||||
<div id="msg"></div>
|
||||
<svg id='graph' viewbox="0 0 1280 1024"
|
||||
<svg id='graph' viewbox="-640 -512 1280 1024"
|
||||
preserveAspectRatio="xMidYMid">
|
||||
<defs>
|
||||
<marker markerHeight="8" markerWidth="8" refY="0"
|
||||
|
|
|
@ -447,7 +447,7 @@ class Graph {
|
|||
crel(
|
||||
'h3',
|
||||
{'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', {
|
||||
'class':'btn btn--delete',
|
||||
|
@ -982,6 +982,8 @@ class Graph {
|
|||
document.getElementById('current_lang').appendChild(crel('span', {
|
||||
'class': 'flag-icon ' + this.language_code
|
||||
}));
|
||||
|
||||
this.distances = this.calculateDistancesFromStart();
|
||||
|
||||
// save state;
|
||||
this.saveState();
|
||||
|
@ -1018,12 +1020,18 @@ class Graph {
|
|||
|
||||
build( isInit ) {
|
||||
this.simulation = d3.forceSimulation( this.messages )
|
||||
.force( "link", d3.forceLink( this.directions ).id( d => d['@id'] ) )
|
||||
.force( "charge", d3.forceManyBody().strength( 100 ) )
|
||||
.force( "center", d3.forceCenter( this.width / 2, this.height / 2 ) )
|
||||
.force( "collide", d3.forceCollide( this.nodeSize * 1.7 ) )
|
||||
.force( "link", d3.forceLink( this.directions ).id( d => d['@id'] ).strength(0) )
|
||||
// .force( "charge", d3.forceManyBody().strength( 100 ) )
|
||||
// .force( "center", d3.forceCenter( this.width / 2, this.height / 2 ) )
|
||||
.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
|
||||
let node = this.nodesG
|
||||
|
@ -1036,7 +1044,7 @@ class Graph {
|
|||
|
||||
let newNodeG = newNode.append( "g" )
|
||||
.attr( 'id', d => d['@id'] )
|
||||
.call( d3.drag( this.simulation ) )
|
||||
// .call( d3.drag( this.simulation ) )
|
||||
.on( 'click', function( d ) {
|
||||
this.clickMsg( d );
|
||||
}.bind( this ) )
|
||||
|
@ -1162,10 +1170,56 @@ class Graph {
|
|||
// this.simulation.restart();
|
||||
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 ) {
|
||||
this.simulation.tick();
|
||||
// this.simulation.tick();
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -265,6 +265,15 @@ img.icon{
|
|||
max-height:100%;
|
||||
overflow-y: auto;
|
||||
|
||||
.directions{
|
||||
h3{
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
margin-right: 5em;
|
||||
}
|
||||
}
|
||||
|
||||
.msg__info, .directions > div{
|
||||
padding: 10px;
|
||||
margin-bottom: 10px;
|
||||
|
|
Loading…
Reference in a new issue