{
  "name": "curvedwall",
  "title": "CurvedWall",
  "type": "registry:component",
  "description": "An arc of wall around the origin — solid concrete or a floor-to-ceiling glass curtain wall with mullions. Compose several arcs to leave door gaps; chords carry cuboid colliders.",
  "dependencies": [
    "@react-three/rapier@^2.2.0",
    "@runek/core@^0.12.0",
    "three@^0.184.0"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "CurvedWall.tsx",
      "content": "import { CuboidCollider, RigidBody } from '@react-three/rapier'\nimport { useWorld, type WorldComponentProps } from '@runek/core'\nimport { useLayoutEffect, useMemo, useRef } from 'react'\nimport * as THREE from 'three'\n\nexport type CurvedWallStyle = 'solid' | 'glass'\n\nexport interface CurvedWallProps extends WorldComponentProps {\n  /** Arc centerline radius, in units. The component origin is the circle's center. */\n  radius?: number\n  /** Sweep angle in radians, centered on the local +Z axis. */\n  arc?: number\n  height?: number\n  thickness?: number\n  /** `glass` renders a transparent curtain wall with mullions; `solid` a plain wall. */\n  style?: CurvedWallStyle\n  /** Chord segments; defaults from `radius × arc`, more segments reading as smoother. */\n  segments?: number\n  /** Solid wall color; defaults to the world palette's `wall` slot. */\n  color?: string\n  /** Glass tint (glass style). */\n  glassColor?: string\n  /** Mullion and rail color (glass style); defaults to the palette's `metal` slot. */\n  frameColor?: string\n}\n\ninterface Chord {\n  /** Chord midpoint angle from local +Z. */\n  angle: number\n  x: number\n  z: number\n  length: number\n}\n\n/**\n * An arc of wall around the component origin — the curved-architecture counterpart of `Wall`.\n * `solid` reads as concrete; `glass` as a floor-to-ceiling curtain wall with mullions at every\n * chord joint. Leave door gaps by composing several arcs. Chords render instanced and each\n * carries a cuboid collider, so the wall blocks like the curve it draws.\n */\nexport function CurvedWall({\n  position = [0, 0, 0],\n  rotation = [0, 0, 0],\n  radius = 6,\n  arc = Math.PI / 2,\n  height = 3,\n  thickness = 0.15,\n  style = 'solid',\n  segments,\n  color,\n  glassColor = '#b7d8e8',\n  frameColor,\n}: CurvedWallProps) {\n  const { unit, palette } = useWorld()\n  const wallColor = color ?? palette.wall\n  const frame = frameColor ?? palette.metal\n  const R = radius * unit\n  const h = height * unit\n  const t = thickness * unit\n  const count = segments ?? Math.min(32, Math.max(4, Math.round((R * arc) / (0.9 * unit))))\n  const glass = style === 'glass'\n  const chordsRef = useRef<THREE.InstancedMesh>(null)\n  const mullionsRef = useRef<THREE.InstancedMesh>(null)\n  const railsRef = useRef<THREE.InstancedMesh>(null)\n\n  const { chords, joints } = useMemo(() => {\n    const step = arc / count\n    // A hair of overlap keeps seams closed on the outside of the curve.\n    const chordLength = 2 * R * Math.sin(step / 2) + t * 0.5\n    const chords: Chord[] = []\n    const joints: { angle: number; x: number; z: number }[] = []\n    for (let i = 0; i < count; i++) {\n      const angle = -arc / 2 + (i + 0.5) * step\n      chords.push({ angle, x: Math.sin(angle) * R, z: Math.cos(angle) * R, length: chordLength })\n    }\n    for (let i = 0; i <= count; i++) {\n      const angle = -arc / 2 + i * step\n      joints.push({ angle, x: Math.sin(angle) * R, z: Math.cos(angle) * R })\n    }\n    return { chords, joints }\n  }, [R, arc, count, t])\n\n  const railH = 0.1 * unit\n\n  useLayoutEffect(() => {\n    const mesh = chordsRef.current\n    if (!mesh) return\n    const dummy = new THREE.Object3D()\n    const paneH = glass ? h - railH * 2 : h\n    chords.forEach((c, i) => {\n      dummy.position.set(c.x, glass ? railH + paneH / 2 : h / 2, c.z)\n      dummy.rotation.set(0, c.angle, 0)\n      dummy.scale.set(c.length, paneH, glass ? t * 0.4 : t)\n      dummy.updateMatrix()\n      mesh.setMatrixAt(i, dummy.matrix)\n    })\n    mesh.instanceMatrix.needsUpdate = true\n  }, [chords, h, t, glass, railH])\n\n  useLayoutEffect(() => {\n    const mesh = mullionsRef.current\n    if (!mesh || !glass) return\n    const dummy = new THREE.Object3D()\n    joints.forEach((j, i) => {\n      dummy.position.set(j.x, h / 2, j.z)\n      dummy.rotation.set(0, j.angle, 0)\n      dummy.scale.set(t * 0.8, h, t * 1.2)\n      dummy.updateMatrix()\n      mesh.setMatrixAt(i, dummy.matrix)\n    })\n    mesh.instanceMatrix.needsUpdate = true\n  }, [joints, h, t, glass])\n\n  useLayoutEffect(() => {\n    const mesh = railsRef.current\n    if (!mesh || !glass) return\n    const dummy = new THREE.Object3D()\n    chords.forEach((c, i) => {\n      for (let level = 0; level < 2; level++) {\n        dummy.position.set(c.x, level === 0 ? railH / 2 : h - railH / 2, c.z)\n        dummy.rotation.set(0, c.angle, 0)\n        dummy.scale.set(c.length, railH, t * 1.1)\n        dummy.updateMatrix()\n        mesh.setMatrixAt(i * 2 + level, dummy.matrix)\n      }\n    })\n    mesh.instanceMatrix.needsUpdate = true\n  }, [chords, h, t, glass, railH])\n\n  return (\n    <RigidBody type=\"fixed\" colliders={false} position={position} rotation={rotation}>\n      {chords.map((c) => (\n        <CuboidCollider\n          key={c.angle.toFixed(4)}\n          args={[c.length / 2, h / 2, t / 2]}\n          position={[c.x, h / 2, c.z]}\n          rotation={[0, c.angle, 0]}\n        />\n      ))}\n      <instancedMesh\n        key={`c${count}${style}`}\n        ref={chordsRef}\n        args={[undefined, undefined, count]}\n        castShadow={!glass}\n        receiveShadow\n      >\n        <boxGeometry />\n        {glass ? (\n          <meshStandardMaterial color={glassColor} transparent opacity={0.28} roughness={0.08} />\n        ) : (\n          <meshStandardMaterial color={wallColor} />\n        )}\n      </instancedMesh>\n      {glass && (\n        <>\n          <instancedMesh\n            key={`m${count}`}\n            ref={mullionsRef}\n            args={[undefined, undefined, count + 1]}\n            castShadow\n          >\n            <boxGeometry />\n            <meshStandardMaterial color={frame} roughness={0.6} />\n          </instancedMesh>\n          <instancedMesh\n            key={`r${count}`}\n            ref={railsRef}\n            args={[undefined, undefined, count * 2]}\n            castShadow\n          >\n            <boxGeometry />\n            <meshStandardMaterial color={frame} roughness={0.6} />\n          </instancedMesh>\n        </>\n      )}\n    </RigidBody>\n  )\n}\n",
      "type": "registry:component"
    }
  ]
}
