SpaSca : open SCAffolding to SPAcially and textualy explore interfaces https://fabien.benetou.fr/pub/home/future_of_text_demo/engine/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
text-code-xr-engine/jxr-extras.js

222 lines
8.7 KiB

9 months ago
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('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
}