"use client"; import React, { useEffect, useRef } from 'react'; import type p5 from 'p5'; // No longer needs width/height props as it will fill its parent interface ZPEParticleSketchProps {} const ZPEParticleSketch: React.FC = () => { const sketchRef = useRef(null); const p5InstanceRef = useRef(null); useEffect(() => { if (typeof window !== 'undefined' && sketchRef.current) { import('p5').then(p5Module => { const P5 = p5Module.default; if (p5InstanceRef.current) { p5InstanceRef.current.remove(); // Cleanup previous instance } const sketch = (p: p5) => { // --- Start of user's p5.js code --- let particles: any[] = []; const numParticles = 100; let zpeStrength = 0.05; let zpeNoise = 0.02; let scanner: { active: boolean; position: p5.Vector | null; radius: number; angle: number; speed: number; detectedParticles: any[]; } = { active: false, position: null as p5.Vector | null, radius: 60, angle: 0, speed: 0.02, detectedParticles: [] as any[] }; let tetrahedralMemory: { nodes: any[]; connections: any[]; capacity: number; scanHistory: any[]; processingPower: number; } = { nodes: [] as any[], connections: [] as any[], capacity: 12, scanHistory: [] as any[], processingPower: 0 }; const TYPES = { QUARK: { size: 4, speed: 1.5, color: [0, 90, 95], memory: 2 }, // Hue, Saturation, Brightness LEPTON: { size: 2.5, speed: 2.2, color: [180, 85, 90], memory: 1 }, GLUON: { size: 1.5, speed: 3, color: [280, 95, 100], memory: 3 } }; let connections: any[] = []; class Particle { type: string; typeProps: { size: number; speed: number; color: number[]; memory: number }; pos: p5.Vector; vel: p5.Vector; zpeOffset: number; size: number; baseColor: number[]; scanned: boolean; inMemory: boolean; memoryNode: any; dataValue: number; timeStored: number = 0; constructor(type?: string) { this.type = type || this.randomType(); this.typeProps = TYPES[this.type as keyof typeof TYPES]; this.pos = P5.Vector.random3D().mult(100); this.vel = P5.Vector.random3D().mult(this.typeProps.speed * 0.5); this.zpeOffset = p.random(1000); this.size = this.typeProps.size; this.baseColor = [...this.typeProps.color]; // HSB this.scanned = false; this.inMemory = false; this.memoryNode = null; this.dataValue = p.floor(p.random(100)); } randomType() { const types = Object.keys(TYPES); return types[p.floor(p.random(types.length))]; } update() { let perturbation = p.createVector( p.noise(this.zpeOffset + p.frameCount * 0.01) - 0.5, p.noise(this.zpeOffset + 1000 + p.frameCount * 0.01) - 0.5, p.noise(this.zpeOffset + 2000 + p.frameCount * 0.01) - 0.5 ).mult(zpeStrength * (this.type === 'GLUON' ? 2 : 1)); this.vel.add(perturbation); this.vel.limit(this.typeProps.speed); this.pos.add(this.vel); if (this.pos.mag() > 150) { // Boundary this.pos.normalize().mult(150); this.vel.mult(-0.5); // Bounce back } if (this.scanned && p.frameCount % 60 === 0) { // Reset scanned status periodically this.scanned = false; } } display() { p.push(); p.translate(this.pos.x, this.pos.y, this.pos.z); let hueShift = (p.frameCount * 0.2 + this.zpeOffset) % 60; let hue = (this.baseColor[0] + hueShift) % 360; let saturation = this.baseColor[1]; let brightness = this.baseColor[2]; if (this.scanned) { brightness = p.min(100, brightness + 20); saturation = p.max(50, saturation - 20); } if (this.inMemory) { p.stroke(60, 100, 100); // Yellowish highlight for in-memory p.strokeWeight(0.5); } else { p.noStroke(); } if (this.type === 'QUARK') { p.fill(hue, saturation, brightness); p.sphere(this.size); } else if (this.type === 'LEPTON') { p.fill(hue, saturation, brightness, 80); // Slightly transparent if (!this.inMemory) { p.stroke(hue, saturation - 20, brightness); p.strokeWeight(0.5); } p.sphere(this.size); } else if (this.type === 'GLUON') { p.fill(hue, saturation, brightness, 70); // More transparent if (!this.inMemory) { p.stroke(hue, saturation - 20, brightness + 10); p.strokeWeight(0.8); } p.rotateX(p.frameCount * 0.03); p.rotateY(p.frameCount * 0.04); p.torus(this.size, this.size * 0.4); } p.pop(); } } class TetrahedralNode { pos: p5.Vector; originalPos: p5.Vector; index: number; size: number; active: boolean; storedParticle: Particle | null; processingPower: number; pulseSize: number; connections: number[]; constructor(position: p5.Vector, nodeIndex: number) { this.pos = position.copy(); this.originalPos = position.copy(); this.index = nodeIndex; this.size = 5; this.active = false; this.storedParticle = null; this.processingPower = 0; this.pulseSize = 0; this.connections = []; // Store indices of connected nodes } update() { // Drift back to original position let target = this.originalPos.copy(); let direction = P5.Vector.sub(target, this.pos); direction.mult(0.1); // Spring-like behavior this.pos.add(direction); if (this.active) { this.pulseSize = 8 + p.sin(p.frameCount * 0.1) * 2; if (this.storedParticle) { this.processingPower = this.storedParticle.dataValue / 100; // Example power } } else { this.pulseSize = p.max(0, this.pulseSize - 0.2); this.processingPower *= 0.95; // Decay power if not active } } display() { p.push(); p.translate(this.pos.x, this.pos.y, this.pos.z); if (this.active) { p.fill(60, 100, 100, 70); p.stroke(60, 100, 100); // Active color (yellowish) } else { p.fill(210, 70, 50, 50); p.stroke(210, 70, 80); // Inactive color (blueish) } p.strokeWeight(0.8); p.sphere(this.size); if (this.pulseSize > 0) { // Pulsing effect p.noFill(); p.stroke(60, 100, 100, 30); p.strokeWeight(0.5); p.sphere(this.pulseSize); } // Display stored particle representation if (this.storedParticle) { p.rotateY(p.frameCount * 0.05); // Gentle rotation p.fill(this.storedParticle.baseColor[0], this.storedParticle.baseColor[1], this.storedParticle.baseColor[2], 50); p.stroke(this.storedParticle.baseColor[0], this.storedParticle.baseColor[1], this.storedParticle.baseColor[2]); p.strokeWeight(0.3); if (this.storedParticle.type === 'QUARK') p.box(3); else if (this.storedParticle.type === 'LEPTON') p.cylinder(2, 4); else p.cone(2, 4); // GLUON } p.pop(); } } function initTetrahedralMemory() { tetrahedralMemory.nodes = []; const baseSize = 60; const v1 = p.createVector(baseSize, 0, -baseSize/2); const v2 = p.createVector(-baseSize, 0, -baseSize/2); const v3 = p.createVector(0, 0, baseSize); const apex = p.createVector(0, baseSize * 1.5, 0); tetrahedralMemory.nodes.push(new TetrahedralNode(v1, 0)); tetrahedralMemory.nodes.push(new TetrahedralNode(v2, 1)); tetrahedralMemory.nodes.push(new TetrahedralNode(v3, 2)); tetrahedralMemory.nodes.push(new TetrahedralNode(apex, 3)); // Add more nodes for a complex structure tetrahedralMemory.nodes.push(new TetrahedralNode(P5.Vector.lerp(v1, v2, 0.5), 4)); tetrahedralMemory.nodes.push(new TetrahedralNode(P5.Vector.lerp(v2, v3, 0.5), 5)); tetrahedralMemory.nodes.push(new TetrahedralNode(P5.Vector.lerp(v3, v1, 0.5), 6)); tetrahedralMemory.nodes.push(new TetrahedralNode(P5.Vector.lerp(v1, apex, 0.5), 7)); tetrahedralMemory.nodes.push(new TetrahedralNode(P5.Vector.lerp(v2, apex, 0.5), 8)); tetrahedralMemory.nodes.push(new TetrahedralNode(P5.Vector.lerp(v3, apex, 0.5), 9)); const center = p.createVector(0, baseSize * 0.5, 0); tetrahedralMemory.nodes.push(new TetrahedralNode(center, 10)); tetrahedralMemory.nodes.push(new TetrahedralNode(P5.Vector.lerp(center, apex, 0.3), 11)); tetrahedralMemory.nodes.push(new TetrahedralNode(P5.Vector.lerp(center, v1, 0.3), 12)); // Ensure index is 12 for capacity createTetrahedralConnections(); } function createTetrahedralConnections() { // Basic tetrahedral connections addMemoryConnection(0, 1); addMemoryConnection(1, 2); addMemoryConnection(2, 0); // Base addMemoryConnection(0, 3); addMemoryConnection(1, 3); addMemoryConnection(2, 3); // To apex // Midpoints addMemoryConnection(4, 0); addMemoryConnection(4, 1); addMemoryConnection(5, 1); addMemoryConnection(5, 2); addMemoryConnection(6, 2); addMemoryConnection(6, 0); addMemoryConnection(7, 0); addMemoryConnection(7, 3); addMemoryConnection(8, 1); addMemoryConnection(8, 3); addMemoryConnection(9, 2); addMemoryConnection(9, 3); // Center connections addMemoryConnection(10, 11); addMemoryConnection(10, 12); addMemoryConnection(10, 4); addMemoryConnection(10, 5); addMemoryConnection(10, 6); addMemoryConnection(11, 3); addMemoryConnection(12, 0); // Corrected to use actual index 12 } function addMemoryConnection(index1: number, index2: number) { let node1 = tetrahedralMemory.nodes[index1]; let node2 = tetrahedralMemory.nodes[index2]; if (!node1 || !node2) return; // Safety check node1.connections.push(index2); node2.connections.push(index1); tetrahedralMemory.connections.push({ from: index1, to: index2, active: false, strength: 0, pulsePhase: p.random(p.TWO_PI) }); } function connectParticles() { connections = []; const gluons = particles.filter(pt => pt.type === 'GLUON'); const quarks = particles.filter(pt => pt.type === 'QUARK'); for (let gluon of gluons) { let closestQuarks = findClosestParticles(gluon, quarks, 2); for (let quark of closestQuarks) { connections.push({ from: gluon, to: quark, strength: p.random(0.4, 0.8) }); } } // Temporary lepton connections if (p.frameCount % 30 === 0) { // Less frequent const leptons = particles.filter(pt => pt.type === 'LEPTON'); if (leptons.length >= 2) { for (let i = 0; i < p.min(5, leptons.length); i++) { // Limit number of connections let lepton1 = leptons[p.floor(p.random(leptons.length))]; let lepton2 = findClosestParticles(lepton1, leptons, 1)[0]; // Find one closest if (lepton1 && lepton2 && lepton1 !== lepton2) { // Ensure different and valid connections.push({ from: lepton1, to: lepton2, strength: 0.3, lifetime: 20, age: 0 }); } } } } } function findClosestParticles(source: Particle, targetList: Particle[], count: number) { return targetList .filter(pt => pt !== source) // Exclude self .sort((a, b) => P5.Vector.dist(source.pos, a.pos) - P5.Vector.dist(source.pos, b.pos)) .slice(0, count); } function updateScanner() { if (!scanner.active) { if (p.random() < 0.005) { // Chance to activate scanner scanner.active = true; scanner.position = P5.Vector.random3D().mult(100); // Random start scanner.detectedParticles = []; } return; } // Scanner movement scanner.angle += scanner.speed; if (scanner.position) { scanner.position.x = p.sin(scanner.angle) * 120; scanner.position.z = p.cos(scanner.angle) * 120; scanner.position.y = p.sin(scanner.angle * 1.5) * 60; // Lissajous-like path } scanner.detectedParticles = []; for (let particle of particles) { if (scanner.position && P5.Vector.dist(particle.pos, scanner.position) < scanner.radius) { scanner.detectedParticles.push(particle); particle.scanned = true; if (!particle.inMemory) addToMemory(particle); } } if (p.random() < 0.01) scanner.active = false; // Chance to deactivate } function renderScanner() { if (!scanner.active || !scanner.position) return; p.push(); p.translate(scanner.position.x, scanner.position.y, scanner.position.z); p.noFill(); p.stroke(120, 100, 100, 20); p.strokeWeight(0.5); p.sphere(scanner.radius); // Scanner field p.fill(120, 100, 100, 50); p.noStroke(); p.sphere(5); // Scanner core // Lines to detected particles if (scanner.detectedParticles.length > 0) { p.stroke(120, 100, 100, 70); p.strokeWeight(0.8); for (let particle of scanner.detectedParticles) { let relativePos = P5.Vector.sub(particle.pos, scanner.position!); p.line(0, 0, 0, relativePos.x, relativePos.y, relativePos.z); } } p.pop(); } function addToMemory(particle: Particle) { let availableNodes = tetrahedralMemory.nodes.filter(n => !n.active); if (availableNodes.length === 0) { // If full, replace oldest let oldestNode = tetrahedralMemory.nodes.reduce((oldest, current) => (!oldest.active || (current.active && oldest.storedParticle && current.storedParticle && oldest.storedParticle.timeStored < current.storedParticle.timeStored)) ? oldest : current ); if (oldestNode.storedParticle) { // Release old particle oldestNode.storedParticle.inMemory = false; oldestNode.storedParticle.memoryNode = null; } oldestNode.active = false; oldestNode.storedParticle = null; availableNodes = [oldestNode]; } let selectedNode = availableNodes[p.floor(p.random(availableNodes.length))]; selectedNode.active = true; selectedNode.storedParticle = particle; particle.inMemory = true; particle.memoryNode = selectedNode; particle.timeStored = p.frameCount; activateMemoryConnections(selectedNode); tetrahedralMemory.scanHistory.push({ particleType: particle.type, timeStamp: p.frameCount, dataValue: particle.dataValue }); if (tetrahedralMemory.scanHistory.length > 20) tetrahedralMemory.scanHistory.shift(); updateMemoryProcessingPower(); } function activateMemoryConnections(node: any) { // node is TetrahedralNode for (let conn of tetrahedralMemory.connections) { if (conn.from === node.index || conn.to === node.index) { let otherNodeIndex = conn.from === node.index ? conn.to : conn.from; let otherNode = tetrahedralMemory.nodes[otherNodeIndex]; if (otherNode.active) { // Connect if other node is also active conn.active = true; conn.strength = 0.8; // Stronger connection if same particle type if (otherNode.storedParticle && node.storedParticle && otherNode.storedParticle.type === node.storedParticle.type) { conn.strength = 1.0; } } } } } function updateMemoryProcessingPower() { let totalPower = 0; for (let node of tetrahedralMemory.nodes) { if (node.active && node.storedParticle) totalPower += node.processingPower; } let activeConnections = tetrahedralMemory.connections.filter(c => c.active); totalPower *= (1 + activeConnections.length / 20); // Boost by active connections tetrahedralMemory.processingPower = totalPower; } function renderConnections() { for (let i = connections.length - 1; i >= 0; i--) { let c = connections[i]; if (c.lifetime) { // For temporary connections like lepton-lepton c.age++; if (c.age > c.lifetime) { connections.splice(i, 1); continue; } } let p1 = c.from.pos; let p2 = c.to.pos; if (c.from.type === 'GLUON') { // Gluon-Quark p.stroke(c.from.baseColor[0], 70, 100, c.strength * 100); p.strokeWeight(0.8 * c.strength); } else { // Lepton-Lepton (temporary) p.stroke(220, 80, 100, 50 * (1 - (c.age || 0)/(c.lifetime || 1))); p.strokeWeight(0.5); } p.line(p1.x, p1.y, p1.z, p2.x, p2.y, p2.z); } } function renderMemoryConnections() { for (let conn of tetrahedralMemory.connections) { let node1 = tetrahedralMemory.nodes[conn.from]; let node2 = tetrahedralMemory.nodes[conn.to]; if (conn.active) { let pulse = (p.sin(p.frameCount * 0.2 + conn.pulsePhase) + 1) / 2; // Pulsing strength p.stroke(60, 100, 100, 30 + conn.strength * 70 * pulse); p.strokeWeight(0.5 + conn.strength * pulse); } else { p.stroke(210, 30, 50, 20); p.strokeWeight(0.3); // Faint inactive connection } p.line(node1.pos.x, node1.pos.y, node1.pos.z, node2.pos.x, node2.pos.y, node2.pos.z); } } function renderMemoryNodes() { for (let node of tetrahedralMemory.nodes) { node.update(); node.display(); } } p.setup = () => { if (sketchRef.current) { p.createCanvas(sketchRef.current.offsetWidth, sketchRef.current.offsetHeight, p.WEBGL); } else { p.createCanvas(600,600, p.WEBGL); // Fallback } p.colorMode(p.HSB, 360, 100, 100, 100); // Hue, Saturation, Brightness, Alpha (0-100 for p5) for (let i = 0; i < numParticles; i++) { let type; if (i < numParticles * 0.5) type = 'QUARK'; else if (i < numParticles * 0.8) type = 'LEPTON'; else type = 'GLUON'; particles.push(new Particle(type)); } initTetrahedralMemory(); }; p.draw = () => { p.background(p.color('hsl(240, 8%, 10%)')); // Slightly darker than main background p.ambientLight(150); p.pointLight(p.color('hsl(0, 0%, 100%)'), p.sin(p.frameCount * 0.02) * 200, p.cos(p.frameCount * 0.02) * 200, 100 ); p.pointLight(p.color('hsl(240, 80%, 80%)'), p.cos(p.frameCount * 0.01) * 150, p.sin(p.frameCount * 0.01) * 150, -50 ); p.orbitControl(2,2,0.1); p.rotateX(p.frameCount * 0.002); p.rotateY(p.frameCount * 0.003); updateScanner(); if (p.frameCount % 10 === 0) connectParticles(); // Connect less frequently // Update and render memory system for (let conn of tetrahedralMemory.connections) { // Decay active connections if (conn.active) { conn.strength *= 0.99; if (conn.strength < 0.1) conn.active = false; } } renderMemoryConnections(); renderMemoryNodes(); renderConnections(); // Particle connections for (let pt of particles) { pt.update(); pt.display(); } renderScanner(); zpeStrength = 0.05 + p.noise(p.frameCount * 0.02) * zpeNoise; // Dynamic ZPE strength }; p.windowResized = () => { if (sketchRef.current) { p.resizeCanvas(sketchRef.current.offsetWidth, sketchRef.current.offsetHeight); } } // --- End of user's p5.js code --- }; p5InstanceRef.current = new P5(sketch, sketchRef.current!); }); } return () => { if (p5InstanceRef.current) { p5InstanceRef.current.remove(); p5InstanceRef.current = null; } }; }, []); // Empty dependency array ensures this runs once on mount and cleans up on unmount return
; }; export default ZPEParticleSketch;