{
	"$schema": "https://ui.shadcn.com/schema/registry-item.json",
	"name": "Beams-JS-TW",
	"title": "Beams",
	"description": "Crossing animated ribbons with customizable properties.",
	"type": "registry:component",
	"files": [
		{
			"type": "registry:component",
			"path": "Beams/Beams.jsx",
			"content": "/* eslint-disable react/no-unknown-property */\nimport { forwardRef, useImperativeHandle, useEffect, useRef, useMemo } from 'react';\n\nimport * as THREE from 'three';\n\nimport { Canvas, useFrame } from '@react-three/fiber';\nimport { PerspectiveCamera } from '@react-three/drei';\nimport { degToRad } from 'three/src/math/MathUtils.js';\n\nfunction extendMaterial(BaseMaterial, cfg) {\n  const physical = THREE.ShaderLib.physical;\n  const { vertexShader: baseVert, fragmentShader: baseFrag, uniforms: baseUniforms } = physical;\n  const baseDefines = physical.defines ?? {};\n\n  const uniforms = THREE.UniformsUtils.clone(baseUniforms);\n\n  const defaults = new BaseMaterial(cfg.material || {});\n\n  if (defaults.color) uniforms.diffuse.value = defaults.color;\n  if ('roughness' in defaults) uniforms.roughness.value = defaults.roughness;\n  if ('metalness' in defaults) uniforms.metalness.value = defaults.metalness;\n  if ('envMap' in defaults) uniforms.envMap.value = defaults.envMap;\n  if ('envMapIntensity' in defaults) uniforms.envMapIntensity.value = defaults.envMapIntensity;\n\n  Object.entries(cfg.uniforms ?? {}).forEach(([key, u]) => {\n    uniforms[key] = u !== null && typeof u === 'object' && 'value' in u ? u : { value: u };\n  });\n\n  let vert = `${cfg.header}\\n${cfg.vertexHeader ?? ''}\\n${baseVert}`;\n  let frag = `${cfg.header}\\n${cfg.fragmentHeader ?? ''}\\n${baseFrag}`;\n\n  for (const [inc, code] of Object.entries(cfg.vertex ?? {})) {\n    vert = vert.replace(inc, `${inc}\\n${code}`);\n  }\n  for (const [inc, code] of Object.entries(cfg.fragment ?? {})) {\n    frag = frag.replace(inc, `${inc}\\n${code}`);\n  }\n\n  const mat = new THREE.ShaderMaterial({\n    defines: { ...baseDefines },\n    uniforms,\n    vertexShader: vert,\n    fragmentShader: frag,\n    lights: true,\n    fog: !!cfg.material?.fog\n  });\n\n  return mat;\n}\n\nconst CanvasWrapper = ({ children }) => (\n  <Canvas dpr={[1, 2]} frameloop=\"always\" className=\"w-full h-full relative\">\n    {children}\n  </Canvas>\n);\n\nconst hexToNormalizedRGB = hex => {\n  const clean = hex.replace('#', '');\n  const r = parseInt(clean.substring(0, 2), 16);\n  const g = parseInt(clean.substring(2, 4), 16);\n  const b = parseInt(clean.substring(4, 6), 16);\n  return [r / 255, g / 255, b / 255];\n};\n\nconst noise = `\nfloat random (in vec2 st) {\n    return fract(sin(dot(st.xy,\n                         vec2(12.9898,78.233)))*\n        43758.5453123);\n}\nfloat noise (in vec2 st) {\n    vec2 i = floor(st);\n    vec2 f = fract(st);\n    float a = random(i);\n    float b = random(i + vec2(1.0, 0.0));\n    float c = random(i + vec2(0.0, 1.0));\n    float d = random(i + vec2(1.0, 1.0));\n    vec2 u = f * f * (3.0 - 2.0 * f);\n    return mix(a, b, u.x) +\n           (c - a)* u.y * (1.0 - u.x) +\n           (d - b) * u.x * u.y;\n}\nvec4 permute(vec4 x){return mod(((x*34.0)+1.0)*x, 289.0);}\nvec4 taylorInvSqrt(vec4 r){return 1.79284291400159 - 0.85373472095314 * r;}\nvec3 fade(vec3 t) {return t*t*t*(t*(t*6.0-15.0)+10.0);}\nfloat cnoise(vec3 P){\n  vec3 Pi0 = floor(P);\n  vec3 Pi1 = Pi0 + vec3(1.0);\n  Pi0 = mod(Pi0, 289.0);\n  Pi1 = mod(Pi1, 289.0);\n  vec3 Pf0 = fract(P);\n  vec3 Pf1 = Pf0 - vec3(1.0);\n  vec4 ix = vec4(Pi0.x, Pi1.x, Pi0.x, Pi1.x);\n  vec4 iy = vec4(Pi0.yy, Pi1.yy);\n  vec4 iz0 = Pi0.zzzz;\n  vec4 iz1 = Pi1.zzzz;\n  vec4 ixy = permute(permute(ix) + iy);\n  vec4 ixy0 = permute(ixy + iz0);\n  vec4 ixy1 = permute(ixy + iz1);\n  vec4 gx0 = ixy0 / 7.0;\n  vec4 gy0 = fract(floor(gx0) / 7.0) - 0.5;\n  gx0 = fract(gx0);\n  vec4 gz0 = vec4(0.5) - abs(gx0) - abs(gy0);\n  vec4 sz0 = step(gz0, vec4(0.0));\n  gx0 -= sz0 * (step(0.0, gx0) - 0.5);\n  gy0 -= sz0 * (step(0.0, gy0) - 0.5);\n  vec4 gx1 = ixy1 / 7.0;\n  vec4 gy1 = fract(floor(gx1) / 7.0) - 0.5;\n  gx1 = fract(gx1);\n  vec4 gz1 = vec4(0.5) - abs(gx1) - abs(gy1);\n  vec4 sz1 = step(gz1, vec4(0.0));\n  gx1 -= sz1 * (step(0.0, gx1) - 0.5);\n  gy1 -= sz1 * (step(0.0, gy1) - 0.5);\n  vec3 g000 = vec3(gx0.x,gy0.x,gz0.x);\n  vec3 g100 = vec3(gx0.y,gy0.y,gz0.y);\n  vec3 g010 = vec3(gx0.z,gy0.z,gz0.z);\n  vec3 g110 = vec3(gx0.w,gy0.w,gz0.w);\n  vec3 g001 = vec3(gx1.x,gy1.x,gz1.x);\n  vec3 g101 = vec3(gx1.y,gy1.y,gz1.y);\n  vec3 g011 = vec3(gx1.z,gy1.z,gz1.z);\n  vec3 g111 = vec3(gx1.w,gy1.w,gz1.w);\n  vec4 norm0 = taylorInvSqrt(vec4(dot(g000,g000),dot(g010,g010),dot(g100,g100),dot(g110,g110)));\n  g000 *= norm0.x; g010 *= norm0.y; g100 *= norm0.z; g110 *= norm0.w;\n  vec4 norm1 = taylorInvSqrt(vec4(dot(g001,g001),dot(g011,g011),dot(g101,g101),dot(g111,g111)));\n  g001 *= norm1.x; g011 *= norm1.y; g101 *= norm1.z; g111 *= norm1.w;\n  float n000 = dot(g000, Pf0);\n  float n100 = dot(g100, vec3(Pf1.x,Pf0.yz));\n  float n010 = dot(g010, vec3(Pf0.x,Pf1.y,Pf0.z));\n  float n110 = dot(g110, vec3(Pf1.xy,Pf0.z));\n  float n001 = dot(g001, vec3(Pf0.xy,Pf1.z));\n  float n101 = dot(g101, vec3(Pf1.x,Pf0.y,Pf1.z));\n  float n011 = dot(g011, vec3(Pf0.x,Pf1.yz));\n  float n111 = dot(g111, Pf1);\n  vec3 fade_xyz = fade(Pf0);\n  vec4 n_z = mix(vec4(n000,n100,n010,n110),vec4(n001,n101,n011,n111),fade_xyz.z);\n  vec2 n_yz = mix(n_z.xy,n_z.zw,fade_xyz.y);\n  float n_xyz = mix(n_yz.x,n_yz.y,fade_xyz.x);\n  return 2.2 * n_xyz;\n}\n`;\n\nconst Beams = ({\n  beamWidth = 2,\n  beamHeight = 15,\n  beamNumber = 12,\n  lightColor = '#ffffff',\n  speed = 2,\n  noiseIntensity = 1.75,\n  scale = 0.2,\n  rotation = 0\n}) => {\n  const meshRef = useRef(null);\n  const beamMaterial = useMemo(\n    () =>\n      extendMaterial(THREE.MeshStandardMaterial, {\n        header: `\n  varying vec3 vEye;\n  varying float vNoise;\n  varying vec2 vUv;\n  varying vec3 vPosition;\n  uniform float time;\n  uniform float uSpeed;\n  uniform float uNoiseIntensity;\n  uniform float uScale;\n  ${noise}`,\n        vertexHeader: `\n  float getPos(vec3 pos) {\n    vec3 noisePos =\n      vec3(pos.x * 0., pos.y - uv.y, pos.z + time * uSpeed * 3.) * uScale;\n    return cnoise(noisePos);\n  }\n  vec3 getCurrentPos(vec3 pos) {\n    vec3 newpos = pos;\n    newpos.z += getPos(pos);\n    return newpos;\n  }\n  vec3 getNormal(vec3 pos) {\n    vec3 curpos = getCurrentPos(pos);\n    vec3 nextposX = getCurrentPos(pos + vec3(0.01, 0.0, 0.0));\n    vec3 nextposZ = getCurrentPos(pos + vec3(0.0, -0.01, 0.0));\n    vec3 tangentX = normalize(nextposX - curpos);\n    vec3 tangentZ = normalize(nextposZ - curpos);\n    return normalize(cross(tangentZ, tangentX));\n  }`,\n        fragmentHeader: '',\n        vertex: {\n          '#include <begin_vertex>': `transformed.z += getPos(transformed.xyz);`,\n          '#include <beginnormal_vertex>': `objectNormal = getNormal(position.xyz);`\n        },\n        fragment: {\n          '#include <dithering_fragment>': `\n    float randomNoise = noise(gl_FragCoord.xy);\n    gl_FragColor.rgb -= randomNoise / 15. * uNoiseIntensity;`\n        },\n        material: { fog: true },\n        uniforms: {\n          diffuse: new THREE.Color(...hexToNormalizedRGB('#000000')),\n          time: { shared: true, mixed: true, linked: true, value: 0 },\n          roughness: 0.3,\n          metalness: 0.3,\n          uSpeed: { shared: true, mixed: true, linked: true, value: speed },\n          envMapIntensity: 10,\n          uNoiseIntensity: noiseIntensity,\n          uScale: scale\n        }\n      }),\n    [speed, noiseIntensity, scale]\n  );\n\n  return (\n    <CanvasWrapper>\n      <group rotation={[0, 0, degToRad(rotation)]}>\n        <PlaneNoise ref={meshRef} material={beamMaterial} count={beamNumber} width={beamWidth} height={beamHeight} />\n        <DirLight color={lightColor} position={[0, 3, 10]} />\n      </group>\n      <ambientLight intensity={1} />\n      <color attach=\"background\" args={['#000000']} />\n      <PerspectiveCamera makeDefault position={[0, 0, 20]} fov={30} />\n    </CanvasWrapper>\n  );\n};\n\nfunction createStackedPlanesBufferGeometry(n, width, height, spacing, heightSegments) {\n  const geometry = new THREE.BufferGeometry();\n  const numVertices = n * (heightSegments + 1) * 2;\n  const numFaces = n * heightSegments * 2;\n  const positions = new Float32Array(numVertices * 3);\n  const indices = new Uint32Array(numFaces * 3);\n  const uvs = new Float32Array(numVertices * 2);\n\n  let vertexOffset = 0;\n  let indexOffset = 0;\n  let uvOffset = 0;\n  const totalWidth = n * width + (n - 1) * spacing;\n  const xOffsetBase = -totalWidth / 2;\n\n  for (let i = 0; i < n; i++) {\n    const xOffset = xOffsetBase + i * (width + spacing);\n    const uvXOffset = Math.random() * 300;\n    const uvYOffset = Math.random() * 300;\n\n    for (let j = 0; j <= heightSegments; j++) {\n      const y = height * (j / heightSegments - 0.5);\n      const v0 = [xOffset, y, 0];\n      const v1 = [xOffset + width, y, 0];\n      positions.set([...v0, ...v1], vertexOffset * 3);\n\n      const uvY = j / heightSegments;\n      uvs.set([uvXOffset, uvY + uvYOffset, uvXOffset + 1, uvY + uvYOffset], uvOffset);\n\n      if (j < heightSegments) {\n        const a = vertexOffset,\n          b = vertexOffset + 1,\n          c = vertexOffset + 2,\n          d = vertexOffset + 3;\n        indices.set([a, b, c, c, b, d], indexOffset);\n        indexOffset += 6;\n      }\n      vertexOffset += 2;\n      uvOffset += 4;\n    }\n  }\n\n  geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));\n  geometry.setAttribute('uv', new THREE.BufferAttribute(uvs, 2));\n  geometry.setIndex(new THREE.BufferAttribute(indices, 1));\n  geometry.computeVertexNormals();\n  return geometry;\n}\n\nconst MergedPlanes = forwardRef(({ material, width, count, height }, ref) => {\n  const mesh = useRef(null);\n  useImperativeHandle(ref, () => mesh.current);\n  const geometry = useMemo(\n    () => createStackedPlanesBufferGeometry(count, width, height, 0, 100),\n    [count, width, height]\n  );\n  useFrame((_, delta) => {\n    mesh.current.material.uniforms.time.value += 0.1 * delta;\n  });\n  return <mesh ref={mesh} geometry={geometry} material={material} />;\n});\nMergedPlanes.displayName = 'MergedPlanes';\n\nconst PlaneNoise = forwardRef((props, ref) => (\n  <MergedPlanes ref={ref} material={props.material} width={props.width} count={props.count} height={props.height} />\n));\nPlaneNoise.displayName = 'PlaneNoise';\n\nconst DirLight = ({ position, color }) => {\n  const dir = useRef(null);\n  useEffect(() => {\n    if (!dir.current) return;\n    const cam = dir.current.shadow.camera;\n    if (!cam) return;\n    cam.top = 24;\n    cam.bottom = -24;\n    cam.left = -24;\n    cam.right = 24;\n    cam.far = 64;\n    dir.current.shadow.bias = -0.004;\n  }, []);\n  return <directionalLight ref={dir} color={color} intensity={1} position={position} />;\n};\n\nexport default Beams;\n"
		}
	],
	"registryDependencies": [],
	"dependencies": [
		"three@^0.180.0",
		"@react-three/fiber@^9.3.0",
		"@react-three/drei@^10.7.4"
	]
}