how to reduce the gap between the images with the continuous loop.

// Start of Selection
import React, { useState, useEffect, useCallback, useRef } from "react";
import { degreesToRadians } from "popmotion";
import { motion, AnimatePresence } from "framer-motion";
import Image from "next/image";
import { Easings } from "@/lib/utils";

interface ProjectImage {
  id: number;
  url: string;
  title: string;
  subtitle: string;
  width: number;
  x: number;
  y: number;
  angle: number;
}

interface Project {
  id: number;
  name: string;
  images: ProjectImage[];
}

// Constant project data defined outside the component
const PROJECTS: Project[] = [
  {
    id: 1,
    name: "Project Alpha",
    images: [
      {
        id: 1,
        url: `https://picsum.photos/800/400?random=1`,
        title: "Alpha Main View",
        subtitle: "Frontend dashboard",
        width: 138.38,
        x: 0,
        y: 0,
        angle: 0,
      },
      {
        id: 2,
        url: `https://picsum.photos/600/400?random=2`,
        title: "Alpha User Profile",
        subtitle: "User settings page",
        width: 138.38,
        x: 0,
        y: 0,
        angle: 0,
      },
      {
        id: 3,
        url: `https://picsum.photos/700/400?random=3`,
        title: "Alpha Analytics",
        subtitle: "Data visualization",
        width: 138.38,
        x: 0,
        y: 0,
        angle: 0,
      },
    ],
  },
  {
    id: 2,
    name: "Project Beta",
    images: [
      {
        id: 4,
        url: `https://picsum.photos/750/400?random=4`,
        title: "Beta Homepage",
        subtitle: "Responsive design",
        width: 138.38,
        x: 0,
        y: 0,
        angle: 0,
      },
      {
        id: 5,
        url: `https://picsum.photos/650/400?random=5`,
        title: "Beta Mobile App",
        subtitle: "iOS interface",
        width: 138.38,
        x: 0,
        y: 0,
        angle: 0,
      },
    ],
  },
  {
    id: 3,
    name: "Project Gamma",
    images: [
      {
        id: 6,
        url: `https://picsum.photos/900/400?random=6`,
        title: "Gamma 3D Model",
        subtitle: "Product showcase",
        width: 138.38,
        x: 0,
        y: 0,
        angle: 0,
      },
      {
        id: 7,
        url: `https://picsum.photos/550/400?random=7`,
        title: "Gamma AR View",
        subtitle: "Augmented reality feature",
        width: 138.38,
        x: 0,
        y: 0,
        angle: 0,
      },
      {
        id: 8,
        url: `https://picsum.photos/720/400?random=8`,
        title: "Gamma User Testing",
        subtitle: "Usability study results",
        width: 138.38,
        x: 0,
        y: 0,
        angle: 0,
      },
    ],
  },
];

