'use client';
/**
* Data Bus
* Trace lines connecting the Cortex Core to memory banks
* Supports pulse animations for access events
*/
import { useMemo, useRef } from 'react';
import { useFrame } from '@react-three/fiber';
import * as THREE from 'three';
interface DataBusProps {
chipWidth?: number;
chipHeight?: number;
coreWidth?: number;
coreHeight?: number;
}
interface BusLineProps {
start: [number, number, number];
end: [number, number, number];
color?: string;
}
// Single bus trace line
function BusLine({ start, end, color = '#FFB347' }: BusLineProps) {
const geometry = useMemo(() => {
const points = [new THREE.Vector3(...start), new THREE.Vector3(...end)];
return new THREE.BufferGeometry().setFromPoints(points);
}, [start, end]);
return (
);
}
// Branching bus traces (main trunk + branches)
function BusBranch({
trunk,
branches,
color = '#FFB347',
}: {
trunk: { start: [number, number, number]; end: [number, number, number] };
branches: { start: [number, number, number]; end: [number, number, number] }[];
color?: string;
}) {
return (
{branches.map((branch, i) => (
))}
);
}
export function DataBus({
chipWidth = 16,
chipHeight = 12,
coreWidth = 2,
coreHeight = 1.2,
}: DataBusProps) {
const sectionHeight = chipHeight / 3;
const halfChipHeight = chipHeight / 2;
const halfCoreHeight = coreHeight / 2;
const halfCoreWidth = coreWidth / 2;
// Z position for all traces (slightly above substrate)
const z = 0.05;
// Generate bus traces
const busTraces = useMemo(() => {
// Main vertical trunk to STM (top)
const stmTrunk = {
start: [0, halfCoreHeight + 0.1, z] as [number, number, number],
end: [0, halfChipHeight - sectionHeight * 0.3, z] as [number, number, number],
};
// Branches to STM grid columns
const stmBranches: { start: [number, number, number]; end: [number, number, number] }[] = [];
const branchY = halfChipHeight - sectionHeight * 0.5;
for (let i = -3; i <= 3; i++) {
if (i !== 0) {
const x = i * 1.5;
stmBranches.push({
start: [0, branchY, z],
end: [x, branchY, z],
});
// Vertical segment to grid
stmBranches.push({
start: [x, branchY, z],
end: [x, halfChipHeight - sectionHeight * 0.2, z],
});
}
}
// Main vertical trunk to LTM (bottom)
const ltmTrunk = {
start: [0, -halfCoreHeight - 0.1, z] as [number, number, number],
end: [0, -halfChipHeight + sectionHeight * 0.3, z] as [number, number, number],
};
// Branches to LTM grid columns
const ltmBranches: { start: [number, number, number]; end: [number, number, number] }[] = [];
const ltmBranchY = -halfChipHeight + sectionHeight * 0.5;
for (let i = -3; i <= 3; i++) {
if (i !== 0) {
const x = i * 1.5;
ltmBranches.push({
start: [0, ltmBranchY, z],
end: [x, ltmBranchY, z],
});
// Vertical segment to grid
ltmBranches.push({
start: [x, ltmBranchY, z],
end: [x, -halfChipHeight + sectionHeight * 0.2, z],
});
}
}
// Horizontal traces to Episodic (left)
const episodicLeftTrunk = {
start: [-halfCoreWidth - 0.1, 0, z] as [number, number, number],
end: [-chipWidth / 2 + 1.5, 0, z] as [number, number, number],
};
const episodicLeftBranches: { start: [number, number, number]; end: [number, number, number] }[] = [];
for (let i = 0; i < 3; i++) {
const x = -halfCoreWidth - 1 - i * 1.5;
episodicLeftBranches.push({
start: [x, 0, z],
end: [x, sectionHeight * 0.3, z],
});
episodicLeftBranches.push({
start: [x, 0, z],
end: [x, -sectionHeight * 0.3, z],
});
}
// Horizontal traces to Episodic (right)
const episodicRightTrunk = {
start: [halfCoreWidth + 0.1, 0, z] as [number, number, number],
end: [chipWidth / 2 - 1.5, 0, z] as [number, number, number],
};
const episodicRightBranches: { start: [number, number, number]; end: [number, number, number] }[] = [];
for (let i = 0; i < 3; i++) {
const x = halfCoreWidth + 1 + i * 1.5;
episodicRightBranches.push({
start: [x, 0, z],
end: [x, sectionHeight * 0.3, z],
});
episodicRightBranches.push({
start: [x, 0, z],
end: [x, -sectionHeight * 0.3, z],
});
}
return {
stm: { trunk: stmTrunk, branches: stmBranches },
ltm: { trunk: ltmTrunk, branches: ltmBranches },
episodicLeft: { trunk: episodicLeftTrunk, branches: episodicLeftBranches },
episodicRight: { trunk: episodicRightTrunk, branches: episodicRightBranches },
};
}, [chipWidth, chipHeight, halfChipHeight, halfCoreHeight, halfCoreWidth, sectionHeight]);
return (
{/* STM bus (top) */}
{/* LTM bus (bottom) */}
{/* Episodic bus (left) */}
{/* Episodic bus (right) */}
{/* Junction nodes at branch points */}
);
}
// Small glowing nodes at bus junctions
function JunctionNodes({
chipWidth,
chipHeight,
coreWidth,
}: {
chipWidth: number;
chipHeight: number;
coreWidth: number;
}) {
const nodesRef = useRef(null);
const nodeGeometry = useMemo(() => new THREE.CircleGeometry(0.06, 8), []);
const nodeMaterial = useMemo(
() =>
new THREE.MeshBasicMaterial({
color: '#FFD700',
transparent: true,
opacity: 0.8,
}),
[]
);
const positions = useMemo(() => {
const sectionHeight = chipHeight / 3;
const halfChipHeight = chipHeight / 2;
const halfCoreWidth = coreWidth / 2;
const z = 0.06;
const nodes: [number, number, number][] = [
// Core connection points
[0, halfCoreWidth * 0.6 + 0.1, z],
[0, -halfCoreWidth * 0.6 - 0.1, z],
[-halfCoreWidth - 0.1, 0, z],
[halfCoreWidth + 0.1, 0, z],
];
// STM branch junctions
const stmBranchY = halfChipHeight - sectionHeight * 0.5;
for (let i = -3; i <= 3; i++) {
if (i !== 0) {
nodes.push([i * 1.5, stmBranchY, z]);
}
}
// LTM branch junctions
const ltmBranchY = -halfChipHeight + sectionHeight * 0.5;
for (let i = -3; i <= 3; i++) {
if (i !== 0) {
nodes.push([i * 1.5, ltmBranchY, z]);
}
}
// Episodic branch junctions
for (let i = 0; i < 3; i++) {
nodes.push([-halfCoreWidth - 1 - i * 1.5, 0, z]);
nodes.push([halfCoreWidth + 1 + i * 1.5, 0, z]);
}
return nodes;
}, [chipHeight, coreWidth]);
// Animate junction glow
useFrame((state) => {
if (nodesRef.current) {
const material = nodesRef.current.material as THREE.MeshBasicMaterial;
const pulse = Math.sin(state.clock.elapsedTime * 3) * 0.5 + 0.5;
material.opacity = 0.5 + pulse * 0.3;
}
});
return (
{positions.map((pos, i) => {
const matrix = new THREE.Matrix4().setPosition(...pos);
nodesRef.current?.setMatrixAt(i, matrix);
return null;
})}
);
}
// Pulse that travels along the data bus
export interface AccessPulseData {
id: number;
memoryId: number;
section: 'stm' | 'episodic' | 'ltm';
gridPosition: [number, number]; // Column, row in grid
color: string;
startTime: number;
}
interface DataBusPulseProps {
pulse: AccessPulseData;
chipWidth: number;
chipHeight: number;
onComplete: (id: number) => void;
}
export function DataBusPulse({
pulse,
chipWidth,
chipHeight,
onComplete,
}: DataBusPulseProps) {
const pulseRef = useRef(null);
const trailRef = useRef(null);
const startTimeRef = useRef(pulse.startTime);
const sectionHeight = chipHeight / 3;
const halfChipHeight = chipHeight / 2;
// Calculate path based on section
const path = useMemo(() => {
const z = 0.1;
const [col] = pulse.gridPosition;
const targetX = (col - 3) * 1.5; // Map grid column to x position
let targetY: number;
let midY: number;
switch (pulse.section) {
case 'stm':
targetY = halfChipHeight - sectionHeight * 0.3;
midY = halfChipHeight - sectionHeight * 0.5;
return [
[0, 0, z], // Start at core
[0, midY, z], // Up the trunk
[targetX, midY, z], // Along branch
[targetX, targetY, z], // To memory
];
case 'ltm':
targetY = -halfChipHeight + sectionHeight * 0.3;
midY = -halfChipHeight + sectionHeight * 0.5;
return [
[0, 0, z],
[0, midY, z],
[targetX, midY, z],
[targetX, targetY, z],
];
case 'episodic':
default:
const side = col < 3 ? -1 : 1;
targetX;
return [
[0, 0, z],
[side * (Math.abs(targetX) + 1), 0, z],
[side * (Math.abs(targetX) + 1), pulse.gridPosition[1] * 0.8, z],
];
}
}, [pulse, halfChipHeight, sectionHeight]);
const duration = 500; // ms
useFrame(() => {
if (!pulseRef.current) return;
const elapsed = Date.now() - startTimeRef.current;
const progress = Math.min(elapsed / duration, 1);
if (progress >= 1) {
onComplete(pulse.id);
return;
}
// Interpolate position along path
const totalSegments = path.length - 1;
const segmentProgress = progress * totalSegments;
const segmentIndex = Math.floor(segmentProgress);
const segmentT = segmentProgress - segmentIndex;
const currentIdx = Math.min(segmentIndex, totalSegments - 1);
const start = path[currentIdx];
const end = path[Math.min(currentIdx + 1, path.length - 1)];
pulseRef.current.position.set(
start[0] + (end[0] - start[0]) * segmentT,
start[1] + (end[1] - start[1]) * segmentT,
start[2] + (end[2] - start[2]) * segmentT
);
// Scale down as it travels
const scale = 1 - progress * 0.3;
pulseRef.current.scale.setScalar(scale);
// Trail follows slightly behind
if (trailRef.current && progress > 0.1) {
const trailProgress = Math.max(0, progress - 0.1);
const trailSegmentProgress = trailProgress * totalSegments;
const trailSegmentIndex = Math.floor(trailSegmentProgress);
const trailSegmentT = trailSegmentProgress - trailSegmentIndex;
const trailIdx = Math.min(trailSegmentIndex, totalSegments - 1);
const trailStart = path[trailIdx];
const trailEnd = path[Math.min(trailIdx + 1, path.length - 1)];
trailRef.current.position.set(
trailStart[0] + (trailEnd[0] - trailStart[0]) * trailSegmentT,
trailStart[1] + (trailEnd[1] - trailStart[1]) * trailSegmentT,
trailStart[2] + (trailEnd[2] - trailStart[2]) * trailSegmentT
);
trailRef.current.scale.setScalar(scale * 0.6);
const trailMaterial = trailRef.current.material as THREE.MeshBasicMaterial;
trailMaterial.opacity = 0.4 * (1 - progress);
}
});
return (
{/* Main pulse */}
{/* Trail */}
);
}