diff --git a/backgrounds/background_4.jpg b/backgrounds/background_4.jpg new file mode 100644 index 0000000000000000000000000000000000000000..13cda86339e4850f794e8f147133db01c3020dbb Binary files /dev/null and b/backgrounds/background_4.jpg differ diff --git a/display/graph.js b/display/graph.js new file mode 100644 index 0000000000000000000000000000000000000000..c339031d0591db90fc5ab23731f9d567de5c5fdc --- /dev/null +++ b/display/graph.js @@ -0,0 +1,281 @@ +const colorPallette = ['rgb(104, 169, 77)', 'rgb(102, 75, 154)', 'rgb(41, 171, 226)', 'rgb(224, 133, 35)', + 'rgb(214, 207, 126)', 'rgb(239, 65, 35)', 'rgb(255, 255, 255)']; +const edgeColors = {}; +var graph_element = document.getElementById('3d-graph'); + + + +class Graph { + constructor(dataUrl) { + this.graph = null; + this.highlightNodes = new Set(); + this.highlightLinks = new Set(); + this.hoverNode = null; + this.firstTick = true; + this.infooverlay = null; + this.loadGraph(dataUrl); + } + + async loadGraph(dataUrl) { + const gData = await fetch(dataUrl).then(res => res.json()) + this.graph = ForceGraph3D({extraRenderers: [new THREE.CSS2DRenderer(), new THREE.CSS3DRenderer()]}) + (document.getElementById('3d-graph')) + .graphData(gData) + .nodeLabel('id') + .nodeAutoColorBy('group') + .nodeColor(node => this.getNodeColor(node)) + .linkWidth(link => this.getLinkWidth(link)) + .onNodeClick(node => { + this.focusOnNode(node); + this.infooverlay.updateInfoOverlay(node); + }) + .onNodeHover(node => { + this.onNodeHover(node); + this.updateHighlight(); + }) + .onLinkHover(link => this.onLinkHover(link)) + .linkColor(link => this.getLinkColor(link)) + .linkOpacity(0.8) + .nodeThreeObjectExtend(false) + .nodeThreeObject(node => this.drawNode(node)) + .onEngineTick(() => this.initializeModel()) + .width(getWidth()) + .height(getHeight()); + } + + getNodeColor(node) { + return this.highlightNodes.has(node) ? node === hoverNode ? 'rgb(255,0,0,1)' : 'rgba(255,160,0,0.8)' : 'rgba(0,255,255,0.6)'; + } + + getLinkColor(link) { + if ('type' in link) { + return edgeColors[link.type] + } + return 'rgb(255, 255, 255)' + } + + getLinkWidth(link) { + return this.highlightLinks.has(link) ? 2 : 0.8; + } + + onNodeHover(node) { + // no state change + if ((!node && !this.highlightNodes.size) || (node && this.hoverNode === node)) return; + + this.highlightNodes.clear(); + this.highlightLinks.clear(); + if (node) { + this.highlightNodes.add(node); + node.neighbors.forEach(neighbor => this.highlightNodes.add(neighbor)); + node.links.forEach(link => this.highlightLinks.add(link)); + } + + this.hoverNode = node || null; + } + + onLinkHover(link) { + this.highlightNodes.clear(); + this.highlightLinks.clear(); + + if (link) { + this.highlightLinks.add(link); + this.highlightNodes.add(link.source); + this.highlightNodes.add(link.target); + } + + this.updateHighlight(); + } + + getLinkClasses() { + const linkClasses = []; + this.graph.graphData().links.forEach(link => linkClasses.push(link.type)); + return [... new Set(linkClasses)]; + } + + mapEdgeColors() { + const linkClasses = this.getLinkClasses() + for (let i = 0; i < linkClasses.length; i++) { + edgeColors[linkClasses[i]] = colorPallette[i % colorPallette.length] + } + } + + updateLinks() { + const gData = this.graph.graphData() + // cross-link node objects + gData.links.forEach(link => { + const a = link.source; + const b = link.target; + if (!a.neighbors) a.neighbors = []; + if (!b.neighbors) b.neighbors = []; + a.neighbors.push(b); + b.neighbors.push(a); + + if (!a.links) a.links = []; + if (!b.links) b.links = []; + a.links.push(link); + b.links.push(link); + }); + this.graph.graphData(gData) + } + + updateHighlight() { + // trigger update of highlighted objects in scene + this.graph + .nodeColor(this.graph.nodeColor()) + .linkWidth(this.graph.linkWidth()) + .linkDirectionalParticles(this.graph.linkDirectionalParticles()); + } + + addBackground() { + const sphereGeometry = new THREE.SphereGeometry(20000, 32, 32); + //const planeGeometry = new THREE.PlaneGeometry(1000, 1000, 1, 1); + const loader = new THREE.TextureLoader(); + //const planeMaterial = new THREE.MeshLambertMaterial({color: 0xFF0000, side: THREE.DoubleSide}); //THREE.BackSide + const planeMaterial = new THREE.MeshBasicMaterial({map: loader.load(plugin_path + 'backgrounds/background_4.jpg'), side: THREE.DoubleSide}); //THREE.BackSide + const mesh = new THREE.Mesh(sphereGeometry, planeMaterial); + mesh.position.set(0, 0, 0); + //mesh.rotation.set(0.5 * Math.PI, 0, 0); + + this.graph.scene().add(mesh); + } + + focusOnNode(node) { + // Aim at node from outside it + const distance = 250; + const distRatio = 1 + distance/Math.hypot(node.x, node.y, node.z); + + this.graph.cameraPosition( + { x: node.x * distRatio, y: node.y * distRatio, z: node.z * distRatio }, // new position + node, // lookAt ({ x, y, z }) + 1000 // ms transition duration + ); + } + + focusOnNodeId(id) { + const gData = this.graph.graphData(); + + gData.nodes.forEach(node => { + if (node.id === id) { + this.onNodeClick(node); + } + }) + } + + resizeGraph() { + this.graph.width(getWidth()); + this.graph.height(getHeight()); + } + + initializeModel() { + if (this.firstTick) { + this.mapEdgeColors(); + this.updateLinks(); + this.addBackground(); + loadComponents(); + this.firstTick = false; + } + } + + drawNode(node) { + // Draw node as label + image + const nodeDiv = document.createElement('div'); + const group = new THREE.Group(); + + const labelDiv = document.createElement('div') + labelDiv.textContent = node.name; + labelDiv.style.color = node.color; + labelDiv.className = 'node-label'; + nodeDiv.appendChild(labelDiv); + + const cssobj = new THREE.CSS3DSprite(nodeDiv); + cssobj.scale.set(0.25, 0.25, 0.25); + cssobj.position.set(0, -6, 0); + group.add(cssobj) + + // Draw node circle image + const textureLoader = new THREE.TextureLoader(); + const imageAlpha = textureLoader.load(plugin_path + 'datasets/images/alpha.png'); + let imageTexture = null; + + if ('image' in node) { + imageTexture = textureLoader.load(plugin_path + 'datasets/images/' + node.image); + } else { + imageTexture = textureLoader.load(plugin_path + 'datasets/images/default.jpg'); + } + + const material = new THREE.SpriteMaterial({map: imageTexture, alphaMap: imageAlpha, transparent: true, + alphaTest: 0.2, depthWrite:false, depthTest: false}); + const sprite = new THREE.Sprite(material); + sprite.renderOrder = 999; // This may not be optimal. But it allows us to render the sprite on top of everything else. + + if ('image' in node) { + sprite.scale.set(20, 20); + } else { + sprite.scale.set(5, 5); + } + + group.add(sprite) + + return group; + } + +} + + +const dataUrl = plugin_path + 'datasets/aud1.json' +G = new Graph(dataUrl); +linkoverlay = new LinkOverlay(G); +infooverlay = new InfoOverlay(); +G.infooverlay = infooverlay; + +function loadComponents() { + linkoverlay.create(); + infooverlay.create(); + add_fullscreen_mode_button(); + add_fullscreen_Listener(); + window.onresize = G.resizeGraph(); +} + +function getWidth() { + return document.getElementById('3d-graph').offsetWidth; +} + +function getHeight() { + return window.innerHeight - 200 +} + + +function getCanvasDivNode() { + const domNode = document.getElementById('3d-graph'); + return domNode.firstChild.firstChild.firstChild; +} + +function add_fullscreen_mode_button() { + const sceneNode = document.getElementById('3d-graph'); + const overlayNode = document.createElement('button'); + overlayNode.className = 'fullscreen_button'; + overlayNode.innerText = 'fullscreen'; + overlayNode.addEventListener("click", fullscreen_mode); + sceneNode.appendChild(overlayNode); +} + +function fullscreen_mode(){ + if(getCanvasDivNode().requestFullscreen) { + getCanvasDivNode().requestFullscreen().catch(); + } +} + +function resize_canvas() { + if(document.fullscreenElement == getCanvasDivNode()) { + G.graph.height(screen.height); + G.graph.width(screen.width); + } else { + G.graph.height(window.innerHeight - 200); + G.graph.width(getWidth()); + } +} + +function add_fullscreen_Listener() { + getCanvasDivNode().addEventListener("fullscreenchange", resize_canvas); +} + diff --git a/display/infooverlay.js b/display/infooverlay.js new file mode 100644 index 0000000000000000000000000000000000000000..854d07ee46ea1e590b9d7e3918c2399c00d39429 --- /dev/null +++ b/display/infooverlay.js @@ -0,0 +1,184 @@ + + + + +class InfoOverlay { + constructor() { + + } + + create() { + const sceneNode = getCanvasDivNode(); + const overlayNode = document.createElement('div'); + overlayNode.id = 'infoOverlay' + overlayNode.className = 'detail-view'; + //overlayNode.innerText = 'Hello there!'; + sceneNode.insertBefore(overlayNode, sceneNode.childNodes[2]); + + const close = document.createElement('div'); + close.innerHTML = '<p>✖</p>'; + close.id = 'infoOverlayCloseButton'; + close.className = 'close-button'; + overlayNode.appendChild(close); + + const topArea = document.createElement('div'); + topArea.className = 'detail-view-top-area'; + overlayNode.appendChild(topArea); + + const nodeImage = document.createElement('img'); + nodeImage.id = 'infoOverlayImage'; + nodeImage.src = plugin_path + 'datasets/images/default.jpg'; + nodeImage.className = 'detail-view-image'; + topArea.appendChild(nodeImage); + + const headline = document.createElement('h2'); + headline.id = 'infoOverlayHeadline'; + headline.innerText = 'Default Text'; + headline.className = 'detail-view-headline'; + topArea.appendChild(headline); + + const textArea = document.createElement('div'); + textArea.className = 'detail-view-text-area'; + overlayNode.appendChild(textArea); + + const description = document.createElement('p'); + description.id = 'infoOverlayDescription'; + description.innerText = 'Default Text' + description.setAttribute("overflow-y", "scroll"); + textArea.appendChild(description); + + const link_container = document.createElement('div'); + const links = document.createElement('div'); + const linkClasses = G.getLinkClasses(); + + link_container.id = 'link_container'; + link_container.className = 'bottom container'; + links.className = 'tab'; + links.id = 'links'; + + for(let i = 0; i < linkClasses.length; i++){ + + const linkType = linkClasses[i]; + const tablink = document.createElement('button'); + tablink.className = 'tablinks'; + tablink.id = linkType + "_id"; + tablink.innerHTML = linkType[0] + linkType[1] + linkType[2]; + tablink.style = "background-color: " + edgeColors[linkClasses[i]]; + tablink.setAttribute("onclick", "openTab(event, " + linkType + "_id)"); //unusual function call + links.appendChild(tablink); + + } + + link_container.appendChild(links); + + for(let i = 0; i < linkClasses.length; i++){ + + const linkType = linkClasses[i]; + const tabcontent = document.createElement('div'); + tabcontent.className = "tabcontent"; + tabcontent.id = linkType + "_id_content"; + tabcontent.style = "background-color: " + edgeColors[linkClasses[i]]; + link_container.appendChild(tabcontent); + + } + + overlayNode.appendChild(link_container); + + jQuery('#infoOverlayCloseButton').click(function () { + jQuery('#infoOverlay').slideUp('fast'); + }); + + //sceneNode.appendChild(overlayNode); + + } + + updateInfoOverlay(node) { + jQuery('#infoOverlayHeadline').text(node.name); + if ('image' in node) { + jQuery('#infoOverlayImage').attr('src', plugin_path + 'datasets/images/' + node.image); + } else { + jQuery('#infoOverlayImage').attr('src', plugin_path + 'datasets/images/default.jpg'); + } + + if ('description' in node) { + jQuery('#infoOverlayDescription').text(node.description); + } else { + jQuery('#infoOverlayDescription').text('Default Text'); + } + + this.updateTabs(node); + + jQuery('#infoOverlay').slideDown('fast'); + } + + updateTabs(node){ + //tabs of the links to other nodes: + //delete old nodes from tabcontent + const tabcontent = document.getElementsByClassName("tabcontent"); + for (let i = 0; i < tabcontent.length; i++) { + while (tabcontent[i].firstChild){ + tabcontent[i].removeChild(tabcontent[i].lastChild); + } + } + //add new nodes to tabcontent + for (let i = 0; i < tabcontent.length; i++) { //for every type of link + node.links.forEach(link => { //and for every link + if(link.type + '_id_content' == tabcontent[i].id){ //is checked if the type is equal + const linkButton = document.createElement('div'); //and if so a new element is created + linkButton.className = "link img"; + var node2; + if(link.source == node){ + node2 = link.target; + } + else if(link.target == node){ + node2 = link.source; + } + linkButton.id = "linkButton_" + node2.id; + const nodeID = node2.id; + + const linkImage = document.createElement('img'); + const linkTitle = document.createElement('div'); + + if ('image' in node2) { + linkImage.src = plugin_path + 'datasets/images/' + node2.image; + } else if(!('image' in node2) && ('id' in node2)) { + linkImage.src = plugin_path + 'datasets/images/default.jpg'; + } + + linkTitle.className = "link title"; + linkTitle.innerHTML = node2.name; + + //linkButton.appendChild(linkTitle); + linkButton.appendChild(linkImage); + + linkButton.addEventListener('click', function (){focusOnNodeId(nodeID)}); + tabcontent[i].appendChild(linkButton); + } + }); + } + } + + + +} + +//is used in createInfoOverlay, as an unusual function call to open link-tabs +function openTab (event, tab) { + var i, tabcontent, tablinks; + + // Get all elements with class="tabcontent" and hide them + tabcontent = document.getElementsByClassName("tabcontent"); + for (i = 0; i < tabcontent.length; i++) { + tabcontent[i].style.display = "none"; + } + + // Get all elements with class="tablinks" and remove the class "active" + tablinks = document.getElementsByClassName("tablinks"); + for (i = 0; i < tablinks.length; i++) { + tablinks[i].className = tablinks[i].className.replace(" active", ""); + } + + // Show the current tab, and add an "active" class to the button that opened the tab + document.getElementById(tab.id + "_content").style.display = "flex"; + event.currentTarget.className += " active"; +} \ No newline at end of file diff --git a/display/linkoverlay.js b/display/linkoverlay.js new file mode 100644 index 0000000000000000000000000000000000000000..5c3acaa72e130648d1c44c34136744c5ffc69bd0 --- /dev/null +++ b/display/linkoverlay.js @@ -0,0 +1,31 @@ + + +class LinkOverlay { + constructor(graph) { + this.graph = graph + } + + create() { + const sceneNode = getCanvasDivNode(); + const overlayNode = document.createElement('div'); + overlayNode.className = 'link-overlay'; + //overlayNode.innerText = 'Hello there!'; + sceneNode.insertBefore(overlayNode, sceneNode.childNodes[2]) + + const linkClasses = this.graph.getLinkClasses(); + const chars = Math.max.apply(Math, linkClasses.map(function (c) { return c.length; })); + + for (let i=0; i<linkClasses.length; i++) { + const relation = document.createElement('div'); + relation.className = 'relation'; + relation.innerHTML = "<p>" + linkClasses[i] + "</p>"; + overlayNode.appendChild(relation); + + const colorStrip = document.createElement('div'); + colorStrip.className = 'rel-container'; + colorStrip.style = "background-color: " + edgeColors[linkClasses[i]] + "; width: " + 10 * chars + "px;"; + relation.appendChild(colorStrip); + } + } + +} \ No newline at end of file diff --git a/graph.js b/graph.js deleted file mode 100644 index 65439fc86c0aa703c62c9ef84b9566f11eae9f31..0000000000000000000000000000000000000000 --- a/graph.js +++ /dev/null @@ -1,462 +0,0 @@ -const highlightNodes = new Set(); -const highlightLinks = new Set(); -let hoverNode = null; -let Graph = null; -let firstTick = true; - -const colorPallette = ['rgb(104, 169, 77)', 'rgb(102, 75, 154)', 'rgb(41, 171, 226)', 'rgb(224, 133, 35)', - 'rgb(214, 207, 126)', 'rgb(239, 65, 35)', 'rgb(255, 255, 255)']; -const edgeColors = {}; -var graph_element = document.getElementById('3d-graph'); - -loadGraph(); - -async function loadGraph() { - const dataUrl = plugin_path + 'datasets/aud1.json' - const gData = await fetch(dataUrl).then(res => res.json()) - Graph = ForceGraph3D({extraRenderers: [new THREE.CSS2DRenderer(), new THREE.CSS3DRenderer()]}) - (document.getElementById('3d-graph')) - .graphData(gData) - .nodeLabel('id') - .nodeAutoColorBy('group') - .nodeColor(node => highlightNodes.has(node) ? node === hoverNode ? 'rgb(255,0,0,1)' : 'rgba(255,160,0,0.8)' : 'rgba(0,255,255,0.6)') - .linkWidth(link => highlightLinks.has(link) ? 2 : 0.8) - .onNodeClick(handleNodeClick) - .onNodeHover(nodeHover) - .onLinkHover(linkHover) - .linkColor(linkColor) - .linkOpacity(0.8) - .nodeThreeObjectExtend(false) - .nodeThreeObject(drawNode) - .onEngineTick(loadComponents) - .width(getWidth()) - .height(getHeight()); - //Graph.renderer().sortObjects = true; -} - - -function loadComponents() { - if (firstTick) { - mapEdgeColors(); - updateLinks(); - add_background(); - createLinkOverlay(); - createInfoOverlay(); - add_fullscreen_mode_button(); - add_fullscreen_Listener(); - window.onresize = resize_graph(); - firstTick = false; - } -} - -function resize_graph() { - Graph.width(getWidth()); - Graph.height(getHeight()); -} - -function getWidth() { - return document.getElementById('3d-graph').offsetWidth; -} - -function getHeight() { - return window.innerHeight - 200 -} - -function mapEdgeColors() { - const linkClasses = getLinkClasses() - for (let i = 0; i < linkClasses.length; i++) { - edgeColors[linkClasses[i]] = colorPallette[i % colorPallette.length] - } -} - - -function getLinkClasses() { - const linkClasses = []; - Graph.graphData().links.forEach(link => linkClasses.push(link.type)); - return [... new Set(linkClasses)]; -} - -function getCanvasDivNode() { - const domNode = document.getElementById('3d-graph'); - return domNode.firstChild.firstChild.firstChild; -} - -function drawNode(node) { - // Draw node as label + image - const nodeDiv = document.createElement('div'); - group = new THREE.Group(); - - const labelDiv = document.createElement('div') - labelDiv.textContent = node.name; - labelDiv.style.color = node.color; - labelDiv.className = 'node-label'; - nodeDiv.appendChild(labelDiv); - - const cssobj = new THREE.CSS3DSprite(nodeDiv); - cssobj.scale.set(0.25, 0.25, 0.25); - cssobj.position.set(0, -6, 0); - group.add(cssobj) - - // Draw node circle image - const textureLoader = new THREE.TextureLoader(); - const imageAlpha = textureLoader.load(plugin_path + 'datasets/images/alpha.png'); - let imageTexture = null; - - if ('image' in node) { - imageTexture = textureLoader.load(plugin_path + 'datasets/images/' + node.image); - } else { - imageTexture = textureLoader.load(plugin_path + 'datasets/images/default.jpg'); - } - - const material = new THREE.SpriteMaterial({map: imageTexture, alphaMap: imageAlpha, transparent: true, - alphaTest: 0.2, depthWrite:false, depthTest: false}); - const sprite = new THREE.Sprite(material); - sprite.renderOrder = 999; // This may not be optimal. But it allows us to render the sprite on top of everything else. - - if ('image' in node) { - sprite.scale.set(20, 20); - } else { - sprite.scale.set(5, 5); - } - - group.add(sprite) - - return group; -} - - -function createInfoOverlay() { - const sceneNode = getCanvasDivNode(); - const overlayNode = document.createElement('div'); - overlayNode.id = 'infoOverlay' - overlayNode.className = 'detail-view'; - //overlayNode.innerText = 'Hello there!'; - sceneNode.insertBefore(overlayNode, sceneNode.childNodes[2]); - - const close = document.createElement('div'); - close.innerHTML = '<p>✖</p>'; - close.id = 'infoOverlayCloseButton'; - close.className = 'close-button'; - overlayNode.appendChild(close); - - const topArea = document.createElement('div'); - topArea.className = 'detail-view-top-area'; - overlayNode.appendChild(topArea); - - const nodeImage = document.createElement('img'); - nodeImage.id = 'infoOverlayImage'; - nodeImage.src = plugin_path + 'datasets/images/default.jpg'; - nodeImage.className = 'detail-view-image'; - topArea.appendChild(nodeImage); - - const headline = document.createElement('h2'); - headline.id = 'infoOverlayHeadline'; - headline.innerText = 'Default Text'; - headline.className = 'detail-view-headline'; - topArea.appendChild(headline); - - const textArea = document.createElement('div'); - textArea.className = 'detail-view-text-area'; - overlayNode.appendChild(textArea); - - const description = document.createElement('p'); - description.id = 'infoOverlayDescription'; - description.innerText = 'Default Text' - description.setAttribute("overflow-y", "scroll"); - textArea.appendChild(description); - - const link_container = document.createElement('div'); - const links = document.createElement('div'); - const linkClasses = getLinkClasses(); - - link_container.id = 'link_container'; - link_container.className = 'bottom container'; - links.className = 'tab'; - links.id = 'links'; - - for(i = 0; i < linkClasses.length; i++){ - - const linkType = linkClasses[i]; - const tablink = document.createElement('button'); - tablink.className = 'tablinks'; - tablink.id = linkType + "_id"; - tablink.innerHTML = linkType[0] + linkType[1] + linkType[2]; - tablink.style = "background-color: " + edgeColors[linkClasses[i]]; - tablink.setAttribute("onclick", "openTab(event, " + linkType + "_id)"); //unusual function call - links.appendChild(tablink); - - } - - link_container.appendChild(links); - - for(i = 0; i < linkClasses.length; i++){ - - const linkType = linkClasses[i]; - const tabcontent = document.createElement('div'); - tabcontent.className = "tabcontent"; - tabcontent.id = linkType + "_id_content"; - tabcontent.style = "background-color: " + edgeColors[linkClasses[i]]; - link_container.appendChild(tabcontent); - - } - - overlayNode.appendChild(link_container); - - jQuery('#infoOverlayCloseButton').click(function () { - jQuery('#infoOverlay').slideUp('fast'); - }); - - //sceneNode.appendChild(overlayNode); - -} - - -function createLinkOverlay() { - const sceneNode = getCanvasDivNode(); - const overlayNode = document.createElement('div'); - overlayNode.className = 'link-overlay'; - //overlayNode.innerText = 'Hello there!'; - sceneNode.insertBefore(overlayNode, sceneNode.childNodes[2]) - - const linkClasses = getLinkClasses(); - const chars = Math.max.apply(Math, linkClasses.map(function (c) { return c.length; })); - - for (let i=0; i<linkClasses.length; i++) { - const relation = document.createElement('div'); - relation.className = 'relation'; - relation.innerHTML = "<p>" + linkClasses[i] + "</p>"; - overlayNode.appendChild(relation); - - const colorStrip = document.createElement('div'); - colorStrip.className = 'rel-container'; - colorStrip.style = "background-color: " + edgeColors[linkClasses[i]] + "; width: " + 10 * chars + "px;"; - relation.appendChild(colorStrip); - } -} - -function add_fullscreen_mode_button() { - const sceneNode = document.getElementById('3d-graph'); - const overlayNode = document.createElement('button'); - overlayNode.className = 'fullscreen_button'; - overlayNode.innerText = 'fullscreen'; - overlayNode.addEventListener("click", fullscreen_mode); - sceneNode.appendChild(overlayNode); -} - -function fullscreen_mode(){ - if(getCanvasDivNode().requestFullscreen) { - getCanvasDivNode().requestFullscreen().catch(); - } -} - -function resize_canvas() { - if(document.fullscreenElement == getCanvasDivNode()) { - Graph.height(screen.height); - Graph.width(screen.width); - } else { - Graph.height(window.innerHeight - 200); - Graph.width(getWidth()); - } -} - -function add_fullscreen_Listener() { - getCanvasDivNode().addEventListener("fullscreenchange", resize_canvas); -} - -function add_background() { - const sphereGeometry = new THREE.SphereGeometry(20000, 32, 32); - //const planeGeometry = new THREE.PlaneGeometry(1000, 1000, 1, 1); - const loader = new THREE.TextureLoader(); - //const planeMaterial = new THREE.MeshLambertMaterial({color: 0xFF0000, side: THREE.DoubleSide}); //THREE.BackSide - const planeMaterial = new THREE.MeshBasicMaterial({map: loader.load(plugin_path + 'backgrounds/background_3.jpg'), side: THREE.DoubleSide}); //THREE.BackSide - const mesh = new THREE.Mesh(sphereGeometry, planeMaterial); - mesh.position.set(0, 0, 0); - //mesh.rotation.set(0.5 * Math.PI, 0, 0); - - Graph.scene().add(mesh); -} - - -function updateLinks() { - const gData = Graph.graphData() - // cross-link node objects - gData.links.forEach(link => { - const a = link.source; - const b = link.target; - if (!a.neighbors) a.neighbors = []; - if (!b.neighbors) b.neighbors = []; - a.neighbors.push(b); - b.neighbors.push(a); - - if (!a.links) a.links = []; - if (!b.links) b.links = []; - a.links.push(link); - b.links.push(link); - }); - Graph.graphData(gData) -} - - - -function handleNodeClick(node) { - focusOnNode(node); - updateInfoOverlay(node); -} - - -function updateInfoOverlay(node) { - jQuery('#infoOverlayHeadline').text(node.name); - if ('image' in node) { - jQuery('#infoOverlayImage').attr('src', plugin_path + 'datasets/images/' + node.image); - } else { - jQuery('#infoOverlayImage').attr('src', plugin_path + 'datasets/images/default.jpg'); - } - - if ('description' in node) { - jQuery('#infoOverlayDescription').text(node.description); - } else { - jQuery('#infoOverlayDescription').text('Default Text'); - } - - updateTabs(node); - - jQuery('#infoOverlay').slideDown('fast'); -} - -function updateTabs(node){ - //tabs of the links to other nodes: - //delete old nodes from tabcontent - tabcontent = document.getElementsByClassName("tabcontent"); - for (i = 0; i < tabcontent.length; i++) { - while (tabcontent[i].firstChild){ - tabcontent[i].removeChild(tabcontent[i].lastChild); - } - } - //add new nodes to tabcontent - for (i = 0; i < tabcontent.length; i++) { //for every type of link - node.links.forEach(link => { //and for every link - if(link.type + '_id_content' == tabcontent[i].id){ //is checked if the type is equal - const linkButton = document.createElement('div'); //and if so a new element is created - linkButton.className = "link img"; - var node2; - if(link.source == node){ - node2 = link.target; - } - else if(link.target == node){ - node2 = link.source; - } - linkButton.id = "linkButton_" + node2.id; - const nodeID = node2.id; - - const linkImage = document.createElement('img'); - const linkTitle = document.createElement('div'); - - if ('image' in node2) { - linkImage.src = plugin_path + 'datasets/images/' + node2.image; - } else if(!('image' in node2) && ('id' in node2)) { - linkImage.src = plugin_path + 'datasets/images/default.jpg'; - } - - linkTitle.className = "link title"; - linkTitle.innerHTML = node2.name; - - //linkButton.appendChild(linkTitle); - linkButton.appendChild(linkImage); - - linkButton.addEventListener('click', function (){focusOnNodeId(nodeID)}); - tabcontent[i].appendChild(linkButton); - } - }); - } -} - -function focusOnNodeId(id) { - const gData = Graph.graphData(); - - gData.nodes.forEach(node => { - if (node.id === id) { - handleNodeClick(node); - } - }) -} - -function focusOnNode(node) { - // Aim at node from outside it - const distance = 250; - const distRatio = 1 + distance/Math.hypot(node.x, node.y, node.z); - - Graph.cameraPosition( - { x: node.x * distRatio, y: node.y * distRatio, z: node.z * distRatio }, // new position - node, // lookAt ({ x, y, z }) - 1000 // ms transition duration - ); -} - - -function nodeHover(node) { - - // no state change - if ((!node && !highlightNodes.size) || (node && hoverNode === node)) return; - - highlightNodes.clear(); - highlightLinks.clear(); - if (node) { - highlightNodes.add(node); - node.neighbors.forEach(neighbor => highlightNodes.add(neighbor)); - node.links.forEach(link => highlightLinks.add(link)); - } - - hoverNode = node || null; - updateHighlight(); -} - - -function linkHover(link) { - highlightNodes.clear(); - highlightLinks.clear(); - - if (link) { - highlightLinks.add(link); - highlightNodes.add(link.source); - highlightNodes.add(link.target); - } - - updateHighlight(); -} - -function linkColor(link) { - if ('type' in link) { - return edgeColors[link.type] - } - return 'rgb(255, 255, 255)' -} - - -function updateHighlight() { - // trigger update of highlighted objects in scene - Graph - .nodeColor(Graph.nodeColor()) - .linkWidth(Graph.linkWidth()) - .linkDirectionalParticles(Graph.linkDirectionalParticles()); -} - -//is used in createInfoOverlay, as an unusual function call to open link-tabs -function openTab (event, tab) { - var i, tabcontent, tablinks; - - // Get all elements with class="tabcontent" and hide them - tabcontent = document.getElementsByClassName("tabcontent"); - for (i = 0; i < tabcontent.length; i++) { - tabcontent[i].style.display = "none"; - } - - // Get all elements with class="tablinks" and remove the class "active" - tablinks = document.getElementsByClassName("tablinks"); - for (i = 0; i < tablinks.length; i++) { - tablinks[i].className = tablinks[i].className.replace(" active", ""); - } - - // Show the current tab, and add an "active" class to the button that opened the tab - document.getElementById(tab.id + "_content").style.display = "flex"; - event.currentTarget.className += " active"; -} \ No newline at end of file diff --git a/knowledge-space.php b/knowledge-space.php index c9f5abddfec9eaa94051c5d975d75a21688d93ab..65aaf3d59d26d35f95229e5c1757c7c8bf64b97f 100644 --- a/knowledge-space.php +++ b/knowledge-space.php @@ -19,10 +19,16 @@ function ks_add_graph(): string $variables = "<script> var plugin_path = '$plugin_dir'; </script>"; - $script_path = $plugin_dir.'graph.js'; - $script = "<script src='$script_path'></script>"; + $script_path1 = $plugin_dir.'display'.DIRECTORY_SEPARATOR.'infooverlay.js'; + $script_path2 = $plugin_dir.'display'.DIRECTORY_SEPARATOR.'linkoverlay.js'; + $script_path3 = $plugin_dir.'display'.DIRECTORY_SEPARATOR.'graph.js'; - return $three . $renderer .$renderer2 . $graph . $div . $variables . $script; + $script1 = "<script src='$script_path1'></script>"; + $script2 = "<script src='$script_path2'></script>"; + $script3 = "<script src='$script_path3'></script>"; + + + return $three . $renderer .$renderer2 . $graph . $div . $variables . $script1 . $script2 .$script3; } function ks_add_editor(): string