public Fin(double yOffset, FinOptions options)
 {
     X         = MathUtils.Random().Lerp(options.X1, options.X2);
     Y         = yOffset.Lerp(options.Y1, options.Y2);
     Vx        = MathUtils.Random().Lerp(options.Vx1, options.Vx2) * (1 - (X - 0.5).Abs());
     Vy        = MathUtils.Random().Lerp(options.Vy1, options.Vy2) * 1.2;
     Gravity   = MathUtils.Random().Lerp(options.Gravity1, options.Gravity2);
     Width     = MathUtils.Random().Lerp(options.Width1, options.Width2);
     CurveBack = MathUtils.Random() > 0.6 ?
                 (MathUtils.Random() > 0.5 ? 1 : -1) * Math.Pow(MathUtils.Random(), 2).Lerp(options.CurveBack1, options.CurveBack2) : 0;
     StartWidth = Width;
     if (MathUtils.Random() > 0.3 * yOffset)
     {
         Life = MathUtils.Random().Lerp(0.8, 1.0);
     }
     else
     {
         Life = MathUtils.Random().Lerp(0.2, 0.4);
         Vy  *= 0.5;
     }
     Life     *= (Gravity / 2.2 + 1) / 2;
     LifeTotal = Life;
     Intensity = (yOffset + MathUtils.Random()) / 2.0;
     HeightAO  = MathUtils.Random().Lerp(0.4, 0.8);
 }
 private static void GenPiece(int size, string dest, FinOptions options)
 {
     if (options.SuperSampling > 1)
     {
         ResizeImage(GenPiece((int)(size * options.SuperSampling), options), size, size).Save(dest);
     }
     else
     {
         GenPiece(size, options).Save(dest);
     }
 }
        private static Image GenPiece(int size, FinOptions options)
        {
            var img = new Bitmap(size, size, PixelFormat.Format32bppArgb);

            using (var grp = Graphics.FromImage(img)) {
                for (int i = 0, t = (int)MathUtils.Random().Lerp(options.FinCount1, options.FinCount2); i < t; i++)
                {
                    new Fin((double)i / (t - 1), options).Run(grp, size);
                }
            }
            return(img);
        }
        private static Image GenMap(int size, int count, FinOptions options)
        {
            var b = new Bitmap(size * count, size, PixelFormat.Format32bppArgb);
            var g = Graphics.FromImage(b);

            g.Clear(Color.Black);
            g.InterpolationMode = InterpolationMode.HighQualityBicubic;
            for (var i = 0; i < count; i++)
            {
                if (options.SuperSampling > 1)
                {
                    g.DrawImage(ResizeImage(GenPiece((int)(size * options.SuperSampling), options), size, size), size * i, 0, size, size);
                }
                else
                {
                    g.DrawImage(GenPiece(size, options), size * i, 0, size, size);
                }
            }
            g.Dispose();

            var rect      = new Rectangle(0, 0, size * count, size);
            var bmpData   = b.LockBits(rect, ImageLockMode.ReadWrite, b.PixelFormat);
            var ptr       = bmpData.Scan0;
            var bytes     = bmpData.Stride * size;
            var rgbValues = new byte[bytes];

            System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes);
            for (var x = 0; x < size * count; x++)
            {
                for (var y = 0; y < size; y++)
                {
                    var position = (y * bmpData.Stride) + (x * Image.GetPixelFormatSize(bmpData.PixelFormat) / 8);
                    var alpha    = rgbValues[position + 3] / 255.0;
                    rgbValues[position + 1] = (byte)alpha.Lerp(229, rgbValues[position + 1]);
                }
            }
            System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, ptr, bytes);
            b.UnlockBits(bmpData);
            return(b);
        }
        public static void Main(string[] args)
        {
            var options = new FinOptions();

            foreach (var line in File.ReadAllLines("config.txt").Select(x => x.Split('=')).Where(x => x.Length == 2).Select(x => new {
                Key = x[0].Trim(),
                Value = x[1].Split(',').Select(FlexibleParser.TryParseDouble).Where(y => y.HasValue).Select(y => y.Value).ToList()
            }))
            {
                void Fill(ref double v1, ref double v2, List <double> values)
                {
                    if (values.Count == 0)
                    {
                        return;
                    }
                    v1 = values[0];
                    v2 = values.Count == 2 ? values[1] : v1;
                }

                switch (line.Key)
                {
                case "FinCount":
                    Fill(ref options.FinCount1, ref options.FinCount2, line.Value);
                    break;

                case "X":
                    Fill(ref options.X1, ref options.X2, line.Value);
                    break;

                case "Y":
                    Fill(ref options.Y1, ref options.Y2, line.Value);
                    break;

                case "Vx":
                    Fill(ref options.Vx1, ref options.Vx2, line.Value);
                    break;

                case "Vy":
                    Fill(ref options.Vy1, ref options.Vy2, line.Value);
                    break;

                case "Gravity":
                    Fill(ref options.Gravity1, ref options.Gravity2, line.Value);
                    break;

                case "Width":
                    Fill(ref options.Width1, ref options.Width2, line.Value);
                    break;

                case "CurveBack":
                    Fill(ref options.CurveBack1, ref options.CurveBack2, line.Value);
                    break;

                case "ResolutionSingle":
                    if (line.Value.Count > 0)
                    {
                        options.Resolution1 = line.Value[0];
                    }
                    break;

                case "ResolutionSet":
                    if (line.Value.Count > 0)
                    {
                        options.Resolution2 = line.Value[0];
                    }
                    break;

                case "CountSingle":
                    if (line.Value.Count > 0)
                    {
                        options.Count1 = line.Value[0];
                    }
                    break;

                case "CountSet":
                    if (line.Value.Count > 0)
                    {
                        options.Count2 = line.Value[0];
                    }
                    break;

                case "SuperSampling":
                    if (line.Value.Count > 0)
                    {
                        options.SuperSampling = line.Value[0];
                    }
                    break;

                default:
                    Console.Error.WriteLine($"Unknown key: {line.Key}");
                    break;
                }
            }

            var destination = Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location) ?? "", "generated");

            Directory.CreateDirectory(destination);
            for (var i = 0; i < (int)options.Count1; i++)
            {
                GenPiece((int)options.Resolution1, Path.Combine(destination, $"{i}.png"), options);
            }
            if ((int)options.Count2 > 0)
            {
                GenMap((int)options.Resolution2, (int)options.Count2, Path.Combine(destination, "atlas.png"), options);
            }
        }
 private static void GenMap(int size, int count, string dest, FinOptions options)
 {
     GenMap(size, count, options).Save(dest);
 }