File size: 3,233 Bytes
ac3f654
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
// 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);
});