{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "scribble-circle",
  "title": "Scribble Circle",
  "description": "A brush circle swept around a region of the frame, opening thin and closing thick, with grain breaking up the dry opening.",
  "dependencies": ["remotion"],
  "registryDependencies": ["@remocn/stop-motion", "@remocn/brush"],
  "files": [
    {
      "path": "registry/remocn/scribble-circle/index.tsx",
      "content": "\"use client\";\n\nimport { useCurrentFrame } from \"remotion\";\nimport {\n  BrushGrain,\n  type BrushPoint,\n  brushFilterId,\n  brushReach,\n  brushRibbon,\n} from \"@/components/remocn/brush\";\nimport { DEFAULT_STEP, hashRange, steppedRamp } from \"@/lib/remocn/stop-motion\";\n\nconst SAMPLES_PER_LAP = 48;\nconst NOISE = 0.035;\nconst CENTRE = 0.02;\n\nexport type ScribbleCircleGeometry = {\n  width: number;\n  height: number;\n  strokeWidth: number;\n  laps?: number;\n  pressure?: number;\n  release?: number;\n  grain?: number;\n  seed?: string;\n  points?: number;\n  progress?: number;\n};\n\nexport function scribbleCircleCentre(\n  width: number,\n  height: number,\n  seed = \"scribble\",\n): BrushPoint {\n  return {\n    x: width / 2 + hashRange(`${seed}:cx`, -CENTRE, CENTRE) * width,\n    y: height / 2 + hashRange(`${seed}:cy`, -CENTRE, CENTRE) * height,\n  };\n}\n\nexport function scribbleCirclePoints(args: {\n  width: number;\n  height: number;\n  laps?: number;\n  seed?: string;\n  points?: number;\n}): BrushPoint[] {\n  const laps = args.laps ?? 1.15;\n  const seed = args.seed ?? \"scribble\";\n  const points = args.points ?? SAMPLES_PER_LAP;\n\n  const count = Math.max(2, Math.ceil(laps * points));\n  const rx = args.width / 2;\n  const ry = args.height / 2;\n  const { x: cx, y: cy } = scribbleCircleCentre(args.width, args.height, seed);\n\n  return Array.from({ length: count }, (_, i) => {\n    const angle = laps * Math.PI * 2 * (i / (count - 1));\n    const scale = 1 + hashRange(`${seed}:r${i}`, -NOISE, NOISE);\n    return {\n      x: cx + rx * scale * Math.cos(angle),\n      y: cy + ry * scale * Math.sin(angle),\n    };\n  });\n}\n\nexport function scribbleCirclePath(args: ScribbleCircleGeometry): {\n  d: string;\n  viewBox: string;\n  marginX: number;\n  marginY: number;\n} {\n  const grain = args.grain ?? 1;\n  const reach = brushReach(args.strokeWidth, grain);\n  const marginX = reach + (args.width / 2) * NOISE + args.width * CENTRE;\n  const marginY = reach + (args.height / 2) * NOISE + args.height * CENTRE;\n\n  return {\n    d: brushRibbon(scribbleCirclePoints(args), {\n      strokeWidth: args.strokeWidth,\n      pressure: args.pressure,\n      release: args.release,\n      progress: args.progress,\n    }),\n    viewBox: `${-marginX} ${-marginY} ${args.width + marginX * 2} ${args.height + marginY * 2}`,\n    marginX,\n    marginY,\n  };\n}\n\nexport function scribbleCircleProgress(\n  frame: number,\n  options?: { delay?: number; durationSteps?: number; step?: number },\n): number {\n  const delay = options?.delay ?? 0;\n  const durationSteps = options?.durationSteps ?? 10;\n  const step = options?.step ?? DEFAULT_STEP;\n  return steppedRamp(frame, delay, delay + durationSteps * step, { step });\n}\n\nexport interface ScribbleCircleProps {\n  width: number;\n  height: number;\n  color?: string;\n  strokeWidth?: number;\n  pressure?: number;\n  release?: number;\n  grain?: number;\n  delay?: number;\n  durationSteps?: number;\n  laps?: number;\n  seed?: string;\n  step?: number;\n}\n\nexport function ScribbleCircle({\n  width,\n  height,\n  color = \"#6f7f35\",\n  strokeWidth = 14,\n  pressure = 0.2,\n  release = 1,\n  grain = 1,\n  delay = 0,\n  durationSteps = 10,\n  laps = 1.15,\n  seed = \"scribble\",\n  step = DEFAULT_STEP,\n}: ScribbleCircleProps) {\n  const frame = useCurrentFrame();\n  const progress = scribbleCircleProgress(frame, {\n    delay,\n    durationSteps,\n    step,\n  });\n  const { d, viewBox, marginX, marginY } = scribbleCirclePath({\n    width,\n    height,\n    strokeWidth,\n    laps,\n    pressure,\n    release,\n    grain,\n    seed,\n    progress,\n  });\n\n  return (\n    <div\n      style={{\n        position: \"relative\",\n        width,\n        height,\n        opacity: progress > 0 ? 1 : 0,\n      }}\n    >\n      <svg\n        width={width + marginX * 2}\n        height={height + marginY * 2}\n        viewBox={viewBox}\n        style={{\n          position: \"absolute\",\n          left: -marginX,\n          top: -marginY,\n          display: \"block\",\n          overflow: \"visible\",\n        }}\n      >\n        <title>Scribbled circle</title>\n        <BrushGrain seed={seed} strokeWidth={strokeWidth} grain={grain} />\n        <path\n          d={d}\n          fill={color}\n          opacity={0.85}\n          filter={\n            grain > 0\n              ? `url(#${brushFilterId(seed, strokeWidth, grain)})`\n              : undefined\n          }\n        />\n      </svg>\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/remocn/scribble-circle.tsx"
    }
  ],
  "type": "registry:component"
}
