live renderhtml
htmlv.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Afrho-Pod Hypergraph Visualization</title>
<style>
body {
margin: 0;
padding: 0;
overflow: hidden;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #0a0e27 0%, #1a1f3a 100%);
color: white;
}
#container {
position: absolute;
width: 100%;
height: 100%;
overflow: hidden;
}
#info {
position: absolute;
top: 10px;
left: 10px;
background: rgba(0, 0, 0, 0.7);
padding: 10px 20px;
border-radius: 8px;
font-size: 14px;
z-index: 100;
}
#controls {
position: absolute;
top: 10px;
right: 10px;
background: rgba(0, 0, 0, 0.7);
padding: 15px;
border-radius: 8px;
z-index: 100;
}
.control-group {
margin-bottom: 10px;
}
.control-group label {
display: block;
margin-bottom: 5px;
font-size: 12px;
color: #ccc;
}
button {
background: #4a6bff;
color: white;
border: none;
padding: 8px 15px;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
margin-right: 5px;
transition: background 0.3s;
}
button:hover {
background: #3a5bef;
}
select, input {
background: rgba(255, 255, 255, 0.1);
color: white;
border: 1px solid rgba(255, 255, 255, 0.2);
padding: 8px;
border-radius: 4px;
font-size: 12px;
width: 100%;
}
#stats {
position: absolute;
bottom: 10px;
left: 10px;
background: rgba(0, 0, 0, 0.7);
padding: 10px 20px;
border-radius: 8px;
font-size: 12px;
z-index: 100;
}
#node-info {
position: absolute;
bottom: 10px;
right: 10px;
background: rgba(0, 0, 0, 0.8);
padding: 15px;
border-radius: 8px;
font-size: 12px;
max-width: 300px;
display: none;
z-index: 100;
}
.loading {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: #4a6bff;
font-size: 18px;
z-index: 100;
}
.metric-card {
background: rgba(0, 0, 0, 0.5);
padding: 10px;
border-radius: 6px;
margin: 5px 0;
}
.metric-label {
font-size: 10px;
color: #aaa;
margin-bottom: 3px;
}
.metric-value {
font-size: 16px;
font-weight: bold;
}
</style>
</head>
<body>
<div id="loading" class="loading">Loading hypergraph visualization...</div>
<div id="info">
<strong>Afrho-Pod Hypergraph Visualization</strong>
<div>Interactive 3D network topology explorer</div>
</div>
<div id="controls">
<div class="control-group">
<label>Visualization Mode</label>
<select id="mode-select">
<option value="force">Force-Directed Graph</option>
<option value="hypergraph">Hypergraph</option>
<option value="3d">3D Network</option>
<option value="organic">Organic Art</option>
</select>
</div>
<div class="control-group">
<label>Node Type Filter</label>
<select id="node-filter">
<option value="all">All Nodes</option>
<option value="server">Servers</option>
<option value="pod">Pods</option>
<option value="device">Devices</option>
<option value="vpn">VPN</option>
</select>
</div>
<div class="control-group">
<button id="refresh-btn">Refresh Data</button>
<button id="reset-btn">Reset View</button>
</div>
<div class="control-group">
<label>Metrics</label>
<button id="toggle-metrics">Show/Hide</button>
</div>
</div>
<div id="stats">
<div class="metric-card">
<div class="metric-label">Total Nodes</div>
<div class="metric-value" id="node-count">0</div>
</div>
<div class="metric-card">
<div class="metric-label">Total Edges</div>
<div class="metric-value" id="edge-count">0</div>
</div>
<div class="metric-card">
<div class="metric-label">Active Connections</div>
<div class="metric-value" id="active-connections">0</div>
</div>
</div>
<div id="node-info">
<h3 id="node-title">Node Information</h3>
<div id="node-details"></div>
</div>
<div id="container"></div>
<!-- Three.js and other libraries -->
<script src="https://cdn.jsdelivr.net/npm/three@0.132.2/build/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.132.2/examples/js/controls/OrbitControls.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.132.2/examples/js/controls/TrackballControls.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.132.2/examples/js/loaders/GLTFLoader.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.132.2/examples/js/geometries/ConvexGeometry.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.132.2/examples/js/geometries/ConvexBufferGeometry.js"></script>
<!-- D3.js for force-directed graph -->
<script src="https://d3js.org/d3.v7.min.js"></script>
<script>
// Global variables
let scene, camera, renderer, controls;
let nodes = [], edges = [], nodeObjects = [], edgeObjects = [];
let selectedNode = null;
let visualizationMode = 'force';
let nodeFilter = 'all';
// Network data - this would be fetched from API in real implementation
let networkData = {
nodes: [],
edges: []
};
// Color schemes
const colorSchemes = {
server: 0x4a6bff,
pod: 0x28a745,
device: 0xffc107,
vpn: 0xdc3545,
container: 0x6a5acd,
vm: 0x17a2b8
};
// Initialize the scene
function init() {
// Create scene
scene = new THREE.Scene();
scene.background = new THREE.Color(0x0a0e27);
// Create camera
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 10000);
camera.position.set(0, 0, 1000);
// Create renderer
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(window.devicePixelRatio);
document.getElementById('container').appendChild(renderer.domElement);
// Create controls
controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.05;
controls.screenSpacePanning = false;
controls.minDistance = 100;
controls.maxDistance = 5000;
// Add lights
const ambientLight = new THREE.AmbientLight(0x404040, 0.5);
scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
directionalLight.position.set(1, 1, 1);
scene.add(directionalLight);
// Add grid helper
const gridHelper = new THREE.GridHelper(2000, 20, 0x333333, 0x333333);
scene.add(gridHelper);
// Add axes helper
const axesHelper = new THREE.AxesHelper(500);
scene.add(axesHelper);
// Load sample data
loadSampleData();
// Set up event listeners
setupEventListeners();
// Start animation loop
animate();
// Hide loading
document.getElementById('loading').style.display = 'none';
}
// Load sample data (in real implementation, this would be fetched from API)
function loadSampleData() {
// Sample network topology
networkData = {
nodes: [
{ id: 1, name: 'rhiz-ueth', type: 'server', x: 0, y: 0, z: 0, size: 30, color: colorSchemes.server },
{ id: 2, name: 'afrho-pod', type: 'pod', x: 200, y: 100, z: 0, size: 25, color: colorSchemes.pod },
{ id: 3, name: 'exosystem', type: 'server', x: -200, y: 100, z: 0, size: 20, color: colorSchemes.server },
{ id: 4, name: 'wg0', type: 'vpn', x: 100, y: -100, z: 0, size: 15, color: colorSchemes.vpn },
{ id: 5, name: 'operatau-sozv', type: 'vpn', x: -100, y: -100, z: 0, size: 15, color: colorSchemes.vpn },
{ id: 6, name: 'limesdr', type: 'device', x: 300, y: 200, z: 0, size: 10, color: colorSchemes.device },
{ id: 7, name: 'arduino', type: 'device', x: 350, y: -50, z: 0, size: 10, color: colorSchemes.device },
{ id: 8, name: 'yubikey', type: 'device', x: 250, y: -150, z: 0, size: 10, color: colorSchemes.device }
],
edges: [
{ source: 1, target: 2, type: 'vpn', strength: 0.5 },
{ source: 1, target: 3, type: 'network', strength: 0.3 },
{ source: 1, target: 4, type: 'vpn', strength: 0.4 },
{ source: 1, target: 5, type: 'vpn', strength: 0.4 },
{ source: 2, target: 4, type: 'vpn', strength: 0.2 },
{ source: 2, target: 5, type: 'vpn', strength: 0.2 },
{ source: 2, target: 6, type: 'usb', strength: 0.1 },
{ source: 2, target: 7, type: 'usb', strength: 0.1 },
{ source: 2, target: 8, type: 'usb', strength: 0.1 }
]
};
// Update stats
updateStats();
// Create visualization based on current mode
updateVisualization();
}
// Update statistics display
function updateStats() {
document.getElementById('node-count').textContent = networkData.nodes.length;
document.getElementById('edge-count').textContent = networkData.edges.length;
document.getElementById('active-connections').textContent = networkData.edges.length;
}
// Update visualization based on current mode
function updateVisualization() {
// Clear existing objects
clearScene();
if (visualizationMode === 'force') {
createForceDirectedGraph();
} else if (visualizationMode === 'hypergraph') {
createHypergraph();
} else if (visualizationMode === '3d') {
create3DNetwork();
} else if (visualizationMode === 'organic') {
createOrganicArt();
}
}
// Clear the scene
function clearScene() {
// Remove all node and edge objects
nodeObjects.forEach(obj => scene.remove(obj));
edgeObjects.forEach(obj => scene.remove(obj));
nodeObjects = [];
edgeObjects = [];
}
// Create force-directed graph visualization
function createForceDirectedGraph() {
// Create nodes
networkData.nodes.forEach(node => {
if (nodeFilter === 'all' || node.type === nodeFilter) {
const geometry = new THREE.SphereGeometry(node.size, 32, 32);
const material = new THREE.MeshPhongMaterial({
color: node.color,
shininess: 30,
transparent: true,
opacity: 0.8
});
const sphere = new THREE.Mesh(geometry, material);
sphere.position.set(node.x, node.y, node.z);
sphere.userData = node;
scene.add(sphere);
nodeObjects.push(sphere);
}
});
// Create edges
networkData.edges.forEach(edge => {
const sourceNode = networkData.nodes.find(n => n.id === edge.source);
const targetNode = networkData.nodes.find(n => n.id === edge.target);
if (sourceNode && targetNode) {
const sourceObj = nodeObjects.find(obj => obj.userData.id === edge.source);
const targetObj = nodeObjects.find(obj => obj.userData.id === edge.target);
if (sourceObj && targetObj) {
const material = new THREE.LineBasicMaterial({
color: 0x666666,
linewidth: edge.strength * 5,
transparent: true,
opacity: 0.6
});
const geometry = new THREE.BufferGeometry();
const positions = [
sourceObj.position.x, sourceObj.position.y, sourceObj.position.z,
targetObj.position.x, targetObj.position.y, targetObj.position.z
];
geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3));
const line = new THREE.Line(geometry, material);
scene.add(line);
edgeObjects.push(line);
}
}
});
// Add labels
addLabels();
}
// Create hypergraph visualization
function createHypergraph() {
// This would create a more complex hypergraph visualization
// For now, we'll use a similar approach to force-directed but with different styling
createForceDirectedGraph();
}
// Create 3D network visualization
function create3DNetwork() {
// Create nodes in 3D space
networkData.nodes.forEach((node, index) => {
if (nodeFilter === 'all' || node.type === nodeFilter) {
const geometry = new THREE.SphereGeometry(node.size, 32, 32);
const material = new THREE.MeshPhongMaterial({
color: node.color,
shininess: 30
});
const sphere = new THREE.Mesh(geometry, material);
// Position nodes in 3D space
const angle = (index / networkData.nodes.length) * Math.PI * 2;
const radius = 500;
sphere.position.set(
Math.cos(angle) * radius,
(Math.random() - 0.5) * 200,
Math.sin(angle) * radius
);
sphere.userData = node;
scene.add(sphere);
nodeObjects.push(sphere);
}
});
// Create curved edges
networkData.edges.forEach(edge => {
const sourceNode = networkData.nodes.find(n => n.id === edge.source);
const targetNode = networkData.nodes.find(n => n.id === edge.target);
if (sourceNode && targetNode) {
const sourceObj = nodeObjects.find(obj => obj.userData.id === edge.source);
const targetObj = nodeObjects.find(obj => obj.userData.id === edge.target);
if (sourceObj && targetObj) {
// Create a curved line
const curve = new THREE.QuadraticBezierCurve3(
sourceObj.position,
new THREE.Vector3(
(sourceObj.position.x + targetObj.position.x) / 2,
(sourceObj.position.y + targetObj.position.y) / 2 + 200,
(sourceObj.position.z + targetObj.position.z) / 2
),
targetObj.position
);
const points = curve.getPoints(50);
const geometry = new THREE.BufferGeometry().setFromPoints(points);
const material = new THREE.LineBasicMaterial({
color: 0x666666,
linewidth: edge.strength * 3
});
const curveObject = new THREE.Line(geometry, material);
scene.add(curveObject);
edgeObjects.push(curveObject);
}
}
});
// Add labels
addLabels();
}
// Create organic art visualization
function createOrganicArt() {
// This would create a more artistic, organic visualization
// For now, we'll create a simple particle system
// Create a particle system for nodes
const particleCount = networkData.nodes.length * 10;
const particles = new THREE.BufferGeometry();
const positions = [];
const colors = [];
const sizes = [];
networkData.nodes.forEach(node => {
if (nodeFilter === 'all' || node.type === nodeFilter) {
for (let i = 0; i < 10; i++) {
const angle = Math.random() * Math.PI * 2;
const distance = Math.random() * node.size * 2;
positions.push(
node.x + Math.cos(angle) * distance,
node.y + Math.sin(angle) * distance,
node.z + (Math.random() - 0.5) * node.size
);
colors.push(
(node.color >> 16) / 255,
((node.color >> 8) & 0xFF) / 255,
(node.color & 0xFF) / 255
);
sizes.push(Math.random() * 5 + 2);
}
}
});
particles.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3));
particles.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3));
particles.setAttribute('size', new THREE.Float32BufferAttribute(sizes, 1));
const particleMaterial = new THREE.PointsMaterial({
size: 5,
vertexColors: true,
transparent: true,
opacity: 0.8
});
const particleSystem = new THREE.Points(particles, particleMaterial);
scene.add(particleSystem);
nodeObjects.push(particleSystem);
// Add some organic-looking connections
networkData.edges.forEach(edge => {
const sourceNode = networkData.nodes.find(n => n.id === edge.source);
const targetNode = networkData.nodes.find(n => n.id === edge.target);
if (sourceNode && targetNode) {
const sourcePos = new THREE.Vector3(sourceNode.x, sourceNode.y, sourceNode.z);
const targetPos = new THREE.Vector3(targetNode.x, targetNode.y, targetNode.z);
// Create a sine wave connection
const points = [];
const segments = 50;
for (let i = 0; i <= segments; i++) {
const t = i / segments;
const x = sourcePos.x + (targetPos.x - sourcePos.x) * t;
const y = sourcePos.y + (targetPos.y - sourcePos.y) * t + Math.sin(t * Math.PI * 2) * 50;
const z = sourcePos.z + (targetPos.z - sourcePos.z) * t;
points.push(new THREE.Vector3(x, y, z));
}
const geometry = new THREE.BufferGeometry().setFromPoints(points);
const material = new THREE.LineBasicMaterial({
color: 0x666666,
linewidth: edge.strength * 2,
transparent: true,
opacity: 0.5
});
const line = new THREE.Line(geometry, material);
scene.add(line);
edgeObjects.push(line);
}
});
}
// Add labels to nodes
function addLabels() {
// Remove existing labels
const existingLabels = scene.children.filter(obj => obj.userData && obj.userData.type === 'label');
existingLabels.forEach(label => scene.remove(label));
// Add new labels
nodeObjects.forEach(nodeObj => {
if (nodeObj.userData) {
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
const fontSize = 24;
context.font = `${fontSize}px Arial`;
const textWidth = context.measureText(nodeObj.userData.name).width;
canvas.width = textWidth + 20;
canvas.height = fontSize + 20;
context.fillStyle = 'rgba(0, 0, 0, 0.7)';
context.fillRect(0, 0, canvas.width, canvas.height);
context.fillStyle = 'white';
context.font = `${fontSize}px Arial`;
context.textAlign = 'center';
context.textBaseline = 'middle';
context.fillText(nodeObj.userData.name, canvas.width / 2, canvas.height / 2);
const texture = new THREE.CanvasTexture(canvas);
const spriteMaterial = new THREE.SpriteMaterial({ map: texture });
const sprite = new THREE.Sprite(spriteMaterial);
sprite.position.copy(nodeObj.position);
sprite.position.y += nodeObj.userData.size + 20;
sprite.userData = { type: 'label', nodeId: nodeObj.userData.id };
scene.add(sprite);
}
});
}
// Set up event listeners
function setupEventListeners() {
// Mode selection
document.getElementById('mode-select').addEventListener('change', function() {
visualizationMode = this.value;
updateVisualization();
});
// Node filter
document.getElementById('node-filter').addEventListener('change', function() {
nodeFilter = this.value;
updateVisualization();
});
// Refresh button
document.getElementById('refresh-btn').addEventListener('click', function() {
// In real implementation, this would fetch new data from API
console.log('Refreshing data...');
updateVisualization();
});
// Reset button
document.getElementById('reset-btn').addEventListener('click', function() {
controls.reset();
});
// Window resize
window.addEventListener('resize', function() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
// Node click events
window.addEventListener('click', function(event) {
const mouse = new THREE.Vector2();
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
const raycaster = new THREE.Raycaster();
raycaster.setFromCamera(mouse, camera);
const intersects = raycaster.intersectObjects(nodeObjects.filter(obj => obj.type === 'Mesh'));
if (intersects.length > 0) {
const selected = intersects[0].object;
showNodeInfo(selected.userData);
} else {
hideNodeInfo();
}
});
}
// Show node information
function showNodeInfo(node) {
selectedNode = node;
const infoElement = document.getElementById('node-info');
const titleElement = document.getElementById('node-title');
const detailsElement = document.getElementById('node-details');
titleElement.textContent = node.name;
let detailsHTML = `
<div><strong>Type:</strong> ${node.type}</div>
<div><strong>ID:</strong> ${node.id}</div>
<div><strong>Position:</strong> (${node.x.toFixed(1)}, ${node.y.toFixed(1)}, ${node.z.toFixed(1)})</div>
<div><strong>Size:</strong> ${node.size}</div>
`;
// Add type-specific information
if (node.type === 'server') {
detailsHTML += '<div><strong>Role:</strong> Main Server</div>';
} else if (node.type === 'pod') {
detailsHTML += '<div><strong>Role:</strong> IoT Gateway</div>';
} else if (node.type === 'device') {
detailsHTML += '<div><strong>Role:</strong> Peripheral Device</div>';
} else if (node.type === 'vpn') {
detailsHTML += '<div><strong>Role:</strong> VPN Interface</div>';
}
detailsElement.innerHTML = detailsHTML;
infoElement.style.display = 'block';
}
// Hide node information
function hideNodeInfo() {
selectedNode = null;
document.getElementById('node-info').style.display = 'none';
}
// Animation loop
function animate() {
requestAnimationFrame(animate);
// Update controls
controls.update();
// Rotate nodes slightly for organic feel
if (visualizationMode === 'organic') {
nodeObjects.forEach(node => {
if (node.rotation) {
node.rotation.x += 0.001;
node.rotation.y += 0.002;
}
});
}
// Render scene
renderer.render(scene, camera);
}
// Initialize the visualization
init();
</script>
</body>
</html>