const ProjectImageGallery: React.FC = () => {
  const [projects, setProjects] = useState<Project[]>(PROJECTS);
  const [rotation, setRotation] = useState(0);
  const [startAngle, setStartAngle] = useState(degreesToRadians(-2));
  const [endAngle, setEndAngle] = useState(degreesToRadians(182));
  const containerRef = useRef<HTMLDivElement>(null);
  const [ellipseWidth, setEllipseWidth] = useState(2700);
  const [ellipseHeight, setEllipseHeight] = useState(400);
  const [visibleImages, setVisibleImages] = useState<ProjectImage[]>([]);

  // Update ellipse dimensions based on parent size
  useEffect(() => {
    const updateSize = () => {
      if (containerRef.current) {
        const { clientWidth, clientHeight } = containerRef.current;
        setEllipseWidth(clientWidth);
        setEllipseHeight(clientHeight);
      }
    };

    updateSize();

    window.addEventListener("resize", updateSize);
    return () => window.removeEventListener("resize", updateSize);
  }, []);

  const updateImagePositions = useCallback(
    (currentRotation: number) => {
      const allImages = projects.flatMap((project) => project.images);
      const numImages = allImages.length;
      const a = ellipseWidth / 2;
      const b = ellipseHeight / 2;

      // Ramanujan's approximation for ellipse perimeter
      const perimeter =
        Math.PI * (3 * (a + b) - Math.sqrt((3 * a + b) * (a + 3 * b)));
      const stride = perimeter / numImages;
      let angle = currentRotation;

      const updatedProjects = projects.map((project) => ({
        ...project,
        images: project.images.map((image) => {
          let accumulatedArc = 0;
          const deltaTheta = 0.0001;

          while (accumulatedArc < stride) {
            accumulatedArc +=
              Math.sqrt(
                Math.pow(a * Math.sin(angle), 2) +
                  Math.pow(b * Math.cos(angle), 2),
              ) * deltaTheta;
            angle += deltaTheta;
            angle %= 2 * Math.PI;
          }

          const x = a * Math.cos(angle) + a;
          const y = b * Math.sin(angle) + b;

          return { ...image, x, y, angle };
        }),
      }));

      return updatedProjects;
    },
    [ellipseWidth, ellipseHeight],
  );

  const isImageVisible = useCallback(
    (angle: number) => {
      const normalizedAngle = (angle + 2 * Math.PI) % (2 * Math.PI);
      const normalizedStart = (startAngle + 2 * Math.PI) % (2 * Math.PI);
      const normalizedEnd = (endAngle + 2 * Math.PI) % (2 * Math.PI);

      if (normalizedStart <= normalizedEnd) {
        return (
          normalizedAngle >= normalizedStart && normalizedAngle <= normalizedEnd
        );
      } else {
        return (
          normalizedAngle >= normalizedStart || normalizedAngle <= normalizedEnd
        );
      }
    },
    [startAngle, endAngle],
  );

  const determineVisibleImages = useCallback(() => {
    const allImages = projects.flatMap((project) => project.images);
    const visible = allImages.filter((image) => isImageVisible(image.angle));

    // Sort visible images by angle to prevent overlap
    const sortedVisible = visible.sort((a, b) => a.angle - b.angle);

    // Adjust angles to prevent overlap
    const buffer = 0.05; // Minimum angle between images
    for (let i = 1; i < sortedVisible.length; i++) {
      const prev = sortedVisible[i - 1];
      const current = sortedVisible[i];
      if (current.angle - prev.angle < buffer) {
        sortedVisible[i].angle = prev.angle + buffer;
        sortedVisible[i].angle %= 2 * Math.PI;
      }
    }

    return sortedVisible;
  }, [projects, isImageVisible]);

  useEffect(() => {
    const updatedProjects = updateImagePositions(rotation);
    setProjects(updatedProjects);
  }, [rotation, updateImagePositions]);

  useEffect(() => {
    const visibleImages = determineVisibleImages();
    setVisibleImages(visibleImages);
  }, [projects, determineVisibleImages]);

  useEffect(() => {
    const handleWheel = (e: WheelEvent) => {
      const delta = e.deltaY * 0 + e.deltaX;
      setRotation((prevRotation) => prevRotation + delta * 0.001);
    };

    window.addEventListener("wheel", handleWheel);
    return () => window.removeEventListener("wheel", handleWheel);
  }, []);

  return (
    <div
      className="relative mx-auto flex w-full max-w-screen-xl items-center justify-center"
      style={{
        // width: `${ellipseWidth}px`,
        height: `${ellipseHeight}px`,
      }}
    >
      <div className="relative mx-auto w-full px-4 sm:px-6 lg:px-8">
        <div
          ref={containerRef}
          className="relative w-full"
          style={{
            width: "100%",
            paddingTop: `${(ellipseHeight / ellipseWidth) * 100}%`,
          }}
        >
          <AnimatePresence>
            {projects.map((project) =>
              project.images.map(
                (image) =>
                  isImageVisible(image.angle) && (
                    <motion.div
                      key={image.id}
                      initial={{ opacity: 0, scale: 0.8, y: -100 }}
                      animate={{
                        opacity: 1,
                        scale: 1,
                        y: 0,
                        transition: {
                          duration: 0.3,
                          ease: Easings.easeOutCubic,
                        },
                      }}
                      exit={{
                        opacity: 0,
                        scale: 0.8,
                        y: -100,
                        transition: {
                          duration: 0.3,
                          ease: Easings.easeOutExpo,
                        },
                      }}
                      transition={{ duration: 0.3 }}
                      className="absolute inset-x-0"
                      style={{
                        left: `${((image.x - image.width / 2) / ellipseWidth) * 100}%`,
                        top: `${(image.y / ellipseHeight) * 100}%`,
                        width: `${image.width}px`,
                        height: "auto",
                        transform: "translate(-50%, -50%)",
                        zIndex: 20,
                      }}
                    >
                      <Image
                        src={image.url}
                        alt={image.title}
                        placeholder="blur"
                        blurDataURL={image.url}
                        width={image.width}
                        height={300}
                        className="m-0 h-[300px] w-full rounded-lg object-cover shadow-md"
                      />
                      {/* <div className="absolute bottom-0 left-0 right-0 rounded-b-lg bg-black bg-opacity-50 p-4 text-white">
                      <h3 className="text-lg font-semibold">{image.title}</h3>
                      <p className="text-sm">{image.subtitle}</p>
                    </div> */}
                    </motion.div>
                  ),
              ),
            )}
          </AnimatePresence>
          <svg
            className="pointer-events-none absolute left-0 top-0 h-full w-full"
            style={{ zIndex: 10 }}
          >
            <path
              d={`M ${ellipseWidth / 2},${ellipseHeight / 2} 
               L ${(ellipseWidth / 2) * (1 + Math.cos(startAngle))},${
                 (ellipseHeight / 2) * (1 + Math.sin(startAngle))
               } 
               A ${ellipseWidth / 2},${ellipseHeight / 2} 0 ${
                 endAngle - startAngle > Math.PI ? 1 : 0
               },1 
               ${(ellipseWidth / 2) * (1 + Math.cos(endAngle))},${
                 (ellipseHeight / 2) * (1 + Math.sin(endAngle))
               } Z`}
              fill="rgba(255, 255, 255, 0.5)"
              stroke="black"
              strokeWidth="1"
            />
          </svg>
        </div>
        {/* <div className="mt-4">
          <h2 className="mb-2 text-xl font-semibold">Active Project:</h2>
          {visibleImages.length > 0 ? (
            <ul className="list-inside list-disc">
              {Array.from(
                new Set(
                  visibleImages.map(
                    (image) =>
                      projects.find((project) =>
                        project.images.some((img) => img.id === image.id),
                      )?.name,
                  ),
                ),
              ).map((projectName, index) => (
                <li key={index}>
                  <strong>{projectName}</strong>
                </li>
              ))}
            </ul>
          ) : (
            <p>
              No projects are currently visible in the selected angle range.
            </p>
          )}
        </div> */}
      </div>
    </div>
  );
};

export default ProjectImageGallery;