tinker / app /components /three /FloatingTools.tsx
truegleai's picture
feat: full cloud-ready deployment (SQLite + Ollama cloud + integrated WebSocket)
f31cfe8 verified
"use client";
import { useRef, useMemo } from "react";
import { Canvas, useFrame } from "@react-three/fiber";
import { Float, MeshDistortMaterial, Environment } from "@react-three/drei";
import * as THREE from "three";
function FloatingWrench({
position,
scale,
speed,
color,
}: {
position: [number, number, number];
scale: number;
speed: number;
color: string;
}) {
const meshRef = useRef<THREE.Group>(null);
useFrame((state) => {
if (meshRef.current) {
meshRef.current.rotation.x = Math.sin(state.clock.elapsedTime * speed) * 0.3;
meshRef.current.rotation.y = state.clock.elapsedTime * speed * 0.5;
meshRef.current.rotation.z = Math.cos(state.clock.elapsedTime * speed) * 0.2;
}
});
return (
<Float speed={2} rotationIntensity={0.5} floatIntensity={1}>
<group ref={meshRef} position={position} scale={scale}>
{/* Wrench body */}
<mesh position={[0, 0, 0]}>
<boxGeometry args={[0.15, 1.2, 0.08]} />
<MeshDistortMaterial
color={color}
distort={0.15}
speed={2}
metalness={0.8}
roughness={0.2}
/>
</mesh>
{/* Wrench head */}
<mesh position={[0, 0.65, 0]}>
<torusGeometry args={[0.2, 0.06, 8, 16, Math.PI * 1.2]} />
<MeshDistortMaterial
color={color}
distort={0.1}
speed={1.5}
metalness={0.8}
roughness={0.2}
/>
</mesh>
{/* Wrench ring */}
<mesh position={[0, -0.6, 0]}>
<torusGeometry args={[0.15, 0.05, 8, 16]} />
<MeshDistortMaterial
color={color}
distort={0.1}
speed={1.5}
metalness={0.8}
roughness={0.2}
/>
</mesh>
</group>
</Float>
);
}
function FloatingGear({
position,
scale,
speed,
color,
}: {
position: [number, number, number];
scale: number;
speed: number;
color: string;
}) {
const meshRef = useRef<THREE.Mesh>(null);
useFrame((state) => {
if (meshRef.current) {
meshRef.current.rotation.x = state.clock.elapsedTime * speed * 0.3;
meshRef.current.rotation.z = state.clock.elapsedTime * speed * 0.5;
}
});
return (
<Float speed={1.5} rotationIntensity={0.3} floatIntensity={0.8}>
<mesh ref={meshRef} position={position} scale={scale}>
<torusGeometry args={[0.3, 0.1, 8, 12]} />
<MeshDistortMaterial
color={color}
distort={0.2}
speed={speed}
metalness={0.7}
roughness={0.3}
/>
</mesh>
</Float>
);
}
function FloatingCube({
position,
scale,
speed,
color,
}: {
position: [number, number, number];
scale: number;
speed: number;
color: string;
}) {
const meshRef = useRef<THREE.Mesh>(null);
useFrame((state) => {
if (meshRef.current) {
meshRef.current.rotation.x = state.clock.elapsedTime * speed * 0.4;
meshRef.current.rotation.y = state.clock.elapsedTime * speed * 0.6;
}
});
return (
<Float speed={2.5} rotationIntensity={0.8} floatIntensity={1.2}>
<mesh ref={meshRef} position={position} scale={scale}>
<octahedronGeometry args={[0.3, 0]} />
<MeshDistortMaterial
color={color}
distort={0.3}
speed={speed}
metalness={0.6}
roughness={0.4}
wireframe
/>
</mesh>
</Float>
);
}
function FloatingSphere({
position,
scale,
speed,
color,
}: {
position: [number, number, number];
scale: number;
speed: number;
color: string;
}) {
const meshRef = useRef<THREE.Mesh>(null);
useFrame((state) => {
if (meshRef.current) {
meshRef.current.rotation.y = state.clock.elapsedTime * speed * 0.3;
}
});
return (
<Float speed={1.8} rotationIntensity={0.2} floatIntensity={0.6}>
<mesh ref={meshRef} position={position} scale={scale}>
<sphereGeometry args={[0.25, 16, 16]} />
<MeshDistortMaterial
color={color}
distort={0.4}
speed={speed * 1.5}
metalness={0.5}
roughness={0.5}
/>
</mesh>
</Float>
);
}
function Scene() {
const objects = useMemo(
() => [
{
type: "wrench",
position: [-3, 1, -2] as [number, number, number],
scale: 1.2,
speed: 0.3,
color: "#00F5FF",
},
{
type: "gear",
position: [2, -1, -3] as [number, number, number],
scale: 0.8,
speed: 0.4,
color: "#9945FF",
},
{
type: "cube",
position: [-1, 2, -4] as [number, number, number],
scale: 1,
speed: 0.5,
color: "#14F195",
},
{
type: "wrench",
position: [3, 0, -2] as [number, number, number],
scale: 0.9,
speed: 0.35,
color: "#B87333",
},
{
type: "sphere",
position: [0, -2, -3] as [number, number, number],
scale: 1.1,
speed: 0.25,
color: "#4A5568",
},
{
type: "gear",
position: [-2, -1, -4] as [number, number, number],
scale: 0.6,
speed: 0.45,
color: "#00F5FF",
},
{
type: "cube",
position: [1, 1, -5] as [number, number, number],
scale: 0.7,
speed: 0.55,
color: "#9945FF",
},
{
type: "sphere",
position: [-3, -2, -3] as [number, number, number],
scale: 0.5,
speed: 0.3,
color: "#14F195",
},
{
type: "wrench",
position: [2, 2, -4] as [number, number, number],
scale: 0.7,
speed: 0.4,
color: "#B87333",
},
{
type: "gear",
position: [0, 0, -6] as [number, number, number],
scale: 1.5,
speed: 0.2,
color: "#00F5FF",
},
],
[]
);
return (
<>
<ambientLight intensity={0.3} />
<pointLight position={[10, 10, 10]} intensity={1} color="#00F5FF" />
<pointLight position={[-10, -10, -10]} intensity={0.5} color="#9945FF" />
<pointLight position={[0, 5, 5]} intensity={0.8} color="#14F195" />
{objects.map((obj, index) => {
switch (obj.type) {
case "wrench":
return (
<FloatingWrench
key={index}
position={obj.position}
scale={obj.scale}
speed={obj.speed}
color={obj.color}
/>
);
case "gear":
return (
<FloatingGear
key={index}
position={obj.position}
scale={obj.scale}
speed={obj.speed}
color={obj.color}
/>
);
case "cube":
return (
<FloatingCube
key={index}
position={obj.position}
scale={obj.scale}
speed={obj.speed}
color={obj.color}
/>
);
case "sphere":
return (
<FloatingSphere
key={index}
position={obj.position}
scale={obj.scale}
speed={obj.speed}
color={obj.color}
/>
);
default:
return null;
}
})}
{/* Particles */}
<Particles />
</>
);
}
function Particles() {
const pointsRef = useRef<THREE.Points>(null);
const count = 200;
const positions = useMemo(() => {
const pos = new Float32Array(count * 3);
for (let i = 0; i < count; i++) {
pos[i * 3] = (Math.random() - 0.5) * 20;
pos[i * 3 + 1] = (Math.random() - 0.5) * 20;
pos[i * 3 + 2] = (Math.random() - 0.5) * 20;
}
return pos;
}, []);
useFrame((state) => {
if (pointsRef.current) {
pointsRef.current.rotation.y = state.clock.elapsedTime * 0.02;
pointsRef.current.rotation.x = state.clock.elapsedTime * 0.01;
}
});
return (
<points ref={pointsRef}>
<bufferGeometry>
<bufferAttribute
attach="attributes-position"
args={[positions, 3]}
count={count}
array={positions}
itemSize={3}
/>
</bufferGeometry>
<pointsMaterial
size={0.03}
color="#00F5FF"
transparent
opacity={0.6}
sizeAttenuation
/>
</points>
);
}
export function FloatingTools() {
return (
<Canvas
camera={{ position: [0, 0, 8], fov: 60 }}
style={{ background: "transparent" }}
dpr={[1, 2]}
>
<Scene />
</Canvas>
);
}