{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "animated-grid-pattern",
  "title": "animated-grid-pattern",
  "description": "An animated grid pattern component.",
  "dependencies": [
    "react",
    "three",
    "react-three-fiber"
  ],
  "files": [
    {
      "path": "registry/eldoraui/animated-grid-pattern.tsx",
      "content": "\"use client\"\n\nimport React, { useEffect, useMemo, useRef, useState } from \"react\"\nimport { Canvas, useFrame, useThree } from \"@react-three/fiber\"\nimport { useTheme } from \"next-themes\"\nimport { ExtrudeGeometry, Shape } from \"three\"\nimport * as THREE from \"three\"\n\ninterface GridProps {\n  position: [number, number, number]\n  width?: number\n  length?: number\n  cornerRadius?: number\n  gridPosition: [number, number]\n  hoveredGrid: [number, number] | null\n  rippleScale?: number\n  rippleRadius?: number\n}\n\nconst Grid = ({\n  position,\n  width = 4,\n  length = 4,\n  cornerRadius = 2,\n  gridPosition,\n  hoveredGrid,\n  rippleScale = 0.3,\n  rippleRadius = 3,\n}: GridProps) => {\n  const meshRef = useRef<THREE.Mesh>(null)\n  const [currentScale, setCurrentScale] = useState(1)\n  const { theme } = useTheme()\n\n  const gridColor = theme === \"dark\" ? \"#232323\" : \"#e5e7eb\"\n\n  const geometry = useMemo(() => {\n    const shape = new Shape()\n    const angleStep = Math.PI * 0.5\n    const radius = cornerRadius\n\n    const halfWidth = width / 2\n    const halfLength = length / 2\n\n    shape.absarc(\n      halfWidth - radius,\n      halfLength - radius,\n      radius,\n      angleStep * 0,\n      angleStep * 1\n    )\n    shape.absarc(\n      -halfWidth + radius,\n      halfLength - radius,\n      radius,\n      angleStep * 1,\n      angleStep * 2\n    )\n    shape.absarc(\n      -halfWidth + radius,\n      -halfLength + radius,\n      radius,\n      angleStep * 2,\n      angleStep * 3\n    )\n    shape.absarc(\n      halfWidth - radius,\n      -halfLength + radius,\n      radius,\n      angleStep * 3,\n      angleStep * 4\n    )\n\n    const extrudeSettings = {\n      depth: 0.3,\n      bevelEnabled: true,\n      bevelThickness: 0.05,\n      bevelSize: 0.05,\n      bevelSegments: 20,\n      curveSegments: 20,\n    }\n\n    const geometry = new ExtrudeGeometry(shape, extrudeSettings)\n    geometry.center()\n\n    return geometry\n  }, [width, length, cornerRadius])\n\n  useEffect(() => {\n    return () => {\n      geometry.dispose()\n    }\n  }, [geometry])\n\n  useFrame(() => {\n    if (meshRef.current) {\n      let targetScale = 1\n\n      const isThisGridHovered =\n        hoveredGrid &&\n        gridPosition[0] === hoveredGrid[0] &&\n        gridPosition[1] === hoveredGrid[1]\n\n      if (isThisGridHovered) {\n        targetScale = 5\n      } else if (hoveredGrid) {\n        const dx = gridPosition[0] - hoveredGrid[0]\n        const dz = gridPosition[1] - hoveredGrid[1]\n        const distance = Math.sqrt(dx * dx + dz * dz)\n\n        if (distance <= rippleRadius && distance > 0) {\n          const falloff = Math.max(0, 1 - distance / rippleRadius)\n          const rippleEffect = falloff * rippleScale\n          targetScale = 1 + rippleEffect * 3\n        }\n      }\n\n      const lerpFactor = 0.1\n      const newScale = currentScale + (targetScale - currentScale) * lerpFactor\n      setCurrentScale(newScale)\n\n      meshRef.current.scale.z = newScale\n    }\n  })\n\n  useEffect(() => {\n    if (meshRef.current) {\n      meshRef.current.userData.gridPosition = gridPosition\n    }\n  }, [gridPosition])\n\n  return (\n    <mesh\n      ref={meshRef}\n      geometry={geometry}\n      position={position}\n      rotation={[Math.PI / 2, 0, 0]}\n    >\n      <meshPhysicalMaterial\n        color={gridColor}\n        roughness={0.5}\n        metalness={1}\n        clearcoat={1}\n        clearcoatRoughness={0}\n        clearcoatNormalScale={1}\n        clearcoatNormalMap={null}\n      />\n    </mesh>\n  )\n}\n\nfunction HoverDetector({\n  onHoverChange,\n}: {\n  gridSize: number\n  spacingX: number\n  spacingZ: number\n  onHoverChange: (hoveredGrid: [number, number] | null) => void\n}) {\n  const { camera, raycaster, pointer, scene } = useThree()\n\n  useFrame(() => {\n    raycaster.setFromCamera(pointer, camera)\n\n    const intersects = raycaster.intersectObjects(scene.children, true)\n\n    if (intersects.length > 0) {\n      for (const intersect of intersects) {\n        const mesh = intersect.object\n        if (mesh.userData && mesh.userData.gridPosition) {\n          const gridPos = mesh.userData.gridPosition as [number, number]\n          onHoverChange(gridPos)\n          return\n        }\n      }\n    }\n\n    onHoverChange(null)\n  })\n\n  return null\n}\n\ninterface GridOfGridesProps {\n  gridSize: number\n  gridWidth: number\n  gridLength: number\n  gap: number\n  rippleScale: number\n  rippleRadius: number\n  cornerRadius: number\n}\n\nfunction GridOfGrides({\n  gridSize,\n  gridWidth,\n  gridLength,\n  gap,\n  rippleScale,\n  rippleRadius,\n  cornerRadius,\n}: GridOfGridesProps) {\n  const spacingX = gridWidth + gap\n  const spacingZ = gridLength + gap\n\n  const [hoveredGrid, setHoveredGrid] = useState<[number, number] | null>(null)\n\n  const Grides = []\n\n  for (let x = 0; x < gridSize; x++) {\n    for (let z = 0; z < gridSize; z++) {\n      const posX = (x - (gridSize - 1) / 2) * spacingX\n      const posZ = (z - (gridSize - 1) / 2) * spacingZ\n\n      Grides.push(\n        <Grid\n          key={`${x}-${z}`}\n          position={[posX, -0.85, posZ]}\n          width={gridWidth}\n          length={gridLength}\n          cornerRadius={cornerRadius}\n          gridPosition={[x, z]}\n          hoveredGrid={hoveredGrid}\n          rippleScale={rippleScale}\n          rippleRadius={rippleRadius}\n        />\n      )\n    }\n  }\n\n  return (\n    <>\n      <HoverDetector\n        gridSize={gridSize}\n        spacingX={spacingX}\n        spacingZ={spacingZ}\n        onHoverChange={setHoveredGrid}\n      />\n      {Grides}\n    </>\n  )\n}\n\ninterface AnimatedGridPatternProps {\n  gridSize?: number\n  gridWidth?: number\n  gridLength?: number\n  gap?: number\n  rippleScale?: number\n  rippleRadius?: number\n  cornerRadius?: number\n  cameraPosition?: [number, number, number]\n  cameraRotation?: [number, number, number]\n  fov?: number\n}\n\nexport function AnimatedGridPattern({\n  gridSize = 10,\n  gridWidth = 4,\n  gridLength = 4,\n  gap = 0.05,\n  rippleScale = 2.5,\n  rippleRadius = 2,\n  cornerRadius = 0.8,\n  cameraPosition = [-9.31, 12, 24.72],\n  cameraRotation = [-0.65, -0.2, -0.13],\n  fov = 35,\n}: AnimatedGridPatternProps) {\n  return (\n    <div className=\"relative z-0 h-full w-full bg-gray-50 dark:bg-black\">\n      <Canvas\n        camera={{\n          position: cameraPosition,\n          rotation: cameraRotation,\n          fov: fov,\n        }}\n      >\n        <ambientLight intensity={1} />\n\n        <directionalLight position={[10, 15, 10]} intensity={10} castShadow />\n\n        <directionalLight\n          position={[-10, 10, -5]}\n          intensity={10}\n          color=\"#ffffff\"\n        />\n\n        <directionalLight\n          position={[5, -10, 15]}\n          intensity={5}\n          color=\"#f0f8ff\"\n        />\n\n        <pointLight position={[0, 20, 3]} intensity={2} distance={50} />\n\n        <pointLight\n          position={[15, 5, 15]}\n          intensity={1.5}\n          distance={40}\n          color=\"#ffffff\"\n        />\n\n        <GridOfGrides\n          gridSize={gridSize}\n          gridWidth={gridWidth}\n          gridLength={gridLength}\n          gap={gap}\n          rippleScale={rippleScale}\n          rippleRadius={rippleRadius}\n          cornerRadius={cornerRadius}\n        />\n      </Canvas>\n    </div>\n  )\n}\n",
      "type": "registry:ui"
    }
  ],
  "type": "registry:ui"
}