diff --git a/package-lock.json b/package-lock.json index 25f5d3d39101841d7e907207a7be27b023866c92..447ddfd84bf36b8fc0375ee439e97c6cc5ef03c8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,8 +22,10 @@ "devDependencies": { "@babel/core": "^7.17.9", "@babel/eslint-parser": "^7.17.0", + "@babel/plugin-transform-runtime": "^7.17.10", "@babel/preset-env": "^7.16.11", "@babel/preset-react": "^7.16.7", + "@babel/runtime": "^7.17.9", "@types/jquery": "^3.5.14", "@types/prop-types": "^15.7.5", "@types/react": "^18.0.6", @@ -1448,6 +1450,26 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-transform-runtime": { + "version": "7.17.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.17.10.tgz", + "integrity": "sha512-6jrMilUAJhktTr56kACL8LnWC5hx3Lf27BS0R0DSyW/OoJfb/iTHeE96V3b1dgKG3FSFdd/0culnYWMkjcKCig==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "babel-plugin-polyfill-corejs2": "^0.3.0", + "babel-plugin-polyfill-corejs3": "^0.5.0", + "babel-plugin-polyfill-regenerator": "^0.3.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/plugin-transform-spread": { "version": "7.16.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.16.7.tgz", @@ -13246,6 +13268,20 @@ "@babel/helper-plugin-utils": "^7.16.7" } }, + "@babel/plugin-transform-runtime": { + "version": "7.17.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.17.10.tgz", + "integrity": "sha512-6jrMilUAJhktTr56kACL8LnWC5hx3Lf27BS0R0DSyW/OoJfb/iTHeE96V3b1dgKG3FSFdd/0culnYWMkjcKCig==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/helper-plugin-utils": "^7.16.7", + "babel-plugin-polyfill-corejs2": "^0.3.0", + "babel-plugin-polyfill-corejs3": "^0.5.0", + "babel-plugin-polyfill-regenerator": "^0.3.0", + "semver": "^6.3.0" + } + }, "@babel/plugin-transform-shorthand-properties": { "version": "7.16.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.16.7.tgz", diff --git a/package.json b/package.json index 09c5ba15a3fafe54bc2932bac2c8a1665673e88c..b13859a46de408955a3a959ed2a239256155164b 100644 --- a/package.json +++ b/package.json @@ -38,8 +38,10 @@ "devDependencies": { "@babel/core": "^7.17.9", "@babel/eslint-parser": "^7.17.0", + "@babel/plugin-transform-runtime": "^7.17.10", "@babel/preset-env": "^7.16.11", "@babel/preset-react": "^7.16.7", + "@babel/runtime": "^7.17.9", "@types/jquery": "^3.5.14", "@types/prop-types": "^15.7.5", "@types/react": "^18.0.6", diff --git a/src/display/display.tsx b/src/display/display.tsx index c7a33aa92082b2f504d757d366e4f446032f5dd6..6a3cf87dcf46dddfae5b6e9108c6c10bf1b54816 100644 --- a/src/display/display.tsx +++ b/src/display/display.tsx @@ -1,22 +1,47 @@ import * as Config from "../config"; import { FilterOverlay } from "./components/filteroverlay"; import { NodeInfoOverlay } from "./components/nodeinfo"; -import Graph from "./graph"; + import React from "react"; import screenfull from "screenfull"; +import { GraphRenderer } from "./renderer"; +import Graph from "./graph"; +import { loadGraphJson } from "../datasets"; +import PropTypes, { InferType } from "prop-types"; + +class Display extends React.Component< + InferType<typeof Display.propTypes>, + InferType<typeof Display.stateTypes> +> { + props: InferType<typeof Display.propTypes>; + state: InferType<typeof Display.stateTypes>; -class Display extends React.PureComponent { - graph: Graph; infoOverlay: NodeInfoOverlay; sceneNode: HTMLElement; + static propTypes = { + spaceId: PropTypes.string.isRequired, + }; + + static stateTypes = { + graph: Graph, + }; + + constructor(props: InferType<typeof Display.propTypes>) { + super(props); + this.state = { + graph: null, + }; + } + componentDidMount() { - this.graph = new Graph( - Config.SPACE, - this.loadGraphComponents.bind(this) - ); - this.infoOverlay = new NodeInfoOverlay(this.graph); - this.graph.infoOverlay = this.infoOverlay; + const fetchGraph = async () => { + const graphData = await loadGraphJson(this.props.spaceId); + const graph = new Graph(graphData.nodes, graphData.links); + this.setState({ graph: graph }); + }; + fetchGraph(); + // this.infoOverlay = new NodeInfoOverlay(this.graph); } /** @@ -24,10 +49,8 @@ class Display extends React.PureComponent { * its content */ loadGraphComponents() { - this.infoOverlay.create(); - - this.sceneNode = document.getElementById("kg-display"); - + // this.infoOverlay.create(); + // this.sceneNode = document.getElementById("kg-display"); /* * This call is necessary since the graph can only load after the * component has mounted (the graph requires a fixed <div> element), @@ -38,7 +61,7 @@ class Display extends React.PureComponent { * this call could be substituted by changing the object state (which * causes a re-render) */ - this.forceUpdate(); + // this.forceUpdate(); // filterOverlay.filterChangedCallback = (cls) => // infoOverlay.bottomMenu.toggleTabVisibility(cls); // createFullScreenButton(); @@ -47,7 +70,7 @@ class Display extends React.PureComponent { toggleFullscreen() { if (screenfull.isEnabled) { screenfull.toggle(this.sceneNode); - this.graph.resize(); + // this.graph.resize(); } else { console.log("No fullscreen mode available :("); } @@ -63,11 +86,11 @@ class Display extends React.PureComponent { > <p>⤢</p> </div> - {this.graph && ( - <FilterOverlay graph={this.graph} type={"node"} /> - )} - + {/*{this.graph && (*/} + {/* <FilterOverlay graph={this.graph} type={"node"} />*/} + {/*)}*/} <div id="3d-graph" /> + {this.state.graph && <GraphRenderer graph={this.state.graph} />} </div> ); } diff --git a/src/display/graph.ts b/src/display/graph.ts index eb7a04a893b9257a985b0cdef31407e9fba8bacb..cca49ccc4692c15af194bd081b22e3064a31b982 100644 --- a/src/display/graph.ts +++ b/src/display/graph.ts @@ -17,6 +17,12 @@ export interface GraphLink extends LinkData { material?: LineMaterial; } +interface Link { + source: string; + target: string; + type?: string; +} + export interface LinkData { source: NodeData; target: NodeData; @@ -39,19 +45,27 @@ export interface Coordinate { z: number; } -export class Graph { +export default class Graph { public nodes: NodeData[]; public links: LinkData[]; private idToNode: Map<string, NodeData>; - constructor(nodes: NodeData[], links: LinkData[]) { + constructor(nodes: NodeData[], links: Link[]) { this.nodes = nodes; - this.links = links; - this.idToNode = new Map<string, NodeData>(); nodes.forEach((node) => { this.idToNode.set(node.id, node); }); + + this.links = links.map((link) => { + return { + source: this.idToNode.get(link.source), + target: this.idToNode.get(link.target), + type: link.type, + }; + }); + + this.resetNodeData(); this.updateNodeData(); } @@ -104,9 +118,17 @@ export class Graph { nodeTypes: Map<string, boolean>, linkTypes: Map<string, boolean> ): Graph { + const links = this.links.filter((l) => linkTypes.get(l.type)); + return new Graph( this.nodes.filter((l) => nodeTypes.get(l.type)), - this.links.filter((l) => linkTypes.get(l.type)) + links.map((link) => { + return { + source: link.source.id, + target: link.target.id, + type: link.type, + }; + }) ); } } diff --git a/src/display/renderer.tsx b/src/display/renderer.tsx index 170cb408c11eac379a75852304d369b0979cc092..fb340ace17d4c1bd0759256148a781150262fc3b 100644 --- a/src/display/renderer.tsx +++ b/src/display/renderer.tsx @@ -1,32 +1,32 @@ import * as Config from "../config"; import * as Helpers from "./helpers"; -import { loadGraphJson } from "../datasets"; +// import { loadGraphJson } from "../datasets"; import * as THREE from "three"; import { ForceGraph3D } from "react-force-graph"; -import screenfull from "screenfull"; +// import screenfull from "screenfull"; -import { - CSS3DRenderer, - CSS3DSprite, -} from "three/examples/jsm/renderers/CSS3DRenderer.js"; +// import { +// CSS3DRenderer, +// CSS3DSprite, +// } from "three/examples/jsm/renderers/CSS3DRenderer.js"; import { MODE, DRAG_THRESHOLD_3D } from "../config"; -import background from "./background.jpg"; +// import background from "./background.jpg"; +//import { Line2, LineGeometry, LineMaterial } from "three-fatline"; import { Line2, LineGeometry, LineMaterial } from "three-fatline"; import React from "react"; import PropTypes, { InferType } from "prop-types"; import SpriteText from "three-spritetext"; import { Object3D } from "three"; -import { +import Graph, { Coordinate, - Graph, GraphLink, GraphNode, LinkData, NodeData, } from "./graph"; -import { graph } from "../editor/js/editor"; +// import { graph } from "../editor/js/editor"; export class GraphRenderer extends React.Component< InferType<typeof GraphRenderer.propTypes>, @@ -36,13 +36,13 @@ export class GraphRenderer extends React.Component< state: InferType<typeof GraphRenderer.stateTypes>; forceGraph: React.RefObject<any>; // using typeof ForceGraph3d produces an error here... edgeColors: Map<string, string>; + nodeColors: Map<string, string>; static propTypes = { - graph: PropTypes.instanceOf(Graph), - spaceId: PropTypes.string.isRequired, + graph: PropTypes.instanceOf(Graph).isRequired, loadingFinishedCallback: PropTypes.func, onNodeClicked: PropTypes.func, - isFullscreen: PropTypes, + isFullscreen: PropTypes.bool, }; static stateTypes = { @@ -59,6 +59,11 @@ export class GraphRenderer extends React.Component< hoverNode: null, }; this.forceGraph = React.createRef(); + this.edgeColors = new Map<string, string>(); + this.nodeColors = new Map<string, string>(); + + this.mapLinkColors(); + this.mapNodeColors(); // TODO: NodeVisibility, linkVisibility, graphLoading has to be moved to parent component } @@ -81,12 +86,51 @@ export class GraphRenderer extends React.Component< // } drawNode(node: GraphNode): Object3D { - const sprite = new SpriteText(node.id); - sprite.color = node.color; - sprite.textHeight = 8; + const sprite = new SpriteText(node.name); + sprite.color = "white"; + sprite.backgroundColor = "black"; // TODO: Set this dynamically based on the node type + sprite.textHeight = 5; + sprite.padding = 2; + sprite.borderRadius = 5; + sprite.borderWidth = 3; + sprite.borderColor = "black"; + return sprite; } + drawLink(link: LinkData) { + const colors = new Float32Array( + [].concat( + ...[link.target, link.source] + .map((node) => this.nodeColors.get(node.type)) + .map((color) => color.replace(/[^\d,]/g, "").split(",")) // Extract rgb() color components + .map((rgb) => rgb.map((v) => parseInt(v) / 255)) + ) + ); + + const geometry = new LineGeometry(); + geometry.setPositions([0, 0, 0, 1, 1, 1]); + geometry.setColors(colors); + + const material = new LineMaterial({ + color: 0xffffff, + linewidth: Config.LINK_WIDTH, // in world units with size attenuation, pixels otherwise + vertexColors: true, + + resolution: new THREE.Vector2( + window.screen.width, + window.screen.height + ), // Set the resolution to the maximum width and height of the screen. + dashed: false, + alphaToCoverage: true, + }); + + const line = new Line2(geometry, material); + line.computeLineDistances(); + line.scale.set(1, 1, 1); + return line; + } + onNodeHover(node: GraphNode) { // no state change if ( @@ -156,7 +200,7 @@ export class GraphRenderer extends React.Component< focusOnNode(node: GraphNode) { // Aim at node from outside it - const distance = 250; + const distance = 400; const distRatio = 1 + distance / Math.hypot(node.x, node.y, node.z); this.forceGraph.current.cameraPosition( @@ -177,14 +221,15 @@ export class GraphRenderer extends React.Component< return "rgb(255, 255, 255)"; } - getLinkWidth(link) { - return this.highlightLinks.has(link) ? 2 : 0.8; + getLinkWidth(link: LinkData) { + return this.state.highlightLinks.has(link) ? 2 : 0.8; } /** * Maps the colors of the color palette to the different edge types */ mapLinkColors() { + // TODO: Move this to the graph data structure - access is also needed in the other menues? const linkClasses = this.props.graph.getLinkClasses(); for (let i = 0; i < linkClasses.length; i++) { this.edgeColors.set( @@ -194,287 +239,32 @@ export class GraphRenderer extends React.Component< } } - resize() { - if (screenfull.isFullscreen) { - this.graph.height(screen.height); - this.graph.width(screen.width); - } else { - this.graph.height(window.innerHeight - 200); - this.graph.width(Helpers.getWidth()); - } - } - - render() { - this.mapLinkColors(); - - return ( - <ForceGraph3D - ref={this.forceGraph} - width={scree} - graphData={this.state.graph} - rendererConfig={{ antialias: true }} - nodeThreeObject={(node: GraphNode) => this.drawNode(node)} - onNodeClick={(node: GraphNode) => this.onNodeClick(node)} - onNodeHover={(node: GraphNode) => this.onNodeHover(node)} - onLinkHover={(link: GraphLink, previousLink: GraphLink) => - this.onLinkHover(link, previousLink) - } - onNodeDragEnd={(node: GraphNode, translate: Coordinate) => - this.onNodeDragEnd(node, translate) - } - /> - ); - } -} - -/** - * The main ForceGraph. Displays the graph and handles all connected events. - */ -export default class Graph2 { - /** - * Constructs a new Graph object. - * @param {string} spaceId Name of the knowledge space that should be loaded - * @param {function} loadingFinishedCallback Callback that is called when the graph is fully loaded. - */ - constructor(spaceId, loadingFinishedCallback = Function()) { - this.graph = null; - this.gData = null; - - this.highlightNodes = new Set(); - this.highlightLinks = new Set(); - this.hoverNode = null; - this.edgeColors = {}; - this.nodeColors = {}; - this.idToNode = {}; - - this.firstTick = true; - this.engineFrozen = false; - this.allowRedraw = false; - this.infoOverlay = null; - - this.edgeTypeVisibility = {}; - this.nodeTypeVisibility = {}; - - this.loadingFinishedCallback = loadingFinishedCallback; - - this.loadGraph(spaceId); - } - - /** - * Loads the graph by constructing a new ForceGraph3D object. - * Also fetches the JSON data from the given space. - * @param {string} spaceId ID to a JSON object defining the graph structure. - * @returns {Promise<void>} - */ - async loadGraph(spaceId) { - this.gData = await loadGraphJson(spaceId); - this.graph = ForceGraph3D({ - extraRenderers: [new CSS3DRenderer()], - rendererConfig: { antialias: true }, - })(document.getElementById("3d-graph")) - .graphData(this.gData) - .nodeLabel("hidden") // Just a value that is not present as node attribute. - //.nodeAutoColorBy("group") - //.nodeColor((node) => this.getNodeColor(node)) - //.linkWidth((link) => this.getLinkWidth(link)) - .onNodeClick((node) => this.onNodeClick(node)) - .onNodeHover((node) => { - this.onNodeHover(node); - this.updateHighlight(); - }) - .onLinkHover((link, previousLink) => - this.onLinkHover(link, previousLink) - ) - .onNodeDrag(() => { - this.allowRedraw = true; - }) - .onNodeDragEnd((node, translate) => - this.onNodeDragEnd(node, translate) - ) - .onEngineStop(() => this.simulationStop()) - //.linkColor((link) => this.getLinkColor(link)) - .linkPositionUpdate((line, { start, end }) => - this.updateLinkPosition(line, start, end) - ) - //.linkOpacity(0.8) - .nodeThreeObjectExtend(false) - .nodeThreeObject((node) => this.drawNode(node)) - //.linkThreeObject((link) => this.drawLink(link)) - .onEngineTick(() => this.initializeModel()) - .width(Helpers.getWidth()) - .height(Helpers.getHeight()); - - setTimeout(() => this.simulationStop(), 3000); - } - - /** - * Initializes all component which are dependent on the graph data after the graph has finished loading - * (after it has computed its first tick.) - */ - initializeModel() { - if (this.firstTick) { - // Initialize data structures - this.mapLinkColors(); - this.mapNodeColors(); - this.updateNodeData(); - this.updateNodeMap(); - this.addBackground(); - - // Can only be called after link colors have been mapped. - this.graph.linkThreeObject((link) => this.drawLink(link)); - - // Catch resize events - document.addEventListener("fullscreenchange", () => this.resize()); - window.addEventListener("resize", () => this.resize()); - - // Initialize visibility states - this.getLinkClasses().forEach( - (item) => (this.edgeTypeVisibility[item] = true) - ); - this.getNodeClasses().forEach( - (item) => (this.nodeTypeVisibility[item] = true) - ); - - this.firstTick = false; - this.loadingFinishedCallback(); - } - } - - // TODO: Move this up to the class which is handling the graph - // updateVisibility() { - // this.updateGraphData(); - // this.removeFloatingLinks(); - // this.updateNodeData(); - // this.removeFloatingNodes(); - // } - // - // removeFloatingNodes() { - // const gData = this.graph.graphData(); - // const nodes = gData.nodes.filter((node) => node.neighbors.length > 0); - // const data = { - // nodes: nodes, - // links: gData.links, - // }; - // this.graph.graphData(data); - // } - // - // removeFloatingLinks() { - // const gData = this.graph.graphData(); - // const links = gData.links.filter( - // (link) => - // this.nodeTypeVisibility[link.target.type] & - // this.nodeTypeVisibility[link.source.type] - // ); - // const data = { - // nodes: gData.nodes, - // links: links, - // }; - // this.graph.graphData(data); - // } - - // /** - // * Resets additional node values. - // * @see updateNodeData - // */ - - 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(background), - 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); - } - /** * Maps the colors of the color palette to the different edge types */ - mapLinkColors() { - const linkClasses = this.getLinkClasses(); - for (let i = 0; i < linkClasses.length; i++) { - this.edgeColors[linkClasses[i]] = - Config.COLOR_PALETTE[i % Config.COLOR_PALETTE.length]; - } - } - mapNodeColors() { - const nodeClasses = this.getNodeClasses(); + // TODO: Move this to the graph data structure - access is also needed in the other menues? + const nodeClasses = this.props.graph.getNodeClasses(); for (let i = 0; i < nodeClasses.length; i++) { - this.nodeColors[nodeClasses[i]] = - Config.COLOR_PALETTE[i % Config.COLOR_PALETTE.length]; + this.nodeColors.set( + nodeClasses[i], + Config.COLOR_PALETTE[i % Config.COLOR_PALETTE.length] + ); } } - drawLink(link) { - const colors = new Float32Array( - [].concat( - ...[link.target, link.source] - .map((node) => this.nodeColors[node.type]) - .map((color) => color.replace(/[^\d,]/g, "").split(",")) // Extract rgb() color components - .map((rgb) => rgb.map((v) => v / 255)) - ) - ); - - const geometry = new LineGeometry(); - geometry.setPositions([0, 0, 0, 1, 1, 1]); - geometry.setColors(colors); - - const material = new LineMaterial({ - color: 0xffffff, - linewidth: Config.LINK_WIDTH, // in world units with size attenuation, pixels otherwise - vertexColors: true, - - resolution: new THREE.Vector2( - window.screen.width, - window.screen.height - ), // Set the resolution to the maximum width and height of the screen. - dashed: false, - alphaToCoverage: true, - }); - - const line = new Line2(geometry, material); - line.computeLineDistances(); - line.scale.set(1, 1, 1); - return line; - } - - simulationStop() { - this.engineFrozen = true; - this.stopPhysics(); - } - - stopPhysics() { - const data = this.graph.graphData(); - data["nodes"].forEach((n) => { - n.fx = n.x; - n.fy = n.y; - n.fz = n.z; - }); - this.graph.graphData(data); + resize() { + // TODO + // if (screenfull.isFullscreen) { + // this.forceGraph.height(screen.height); + // this.forceGraph.width(screen.width); + // } else { + // this.forceGraph.height(window.innerHeight - 200); + // this.forceGraph.width(Helpers.getWidth()); + // } } - updateLinkPosition(line, start, end) { - if (!this.allowRedraw) { - if (this.engineFrozen) { - return true; - } - } - + updateLinkPosition(line: Line2, start: Coordinate, end: Coordinate) { if (!(line instanceof Line2)) { return false; } @@ -482,15 +272,19 @@ export default class Graph2 { const startR = 4; const endR = 4; const lineLen = Math.sqrt( - ["x", "y", "z"] - .map((dim) => Math.pow((end[dim] || 0) - (start[dim] || 0), 2)) - .reduce((acc, v) => acc + v, 0) + Math.pow(end.x - start.x, 2) + + Math.pow(end.y - start.y, 2) + + Math.pow(end.z - start.z, 2) ); const positions = [startR / lineLen, 1 - endR / lineLen] .map((t) => ["x", "y", "z"].map( - (dim) => start[dim] + (end[dim] - start[dim]) * t + (dim) => + start[dim as keyof typeof start] + + (end[dim as keyof typeof end] - + start[dim as keyof typeof start]) * + t ) ) .flat(); @@ -502,67 +296,32 @@ export default class Graph2 { return true; } - drawNode(node) { - // Draw node as label + image - const nodeDiv = Helpers.createDiv( - "node-container", - document.getElementById("3d-graph") - ); - const group = new THREE.Group(); - - const labelDiv = Helpers.createDiv("node-label", nodeDiv, { - textContent: node.name, - }); - labelDiv.classList.add("no-select"); - labelDiv.style.color = node.color; - - const cssobj = new CSS3DSprite(nodeDiv); - cssobj.scale.set(0.25, 0.25, 0.25); - cssobj.position.set(0, -6, 0); - cssobj.element.style.pointerEvents = "none"; - group.add(cssobj); - - // Draw node circle image - const textureLoader = new THREE.TextureLoader(); - textureLoader.setCrossOrigin("anonymous"); - const imageAlpha = textureLoader.load( - Config.PLUGIN_PATH + "datasets/images/alpha.png" + render() { + return ( + <ForceGraph3D + ref={this.forceGraph} + width={Helpers.getWidth()} // TODO: Replace Helpers? + height={Helpers.getHeight()} + // extraRenderers={[new CSS3DRenderer()]} + graphData={this.props.graph} + rendererConfig={{ antialias: true }} + nodeLabel={"hidden"} + // nodeThreeObjectExtend={false} + nodeThreeObject={(node: GraphNode) => this.drawNode(node)} + linkThreeObject={(link: LinkData) => this.drawLink(link)} + onNodeClick={(node: GraphNode) => this.onNodeClick(node)} + // onNodeHover={(node: GraphNode) => this.onNodeHover(node)} + // onLinkHover={(link: GraphLink, previousLink: GraphLink) => + // this.onLinkHover(link, previousLink) + // } + linkPositionUpdate={( + line: Line2, + coords: { start: Coordinate; end: Coordinate } + ) => this.updateLinkPosition(line, coords.start, coords.end)} + // onNodeDragEnd={(node: GraphNode, translate: Coordinate) => + // this.onNodeDragEnd(node, translate) + // } + /> ); - let imageTexture = null; - - if ("image" in node) { - if (node.image.startsWith("http")) { - imageTexture = textureLoader.load(node.image); - } else { - imageTexture = textureLoader.load( - Config.PLUGIN_PATH + "datasets/images/" + node.image - ); - } - } else { - imageTexture = textureLoader.load( - Config.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; } } diff --git a/src/frontend.tsx b/src/frontend.tsx index 97257cc69032715d46ef6c286e954582dcc56ccf..6586be57ef12534abef805ee160ff2f86beec992 100644 --- a/src/frontend.tsx +++ b/src/frontend.tsx @@ -1,11 +1,11 @@ import React from "react"; -import ReactDOM from "react-dom"; +import { createRoot } from "react-dom/client"; +import * as Config from "./config"; import "./display/display.css"; import "./display/display"; import Display from "./display/display"; -ReactDOM.render( - <Display />, - document.getElementById("knowledge-space-display") -); +const container = document.getElementById("knowledge-space-display"); +const root = createRoot(container); +root.render(<Display spaceId={Config.SPACE} />); diff --git a/tsconfig.json b/tsconfig.json index 7811427ddc9ca79252c3cba41c4cb4f77aed181a..6cfadcdc47eca6b88566b5a43b542adcf48e974a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,6 +5,10 @@ "strictNullChecks": false, "module": "es6", "target": "es6", + "lib": [ + "es2019", + "DOM" + ], "jsx": "preserve", "allowJs": true, "moduleResolution": "node", diff --git a/webpack.common.js b/webpack.common.js index 543a79ee38510e6f5984461f85256153ce029987..1bf65e0f31f053fdedf5ec54b8cfa0a4255624a2 100644 --- a/webpack.common.js +++ b/webpack.common.js @@ -3,6 +3,7 @@ const MiniCssExtractPlugin = require("mini-css-extract-plugin"); const CssMinimizerPlugin = require("css-minimizer-webpack-plugin"); const babelOptions = { + plugins: ["@babel/plugin-transform-runtime"], presets: ["@babel/preset-env", "@babel/preset-react"], };