parent
a3042794db
commit
ffc8494062
@ -1,3 +1,221 @@ |
|||||||
console.log('jxr extras to gradually add, by default could refer to branches instead then add as components proper') |
console.log('jxr extras to gradually add, by default could refer to branches instead then add as components proper') |
||||||
console.log('arguably utils and additional interactions beyond pinche could be extras rather than core') |
console.log('arguably utils and additional interactions beyond pinche could be extras rather than core') |
||||||
console.log('extra could also be empty...') |
console.log('extra could also be empty...') |
||||||
|
|
||||||
|
// from https://glitch.com/edit/#!/zen-pim?path=graph-cyto-headless.html
|
||||||
|
// see also https://observablehq.com/@utopiah/d3-pim-graph
|
||||||
|
// motivated by https://these.arthurperret.fr/chapitre-4.html#cosmographe-et-cosmoscope
|
||||||
|
|
||||||
|
const cytoJson = "https://vatelier.benetou.fr/MyDemo/newtooling/wiki_graph_cyto.json" |
||||||
|
|
||||||
|
var cy |
||||||
|
var jsonLoaded |
||||||
|
|
||||||
|
fetch(cytoJson) |
||||||
|
.then(function (response) { |
||||||
|
return response.json(); |
||||||
|
}) |
||||||
|
.then(function (json) { |
||||||
|
// take nodes from wiki.Nodes then construct an array in elements for cytoscape format
|
||||||
|
// or directly from JSON http://js.cytoscape.org/#notation/elements-json
|
||||||
|
jsonLoaded = json |
||||||
|
startCytoWithData(json) |
||||||
|
}) |
||||||
|
.catch(function (err) { |
||||||
|
console.log(err); |
||||||
|
}); |
||||||
|
|
||||||
|
// should instead directly use https://vatelier.net/MyDemo/newtooling/wiki_graph_cyto.json
|
||||||
|
// generated from https://vatelier.net/MyDemo/newtooling/build_graph_cytograph.js
|
||||||
|
// cf instead http://js.cytoscape.org/demos/tokyo-railways/tokyo-railways.json more detail
|
||||||
|
// with details on how to load the JSON
|
||||||
|
|
||||||
|
function runCytoAnalysis(){ |
||||||
|
console.log('----------------- network analysis started --------------')
|
||||||
|
if (!cy.nodes('[id = "Analysis.Analysis"]').length) { |
||||||
|
console.warn('Wrong dataset, cancelling') |
||||||
|
return |
||||||
|
} |
||||||
|
// example of queries
|
||||||
|
var aStar = cy.elements().aStar({ root: '[id = "Analysis.Analysis"]', goal: '[id = "Analysis.CostsAndBenefitsOfSocietalMembership"]' }) |
||||||
|
if ( aStar.path){ |
||||||
|
console.log("Path from Analysis.Analysis to Analysis.CostsAndBenefitsOfSocietalMembership", aStar.path.edges() , aStar.path.nodes() ) |
||||||
|
aStar.path.select() |
||||||
|
} |
||||||
|
console.log( cy.nodes('[id = "Analysis.Analysis"]').neighborhood() ) |
||||||
|
before = Date.now() |
||||||
|
var bc = cy.elements().bc() |
||||||
|
after = Date.now() |
||||||
|
console.log("took ", after-before, "ms to run.") |
||||||
|
console.log( 'bc of j: ' + bc.betweenness('[id = "Analysis.Analysis"]') ) |
||||||
|
console.log( cy.nodes('[id = "Analysis.CostsAndBenefitsOfSocietalMembership"]').neighborhood() ) |
||||||
|
//no need to run cy.elements().bc() again. It's done once for the whole graph.
|
||||||
|
console.log( 'bc of j: ' + bc.betweenness('[id = "Analysis.CostsAndBenefitsOfSocietalMembership"]') ) |
||||||
|
console.log('----------------- network analysis done -----------------') |
||||||
|
} |
||||||
|
|
||||||
|
function startCytoWithData(json){ |
||||||
|
cy = cytoscape({ |
||||||
|
headless:true, |
||||||
|
elements: json.elements |
||||||
|
}) |
||||||
|
|
||||||
|
//runCytoAnalysis() //quite demanding, skipped for now.
|
||||||
|
|
||||||
|
let defaults = { |
||||||
|
name: 'euler', |
||||||
|
springLength: edge => 80, |
||||||
|
springCoeff: edge => 0.0008, |
||||||
|
mass: node => 4, |
||||||
|
gravity: -1.2, |
||||||
|
pull: 0.001, |
||||||
|
theta: 0.666, |
||||||
|
dragCoeff: 0.02, |
||||||
|
movementThreshold: 1, |
||||||
|
timeStep: 20, |
||||||
|
refresh: 10, |
||||||
|
animate: true, |
||||||
|
animationDuration: undefined, |
||||||
|
animationEasing: undefined, |
||||||
|
maxIterations: 1000, |
||||||
|
maxSimulationTime: 4000, |
||||||
|
ungrabifyWhileSimulating: false, |
||||||
|
fit: true, |
||||||
|
padding: 30, |
||||||
|
// Constrain layout bounds with one of
|
||||||
|
// - { x1, y1, x2, y2 }
|
||||||
|
// - { x1, y1, w, h }
|
||||||
|
// - undefined / null : Unconstrained
|
||||||
|
boundingBox: undefined, |
||||||
|
|
||||||
|
// Layout event callbacks; equivalent to `layout.one('layoutready', callback)` for example
|
||||||
|
ready: function(){ console.log("graph ready", cy.json()) }, // on layoutready
|
||||||
|
stop: stableGraph(), // on layoutstop
|
||||||
|
randomize: false |
||||||
|
}; |
||||||
|
|
||||||
|
// disabled for tests
|
||||||
|
//cy.layout( defaults ).run(); // too demanding for the entire graph, should limit to a subset
|
||||||
|
} |
||||||
|
|
||||||
|
function stableGraph(){ |
||||||
|
var exportableJSON = cy.json() |
||||||
|
console.log( 'exportable JSON', exportableJSON ); |
||||||
|
// not actually stable!
|
||||||
|
// still could be used as a form of caching BUT... would take into account new nodes added since
|
||||||
|
|
||||||
|
var node = "ReadingNotes.ApocalypticAI" |
||||||
|
if (!cy.nodes('[id = "'+node+'"]').length) { |
||||||
|
console.warn('Wrong dataset, cancelling') |
||||||
|
return |
||||||
|
} |
||||||
|
console.log('should add/update AFrame nodes e.g', cy.elements('[id = "'+node+'"]').position()) |
||||||
|
console.log('could add all the nodes then their links with proper attributes in order to do select() after') |
||||||
|
// using e.g. cy.nodes().forEach( n => console.log(n.data(), n.position() ) )
|
||||||
|
// cy.edges().forEach( e => console.log(e.data(), e.sourceEndpoint(), e.targetEndpoint() ))
|
||||||
|
// warning, very costly!
|
||||||
|
// run on entire wiki though whereas previous D3 instance limited to 10 pages and their targets
|
||||||
|
} |
||||||
|
|
||||||
|
let edges_to_display = [] |
||||||
|
function displayLeafs(graph, graphEl, rootId, rootEl, depth=3){ |
||||||
|
console.log( graph[rootId].Id ) |
||||||
|
if (depth<1) return |
||||||
|
graph[rootId].Targets.map( l => { |
||||||
|
console.log( "-", graph[l].Id ) |
||||||
|
let x = addNodeFromGraph(graph[l].Id, "" + (Math.random()*2) + " " + (Math.random()*2) + " -" + (Math.random()*2) ) |
||||||
|
// layout could be done with Cytoscape but somehow seems I can't use it properly, in headless mode or not.
|
||||||
|
x.setAttribute('update-links-on-pinchended', true) |
||||||
|
x.setAttribute('toggle-links-on-left-pinchended', true) |
||||||
|
console.log("linking:", rootEl, x) |
||||||
|
edges_to_display.push({graphel:graphEl, source:rootEl, target:x}) |
||||||
|
displayLeafs( graph, graphEl, graph[l].Id, x, --depth) |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
AFRAME.registerComponent('toggle-links-on-left-pinchended', { |
||||||
|
init: function(){ |
||||||
|
let graphEl = document.querySelector("#graphroot") |
||||||
|
let el = this.el |
||||||
|
this.el.addEventListener('lreleased', function (event) { |
||||||
|
// if it has children (...how knowing that we are not using the hierarchy?)
|
||||||
|
// delete them, including links (should be recursive too)
|
||||||
|
// if not, displayLeafs( mynodes, graphEl, root.Id, rootEl, 1)
|
||||||
|
// assumes not for now
|
||||||
|
displayLeafs( wikiStructure, graphEl, el.getAttribute("value"), el, 1) |
||||||
|
setTimeout( _ => { edges_to_display.map( i => addEdgeBetweenNodesFromGraph( i.graphel, i.source, i.target ))}, 2000 ) |
||||||
|
}) |
||||||
|
} |
||||||
|
}) |
||||||
|
|
||||||
|
AFRAME.registerComponent('update-links-on-pinchended', { |
||||||
|
init: function(){ |
||||||
|
let rootEl = document.querySelector("#graphroot") |
||||||
|
let el = this.el |
||||||
|
this.el.addEventListener('released', function (event) { |
||||||
|
Object.keys( rootEl.components ) // get all links
|
||||||
|
.filter( i => i.indexOf(el.id) > -1 ) // keeps links related to the moved node
|
||||||
|
.map( i => { // for each link
|
||||||
|
let newpos = AFRAME.utils.coordinates.stringify( el.getAttribute("position") ) |
||||||
|
let [src,tgt] = i.replace("line__","").split("__to__"); |
||||||
|
srcpos = AFRAME.utils.coordinates.stringify( rootEl.getAttribute(i).start ) |
||||||
|
tgtpos = AFRAME.utils.coordinates.stringify( rootEl.getAttribute(i).end ) |
||||||
|
if (src==el.id){ |
||||||
|
rootEl.setAttribute(i, { start: newpos, end: tgtpos }) |
||||||
|
} else { |
||||||
|
rootEl.setAttribute(i, { start: srcpos, end: newpos }) |
||||||
|
} |
||||||
|
}) |
||||||
|
}) |
||||||
|
} |
||||||
|
}) |
||||||
|
|
||||||
|
let nodes = [] |
||||||
|
let edges = [] |
||||||
|
let wikiStructure = null |
||||||
|
|
||||||
|
fetch('https://vatelier.benetou.fr/MyDemo/newtooling/wiki_graph.json').then( r => r.json() ).then( r => displayGraph(r.Nodes) ); |
||||||
|
// alternatively there is the issue component that could also display linked issues
|
||||||
|
function displayGraph(mynodes){ |
||||||
|
wikiStructure = mynodes |
||||||
|
let startingNode = "Wiki.VirtualRealityInterface" |
||||||
|
let root = mynodes[startingNode] |
||||||
|
|
||||||
|
let graphEl = document.createElement("a-entity") |
||||||
|
graphEl.id = "graphroot" |
||||||
|
AFRAME.scenes[0].appendChild( graphEl ) |
||||||
|
let node_names = Object.keys( mynodes ) |
||||||
|
|
||||||
|
let rootEl = addNodeFromGraph(root.Id, "" + (Math.random()*2-1) + " " + (Math.random()+1) + " -" + (Math.random()*2-0.5) ) |
||||||
|
rootEl.setAttribute('update-links-on-pinchended', true) |
||||||
|
displayLeafs( mynodes, graphEl, root.Id, rootEl, 2) |
||||||
|
setTimeout( _ => { edges_to_display.map( i => addEdgeBetweenNodesFromGraph( i.graphel, i.source, i.target ))}, 2000 ) |
||||||
|
// quite unreliable
|
||||||
|
// should listen to an event instead to insure that nodes are all created before
|
||||||
|
} |
||||||
|
|
||||||
|
function addNodeFromGraph(name, position="0 0 0"){ |
||||||
|
// add sphere, with its name, make it a target
|
||||||
|
// define what "it" is knowing we can't move a children with an offset
|
||||||
|
// consequently parenting should be done by the text
|
||||||
|
let el = addNewNote(name, position, ".1 .1 .1", "node_" +crypto.randomUUID(), "node_from_graph") |
||||||
|
let sphereEl = document.createElement("a-sphere") |
||||||
|
sphereEl.setAttribute("radius", .1) |
||||||
|
sphereEl.setAttribute("segments-height", 4) |
||||||
|
sphereEl.setAttribute("segments-width", 4) |
||||||
|
sphereEl.setAttribute("wireframe", true) |
||||||
|
sphereEl.setAttribute("position", "0 -.1 0") |
||||||
|
el.appendChild(sphereEl) |
||||||
|
// position shouldn't have to be offset
|
||||||
|
return el |
||||||
|
} |
||||||
|
|
||||||
|
function addEdgeBetweenNodesFromGraph(graphEl, a, b){ |
||||||
|
// a.setAttribute( "line-link-entities", {source: a.id, target: b.id} ) doesn't seem work, back to basics for now
|
||||||
|
graphEl.setAttribute("line__"+a.id+"__to__"+b.id, { |
||||||
|
start: AFRAME.utils.coordinates.stringify( a.getAttribute("position") ) , |
||||||
|
end: AFRAME.utils.coordinates.stringify( b.getAttribute("position") )
|
||||||
|
}) |
||||||
|
// not that this doesn't take into account the parent node moving
|
||||||
|
} |
||||||
|
|
||||||
|
Loading…
Reference in new issue