From 87108efff7f7eb38674a55da9eb40bf3d9af16d3 Mon Sep 17 00:00:00 2001 From: Lazaro Alonso Date: Thu, 15 Jan 2026 22:42:18 +0100 Subject: [PATCH 1/8] more dots --- src/components/plots/MorphingPoints.tsx | 161 +++++++++++------- .../textures/shaders/LandingVertex.glsl | 55 ++++-- 2 files changed, 134 insertions(+), 82 deletions(-) diff --git a/src/components/plots/MorphingPoints.tsx b/src/components/plots/MorphingPoints.tsx index 6d860408..53b91691 100644 --- a/src/components/plots/MorphingPoints.tsx +++ b/src/components/plots/MorphingPoints.tsx @@ -11,26 +11,9 @@ import { useGlobalStore, usePlotStore } from '@/utils/GlobalStates'; import { useShallow } from 'zustand/shallow'; -// Define the type for our custom shader material's uniforms -type MorphMaterialType = THREE.ShaderMaterial & { - uniforms: { - uSphereMix: { value: number }; - uCubeMix: { value: number }; - uPlaneMix: { value: number }; - uSize: { value: number }; - uTime: { value: number }; - }; -}; - -// Define the shader material using drei's helper - - -// Make the material available to extend - - const MorphingPoints = () => { const pointsRef = useRef(null); - const count = 15625; // Total number of points + const count = 15625; const {gl} = useThree(); const { setMaxTextureSize, setMax3DTextureSize } = usePlotStore(useShallow(state => ({ setMaxTextureSize: state.setMaxTextureSize, @@ -44,20 +27,22 @@ const MorphingPoints = () => { setMaxTextureSize(context.getParameter(context.MAX_TEXTURE_SIZE)) },[]) - const {colormap} = useGlobalStore(useShallow(state => ({ colormap: state.colormap }))) - // Pre-calculate the point positions for each shape using useMemo for performance - const { spherePositions, cubePositions, planePositions } = useMemo(() => { + + // Generate positions with improved distribution and spawn points + const { spherePositions, cubePositions, planePositions, spawnPositions, delays } = useMemo(() => { const spherePositions = new Float32Array(count * 3); const cubePositions = new Float32Array(count * 3); const planePositions = new Float32Array(count * 3); + const spawnPositions = new Float32Array(count * 3); + const delays = new Float32Array(count); - // --- Sphere Positions (using Fibonacci lattice for even distribution) --- - const phi = Math.PI * (3.0 - Math.sqrt(5.0)); // Golden angle + // Sphere - Fibonacci lattice for even distribution + const phi = Math.PI * (3.0 - Math.sqrt(5.0)); for (let i = 0; i < count; i++) { - const y = 1 - (i / (count - 1)) * 2; // y goes from 1 to -1 + const y = 1 - (i / (count - 1)) * 2; const radius = Math.sqrt(1 - y * y); const theta = phi * i; @@ -67,10 +52,22 @@ const MorphingPoints = () => { spherePositions[i * 3] = x * 1.2; spherePositions[i * 3 + 1] = y * 1.2; spherePositions[i * 3 + 2] = z * 1.2; + + // Spawn positions - random directions from center + const spawnTheta = Math.random() * Math.PI * 2; + const spawnPhi = Math.random() * Math.PI; + const spawnDist = 4 + Math.random() * 2; + + spawnPositions[i * 3] = Math.sin(spawnPhi) * Math.cos(spawnTheta) * spawnDist; + spawnPositions[i * 3 + 1] = Math.sin(spawnPhi) * Math.sin(spawnTheta) * spawnDist; + spawnPositions[i * 3 + 2] = Math.cos(spawnPhi) * spawnDist; + + // Random delays for staggered arrival (0 to 1) + delays[i] = Math.random(); } - // --- Cube Positions (16x16x16 grid) --- - const cubRes = 25 + // Cube - grid distribution + const cubRes = 25; let i = 0; for (let x = 0; x < cubRes; x++) { for (let y = 0; y < cubRes; y++) { @@ -83,93 +80,126 @@ const MorphingPoints = () => { } } - // --- Plane Positions (64x64 grid) --- - const planeRes = 125 + // Plane - grid distribution + const planeRes = 125; i = 0; for (let x = 0; x < planeRes; x++) { for (let y = 0; y < planeRes; y++) { - planePositions[i * 3] = (x / planeRes - 0.5) * 2.5 ; - planePositions[i * 3 + 1] = (y / planeRes - 0.5) * 2.5 ; + planePositions[i * 3] = (x / planeRes - 0.5) * 2.5; + planePositions[i * 3 + 1] = (y / planeRes - 0.5) * 2.5; planePositions[i * 3 + 2] = 0; i++; } } - return { spherePositions, cubePositions, planePositions }; + return { spherePositions, cubePositions, planePositions, spawnPositions, delays }; }, [count]); - const MorphMaterial = useMemo(()=>new THREE.ShaderMaterial({ + const MorphMaterial = useMemo(()=>new THREE.ShaderMaterial({ glslVersion: THREE.GLSL3, uniforms: { - uSphereMix: {value: 1.0}, + uSphereMix: {value: 0.0}, uCubeMix: {value: 0.0}, uPlaneMix: {value: 0.0}, uTime: {value: 0.0}, + uArrivalProgress: {value: 0.0}, cmap: { value: colormap} }, vertexShader, fragmentShader }),[]) - // Animation effect + + // Enhanced animation with arrival and smooth transitions useEffect(() => { let tl: gsap.core.Timeline | null = null; if (MorphMaterial) { const uniforms = MorphMaterial.uniforms; - // Create a GSAP timeline for the morphing animation tl = gsap.timeline({ - repeat: -1, // Loop indefinitely + repeat: -1, yoyo: false, }); - const duration = 2; // Duration of each morph transition - const delay = 3; // Time to hold each shape before morphing + const arrivalDuration = 3; + const duration = 2.5; + const delay = 2.5; - // 1. Morph from Sphere to Cube - tl.to(uniforms.uCubeMix, { + // Initial arrival to sphere + tl.to(uniforms.uArrivalProgress, { value: 1, + duration: arrivalDuration, + ease: 'power2.out', + }); + + tl.to(uniforms.uSphereMix, { + value: 1, + duration: arrivalDuration, + ease: 'power2.out', + }, "<"); + + // Hold sphere + tl.to({}, { duration: delay }); + + // Morph to Cube with fluid motion + tl.to(uniforms.uSphereMix, { + value: 0, duration, - delay, - ease: 'power2.inOut', + ease: 'power1.inOut', }); + + tl.to(uniforms.uCubeMix, { + value: 1, + duration, + ease: 'power1.inOut', + }, "<"); + + // Hold cube + tl.to({}, { duration: delay }); - // 2. Morph from Cube to Plane + // Morph to Plane tl.to(uniforms.uCubeMix, { value: 0, duration, - delay, - ease: 'power2.inOut', + ease: 'power1.inOut', }); + tl.to(uniforms.uPlaneMix, { value: 1, duration, - ease: 'power2.inOut', - }, "<"); // Animate at the same time as the previous tween + ease: 'power1.inOut', + }, "<"); + + // Hold plane + tl.to({}, { duration: delay }); - // 3. Morph from Plane back to Sphere + // Morph back to Sphere tl.to(uniforms.uPlaneMix, { value: 0, duration, - delay, - ease: 'power2.inOut', + ease: 'power1.inOut', }); + + tl.to(uniforms.uSphereMix, { + value: 1, + duration, + ease: 'power1.inOut', + }, "<"); } - // Cleanup function - always return this + return () => { if (tl) { tl.kill(); } }; - }, []); + }, [MorphMaterial]); - // Update time uniform for the dynamic wave animation in the shader useFrame((state) => { if(MorphMaterial){ MorphMaterial.uniforms.uTime.value = state.clock.getElapsedTime(); } if (pointsRef.current) { - pointsRef.current.rotation.y += 0.001; // Slow rotation around Y-axis + pointsRef.current.rotation.y += 0.001; pointsRef.current.rotation.x += 0.001; } }); @@ -183,11 +213,14 @@ const MorphingPoints = () => { return ( - {/* The 'position' attribute is not used by the shader for final position, - but it's good practice to have it. We'll use sphere positions as the base. */} + { args={[planePositions, 3]} count={count} /> + - {/* Use the custom morphMaterial */} ); }; @@ -215,15 +252,11 @@ const MorphingPoints = () => { export const LandingShapes = () =>{ return(
- {/*
- Browzarr -
*/} - - - + +
) } \ No newline at end of file diff --git a/src/components/textures/shaders/LandingVertex.glsl b/src/components/textures/shaders/LandingVertex.glsl index fcc021b1..6a9aa6f2 100644 --- a/src/components/textures/shaders/LandingVertex.glsl +++ b/src/components/textures/shaders/LandingVertex.glsl @@ -3,43 +3,62 @@ uniform float uCubeMix; uniform float uPlaneMix; uniform float uSize; uniform float uTime; +uniform float uArrivalProgress; uniform sampler2D cmap; attribute vec3 aSpherePosition; attribute vec3 aCubePosition; attribute vec3 aPlanePosition; +attribute vec3 aSpawnPosition; +attribute float aDelay; varying vec3 vColor; +// Flow field for organic, fluid-like movement +vec3 flowField(vec3 pos, float time) { + float scale = 0.005; + return vec3( + sin(pos.x * scale + time) * cos(pos.y * scale), + cos(pos.y * scale + time) * sin(pos.z * scale), + sin(pos.z * scale) * cos(pos.x * scale + time) + ) * 0.3; +} + void main() { + // Calculate staggered arrival - each particle arrives at different time + float particleArrival = smoothstep(aDelay, aDelay + 0.3, uArrivalProgress); + // Linearly interpolate between the three shapes using the mix uniforms vec3 pos = mix(aSpherePosition, aCubePosition, uCubeMix); pos = mix(pos, aPlanePosition, uPlaneMix); - - // Add a slight sine wave animation to make it more dynamic - // pos.y += sin(pos.x * 50.0 + uTime) * 0.005; - // pos.x += cos(pos.y * 50.0 + uTime) * 0.005; - + + // Add flow field for fluid, organic movement during transitions + vec3 flow = flowField(pos, uTime); + float transitionAmount = uCubeMix + uPlaneMix; + float flowStrength = transitionAmount * 0.5; // More flow during transitions + pos += flow * flowStrength; + + // Interpolate from spawn position to morphed position based on arrival + vec3 finalPos = mix(aSpawnPosition, pos, particleArrival); + + // Color calculation float minBrightness = 0.2; float maxBrightness = 0.96; - - float r = sin(pos.z + (uTime * 0.2 ) ) ; - float g = cos(pos.y + (uTime * 0.3 ) ); - float b = cos(pos.x+pos.y + (uTime * 0.5)); - + float r = sin(finalPos.z + (uTime * 0.2)); + float g = cos(finalPos.y + (uTime * 0.3)); + float b = cos(finalPos.x + finalPos.y + (uTime * 0.5)); vColor = vec3(r, g, b); - float mag = min(((sin(r+g+b) + 1.) /2.), 0.996) ; + + float mag = min(((sin(r + g + b) + 1.0) / 2.0), 0.996); vec4 sampled = texture(cmap, vec2(mag, 0.5)); vColor = sampled.rgb; - // Calculate luminance (perceived brightness) - - vec4 modelPosition = modelMatrix * vec4(pos, 1.0); + + // Transform position + vec4 modelPosition = modelMatrix * vec4(finalPos, 1.0); vec4 viewPosition = viewMatrix * modelPosition; vec4 projectedPosition = projectionMatrix * viewPosition; - gl_Position = projectedPosition; - + // Make points smaller as they are further away (perspective) gl_PointSize = (15.0 / -viewPosition.z); -} - +} \ No newline at end of file From b969171441a8e5aa0d50098aec22af8c33f45320 Mon Sep 17 00:00:00 2001 From: Lazaro Alonso Date: Thu, 15 Jan 2026 23:13:00 +0100 Subject: [PATCH 2/8] correct sequence --- src/components/plots/MorphingPoints.tsx | 68 +++++++++++-------- .../textures/shaders/LandingVertex.glsl | 7 +- 2 files changed, 45 insertions(+), 30 deletions(-) diff --git a/src/components/plots/MorphingPoints.tsx b/src/components/plots/MorphingPoints.tsx index 53b91691..bcb6584d 100644 --- a/src/components/plots/MorphingPoints.tsx +++ b/src/components/plots/MorphingPoints.tsx @@ -3,6 +3,7 @@ import React, { useMemo, useRef, useEffect } from 'react'; import * as THREE from 'three'; import { Canvas, extend, useFrame, useThree } from '@react-three/fiber'; +import { OrbitControls } from '@react-three/drei'; import gsap from 'gsap'; import vertexShader from '@/components/textures/shaders/LandingVertex.glsl' import fragmentShader from '@/components/textures/shaders/LandingFrag.glsl' @@ -101,6 +102,7 @@ const MorphingPoints = () => { uSphereMix: {value: 0.0}, uCubeMix: {value: 0.0}, uPlaneMix: {value: 0.0}, + uRandomMix: {value: 0.0}, uTime: {value: 0.0}, uArrivalProgress: {value: 0.0}, cmap: { value: colormap} @@ -141,50 +143,48 @@ const MorphingPoints = () => { // Hold sphere tl.to({}, { duration: delay }); - // Morph to Cube with fluid motion - tl.to(uniforms.uSphereMix, { - value: 0, + // Morph to Cube + tl.to([uniforms.uSphereMix, uniforms.uCubeMix], { + value: (index) => index === 0 ? 0 : 1, duration, ease: 'power1.inOut', }); - - tl.to(uniforms.uCubeMix, { - value: 1, - duration, - ease: 'power1.inOut', - }, "<"); // Hold cube tl.to({}, { duration: delay }); // Morph to Plane - tl.to(uniforms.uCubeMix, { - value: 0, + tl.to([uniforms.uCubeMix, uniforms.uPlaneMix], { + value: (index) => index === 0 ? 0 : 1, duration, ease: 'power1.inOut', }); - - tl.to(uniforms.uPlaneMix, { - value: 1, - duration, - ease: 'power1.inOut', - }, "<"); // Hold plane tl.to({}, { duration: delay }); - // Morph back to Sphere - tl.to(uniforms.uPlaneMix, { - value: 0, + // Morph to Random/Dispersed state + tl.to([uniforms.uPlaneMix, uniforms.uRandomMix], { + value: (index) => index === 0 ? 0 : 1, duration, ease: 'power1.inOut', }); - - tl.to(uniforms.uSphereMix, { - value: 1, - duration, - ease: 'power1.inOut', - }, "<"); + + // Hold random + tl.to({}, { duration: delay }); + + // Disperse back to spawn (reset arrival progress) + tl.to(uniforms.uArrivalProgress, { + value: 0, + duration: duration, + ease: 'power2.in', + }); + + // Reset shape mixes simultaneously + tl.to([uniforms.uRandomMix, uniforms.uSphereMix], { + value: 0, + duration: 0.01, // Instant reset + }); } return () => { @@ -199,8 +199,8 @@ const MorphingPoints = () => { MorphMaterial.uniforms.uTime.value = state.clock.getElapsedTime(); } if (pointsRef.current) { - pointsRef.current.rotation.y += 0.001; - pointsRef.current.rotation.x += 0.001; + pointsRef.current.rotation.y += 0.0002; // Reduced from 0.001 + pointsRef.current.rotation.x += 0.0001; // Reduced from 0.001 } }); @@ -223,6 +223,11 @@ const MorphingPoints = () => { args={[spawnPositions, 3]} count={count} /> + { + diff --git a/src/components/textures/shaders/LandingVertex.glsl b/src/components/textures/shaders/LandingVertex.glsl index 6a9aa6f2..4eafdef1 100644 --- a/src/components/textures/shaders/LandingVertex.glsl +++ b/src/components/textures/shaders/LandingVertex.glsl @@ -1,6 +1,7 @@ uniform float uSphereMix; uniform float uCubeMix; uniform float uPlaneMix; +uniform float uRandomMix; uniform float uSize; uniform float uTime; uniform float uArrivalProgress; @@ -10,6 +11,7 @@ attribute vec3 aSpherePosition; attribute vec3 aCubePosition; attribute vec3 aPlanePosition; attribute vec3 aSpawnPosition; +attribute vec3 aRandomPosition; attribute float aDelay; varying vec3 vColor; @@ -28,13 +30,14 @@ void main() { // Calculate staggered arrival - each particle arrives at different time float particleArrival = smoothstep(aDelay, aDelay + 0.3, uArrivalProgress); - // Linearly interpolate between the three shapes using the mix uniforms + // Linearly interpolate between the four shapes using the mix uniforms vec3 pos = mix(aSpherePosition, aCubePosition, uCubeMix); pos = mix(pos, aPlanePosition, uPlaneMix); + pos = mix(pos, aRandomPosition, uRandomMix); // Add flow field for fluid, organic movement during transitions vec3 flow = flowField(pos, uTime); - float transitionAmount = uCubeMix + uPlaneMix; + float transitionAmount = uCubeMix + uPlaneMix + uRandomMix; float flowStrength = transitionAmount * 0.5; // More flow during transitions pos += flow * flowStrength; From f9995188c45250ac353564f60f0bf4bad1aca855 Mon Sep 17 00:00:00 2001 From: Lazaro Alonso Date: Fri, 16 Jan 2026 00:09:22 +0100 Subject: [PATCH 3/8] smooth --- src/components/plots/MorphingPoints.tsx | 28 ++++++++++++------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/components/plots/MorphingPoints.tsx b/src/components/plots/MorphingPoints.tsx index bcb6584d..1c29605d 100644 --- a/src/components/plots/MorphingPoints.tsx +++ b/src/components/plots/MorphingPoints.tsx @@ -73,9 +73,9 @@ const MorphingPoints = () => { for (let x = 0; x < cubRes; x++) { for (let y = 0; y < cubRes; y++) { for (let z = 0; z < cubRes; z++) { - cubePositions[i * 3] = (x / cubRes - 0.5) * 2; - cubePositions[i * 3 + 1] = (y / cubRes - 0.5) * 2; - cubePositions[i * 3 + 2] = (z / cubRes - 0.5) * 2; + cubePositions[i * 3] = (x / (cubRes - 1) - 0.5) * 2; + cubePositions[i * 3 + 1] = (y / (cubRes - 1) - 0.5) * 2; + cubePositions[i * 3 + 2] = (z / (cubRes - 1) - 0.5) * 2; i++; } } @@ -86,8 +86,8 @@ const MorphingPoints = () => { i = 0; for (let x = 0; x < planeRes; x++) { for (let y = 0; y < planeRes; y++) { - planePositions[i * 3] = (x / planeRes - 0.5) * 2.5; - planePositions[i * 3 + 1] = (y / planeRes - 0.5) * 2.5; + planePositions[i * 3] = (x / (planeRes - 1) - 0.5) * 2.5; + planePositions[i * 3 + 1] = (y / (planeRes - 1) - 0.5) * 2.5; planePositions[i * 3 + 2] = 0; i++; } @@ -111,7 +111,7 @@ const MorphingPoints = () => { fragmentShader }),[]) - // Enhanced animation with arrival and smooth transitions + // Enhanced animation with smoother arrival useEffect(() => { let tl: gsap.core.Timeline | null = null; @@ -123,21 +123,21 @@ const MorphingPoints = () => { yoyo: false, }); - const arrivalDuration = 3; + const arrivalDuration = 4.5; // Increased from 3 for much smoother arrival const duration = 2.5; const delay = 2.5; - // Initial arrival to sphere + // Initial arrival to sphere - smoother with custom easing tl.to(uniforms.uArrivalProgress, { value: 1, duration: arrivalDuration, - ease: 'power2.out', + ease: 'power1.out', // Gentler easing }); tl.to(uniforms.uSphereMix, { value: 1, duration: arrivalDuration, - ease: 'power2.out', + ease: 'power1.out', }, "<"); // Hold sphere @@ -173,11 +173,11 @@ const MorphingPoints = () => { // Hold random tl.to({}, { duration: delay }); - // Disperse back to spawn (reset arrival progress) + // Disperse back to spawn (reset arrival progress) - smoother exit tl.to(uniforms.uArrivalProgress, { value: 0, duration: duration, - ease: 'power2.in', + ease: 'power1.in', }); // Reset shape mixes simultaneously @@ -199,8 +199,8 @@ const MorphingPoints = () => { MorphMaterial.uniforms.uTime.value = state.clock.getElapsedTime(); } if (pointsRef.current) { - pointsRef.current.rotation.y += 0.0002; // Reduced from 0.001 - pointsRef.current.rotation.x += 0.0001; // Reduced from 0.001 + pointsRef.current.rotation.y += 0.0002; + pointsRef.current.rotation.x += 0.0001; } }); From 09999f8141b9460f8c0781b2fe30149b5caffc92 Mon Sep 17 00:00:00 2001 From: Lazaro Alonso Date: Fri, 16 Jan 2026 00:19:45 +0100 Subject: [PATCH 4/8] silver --- src/components/plots/MorphingPoints.tsx | 435 ++++++++---------- .../textures/shaders/LandingFrag.glsl | 17 +- .../textures/shaders/LandingVertex.glsl | 96 ++-- 3 files changed, 242 insertions(+), 306 deletions(-) diff --git a/src/components/plots/MorphingPoints.tsx b/src/components/plots/MorphingPoints.tsx index 1c29605d..3414042b 100644 --- a/src/components/plots/MorphingPoints.tsx +++ b/src/components/plots/MorphingPoints.tsx @@ -1,274 +1,211 @@ "use client"; -import React, { useMemo, useRef, useEffect } from 'react'; -import * as THREE from 'three'; -import { Canvas, extend, useFrame, useThree } from '@react-three/fiber'; -import { OrbitControls } from '@react-three/drei'; -import gsap from 'gsap'; -import vertexShader from '@/components/textures/shaders/LandingVertex.glsl' -import fragmentShader from '@/components/textures/shaders/LandingFrag.glsl' -import './Plots.css'; -import { useGlobalStore, usePlotStore } from '@/utils/GlobalStates'; -import { useShallow } from 'zustand/shallow'; - +import React, { useMemo, useRef, useEffect } from "react"; +import * as THREE from "three"; +import { Canvas, useFrame, useThree } from "@react-three/fiber"; +import { OrbitControls } from "@react-three/drei"; +import gsap from "gsap"; +import vertexShader from "@/components/textures/shaders/LandingVertex.glsl"; +import fragmentShader from "@/components/textures/shaders/LandingFrag.glsl"; +import "./Plots.css"; +import { useGlobalStore, usePlotStore } from "@/utils/GlobalStates"; +import { useShallow } from "zustand/shallow"; + +const COUNT = 15625; const MorphingPoints = () => { const pointsRef = useRef(null); - const count = 15625; - const {gl} = useThree(); - const { setMaxTextureSize, setMax3DTextureSize } = usePlotStore(useShallow(state => ({ - setMaxTextureSize: state.setMaxTextureSize, - setMax3DTextureSize: state.setMax3DTextureSize - }))) - - useEffect(()=>{ - const context = gl.getContext() - //@ts-expect-error This parameter does exist - setMax3DTextureSize(context.getParameter(context.MAX_3D_TEXTURE_SIZE)) - setMaxTextureSize(context.getParameter(context.MAX_TEXTURE_SIZE)) - },[]) - - const {colormap} = useGlobalStore(useShallow(state => ({ - colormap: state.colormap - }))) - - // Generate positions with improved distribution and spawn points - const { spherePositions, cubePositions, planePositions, spawnPositions, delays } = useMemo(() => { - const spherePositions = new Float32Array(count * 3); - const cubePositions = new Float32Array(count * 3); - const planePositions = new Float32Array(count * 3); - const spawnPositions = new Float32Array(count * 3); - const delays = new Float32Array(count); - - // Sphere - Fibonacci lattice for even distribution - const phi = Math.PI * (3.0 - Math.sqrt(5.0)); - for (let i = 0; i < count; i++) { - const y = 1 - (i / (count - 1)) * 2; - const radius = Math.sqrt(1 - y * y); - const theta = phi * i; - - const x = Math.cos(theta) * radius; - const z = Math.sin(theta) * radius; - - spherePositions[i * 3] = x * 1.2; - spherePositions[i * 3 + 1] = y * 1.2; - spherePositions[i * 3 + 2] = z * 1.2; - - // Spawn positions - random directions from center - const spawnTheta = Math.random() * Math.PI * 2; - const spawnPhi = Math.random() * Math.PI; - const spawnDist = 4 + Math.random() * 2; - - spawnPositions[i * 3] = Math.sin(spawnPhi) * Math.cos(spawnTheta) * spawnDist; - spawnPositions[i * 3 + 1] = Math.sin(spawnPhi) * Math.sin(spawnTheta) * spawnDist; - spawnPositions[i * 3 + 2] = Math.cos(spawnPhi) * spawnDist; - - // Random delays for staggered arrival (0 to 1) + const { gl } = useThree(); + + const { setMaxTextureSize, setMax3DTextureSize } = usePlotStore( + useShallow((s) => ({ + setMaxTextureSize: s.setMaxTextureSize, + setMax3DTextureSize: s.setMax3DTextureSize, + })) + ); + + useEffect(() => { + const ctx = gl.getContext(); + setMaxTextureSize(ctx.getParameter(ctx.MAX_TEXTURE_SIZE)); + if (gl.capabilities.isWebGL2) { + setMax3DTextureSize(ctx.getParameter(ctx.MAX_3D_TEXTURE_SIZE)); + } + }, [gl, setMaxTextureSize, setMax3DTextureSize]); + + const { colormap } = useGlobalStore( + useShallow((s) => ({ colormap: s.colormap })) + ); + + // Geometry data + const { + spherePositions, + cubePositions, + planePositions, + spawnPositions, + delays, + } = useMemo(() => { + const sphere = new Float32Array(COUNT * 3); + const cube = new Float32Array(COUNT * 3); + const plane = new Float32Array(COUNT * 3); + const spawn = new Float32Array(COUNT * 3); + const delays = new Float32Array(COUNT); + + // Sphere (Fibonacci) + const phi = Math.PI * (3 - Math.sqrt(5)); + for (let i = 0; i < COUNT; i++) { + const y = 1 - (i / (COUNT - 1)) * 2; + const r = Math.sqrt(1 - y * y); + const t = phi * i; + + sphere[i * 3 + 0] = Math.cos(t) * r * 1.2; + sphere[i * 3 + 1] = y * 1.2; + sphere[i * 3 + 2] = Math.sin(t) * r * 1.2; + + const a = Math.random() * Math.PI * 2; + const b = Math.random() * Math.PI; + const d = 4 + Math.random() * 2; + + spawn[i * 3 + 0] = Math.sin(b) * Math.cos(a) * d; + spawn[i * 3 + 1] = Math.sin(b) * Math.sin(a) * d; + spawn[i * 3 + 2] = Math.cos(b) * d; + delays[i] = Math.random(); } - // Cube - grid distribution - const cubRes = 25; - let i = 0; - for (let x = 0; x < cubRes; x++) { - for (let y = 0; y < cubRes; y++) { - for (let z = 0; z < cubRes; z++) { - cubePositions[i * 3] = (x / (cubRes - 1) - 0.5) * 2; - cubePositions[i * 3 + 1] = (y / (cubRes - 1) - 0.5) * 2; - cubePositions[i * 3 + 2] = (z / (cubRes - 1) - 0.5) * 2; - i++; + // Cube + const r = 25; + let idx = 0; + for (let x = 0; x < r; x++) + for (let y = 0; y < r; y++) + for (let z = 0; z < r; z++) { + cube[idx * 3 + 0] = (x / (r - 1) - 0.5) * 2; + cube[idx * 3 + 1] = (y / (r - 1) - 0.5) * 2; + cube[idx * 3 + 2] = (z / (r - 1) - 0.5) * 2; + idx++; } - } - } - // Plane - grid distribution - const planeRes = 125; - i = 0; - for (let x = 0; x < planeRes; x++) { - for (let y = 0; y < planeRes; y++) { - planePositions[i * 3] = (x / (planeRes - 1) - 0.5) * 2.5; - planePositions[i * 3 + 1] = (y / (planeRes - 1) - 0.5) * 2.5; - planePositions[i * 3 + 2] = 0; - i++; + // Plane + const p = 125; + idx = 0; + for (let x = 0; x < p; x++) + for (let y = 0; y < p; y++) { + plane[idx * 3 + 0] = (x / (p - 1) - 0.5) * 2.5; + plane[idx * 3 + 1] = (y / (p - 1) - 0.5) * 2.5; + plane[idx * 3 + 2] = 0; + idx++; } - } - return { spherePositions, cubePositions, planePositions, spawnPositions, delays }; - }, [count]); - - const MorphMaterial = useMemo(()=>new THREE.ShaderMaterial({ - glslVersion: THREE.GLSL3, - uniforms: { - uSphereMix: {value: 0.0}, - uCubeMix: {value: 0.0}, - uPlaneMix: {value: 0.0}, - uRandomMix: {value: 0.0}, - uTime: {value: 0.0}, - uArrivalProgress: {value: 0.0}, - cmap: { value: colormap} - }, - vertexShader, - fragmentShader - }),[]) - - // Enhanced animation with smoother arrival + return { + spherePositions: sphere, + cubePositions: cube, + planePositions: plane, + spawnPositions: spawn, + delays, + }; + }, []); + + const material = useMemo( + () => + new THREE.ShaderMaterial({ + glslVersion: THREE.GLSL3, + transparent: true, + depthWrite: false, + blending: THREE.AdditiveBlending, + uniforms: { + uSphereMix: { value: 0 }, + uCubeMix: { value: 0 }, + uPlaneMix: { value: 0 }, + uRandomMix: { value: 0 }, + uArrivalProgress: { value: 0 }, + uHold: { value: 0 }, + uTime: { value: 0 }, + uSize: { value: 15 }, + cmap: { value: colormap }, + }, + vertexShader, + fragmentShader, + }), + [] + ); + useEffect(() => { - let tl: gsap.core.Timeline | null = null; - - if (MorphMaterial) { - const uniforms = MorphMaterial.uniforms; - - tl = gsap.timeline({ - repeat: -1, - yoyo: false, - }); - - const arrivalDuration = 4.5; // Increased from 3 for much smoother arrival - const duration = 2.5; - const delay = 2.5; - - // Initial arrival to sphere - smoother with custom easing - tl.to(uniforms.uArrivalProgress, { - value: 1, - duration: arrivalDuration, - ease: 'power1.out', // Gentler easing - }); - - tl.to(uniforms.uSphereMix, { - value: 1, - duration: arrivalDuration, - ease: 'power1.out', - }, "<"); - - // Hold sphere - tl.to({}, { duration: delay }); - - // Morph to Cube - tl.to([uniforms.uSphereMix, uniforms.uCubeMix], { - value: (index) => index === 0 ? 0 : 1, - duration, - ease: 'power1.inOut', - }); - - // Hold cube - tl.to({}, { duration: delay }); - - // Morph to Plane - tl.to([uniforms.uCubeMix, uniforms.uPlaneMix], { - value: (index) => index === 0 ? 0 : 1, - duration, - ease: 'power1.inOut', - }); - - // Hold plane - tl.to({}, { duration: delay }); - - // Morph to Random/Dispersed state - tl.to([uniforms.uPlaneMix, uniforms.uRandomMix], { - value: (index) => index === 0 ? 0 : 1, - duration, - ease: 'power1.inOut', - }); - - // Hold random - tl.to({}, { duration: delay }); - - // Disperse back to spawn (reset arrival progress) - smoother exit - tl.to(uniforms.uArrivalProgress, { - value: 0, - duration: duration, - ease: 'power1.in', - }); - - // Reset shape mixes simultaneously - tl.to([uniforms.uRandomMix, uniforms.uSphereMix], { - value: 0, - duration: 0.01, // Instant reset - }); - } + material.uniforms.cmap.value = colormap; + }, [colormap, material]); - return () => { - if (tl) { - tl.kill(); - } + useEffect(() => { + const u = material.uniforms; + const tl = gsap.timeline({ repeat: -1 }); + + const arrive = 4.5; + const morph = 2.5; + const hold = 2.5; + + tl.to(u.uArrivalProgress, { value: 1, duration: arrive, ease: "power1.out" }); + tl.to(u.uSphereMix, { value: 1, duration: arrive }, "<"); + + const addHold = () => { + tl.to(u.uHold, { value: 1, duration: 0.01 }); + tl.to({}, { duration: hold }); + tl.to(u.uHold, { value: 0, duration: 0.01 }); }; - }, [MorphMaterial]); - - useFrame((state) => { - if(MorphMaterial){ - MorphMaterial.uniforms.uTime.value = state.clock.getElapsedTime(); - } - if (pointsRef.current) { - pointsRef.current.rotation.y += 0.0002; - pointsRef.current.rotation.x += 0.0001; - } - }); - useEffect(()=>{ - if(MorphMaterial){ - MorphMaterial.uniforms.cmap.value = colormap + addHold(); + + tl.to([u.uSphereMix, u.uCubeMix], { + value: (i) => (i === 0 ? 0 : 1), + duration: morph, + }); + + addHold(); + + tl.to([u.uCubeMix, u.uPlaneMix], { + value: (i) => (i === 0 ? 0 : 1), + duration: morph, + }); + + addHold(); + + tl.to([u.uPlaneMix, u.uRandomMix], { + value: (i) => (i === 0 ? 0 : 1), + duration: morph, + }); + + addHold(); + + tl.to(u.uArrivalProgress, { value: 0, duration: morph }); + tl.set([u.uRandomMix, u.uSphereMix], { value: 0 }); + + return () => tl.kill(); + }, [material]); + + useFrame((state) => { + material.uniforms.uTime.value = state.clock.getElapsedTime(); + if (pointsRef.current) { + pointsRef.current.rotation.y += 0.0002; + pointsRef.current.rotation.x += 0.0001; } - },[colormap]) + }); return ( - + - - - - - - - + + + + + + + ); }; - -export const LandingShapes = () =>{ - return( -
- - - - -
- ) -} \ No newline at end of file +export const LandingShapes = () => ( +
+ + + + +
+); diff --git a/src/components/textures/shaders/LandingFrag.glsl b/src/components/textures/shaders/LandingFrag.glsl index 89f0d0f1..dec8390b 100644 --- a/src/components/textures/shaders/LandingFrag.glsl +++ b/src/components/textures/shaders/LandingFrag.glsl @@ -1,9 +1,18 @@ -// Fragment Shader +precision highp float; in vec3 vColor; +in float vGreyMix; + out vec4 Color; void main() { - // Simple white color for the points - Color = vec4(vColor, 0.8); -} \ No newline at end of file + // circular points + vec2 uv = gl_PointCoord - 0.5; + float d = length(uv); + if (d > 0.5) discard; + + vec3 silver = vec3(0.75); + vec3 finalColor = mix(vColor, silver, vGreyMix); + + Color = vec4(finalColor, 0.8); +} diff --git a/src/components/textures/shaders/LandingVertex.glsl b/src/components/textures/shaders/LandingVertex.glsl index 4eafdef1..fd43f260 100644 --- a/src/components/textures/shaders/LandingVertex.glsl +++ b/src/components/textures/shaders/LandingVertex.glsl @@ -1,67 +1,57 @@ +precision highp float; + uniform float uSphereMix; uniform float uCubeMix; uniform float uPlaneMix; uniform float uRandomMix; -uniform float uSize; -uniform float uTime; uniform float uArrivalProgress; +uniform float uHold; +uniform float uTime; +uniform float uSize; uniform sampler2D cmap; -attribute vec3 aSpherePosition; -attribute vec3 aCubePosition; -attribute vec3 aPlanePosition; -attribute vec3 aSpawnPosition; -attribute vec3 aRandomPosition; -attribute float aDelay; +in vec3 aSpherePosition; +in vec3 aCubePosition; +in vec3 aPlanePosition; +in vec3 aSpawnPosition; +in vec3 aRandomPosition; +in float aDelay; -varying vec3 vColor; +out vec3 vColor; +out float vGreyMix; -// Flow field for organic, fluid-like movement -vec3 flowField(vec3 pos, float time) { - float scale = 0.005; +vec3 flowField(vec3 p, float t) { + float s = 0.4; return vec3( - sin(pos.x * scale + time) * cos(pos.y * scale), - cos(pos.y * scale + time) * sin(pos.z * scale), - sin(pos.z * scale) * cos(pos.x * scale + time) + sin(p.x * s + t) * cos(p.y * s), + cos(p.y * s + t) * sin(p.z * s), + sin(p.z * s) * cos(p.x * s + t) ) * 0.3; } void main() { - // Calculate staggered arrival - each particle arrives at different time - float particleArrival = smoothstep(aDelay, aDelay + 0.3, uArrivalProgress); - - // Linearly interpolate between the four shapes using the mix uniforms - vec3 pos = mix(aSpherePosition, aCubePosition, uCubeMix); - pos = mix(pos, aPlanePosition, uPlaneMix); - pos = mix(pos, aRandomPosition, uRandomMix); - - // Add flow field for fluid, organic movement during transitions - vec3 flow = flowField(pos, uTime); - float transitionAmount = uCubeMix + uPlaneMix + uRandomMix; - float flowStrength = transitionAmount * 0.5; // More flow during transitions - pos += flow * flowStrength; - - // Interpolate from spawn position to morphed position based on arrival - vec3 finalPos = mix(aSpawnPosition, pos, particleArrival); - - // Color calculation - float minBrightness = 0.2; - float maxBrightness = 0.96; - float r = sin(finalPos.z + (uTime * 0.2)); - float g = cos(finalPos.y + (uTime * 0.3)); - float b = cos(finalPos.x + finalPos.y + (uTime * 0.5)); - vColor = vec3(r, g, b); - - float mag = min(((sin(r + g + b) + 1.0) / 2.0), 0.996); - vec4 sampled = texture(cmap, vec2(mag, 0.5)); - vColor = sampled.rgb; - - // Transform position - vec4 modelPosition = modelMatrix * vec4(finalPos, 1.0); - vec4 viewPosition = viewMatrix * modelPosition; - vec4 projectedPosition = projectionMatrix * viewPosition; - gl_Position = projectedPosition; - - // Make points smaller as they are further away (perspective) - gl_PointSize = (15.0 / -viewPosition.z); -} \ No newline at end of file + float arrival = smoothstep(aDelay, aDelay + 0.3, uArrivalProgress); + + vec3 shape = mix(aSpherePosition, aCubePosition, uCubeMix); + shape = mix(shape, aPlanePosition, uPlaneMix); + shape = mix(shape, aRandomPosition, uRandomMix); + + float transition = uCubeMix + uPlaneMix + uRandomMix; + shape += flowField(shape, uTime) * transition * 0.5; + + vec3 pos = mix(aSpawnPosition, shape, arrival); + + float dist = distance(pos, shape); + float farMask = smoothstep(0.3, 0.8, dist); + vGreyMix = farMask * uHold; + + float signal = sin(pos.x + pos.y + pos.z + uTime * 0.3); + float mag = clamp((signal + 1.0) * 0.5, 0.0, 0.996); + vColor = texture(cmap, vec2(mag, 0.5)).rgb; + + vec4 mv = viewMatrix * modelMatrix * vec4(pos, 1.0); + gl_Position = projectionMatrix * mv; + + float sizeScale = mix(1.0, 0.65, vGreyMix); + gl_PointSize = clamp((uSize * sizeScale) / -mv.z, 2.0, 20.0); +} From 4374bf78520b0a46cd94b2a2ef56e489494f4b58 Mon Sep 17 00:00:00 2001 From: Lazaro Alonso Date: Fri, 16 Jan 2026 00:35:25 +0100 Subject: [PATCH 5/8] shader --- .../textures/shaders/LandingFrag.glsl | 5 ---- .../textures/shaders/LandingVertex.glsl | 25 ++++++++++++++++--- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/src/components/textures/shaders/LandingFrag.glsl b/src/components/textures/shaders/LandingFrag.glsl index dec8390b..8c56c49b 100644 --- a/src/components/textures/shaders/LandingFrag.glsl +++ b/src/components/textures/shaders/LandingFrag.glsl @@ -6,11 +6,6 @@ in float vGreyMix; out vec4 Color; void main() { - // circular points - vec2 uv = gl_PointCoord - 0.5; - float d = length(uv); - if (d > 0.5) discard; - vec3 silver = vec3(0.75); vec3 finalColor = mix(vColor, silver, vGreyMix); diff --git a/src/components/textures/shaders/LandingVertex.glsl b/src/components/textures/shaders/LandingVertex.glsl index fd43f260..dd67aaca 100644 --- a/src/components/textures/shaders/LandingVertex.glsl +++ b/src/components/textures/shaders/LandingVertex.glsl @@ -20,6 +20,7 @@ in float aDelay; out vec3 vColor; out float vGreyMix; +// Organic flow field (active only during transitions) vec3 flowField(vec3 p, float t) { float s = 0.4; return vec3( @@ -30,28 +31,44 @@ vec3 flowField(vec3 p, float t) { } void main() { + /* ---------------- Arrival (staggered) ---------------- */ float arrival = smoothstep(aDelay, aDelay + 0.3, uArrivalProgress); + /* ---------------- Active shape ---------------- */ vec3 shape = mix(aSpherePosition, aCubePosition, uCubeMix); shape = mix(shape, aPlanePosition, uPlaneMix); shape = mix(shape, aRandomPosition, uRandomMix); + /* ---------------- Flow during transitions ---------------- */ float transition = uCubeMix + uPlaneMix + uRandomMix; shape += flowField(shape, uTime) * transition * 0.5; + /* ---------------- Spawn → shape ---------------- */ vec3 pos = mix(aSpawnPosition, shape, arrival); + /* ---------------- Distance-based silvering ---------------- */ float dist = distance(pos, shape); - float farMask = smoothstep(0.3, 0.8, dist); - vGreyMix = farMask * uHold; + // How far is "far" + float farMask = smoothstep(0.35, 0.9, dist); + + // Smooth hold transition (prevents popping) + float holdEase = smoothstep(0.0, 1.0, uHold); + + // Final silver influence + vGreyMix = farMask * holdEase; + + /* ---------------- Color signal → colormap ---------------- */ float signal = sin(pos.x + pos.y + pos.z + uTime * 0.3); float mag = clamp((signal + 1.0) * 0.5, 0.0, 0.996); vColor = texture(cmap, vec2(mag, 0.5)).rgb; + /* ---------------- Transform ---------------- */ vec4 mv = viewMatrix * modelMatrix * vec4(pos, 1.0); gl_Position = projectionMatrix * mv; - float sizeScale = mix(1.0, 0.65, vGreyMix); - gl_PointSize = clamp((uSize * sizeScale) / -mv.z, 2.0, 20.0); + /* ---------------- Point size (smooth + smaller when silver) ---------------- */ + float sizeScale = mix(1.0, 0.5, vGreyMix); + float size = (uSize * sizeScale) / -mv.z; + gl_PointSize = clamp(size, 1.5, 20.0); } From f3b4000950bea0d2c8992d7bf7075a4a0e9a5679 Mon Sep 17 00:00:00 2001 From: Lazaro Alonso Date: Fri, 16 Jan 2026 00:45:00 +0100 Subject: [PATCH 6/8] silver --- .../textures/shaders/LandingFrag.glsl | 4 +- .../textures/shaders/LandingVertex.glsl | 61 +++++++++++-------- 2 files changed, 36 insertions(+), 29 deletions(-) diff --git a/src/components/textures/shaders/LandingFrag.glsl b/src/components/textures/shaders/LandingFrag.glsl index 8c56c49b..69d0400e 100644 --- a/src/components/textures/shaders/LandingFrag.glsl +++ b/src/components/textures/shaders/LandingFrag.glsl @@ -2,12 +2,12 @@ precision highp float; in vec3 vColor; in float vGreyMix; +in float vAlpha; out vec4 Color; void main() { vec3 silver = vec3(0.75); vec3 finalColor = mix(vColor, silver, vGreyMix); - - Color = vec4(finalColor, 0.8); + Color = vec4(finalColor, vAlpha); } diff --git a/src/components/textures/shaders/LandingVertex.glsl b/src/components/textures/shaders/LandingVertex.glsl index dd67aaca..2ba7fd57 100644 --- a/src/components/textures/shaders/LandingVertex.glsl +++ b/src/components/textures/shaders/LandingVertex.glsl @@ -1,5 +1,4 @@ precision highp float; - uniform float uSphereMix; uniform float uCubeMix; uniform float uPlaneMix; @@ -9,18 +8,17 @@ uniform float uHold; uniform float uTime; uniform float uSize; uniform sampler2D cmap; - in vec3 aSpherePosition; in vec3 aCubePosition; in vec3 aPlanePosition; in vec3 aSpawnPosition; in vec3 aRandomPosition; in float aDelay; - out vec3 vColor; out float vGreyMix; +out float vAlpha; -// Organic flow field (active only during transitions) +// Organic flow field (only during transitions) vec3 flowField(vec3 p, float t) { float s = 0.4; return vec3( @@ -31,44 +29,53 @@ vec3 flowField(vec3 p, float t) { } void main() { - /* ---------------- Arrival (staggered) ---------------- */ + /* ---------------- Arrival ---------------- */ float arrival = smoothstep(aDelay, aDelay + 0.3, uArrivalProgress); - + /* ---------------- Active shape ---------------- */ vec3 shape = mix(aSpherePosition, aCubePosition, uCubeMix); shape = mix(shape, aPlanePosition, uPlaneMix); shape = mix(shape, aRandomPosition, uRandomMix); - - /* ---------------- Flow during transitions ---------------- */ + + /* ---------------- Flow during morphs ---------------- */ float transition = uCubeMix + uPlaneMix + uRandomMix; shape += flowField(shape, uTime) * transition * 0.5; - + /* ---------------- Spawn → shape ---------------- */ vec3 pos = mix(aSpawnPosition, shape, arrival); - - /* ---------------- Distance-based silvering ---------------- */ + + /* ---------------- Distance logic ---------------- */ float dist = distance(pos, shape); - - // How far is "far" + // Distance influence (soft) float farMask = smoothstep(0.35, 0.9, dist); - - // Smooth hold transition (prevents popping) - float holdEase = smoothstep(0.0, 1.0, uHold); - - // Final silver influence - vGreyMix = farMask * holdEase; - - /* ---------------- Color signal → colormap ---------------- */ + + // Staggered hold easing - each particle transitions at different time + float holdOffset = aDelay * 0.5; // Use half the delay range for smoother stagger + float particleHold = smoothstep(holdOffset, holdOffset + 0.5, uHold); + + // Silver influence + vGreyMix = farMask * particleHold; + + /* ---------------- Subtle shimmer for near points during hold ---------------- */ + float shimmer = sin(uTime * 1.8 + pos.x * 3.0 + pos.y * 2.0) * 0.04; + float shimmerMask = (1.0 - farMask) * particleHold; + + /* ---------------- Color → colormap ---------------- */ float signal = sin(pos.x + pos.y + pos.z + uTime * 0.3); - float mag = clamp((signal + 1.0) * 0.5, 0.0, 0.996); + float mag = clamp((signal + 1.0) * 0.5 + shimmer * shimmerMask, 0.0, 0.996); vColor = texture(cmap, vec2(mag, 0.5)).rgb; - + + /* ---------------- Alpha polish ---------------- */ + // Silver points slightly more transparent + vAlpha = mix(0.85, 0.55, pow(vGreyMix, 1.2)); + /* ---------------- Transform ---------------- */ vec4 mv = viewMatrix * modelMatrix * vec4(pos, 1.0); gl_Position = projectionMatrix * mv; - - /* ---------------- Point size (smooth + smaller when silver) ---------------- */ - float sizeScale = mix(1.0, 0.5, vGreyMix); + + /* ---------------- Point size (nonlinear shrink) ---------------- */ + float shrink = pow(vGreyMix, 1.4); + float sizeScale = mix(1.0, 0.4, shrink); float size = (uSize * sizeScale) / -mv.z; gl_PointSize = clamp(size, 1.5, 20.0); -} +} \ No newline at end of file From f25c3141345359a695c1cddada93b2a7ebec0d58 Mon Sep 17 00:00:00 2001 From: Lazaro Alonso Date: Fri, 16 Jan 2026 07:05:31 +0100 Subject: [PATCH 7/8] types --- src/components/plots/MorphingPoints.tsx | 103 +++++++++++++++--------- 1 file changed, 63 insertions(+), 40 deletions(-) diff --git a/src/components/plots/MorphingPoints.tsx b/src/components/plots/MorphingPoints.tsx index 3414042b..cb434c17 100644 --- a/src/components/plots/MorphingPoints.tsx +++ b/src/components/plots/MorphingPoints.tsx @@ -13,8 +13,15 @@ import { useShallow } from "zustand/shallow"; const COUNT = 15625; +// Seeded random number generator for deterministic results +const seededRandom = (seed: number) => { + const x = Math.sin(seed++) * 10000; + return x - Math.floor(x); +}; + const MorphingPoints = () => { const pointsRef = useRef(null); + const materialRef = useRef(null); const { gl } = useThree(); const { setMaxTextureSize, setMax3DTextureSize } = usePlotStore( @@ -26,9 +33,13 @@ const MorphingPoints = () => { useEffect(() => { const ctx = gl.getContext(); + setMaxTextureSize(ctx.getParameter(ctx.MAX_TEXTURE_SIZE)); - if (gl.capabilities.isWebGL2) { - setMax3DTextureSize(ctx.getParameter(ctx.MAX_3D_TEXTURE_SIZE)); + + if (ctx instanceof WebGL2RenderingContext) { + setMax3DTextureSize( + ctx.getParameter(ctx.MAX_3D_TEXTURE_SIZE) + ); } }, [gl, setMaxTextureSize, setMax3DTextureSize]); @@ -36,7 +47,7 @@ const MorphingPoints = () => { useShallow((s) => ({ colormap: s.colormap })) ); - // Geometry data + // Geometry data - use seeded random for deterministic results const { spherePositions, cubePositions, @@ -61,15 +72,16 @@ const MorphingPoints = () => { sphere[i * 3 + 1] = y * 1.2; sphere[i * 3 + 2] = Math.sin(t) * r * 1.2; - const a = Math.random() * Math.PI * 2; - const b = Math.random() * Math.PI; - const d = 4 + Math.random() * 2; + // Use seeded random for deterministic spawn positions + const a = seededRandom(i * 3) * Math.PI * 2; + const b = seededRandom(i * 3 + 1) * Math.PI; + const d = 4 + seededRandom(i * 3 + 2) * 2; spawn[i * 3 + 0] = Math.sin(b) * Math.cos(a) * d; spawn[i * 3 + 1] = Math.sin(b) * Math.sin(a) * d; spawn[i * 3 + 2] = Math.cos(b) * d; - delays[i] = Math.random(); + delays[i] = seededRandom(i * 5); } // Cube @@ -104,36 +116,33 @@ const MorphingPoints = () => { }; }, []); - const material = useMemo( - () => - new THREE.ShaderMaterial({ - glslVersion: THREE.GLSL3, - transparent: true, - depthWrite: false, - blending: THREE.AdditiveBlending, - uniforms: { - uSphereMix: { value: 0 }, - uCubeMix: { value: 0 }, - uPlaneMix: { value: 0 }, - uRandomMix: { value: 0 }, - uArrivalProgress: { value: 0 }, - uHold: { value: 0 }, - uTime: { value: 0 }, - uSize: { value: 15 }, - cmap: { value: colormap }, - }, - vertexShader, - fragmentShader, - }), - [] + const initialUniforms = useMemo( + () => ({ + uSphereMix: { value: 0 }, + uCubeMix: { value: 0 }, + uPlaneMix: { value: 0 }, + uRandomMix: { value: 0 }, + uArrivalProgress: { value: 0 }, + uHold: { value: 0 }, + uTime: { value: 0 }, + uSize: { value: 15 }, + cmap: { value: colormap }, + }), + [colormap] ); + // Update colormap when it changes useEffect(() => { - material.uniforms.cmap.value = colormap; - }, [colormap, material]); + if (materialRef.current) { + materialRef.current.uniforms.cmap.value = colormap; + } + }, [colormap]); + // Animation timeline useEffect(() => { - const u = material.uniforms; + if (!materialRef.current) return; + + const u = materialRef.current.uniforms; const tl = gsap.timeline({ repeat: -1 }); const arrive = 4.5; @@ -152,21 +161,21 @@ const MorphingPoints = () => { addHold(); tl.to([u.uSphereMix, u.uCubeMix], { - value: (i) => (i === 0 ? 0 : 1), + value: (i: number) => (i === 0 ? 0 : 1), duration: morph, }); addHold(); tl.to([u.uCubeMix, u.uPlaneMix], { - value: (i) => (i === 0 ? 0 : 1), + value: (i: number) => (i === 0 ? 0 : 1), duration: morph, }); addHold(); tl.to([u.uPlaneMix, u.uRandomMix], { - value: (i) => (i === 0 ? 0 : 1), + value: (i: number) => (i === 0 ? 0 : 1), duration: morph, }); @@ -175,11 +184,15 @@ const MorphingPoints = () => { tl.to(u.uArrivalProgress, { value: 0, duration: morph }); tl.set([u.uRandomMix, u.uSphereMix], { value: 0 }); - return () => tl.kill(); - }, [material]); + return () => { + tl.kill(); + }; + }, []); useFrame((state) => { - material.uniforms.uTime.value = state.clock.getElapsedTime(); + if (materialRef.current) { + materialRef.current.uniforms.uTime.value = state.clock.getElapsedTime(); + } if (pointsRef.current) { pointsRef.current.rotation.y += 0.0002; pointsRef.current.rotation.x += 0.0001; @@ -187,7 +200,7 @@ const MorphingPoints = () => { }); return ( - + @@ -197,6 +210,16 @@ const MorphingPoints = () => { + ); }; @@ -208,4 +231,4 @@ export const LandingShapes = () => ( -); +); \ No newline at end of file From a4ac86b680e554e98b355e06b32daadc963e6a7a Mon Sep 17 00:00:00 2001 From: Lazaro Alonso Date: Fri, 16 Jan 2026 07:08:56 +0100 Subject: [PATCH 8/8] hover --- src/components/plots/MorphingPoints.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/plots/MorphingPoints.tsx b/src/components/plots/MorphingPoints.tsx index cb434c17..5851e18d 100644 --- a/src/components/plots/MorphingPoints.tsx +++ b/src/components/plots/MorphingPoints.tsx @@ -126,15 +126,16 @@ const MorphingPoints = () => { uHold: { value: 0 }, uTime: { value: 0 }, uSize: { value: 15 }, - cmap: { value: colormap }, + cmap: { value: null }, }), - [colormap] + [] ); // Update colormap when it changes useEffect(() => { if (materialRef.current) { materialRef.current.uniforms.cmap.value = colormap; + materialRef.current.needsUpdate = true; } }, [colormap]);