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;
|
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;
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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',
|
||||||
|
@ -983,6 +983,8 @@ class Graph {
|
||||||
'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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in a new issue