// main.js import * as THREE from "https://unpkg.com/three@0.164.0/build/three.module.js"; import { VRButton } from "https://unpkg.com/three@0.164.0/examples/jsm/webxr/VRButton.js"; // ----- 1. Basic setup ----- const scene = new THREE.Scene(); scene.background = new THREE.Color(0x000000); const camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 0.1, 1000 ); camera.position.set(0, 1.6, 3); // typical VR eye height const renderer = new THREE.WebGLRenderer({ antialias: true }); renderer.setSize(window.innerWidth, window.innerHeight); renderer.xr.enabled = true; document.body.appendChild(renderer.domElement); // Add VR button document.body.appendChild(VRButton.createButton(renderer)); // Simple light setup const light = new THREE.DirectionalLight(0xffffff, 0.8); light.position.set(5, 10, 7); scene.add(light); const ambient = new THREE.AmbientLight(0xffffff, 0.3); scene.add(ambient); // ----- 2. Sample graph data (replace with Neo4j later) ----- // Minimal example: 6 nodes, some edges const graphData = { nodes: [ { id: "A", label: "Node A" }, { id: "B", label: "Node B" }, { id: "C", label: "Node C" }, { id: "D", label: "Node D" }, { id: "E", label: "Node E" }, { id: "F", label: "Node F" } ], edges: [ { source: "A", target: "B" }, { source: "A", target: "C" }, { source: "B", target: "D" }, { source: "C", target: "D" }, { source: "C", target: "E" }, { source: "E", target: "F" } ] }; // ----- 3. Layout: place nodes around a circle ----- const nodeMap = new Map(); // id -> mesh const radius = 2; const nodeGeometry = new THREE.SphereGeometry(0.08, 16, 16); const nodeMaterial = new THREE.MeshStandardMaterial({ color: 0x00bcd4 }); graphData.nodes.forEach((node, index) => { const angle = (index / graphData.nodes.length) * Math.PI * 2; const x = radius * Math.cos(angle); const z = radius * Math.sin(angle); const y = 0; const mesh = new THREE.Mesh(nodeGeometry, nodeMaterial.clone()); mesh.position.set(x, y, z); mesh.userData = { id: node.id, label: node.label }; scene.add(mesh); nodeMap.set(node.id, mesh); }); // ----- 4. Draw edges as lines between node positions ----- const edgeMaterial = new THREE.LineBasicMaterial({ color: 0xffffff, linewidth: 1 }); graphData.edges.forEach((edge) => { const sourceMesh = nodeMap.get(edge.source); const targetMesh = nodeMap.get(edge.target); if (!sourceMesh || !targetMesh) return; const points = []; points.push(sourceMesh.position.clone()); points.push(targetMesh.position.clone()); const edgeGeometry = new THREE.BufferGeometry().setFromPoints(points); const line = new THREE.Line(edgeGeometry, edgeMaterial); scene.add(line); }); // ----- 5. Subtle animation for visual interest ----- function animate() { renderer.setAnimationLoop(renderScene); } function renderScene() { // Slowly rotate the whole graph scene.rotation.y += 0.0015; renderer.render(scene, camera); } animate(); // ----- 6. Handle resize ----- window.addEventListener("resize", () => { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); });