// Set mass of the body based on the position to the center of mass
        /// <summary>
        /// Generate the positions, masses and orbits of the bodies specified count.
        /// </summary>
        /// <param name="bodyCount">The total number of bodies wanted.</param>
        /// <param name="staticBodyCount">The number of bodies that should not move.</param>
        /// <param name="bodiesRadii">The radius of each body wanted.</param>
        /// <param name="worldSize">The full size of the world/screen.</param>
        /// <param name="verbose">Whether debug logs should be displayed.</param>
        /// <returns>An array of BodyData if the generation succeeded, null otherwise.</returns>
        public BodyData[] Generate(int bodyCount, StaticBodyCount staticBodyCount, float[] bodiesRadii, Vector2 worldSize, bool verbose)
        {
            bool hasConverged = true;

            BodyData[] bodies      = new BodyData[bodyCount];
            Vector2    worldExtent = worldSize * 0.5f;

            this.verbose = verbose;

            string[] names = new string[bodyCount];

            Vector2[] positions = null;
            while (positions == null)
            {
                positions = GenerateBodiesPositions(bodyCount, worldExtent, bodiesRadii);
            }

            float[] masses       = GenerateBodiesMasses(bodyCount);
            Vector2 centerOfMass = FindCenterOfMass(bodyCount, positions, masses);

            float[]   eccentricities = new float[bodyCount];
            Vector2[] centers        = new Vector2[bodyCount];
            float[]   orbitalSpeeds  = new float[bodyCount];

            for (int i = 0; i < bodyCount; i++)
            {
                names[i]         = "Body" + i;
                orbitalSpeeds[i] = defaultOrbitalSpeed;

                // Find max semi major axis length
                Vector2 maxOppositePosition = new Vector3(centerOfMass.x, centerOfMass.y) + Quaternion.Euler(0, 0, 180) * (positions[i] - centerOfMass);
                if (maxOppositePosition.x < centerOfMass.x)
                {
                    maxOppositePosition.x -= bodiesRadii[i];
                }
                else
                {
                    maxOppositePosition.x += bodiesRadii[i];
                }
                float proportionOutside = GetMaxProportionOutsideScreen(maxOppositePosition, worldExtent);
                float maxSemiMajorAxis  = (maxOppositePosition - centerOfMass).magnitude;
                if (proportionOutside > 0)
                {
                    maxSemiMajorAxis -= maxSemiMajorAxis * proportionOutside;
                }

                float actualMinEccentricity = ((centerOfMass - positions[i]).magnitude - maxSemiMajorAxis) / maxSemiMajorAxis; // minFocalDistance is the difference between the theoretical and the actual maxSemiMajorAxis
                if (actualMinEccentricity > maxEccentricity)
                {
                    if (verbose)
                    {
                        Debug.LogWarning("It is impossible for the current generated system configuration to find an eccentricity for " + names[i] + " that is below the max specified");

                        Debug.Log("semi major axis proportion outside: " + positions[i] + " -> " + proportionOutside);
                        Debug.Log("max semi major axis: " + maxSemiMajorAxis);
                    }

                    hasConverged = false;
                }
                else
                {
                    if (actualMinEccentricity < minEccentricity)
                    {
                        actualMinEccentricity = minEccentricity;
                    }

                    // Find the fitting eccentricity and check if the ellipse fits in the screen
                    Vector2 maxB1, maxB2;
                    float   semiMajorAxis, semiMinorAxis, focalDistance, maxB1Outside, maxB2Outside, maxBOutside;
                    int     safeWhileCount = 500;
                    bool    notEccentricEnough, noSolutionPossible;
                    do
                    {
                        notEccentricEnough = false;
                        noSolutionPossible = false;

                        // Choose an eccentricity and find the relative parameters
                        eccentricities[i] = Random.Range(actualMinEccentricity, maxEccentricity);
                        semiMajorAxis     = (centerOfMass - positions[i]).magnitude / (eccentricities[i] + 1f);
                        focalDistance     = semiMajorAxis * eccentricities[i];
                        semiMinorAxis     = Mathf.Sqrt(Mathf.Pow(semiMajorAxis, 2) - Mathf.Pow(focalDistance, 2));
                        centers[i]        = positions[i] + (centerOfMass - positions[i]).normalized * semiMajorAxis;

                        // Compute vertical bounds
                        maxB1    = new Vector3(centers[i].x, centers[i].y) + Quaternion.Euler(0, 0, 90) * Vector2.right * semiMinorAxis;
                        maxB2    = new Vector3(centers[i].x, centers[i].y) + Quaternion.Euler(0, 0, -90) * Vector2.right * semiMinorAxis;
                        maxB1.y += bodiesRadii[i];
                        maxB2.y -= bodiesRadii[i];

                        // Check vertical bounds and reduce eccentricity if needed
                        maxB1Outside = GetMaxProportionOutsideScreen(maxB1, worldExtent);
                        maxB2Outside = GetMaxProportionOutsideScreen(maxB2, worldExtent);
                        maxBOutside  = Mathf.Max(maxB1Outside, maxB2Outside);
                        if (maxBOutside > 0)
                        {
                            if (actualMinEccentricity < maxEccentricity)
                            {
                                actualMinEccentricity = eccentricities[i]; // Converge toward the value by resetting the min value
                                notEccentricEnough    = true;
                            }
                            else
                            {
                                noSolutionPossible = true;
                            }
                        }

                        safeWhileCount--;
                    } while (notEccentricEnough && !noSolutionPossible && safeWhileCount > 0);

                    if (safeWhileCount <= 0 || noSolutionPossible)
                    {
                        if (verbose)
                        {
                            Debug.LogWarning("In the current generated system it is not possible to find an eccentricity for " + names[i] + " that is below the max eccentricity");

                            Debug.Log("maxB1outside: " + positions[i] + " -> " + maxB1Outside);
                            Debug.Log("maxB2outside: " + positions[i] + " -> " + maxB2Outside);

                            Debug.Log("eccentricity: " + eccentricities[i]);
                            Debug.Log("center: " + centers[i]);
                            Debug.Log("semiMajorAxis: " + semiMajorAxis);
                            Debug.Log("semiMinorAxis: " + semiMinorAxis);

                            Debug.Log("maxVerticalPos" + maxB1);
                            Debug.Log("maxVerticalPos" + maxB2);
                        }

                        hasConverged = false;
                    }
                }
            }

            // Set static bodies
            if (staticBodyCount != StaticBodyCount.NONE)
            {
                int closestBodyToCenterOfMassIndex = GetClosestBodyToCenterOfMassIndex(bodyCount, positions, centerOfMass, Mathf.Max(worldSize.x, worldSize.y));
                if (staticBodyCount == StaticBodyCount.ONE)
                {
                    positions[closestBodyToCenterOfMassIndex]     = centers[closestBodyToCenterOfMassIndex];
                    orbitalSpeeds[closestBodyToCenterOfMassIndex] = 0;
                }
                else
                {
                    for (int i = 0; i < bodyCount; i++)
                    {
                        orbitalSpeeds[i] = 0;
                    }
                }
            }

            if (hasConverged)
            {
                for (int i = 0; i < bodyCount; i++)
                {
                    bodies[i].name         = names[i];
                    bodies[i].position     = positions[i];
                    bodies[i].mass         = masses[i];
                    bodies[i].orbitalSpeed = orbitalSpeeds[i];

                    // Rotate orbit to match the apsides line
                    if (positions[i].y < centerOfMass.y)
                    {
                        bodies[i].orbitTilt = -Vector2.Angle(Vector2.right, (positions[i] - centerOfMass).normalized);
                    }
                    else
                    {
                        bodies[i].orbitTilt = Vector2.Angle(Vector2.right, (positions[i] - centerOfMass).normalized);
                    }

                    bodies[i].eccentricity  = eccentricities[i];
                    bodies[i].ellipseCenter = centers[i];
                    bodies[i].centerOfMass  = centerOfMass;
                }
                return(bodies);
            }
            else
            {
                return(null);
            }
        }
 public void SetStaticBodyCount(int value)
 {
     staticBodyCount = GetStaticBodyCount(value);
 }