{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "gravity",
  "type": "registry:block",
  "title": "Gravity",
  "description": "Gravity",
  "files": [
    {
      "path": "components/usages/gravityusage.tsx",
      "content": "\"use client\";\n\nimport React from \"react\";\n\nimport Gravity, { MatterBody } from \"@/registry/open-source/gravity\";\n\nexport default function Usage() {\n\treturn (\n\t\t<div className=\"h-screen w-full flex items-center justify-center relative overflow-hidden bg-background\">\n\t\t\t<div className=\"w-full h-full flex flex-col relative font-azeretMono bg-background\">\n\t\t\t\t<div className=\"pt-20 text-6xl sm:text-7xl md:text-8xl text-foreground dark:text-muted w-full text-center font-calendas italic\">\n\t\t\t\t\tfancy\n\t\t\t\t</div>\n\t\t\t\t<p className=\"pt-4 text-base sm:text-xl md:text-2xl text-foreground dark:text-muted w-full text-center\">\n\t\t\t\t\tcomponents made with:\n\t\t\t\t</p>\n\t\t\t\t<Gravity gravity={{ x: 0, y: 1 }} className=\"w-full h-full\">\n\t\t\t\t\t<MatterBody\n\t\t\t\t\t\tmatterBodyOptions={{\n\t\t\t\t\t\t\tfriction: 0.5,\n\t\t\t\t\t\t\trestitution: 0.2,\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tx=\"30%\"\n\t\t\t\t\t\ty=\"10%\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<div className=\"text-xl sm:text-2xl md:text-3xl bg-blue-500 text-secondary rounded-full hover:cursor-pointer px-8 py-4\">\n\t\t\t\t\t\t\treact\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</MatterBody>\n\t\t\t\t\t<MatterBody\n\t\t\t\t\t\tmatterBodyOptions={{\n\t\t\t\t\t\t\tfriction: 0.5,\n\t\t\t\t\t\t\trestitution: 0.2,\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tx=\"30%\"\n\t\t\t\t\t\ty=\"30%\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<div className=\"text-xl sm:text-2xl md:text-3xl bg-pink-500 text-secondary rounded-full hover:cursor-grab px-8 py-4 \">\n\t\t\t\t\t\t\ttypescript\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</MatterBody>\n\t\t\t\t\t<MatterBody\n\t\t\t\t\t\tmatterBodyOptions={{\n\t\t\t\t\t\t\tfriction: 0.5,\n\t\t\t\t\t\t\trestitution: 0.2,\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tx=\"40%\"\n\t\t\t\t\t\ty=\"20%\"\n\t\t\t\t\t\tangle={10}\n\t\t\t\t\t>\n\t\t\t\t\t\t<div className=\"text-xl sm:text-2xl md:text-3xl bg-teal-400 text-secondary rounded-full hover:cursor-grab px-8 py-4 \">\n\t\t\t\t\t\t\tmotion\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</MatterBody>\n\t\t\t\t\t<MatterBody\n\t\t\t\t\t\tmatterBodyOptions={{\n\t\t\t\t\t\t\tfriction: 0.5,\n\t\t\t\t\t\t\trestitution: 0.2,\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tx=\"75%\"\n\t\t\t\t\t\ty=\"10%\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<div className=\"text-xl sm:text-2xl md:text-3xl bg-red-400 text-secondary rounded-full hover:cursor-grab px-8 py-4 \">\n\t\t\t\t\t\t\ttailwind\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</MatterBody>\n\t\t\t\t\t<MatterBody\n\t\t\t\t\t\tmatterBodyOptions={{\n\t\t\t\t\t\t\tfriction: 0.5,\n\t\t\t\t\t\t\trestitution: 0.2,\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tx=\"80%\"\n\t\t\t\t\t\ty=\"20%\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<div className=\"text-xl sm:text-2xl md:text-3xl bg-orange-400 text-secondary rounded-full hover:cursor-grab px-8 py-4 \">\n\t\t\t\t\t\t\tdrei\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</MatterBody>\n\t\t\t\t\t<MatterBody\n\t\t\t\t\t\tmatterBodyOptions={{\n\t\t\t\t\t\t\tfriction: 0.5,\n\t\t\t\t\t\t\trestitution: 0.2,\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tx=\"50%\"\n\t\t\t\t\t\ty=\"10%\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<div className=\"text-xl sm:text-2xl md:text-3xl bg-yellow-400 text-secondary rounded-full hover:cursor-grab px-8 py-4 \">\n\t\t\t\t\t\t\tmatter-js\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</MatterBody>\n\t\t\t\t</Gravity>\n\t\t\t</div>{\" \"}\n\t\t</div>\n\t);\n}\n",
      "type": "registry:block",
      "target": "~/example.tsx"
    },
    {
      "path": "components/usages/gravityusage.tsx",
      "content": "\"use client\";\n\nimport React from \"react\";\n\nimport Gravity, { MatterBody } from \"@/registry/open-source/gravity\";\n\nexport default function Usage() {\n\treturn (\n\t\t<div className=\"h-screen w-full flex items-center justify-center relative overflow-hidden bg-background\">\n\t\t\t<div className=\"w-full h-full flex flex-col relative font-azeretMono bg-background\">\n\t\t\t\t<div className=\"pt-20 text-6xl sm:text-7xl md:text-8xl text-foreground dark:text-muted w-full text-center font-calendas italic\">\n\t\t\t\t\tfancy\n\t\t\t\t</div>\n\t\t\t\t<p className=\"pt-4 text-base sm:text-xl md:text-2xl text-foreground dark:text-muted w-full text-center\">\n\t\t\t\t\tcomponents made with:\n\t\t\t\t</p>\n\t\t\t\t<Gravity gravity={{ x: 0, y: 1 }} className=\"w-full h-full\">\n\t\t\t\t\t<MatterBody\n\t\t\t\t\t\tmatterBodyOptions={{\n\t\t\t\t\t\t\tfriction: 0.5,\n\t\t\t\t\t\t\trestitution: 0.2,\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tx=\"30%\"\n\t\t\t\t\t\ty=\"10%\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<div className=\"text-xl sm:text-2xl md:text-3xl bg-blue-500 text-secondary rounded-full hover:cursor-pointer px-8 py-4\">\n\t\t\t\t\t\t\treact\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</MatterBody>\n\t\t\t\t\t<MatterBody\n\t\t\t\t\t\tmatterBodyOptions={{\n\t\t\t\t\t\t\tfriction: 0.5,\n\t\t\t\t\t\t\trestitution: 0.2,\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tx=\"30%\"\n\t\t\t\t\t\ty=\"30%\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<div className=\"text-xl sm:text-2xl md:text-3xl bg-pink-500 text-secondary rounded-full hover:cursor-grab px-8 py-4 \">\n\t\t\t\t\t\t\ttypescript\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</MatterBody>\n\t\t\t\t\t<MatterBody\n\t\t\t\t\t\tmatterBodyOptions={{\n\t\t\t\t\t\t\tfriction: 0.5,\n\t\t\t\t\t\t\trestitution: 0.2,\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tx=\"40%\"\n\t\t\t\t\t\ty=\"20%\"\n\t\t\t\t\t\tangle={10}\n\t\t\t\t\t>\n\t\t\t\t\t\t<div className=\"text-xl sm:text-2xl md:text-3xl bg-teal-400 text-secondary rounded-full hover:cursor-grab px-8 py-4 \">\n\t\t\t\t\t\t\tmotion\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</MatterBody>\n\t\t\t\t\t<MatterBody\n\t\t\t\t\t\tmatterBodyOptions={{\n\t\t\t\t\t\t\tfriction: 0.5,\n\t\t\t\t\t\t\trestitution: 0.2,\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tx=\"75%\"\n\t\t\t\t\t\ty=\"10%\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<div className=\"text-xl sm:text-2xl md:text-3xl bg-red-400 text-secondary rounded-full hover:cursor-grab px-8 py-4 \">\n\t\t\t\t\t\t\ttailwind\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</MatterBody>\n\t\t\t\t\t<MatterBody\n\t\t\t\t\t\tmatterBodyOptions={{\n\t\t\t\t\t\t\tfriction: 0.5,\n\t\t\t\t\t\t\trestitution: 0.2,\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tx=\"80%\"\n\t\t\t\t\t\ty=\"20%\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<div className=\"text-xl sm:text-2xl md:text-3xl bg-orange-400 text-secondary rounded-full hover:cursor-grab px-8 py-4 \">\n\t\t\t\t\t\t\tdrei\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</MatterBody>\n\t\t\t\t\t<MatterBody\n\t\t\t\t\t\tmatterBodyOptions={{\n\t\t\t\t\t\t\tfriction: 0.5,\n\t\t\t\t\t\t\trestitution: 0.2,\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tx=\"50%\"\n\t\t\t\t\t\ty=\"10%\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<div className=\"text-xl sm:text-2xl md:text-3xl bg-yellow-400 text-secondary rounded-full hover:cursor-grab px-8 py-4 \">\n\t\t\t\t\t\t\tmatter-js\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</MatterBody>\n\t\t\t\t</Gravity>\n\t\t\t</div>{\" \"}\n\t\t</div>\n\t);\n}\n",
      "type": "registry:ui"
    },
    {
      "path": "registry/open-source/gravity.tsx",
      "content": "\"use client\";\r\n\r\nimport {\r\n\tcreateContext,\r\n\tforwardRef,\r\n\tReactNode,\r\n\tuseCallback,\r\n\tuseContext,\r\n\tuseEffect,\r\n\tuseImperativeHandle,\r\n\tuseRef,\r\n\tuseState,\r\n} from \"react\";\r\n\r\nimport { calculatePosition } from \"@/registry/utilities/calculatePosition\";\r\nimport { cn } from \"@/registry/utilities/cn\";\r\nimport { parsePathToVertices } from \"@/registry/utilities/parsePathToVertices\";\r\nimport Matter, {\r\n\tBodies,\r\n\tCommon,\r\n\tEngine,\r\n\tEvents,\r\n\tMouse,\r\n\tMouseConstraint,\r\n\tQuery,\r\n\tRender,\r\n\tRunner,\r\n\tWorld,\r\n} from \"matter-js\";\r\n\r\n// Credit:\r\n// https://www.fancycomponents.dev/docs/components/physics/gravity\r\n\r\ntype GravityProps = {\r\n\tchildren: ReactNode;\r\n\tdebug?: boolean;\r\n\tgravity?: { x: number; y: number };\r\n\tresetOnResize?: boolean;\r\n\tgrabCursor?: boolean;\r\n\taddTopWall?: boolean;\r\n\tautoStart?: boolean;\r\n\tclassName?: string;\r\n};\r\n\r\ntype PhysicsBody = {\r\n\telement: HTMLElement;\r\n\tbody: Matter.Body;\r\n\tprops: MatterBodyProps;\r\n};\r\n\r\ntype MatterBodyProps = {\r\n\tchildren: ReactNode;\r\n\tmatterBodyOptions?: Matter.IBodyDefinition;\r\n\tisDraggable?: boolean;\r\n\tbodyType?: \"rectangle\" | \"circle\" | \"svg\";\r\n\tsampleLength?: number;\r\n\tx?: number | string;\r\n\ty?: number | string;\r\n\tangle?: number;\r\n\tclassName?: string;\r\n};\r\n\r\nexport type GravityRef = {\r\n\tstart: () => void;\r\n\tstop: () => void;\r\n\treset: () => void;\r\n};\r\n\r\nconst GravityContext = createContext<{\r\n\tregisterElement: (\r\n\t\tid: string,\r\n\t\telement: HTMLElement,\r\n\t\tprops: MatterBodyProps\r\n\t) => void;\r\n\tunregisterElement: (id: string) => void;\r\n} | null>(null);\r\n\r\nexport const MatterBody = ({\r\n\tchildren,\r\n\tclassName,\r\n\tmatterBodyOptions = {\r\n\t\tfriction: 0.1,\r\n\t\trestitution: 0.1,\r\n\t\tdensity: 0.001,\r\n\t\tisStatic: false,\r\n\t},\r\n\tbodyType = \"rectangle\",\r\n\tisDraggable = true,\r\n\tsampleLength = 15,\r\n\tx = 0,\r\n\ty = 0,\r\n\tangle = 0,\r\n\t...props\r\n}: MatterBodyProps) => {\r\n\tconst elementRef = useRef<HTMLDivElement>(null);\r\n\tconst idRef = useRef(Math.random().toString(36).substring(7));\r\n\tconst context = useContext(GravityContext);\r\n\r\n\tuseEffect(() => {\r\n\t\tif (!elementRef.current || !context) return;\r\n\t\tcontext.registerElement(idRef.current, elementRef.current, {\r\n\t\t\tchildren,\r\n\t\t\tmatterBodyOptions,\r\n\t\t\tbodyType,\r\n\t\t\tsampleLength,\r\n\t\t\tisDraggable,\r\n\t\t\tx,\r\n\t\t\ty,\r\n\t\t\tangle,\r\n\t\t\t...props,\r\n\t\t});\r\n\r\n\t\treturn () => context.unregisterElement(idRef.current);\r\n\t}, [props, children, matterBodyOptions, isDraggable]);\r\n\r\n\treturn (\r\n\t\t<div\r\n\t\t\tref={elementRef}\r\n\t\t\tclassName={cn(\r\n\t\t\t\t\"absolute\",\r\n\t\t\t\tclassName,\r\n\t\t\t\tisDraggable && \"pointer-events-none\"\r\n\t\t\t)}\r\n\t\t>\r\n\t\t\t{children}\r\n\t\t</div>\r\n\t);\r\n};\r\n\r\nconst Gravity = forwardRef<GravityRef, GravityProps>(\r\n\t(\r\n\t\t{\r\n\t\t\tchildren,\r\n\t\t\tdebug = false,\r\n\t\t\tgravity = { x: 0, y: 1 },\r\n\t\t\tgrabCursor = true,\r\n\t\t\tresetOnResize = true,\r\n\t\t\taddTopWall = true,\r\n\t\t\tautoStart = true,\r\n\t\t\tclassName,\r\n\t\t\t...props\r\n\t\t},\r\n\t\tref\r\n\t) => {\r\n\t\tconst canvas = useRef<HTMLDivElement>(null);\r\n\t\tconst engine = useRef(Engine.create());\r\n\t\tconst render = useRef<Render>();\r\n\t\tconst runner = useRef<Runner>();\r\n\t\tconst bodiesMap = useRef(new Map<string, PhysicsBody>());\r\n\t\tconst frameId = useRef<number>();\r\n\t\tconst mouseConstraint = useRef<Matter.MouseConstraint>();\r\n\t\tconst mouseDown = useRef(false);\r\n\t\tconst [canvasSize, setCanvasSize] = useState({ width: 0, height: 0 });\r\n\r\n\t\tconst isRunning = useRef(false);\r\n\r\n\t\t// Register Matter.js body in the physics world\r\n\t\tconst registerElement = useCallback(\r\n\t\t\t(id: string, element: HTMLElement, props: MatterBodyProps) => {\r\n\t\t\t\tif (!canvas.current) return;\r\n\t\t\t\tconst width = element.offsetWidth;\r\n\t\t\t\tconst height = element.offsetHeight;\r\n\t\t\t\tconst canvasRect = canvas.current!.getBoundingClientRect();\r\n\r\n\t\t\t\tconst angle = (props.angle || 0) * (Math.PI / 180);\r\n\r\n\t\t\t\tconst x = calculatePosition(props.x, canvasRect.width, width);\r\n\t\t\t\tconst y = calculatePosition(props.y, canvasRect.height, height);\r\n\r\n\t\t\t\tlet body;\r\n\t\t\t\tif (props.bodyType === \"circle\") {\r\n\t\t\t\t\tconst radius = Math.max(width, height) / 2;\r\n\t\t\t\t\tbody = Bodies.circle(x, y, radius, {\r\n\t\t\t\t\t\t...props.matterBodyOptions,\r\n\t\t\t\t\t\tangle: angle,\r\n\t\t\t\t\t\trender: {\r\n\t\t\t\t\t\t\tfillStyle: debug ? \"#888888\" : \"#00000000\",\r\n\t\t\t\t\t\t\tstrokeStyle: debug ? \"#333333\" : \"#00000000\",\r\n\t\t\t\t\t\t\tlineWidth: debug ? 3 : 0,\r\n\t\t\t\t\t\t},\r\n\t\t\t\t\t});\r\n\t\t\t\t} else if (props.bodyType === \"svg\") {\r\n\t\t\t\t\tconst paths = element.querySelectorAll(\"path\");\r\n\t\t\t\t\tconst vertexSets: Matter.Vector[][] = [];\r\n\r\n\t\t\t\t\tpaths.forEach((path) => {\r\n\t\t\t\t\t\tconst d = path.getAttribute(\"d\");\r\n\t\t\t\t\t\tconst p = parsePathToVertices(d!, props.sampleLength);\r\n\t\t\t\t\t\tvertexSets.push(p);\r\n\t\t\t\t\t});\r\n\r\n\t\t\t\t\tbody = Bodies.fromVertices(x, y, vertexSets, {\r\n\t\t\t\t\t\t...props.matterBodyOptions,\r\n\t\t\t\t\t\tangle: angle,\r\n\t\t\t\t\t\trender: {\r\n\t\t\t\t\t\t\tfillStyle: debug ? \"#888888\" : \"#00000000\",\r\n\t\t\t\t\t\t\tstrokeStyle: debug ? \"#333333\" : \"#00000000\",\r\n\t\t\t\t\t\t\tlineWidth: debug ? 3 : 0,\r\n\t\t\t\t\t\t},\r\n\t\t\t\t\t});\r\n\t\t\t\t} else {\r\n\t\t\t\t\tbody = Bodies.rectangle(x, y, width, height, {\r\n\t\t\t\t\t\t...props.matterBodyOptions,\r\n\t\t\t\t\t\tangle: angle,\r\n\t\t\t\t\t\trender: {\r\n\t\t\t\t\t\t\tfillStyle: debug ? \"#888888\" : \"#00000000\",\r\n\t\t\t\t\t\t\tstrokeStyle: debug ? \"#333333\" : \"#00000000\",\r\n\t\t\t\t\t\t\tlineWidth: debug ? 3 : 0,\r\n\t\t\t\t\t\t},\r\n\t\t\t\t\t});\r\n\t\t\t\t}\r\n\r\n\t\t\t\tif (body) {\r\n\t\t\t\t\tWorld.add(engine.current.world, [body]);\r\n\t\t\t\t\tbodiesMap.current.set(id, { element, body, props });\r\n\t\t\t\t}\r\n\t\t\t},\r\n\t\t\t[debug]\r\n\t\t);\r\n\r\n\t\t// Unregister Matter.js body from the physics world\r\n\t\tconst unregisterElement = useCallback((id: string) => {\r\n\t\t\tconst body = bodiesMap.current.get(id);\r\n\t\t\tif (body) {\r\n\t\t\t\tWorld.remove(engine.current.world, body.body);\r\n\t\t\t\tbodiesMap.current.delete(id);\r\n\t\t\t}\r\n\t\t}, []);\r\n\r\n\t\t// Keep react elements in sync with the physics world\r\n\t\tconst updateElements = useCallback(() => {\r\n\t\t\tbodiesMap.current.forEach(({ element, body }) => {\r\n\t\t\t\tconst { x, y } = body.position;\r\n\t\t\t\tconst rotation = body.angle * (180 / Math.PI);\r\n\r\n\t\t\t\telement.style.transform = `translate(${\r\n\t\t\t\t\tx - element.offsetWidth / 2\r\n\t\t\t\t}px, ${y - element.offsetHeight / 2}px) rotate(${rotation}deg)`;\r\n\t\t\t});\r\n\r\n\t\t\tframeId.current = requestAnimationFrame(updateElements);\r\n\t\t}, []);\r\n\r\n\t\tconst initializeRenderer = useCallback(() => {\r\n\t\t\tif (!canvas.current) return;\r\n\r\n\t\t\tconst height = canvas.current.offsetHeight;\r\n\t\t\tconst width = canvas.current.offsetWidth;\r\n\r\n\t\t\tCommon.setDecomp(require(\"poly-decomp\"));\r\n\r\n\t\t\tengine.current.gravity.x = gravity.x;\r\n\t\t\tengine.current.gravity.y = gravity.y;\r\n\r\n\t\t\trender.current = Render.create({\r\n\t\t\t\telement: canvas.current,\r\n\t\t\t\tengine: engine.current,\r\n\t\t\t\toptions: {\r\n\t\t\t\t\twidth,\r\n\t\t\t\t\theight,\r\n\t\t\t\t\twireframes: false,\r\n\t\t\t\t\tbackground: \"#00000000\",\r\n\t\t\t\t},\r\n\t\t\t});\r\n\r\n\t\t\tconst mouse = Mouse.create(render.current.canvas);\r\n\t\t\t// Enables scrolling\r\n\t\t\tmouse.element.removeEventListener(\"wheel\", mouse.mousewheel);\r\n\r\n\t\t\tmouseConstraint.current = MouseConstraint.create(engine.current, {\r\n\t\t\t\tmouse: mouse,\r\n\t\t\t\tconstraint: {\r\n\t\t\t\t\tstiffness: 0.2,\r\n\t\t\t\t\trender: {\r\n\t\t\t\t\t\tvisible: debug,\r\n\t\t\t\t\t},\r\n\t\t\t\t},\r\n\t\t\t});\r\n\r\n\t\t\t// Add walls\r\n\t\t\tconst walls = [\r\n\t\t\t\t// Floor\r\n\t\t\t\tBodies.rectangle(width / 2, height + 10, width, 20, {\r\n\t\t\t\t\tisStatic: true,\r\n\t\t\t\t\tfriction: 1,\r\n\t\t\t\t\trender: {\r\n\t\t\t\t\t\tvisible: debug,\r\n\t\t\t\t\t},\r\n\t\t\t\t}),\r\n\r\n\t\t\t\t// Right wall\r\n\t\t\t\tBodies.rectangle(width + 10, height / 2, 20, height, {\r\n\t\t\t\t\tisStatic: true,\r\n\t\t\t\t\tfriction: 1,\r\n\t\t\t\t\trender: {\r\n\t\t\t\t\t\tvisible: debug,\r\n\t\t\t\t\t},\r\n\t\t\t\t}),\r\n\r\n\t\t\t\t// Left wall\r\n\t\t\t\tBodies.rectangle(-10, height / 2, 20, height, {\r\n\t\t\t\t\tisStatic: true,\r\n\t\t\t\t\tfriction: 1,\r\n\t\t\t\t\trender: {\r\n\t\t\t\t\t\tvisible: debug,\r\n\t\t\t\t\t},\r\n\t\t\t\t}),\r\n\t\t\t];\r\n\r\n\t\t\tconst topWall = addTopWall\r\n\t\t\t\t? Bodies.rectangle(width / 2, -10, width, 20, {\r\n\t\t\t\t\t\tisStatic: true,\r\n\t\t\t\t\t\tfriction: 1,\r\n\t\t\t\t\t\trender: {\r\n\t\t\t\t\t\t\tvisible: debug,\r\n\t\t\t\t\t\t},\r\n\t\t\t\t\t})\r\n\t\t\t\t: null;\r\n\r\n\t\t\tif (topWall) {\r\n\t\t\t\twalls.push(topWall);\r\n\t\t\t}\r\n\r\n\t\t\tconst touchingMouse = () =>\r\n\t\t\t\tQuery.point(\r\n\t\t\t\t\tengine.current.world.bodies,\r\n\t\t\t\t\tmouseConstraint.current?.mouse.position || { x: 0, y: 0 }\r\n\t\t\t\t).length > 0;\r\n\r\n\t\t\tif (grabCursor) {\r\n\t\t\t\tEvents.on(engine.current, \"beforeUpdate\", (event) => {\r\n\t\t\t\t\tif (canvas.current) {\r\n\t\t\t\t\t\tif (!mouseDown.current && !touchingMouse()) {\r\n\t\t\t\t\t\t\tcanvas.current.style.cursor = \"default\";\r\n\t\t\t\t\t\t} else if (touchingMouse()) {\r\n\t\t\t\t\t\t\tcanvas.current.style.cursor = mouseDown.current\r\n\t\t\t\t\t\t\t\t? \"grabbing\"\r\n\t\t\t\t\t\t\t\t: \"grab\";\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t});\r\n\r\n\t\t\t\tcanvas.current.addEventListener(\"mousedown\", (event) => {\r\n\t\t\t\t\tmouseDown.current = true;\r\n\r\n\t\t\t\t\tif (canvas.current) {\r\n\t\t\t\t\t\tif (touchingMouse()) {\r\n\t\t\t\t\t\t\tcanvas.current.style.cursor = \"grabbing\";\r\n\t\t\t\t\t\t} else {\r\n\t\t\t\t\t\t\tcanvas.current.style.cursor = \"default\";\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t});\r\n\t\t\t\tcanvas.current.addEventListener(\"mouseup\", (event) => {\r\n\t\t\t\t\tmouseDown.current = false;\r\n\r\n\t\t\t\t\tif (canvas.current) {\r\n\t\t\t\t\t\tif (touchingMouse()) {\r\n\t\t\t\t\t\t\tcanvas.current.style.cursor = \"grab\";\r\n\t\t\t\t\t\t} else {\r\n\t\t\t\t\t\t\tcanvas.current.style.cursor = \"default\";\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t});\r\n\t\t\t}\r\n\r\n\t\t\tWorld.add(engine.current.world, [mouseConstraint.current, ...walls]);\r\n\r\n\t\t\trender.current.mouse = mouse;\r\n\r\n\t\t\trunner.current = Runner.create();\r\n\t\t\tRender.run(render.current);\r\n\t\t\tupdateElements();\r\n\t\t\trunner.current.enabled = false;\r\n\r\n\t\t\tif (autoStart) {\r\n\t\t\t\trunner.current.enabled = true;\r\n\t\t\t\tstartEngine();\r\n\t\t\t}\r\n\t\t}, [updateElements, debug, autoStart]);\r\n\r\n\t\t// Clear the Matter.js world\r\n\t\tconst clearRenderer = useCallback(() => {\r\n\t\t\tif (frameId.current) {\r\n\t\t\t\tcancelAnimationFrame(frameId.current);\r\n\t\t\t}\r\n\r\n\t\t\tif (mouseConstraint.current) {\r\n\t\t\t\tWorld.remove(engine.current.world, mouseConstraint.current);\r\n\t\t\t}\r\n\r\n\t\t\tif (render.current) {\r\n\t\t\t\tMouse.clearSourceEvents(render.current.mouse);\r\n\t\t\t\tRender.stop(render.current);\r\n\t\t\t\trender.current.canvas.remove();\r\n\t\t\t}\r\n\r\n\t\t\tif (runner.current) {\r\n\t\t\t\tRunner.stop(runner.current);\r\n\t\t\t}\r\n\r\n\t\t\tif (engine.current) {\r\n\t\t\t\tWorld.clear(engine.current.world, false);\r\n\t\t\t\tEngine.clear(engine.current);\r\n\t\t\t}\r\n\r\n\t\t\tbodiesMap.current.clear();\r\n\t\t}, []);\r\n\r\n\t\tconst handleResize = useCallback(() => {\r\n\t\t\tif (!canvas.current || !resetOnResize) return;\r\n\r\n\t\t\tconst newWidth = canvas.current.offsetWidth;\r\n\t\t\tconst newHeight = canvas.current.offsetHeight;\r\n\r\n\t\t\tsetCanvasSize({ width: newWidth, height: newHeight });\r\n\r\n\t\t\t// Clear and reinitialize\r\n\t\t\tclearRenderer();\r\n\t\t\tinitializeRenderer();\r\n\t\t}, [clearRenderer, initializeRenderer, resetOnResize]);\r\n\r\n\t\tconst startEngine = useCallback(() => {\r\n\t\t\tif (runner.current) {\r\n\t\t\t\trunner.current.enabled = true;\r\n\r\n\t\t\t\tRunner.run(runner.current, engine.current);\r\n\t\t\t}\r\n\t\t\tif (render.current) {\r\n\t\t\t\tRender.run(render.current);\r\n\t\t\t}\r\n\t\t\tframeId.current = requestAnimationFrame(updateElements);\r\n\t\t\tisRunning.current = true;\r\n\t\t}, [updateElements, canvasSize]);\r\n\r\n\t\tconst stopEngine = useCallback(() => {\r\n\t\t\tif (!isRunning.current) return;\r\n\r\n\t\t\tif (runner.current) {\r\n\t\t\t\tRunner.stop(runner.current);\r\n\t\t\t}\r\n\t\t\tif (render.current) {\r\n\t\t\t\tRender.stop(render.current);\r\n\t\t\t}\r\n\t\t\tif (frameId.current) {\r\n\t\t\t\tcancelAnimationFrame(frameId.current);\r\n\t\t\t}\r\n\t\t\tisRunning.current = false;\r\n\t\t}, []);\r\n\r\n\t\tconst reset = useCallback(() => {\r\n\t\t\tstopEngine();\r\n\t\t\tbodiesMap.current.forEach(({ element, body, props }) => {\r\n\t\t\t\tbody.angle = props.angle || 0;\r\n\r\n\t\t\t\tconst x = calculatePosition(\r\n\t\t\t\t\tprops.x,\r\n\t\t\t\t\tcanvasSize.width,\r\n\t\t\t\t\telement.offsetWidth\r\n\t\t\t\t);\r\n\t\t\t\tconst y = calculatePosition(\r\n\t\t\t\t\tprops.y,\r\n\t\t\t\t\tcanvasSize.height,\r\n\t\t\t\t\telement.offsetHeight\r\n\t\t\t\t);\r\n\t\t\t\tbody.position.x = x;\r\n\t\t\t\tbody.position.y = y;\r\n\t\t\t});\r\n\t\t\tupdateElements();\r\n\t\t\thandleResize();\r\n\t\t}, []);\r\n\r\n\t\tuseImperativeHandle(\r\n\t\t\tref,\r\n\t\t\t() => ({\r\n\t\t\t\tstart: startEngine,\r\n\t\t\t\tstop: stopEngine,\r\n\t\t\t\treset,\r\n\t\t\t}),\r\n\t\t\t[startEngine, stopEngine]\r\n\t\t);\r\n\r\n\t\tuseEffect(() => {\r\n\t\t\tif (!resetOnResize) return;\r\n\r\n\t\t\tconst debouncedResize = () => setTimeout(handleResize, 500);\r\n\t\t\twindow.addEventListener(\"resize\", debouncedResize);\r\n\r\n\t\t\treturn () => {\r\n\t\t\t\twindow.removeEventListener(\"resize\", debouncedResize);\r\n\t\t\t};\r\n\t\t}, [handleResize, resetOnResize]);\r\n\r\n\t\tuseEffect(() => {\r\n\t\t\tinitializeRenderer();\r\n\t\t\treturn clearRenderer;\r\n\t\t}, [initializeRenderer, clearRenderer]);\r\n\r\n\t\treturn (\r\n\t\t\t<GravityContext.Provider\r\n\t\t\t\tvalue={{ registerElement, unregisterElement }}\r\n\t\t\t>\r\n\t\t\t\t<div\r\n\t\t\t\t\tref={canvas}\r\n\t\t\t\t\tclassName={cn(className, \"absolute top-0 left-0 w-full h-full\")}\r\n\t\t\t\t\t{...props}\r\n\t\t\t\t>\r\n\t\t\t\t\t{children}\r\n\t\t\t\t</div>\r\n\t\t\t</GravityContext.Provider>\r\n\t\t);\r\n\t}\r\n);\r\n\r\nGravity.displayName = \"Gravity\";\r\nexport default Gravity;\r\n",
      "type": "registry:ui"
    },
    {
      "path": "registry/utilities/calculatePosition.ts",
      "content": "export function calculatePosition(\n    value: number | string | undefined,\n    containerSize: number,\n    elementSize: number\n): number {\n    // Handle percentage strings (e.g. \"50%\")\n    if (typeof value === \"string\" && value.endsWith(\"%\")) {\n        const percentage = parseFloat(value) / 100\n        return containerSize * percentage\n    }\n\n    // Handle direct pixel values\n    if (typeof value === \"number\") {\n        return value\n    }\n\n    // If no value provided, center the element\n    return (containerSize - elementSize) / 2\n}\n",
      "type": "registry:ui"
    },
    {
      "path": "registry/utilities/cn.ts",
      "content": "import { ClassValue, clsx } from \"clsx\";\r\nimport { twMerge } from \"tailwind-merge\";\r\n\r\nexport function cn(...inputs: ClassValue[]) {\r\n\treturn twMerge(clsx(inputs));\r\n}\r\n",
      "type": "registry:ui"
    },
    {
      "path": "registry/utilities/parsePathToVertices.ts",
      "content": "import SVGPathCommander from \"svg-path-commander\"\r\n\r\n// Function to convert SVG path `d` to vertices\r\nexport function parsePathToVertices(path: string, sampleLength = 15) {\r\n    // Convert path to absolute commands\r\n    const commander = new SVGPathCommander(path)\r\n\r\n    const points: { x: number; y: number }[] = []\r\n    let lastPoint: { x: number; y: number } | null = null\r\n\r\n    // Get total length of the path\r\n    const totalLength = commander.getTotalLength()\r\n    let length = 0\r\n\r\n    // Sample points along the path\r\n    while (length < totalLength) {\r\n        const point = commander.getPointAtLength(length)\r\n\r\n        // Only add point if it's different from the last one\r\n        if (!lastPoint || point.x !== lastPoint.x || point.y !== lastPoint.y) {\r\n            points.push({ x: point.x, y: point.y })\r\n            lastPoint = point\r\n        }\r\n\r\n        length += sampleLength\r\n    }\r\n\r\n    // Ensure we get the last point\r\n    const finalPoint = commander.getPointAtLength(totalLength)\r\n    if (\r\n        lastPoint &&\r\n        (finalPoint.x !== lastPoint.x || finalPoint.y !== lastPoint.y)\r\n    ) {\r\n        points.push({ x: finalPoint.x, y: finalPoint.y })\r\n    }\r\n\r\n    return points\r\n}",
      "type": "registry:ui"
    }
  ]
}