C# :: Aufgabe #219

1 Lösung Lösung öffentlich

Perlin Noise Algorithm

Fortgeschrittener - C# von Z3RP - 20.06.2018 um 16:18 Uhr
Die Aufgabe besteht einen Perlin Noise Algorithmus zu programmieren der 1D-2D funktioniert.
So genannte Noise Algorithmen werden zum Beispiel dazu verwendet Height Maps zu generieren oder Feuertexturen.
In Spielen werden diese of dazu verwendet das Terrain zu formen. Zum Beispiel in Minecraft, Terraria und vieles mehr.
Noise soll eine natürliche zufälligkeit darstellen.

Bitte nicht mich verurteilen falls falsch liege.

Am besten wäre es wenn man am Ende eine Methode/Klasse hat.

Eingaben Werte:
-int X ( Koordinate )
-int Y ( Koordinate )

Ausgabe:
-ein Float zwischen 0-1

Hier noch ein paar linke zu dem Thema und ein paar Bilder im Anhang:
http://flafla2.github.io/2014/08/09/perlinnoise.html
https://en.wikipedia.org/wiki/Perlin_noise

Lösungen:

vote_ok
von Exception (7090 Punkte) - 06.09.2018 um 09:03 Uhr
Parameter für Anwendung:
1. Welcher Generator wird ausgeführt - "1d", "2d" oder "3d"?
2. Width (in Pixel)
3. Height (in Pixel)
4. Pfad, unter dem die SVG-Datei gespeichert werden soll (C:\\Users\\...\\Folder\\)

Programm.cs
Hier wird entschieden, welcher Generator aufgerufen wird, siehe 1. Parameter.
Quellcode ausblenden C#-Code
using System;
using System.Collections.Generic;
using System.IO;

using Generator;


namespace MapBuilder
{
    class Program
    {
        private static DateTime[] Times;
        private static Generator1d g1d;
        private static Generator2d g2d;
        private static Generator3d g3d;
        private static Dictionary<string, int> Size;
        

        static void Main(string[] param)
        {
            PrintLogo();
            InitiateTime();

            int Width, Height;

            try
            {
                if(param.Length != 4)
                {
                    throw new ArgumentException("Es müssen exakt vier Parameter gegeben sein.");
                }
                if(int.TryParse(param[1], out Width) == false || int.TryParse(param[2], out Height) == false || Width < 100 || Height < 100)
                {
                    throw new ArgumentException("Parameter für Breite/Höhe ungültig.\nBeide müssen mindestens 100px groß sein.");
                }
                if(!Directory.Exists(param[3]))
                {
                    throw new ArgumentException("Pfad \"{0}\"ist nicht gültig.", param[3]);
                }

                Dictionary<string, int> Sizes = new Dictionary<string, int>();
                Sizes.Add("Width", Width);
                Sizes.Add("Height", Height);

                switch (param[0])
                {
                    case  "1":
                    case "1d":
                    case "1D":
                        g1d = new Generator1d(Sizes, param[3]);
                        break;
                    case  "2":
                    case "2d":
                    case "2D":
                        g2d = new Generator2d(Sizes, param[3]);
                        break;
                    case  "3":
                    case "3d":
                    case "3D":
                        Sizes.Add("Depth", Width);
                        g3d = new Generator3d(Sizes, param[3]);
                        break;
                    default:
                        throw new ArgumentException("Parameter \"{0}\" existiert nicht.", param[0]);
                }
            }
            catch (Exception ex)
            {
                Console.ForegroundColor = ConsoleColor.Red;
                Console.WriteLine("+-------------------------------------------------------+");
                Console.WriteLine(ex.Message);
                Console.WriteLine("+-------------------------------------------------------+");
                Console.ResetColor();
            }
            finally
            {
                Times[1] = DateTime.Now;
                Console.WriteLine("+-------------------------------------------------------+");
                Console.WriteLine("Programm beendet.\nDauer: {0}", (Times[1] - Times[0]));
                Console.ReadKey();
            }
        }

        private static void InitiateTime()
        {
            Times = new DateTime[2];
            Times[0] = DateTime.Now;
        }
        private static void PrintLogo()
        {
            Console.BackgroundColor = ConsoleColor.DarkGreen;
            Console.ForegroundColor = ConsoleColor.Black;
            Console.WriteLine("+-------------------------------------------------------+");
            Console.WriteLine("|       A PROGRAM FOR GENERATING VIRTUAL \"TERRAIN\"      |");
            Console.WriteLine("|                                                       |");
            Console.WriteLine("|  #SvgStyle                             by: Exception  |");
            Console.WriteLine("+-------------------------------------------------------+");
            Console.ResetColor();
            Console.WriteLine();
        }
    }
}


Generator.cs
Quellcode ausblenden C#-Code
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;

using Svg;

namespace Generator
{
    class Generator
    {
        private const string PATTERN_FILENAME = @"^[a-zA-Z0-9\-_]{1,20}\.svg$";

        protected string Path;
        protected List<string> Map;
        protected Dictionary<string, int> Size;

        public Generator(Dictionary<string, int> Size, string Path)
        {
            this.Size = new Dictionary<string, int>();
            this.Size = Size;
            this.Path = Path;

            this.Map = new List<string>();
            this.Map.AddRange(SvgFactory.BuildSvgOpening(this.Size));
        }

        protected void AddGrid()
        {
            this.Map.Add("<g id=\"Grid\">");

            // --- horizontal lines ---
            this.Map.Add("<g id=\"Horizontal\">");
            this.Map.Add(SvgFactory.BuildLine(
                            new Dictionary<string, int>() { { "x1", 0 }, { "y1", (this.Size["Height"] / 2) }, { "x2", this.Size["Width"] }, { "y2", (this.Size["Height"] / 2) } },
                            new Color()
                            )
                         );
            for (int y = 0; y <= this.Size["Height"]; y += (this.Size["Height"] / 10))
            {
                this.Map.Add(SvgFactory.BuildLine(
                            new Dictionary<string, int>() { { "x1", 0 }, { "y1", y }, { "x2", this.Size["Width"] }, { "y2", y } },
                            new Color(),
                            "0.5"
                            )
                         );
            }
            this.Map.Add("</g>");

            // --- vertical lines ---
            this.Map.Add("<g id=\"Veritcal\">");
            this.Map.Add(SvgFactory.BuildLine(
                            new Dictionary<string, int>() { { "x1", (this.Size["Width"] / 2) }, { "y1", 0 }, { "x2", (this.Size["Width"] / 2) }, { "y2", this.Size["Height"] } },
                            new Color()
                            )
             );
            for (int x = 0; x <= this.Size["Width"]; x += (this.Size["Width"] / 10))
            {
                this.Map.Add(SvgFactory.BuildLine(
                            new Dictionary<string, int>() { { "x1", x }, { "y1", 0 }, { "x2", x }, { "y2", this.Size["Height"] } },
                            new Color(),
                            "0.5"
                            )
                         );
            }
            this.Map.Add("</g>");

            this.Map.Add("</g>");
        }

        protected void BuildFile(string FileName = "result.svg")
        {
            if(!validateFileName(FileName))
            {
                throw new ArgumentException("Dateiname \"{0}\" nicht gültig.", FileName);
            }

            this.Map.AddRange(SvgFactory.BuildSvgClosing());

            using (FileStream fs = new FileStream(this.Path+FileName, FileMode.Create, FileAccess.Write))
            {
                using (StreamWriter sr = new StreamWriter(fs))
                {
                    foreach (string line in this.Map)
                    {
                        sr.WriteLine(line);
                    }
                }
            }

            Console.WriteLine("Datei erstellt unter \"{0}\"", (this.Path+FileName));
        }

        private bool validateFileName(string fileName)
        {
            return new Regex(PATTERN_FILENAME).IsMatch(fileName);
        }

        /// <returns>Zahl zwischen -1.0 und 1.0</returns>
        public static double GetNumber(double minimum = -1.0, double maximum = 1.0)
        {
            System.Threading.Thread.Sleep(5);    // für Instanziierung von Random r
            Random r = new Random();

            return r.NextDouble() * (maximum - minimum) + minimum;
        }

        public static int GetNumber(int lower, int upper)
        {
            System.Threading.Thread.Sleep(5);
            return new Random().Next(lower, upper);
        }
    }
}


Generator1d.cs
Quellcode ausblenden C#-Code
using System.Collections.Generic;

using Svg;

namespace Generator
{
    class Generator1d : Generator
    {
        private List<double> Positions;

        public Generator1d(Dictionary<string, int> Size, string Path) : base (Size, Path)
        {
            this.Positions = new List<double>();
            this.Generate();
        }

        private void Generate()
        {
            this.AddGrid();
            this.BuildLine();
            this.BuildFile();
        }

        private void BuildLine(int Width = 50)
        {
            this.Map.Add("<g id=\"Line\">");

            // Y-Koordinaten erzeugen
            for(int y = 0; y <= this.Size["Width"]; y+=Width)
            {
                this.Positions.Add(Generator.GetNumber());
            }

            this.TranslatePoints();

            this.Map.Add(SvgFactory.BuldPolyline(this.Positions.ToArray(), new Color(), "1.0", this.Size, Width));

            this.Map.Add("</g>");
        }

        /// <summary>
        /// Passt die Werte [-1...1] auf die Zeichenflächengröße an.
        /// </summary>
        private void TranslatePoints()
        {
            for (int index = 0; index < this.Positions.Count; index++)
            {
                if(this.Positions[index] > 0.0)
                {
                    this.Positions[index] = this.Positions[index] * this.Size["Height"];
                }
                else
                {
                    this.Positions[index] = (this.Positions[index] * this.Size["Height"]) / -1.0;
                }
            }
        }
    }
}


Generator2d.cs
Quellcode ausblenden C#-Code
using System.Collections.Generic;

using Svg;

namespace Generator
{
    class Generator2d : Generator
    {
        private List<List<double>> Positions;
        private Dictionary<string, int> Size;

        public Generator2d(Dictionary<string, int> Size, string Path) : base (Size, Path)
        {
            this.Positions = new List<List<double>>();
            this.Size = Size;
            this.Generate();
        }

        private void Generate()
        {
            this.AddGrid();

            this.BuildRectangles();

            this.BuildFile();
        }

        private void BuildRectangles()
        {
            Dictionary<string, int> Position = new Dictionary<string, int>();
            Dictionary<string, int> Size = new Dictionary<string, int>();

            Position.Add("Width", 0);
            Position.Add("Height", 0);
            Size.Add("Width", this.Size["Height"] / 50);
            Size.Add("Height", this.Size["Height"] / 50);

            this.Map.Add("<g id=\"Rectangles\">");

            for (int y = 0; y < this.Size["Width"]; y += this.Size["Height"] / 50)
            {
                for (int x = 0; x < this.Size["Height"]; x += this.Size["Height"] / 50)
                {
                    Position["Width"] = x;
                    Position["Height"] = y;

                    Dictionary<string, int> colorinformation = new Dictionary<string, int>();
                    int number = Generator.GetNumber(0, 255);
                    colorinformation.Add("R", number);
                    colorinformation.Add("G", number);
                    colorinformation.Add("B", number);

                    this.Map.Add(SvgFactory.BuildRectangle(Position, Size, new Color(colorinformation), new Color(colorinformation)));

                    System.Threading.Thread.Sleep(5);
                }
            }

            this.Map.Add("</g>");
        }
    }
}


Generator3d.cs, laut Aufgabenstellungen waren ja nur 1d und 2d benötigt aber who cares? :D
Quellcode ausblenden C#-Code
using System.Collections.Generic;

using Svg;

namespace Generator
{
    class Generator3d : Generator
    {
        private int rows;
        private int cols;
        private double[,] tiles;

        public Generator3d(Dictionary<string, int> Size, string Path) : base (Size, Path)
        {
            this.rows = Size["Width"]  / 10;
            this.cols = Size["Height"] / 10;
            this.tiles = new double[this.cols, this.rows];

            this.Generate();
        }

        private void Generate()
        {
            this.AddGrid();
            this.AddTerrain();
            this.BuildFile();
        }


        /// <summary>
        /// skewX = 45°
        /// Scale Y = 50%, da 40° skewX
        /// Translation = 50% der Größe, für Unterkante.
        /// </summary>
        private void AddTerrain()
        {
            this.Map.Add("<g id=\"terrain\">");

            for (int col = 0; col < 10; col++)
            {
                for (int row = 0; row < 10; row++)
                {
                    double perlin = GetNumber(0, 25);

                    Dictionary<string, int> Position = new Dictionary<string, int>();
                    Position["Width"] = row * this.rows;
                    Position["Height"] = col * this.cols;

                    Dictionary<string, int> Size = new Dictionary<string, int>();
                    Size["Width"] = this.Size["Width"] / 10;
                    Size["Height"] = this.Size["Height"] / 10;

                    double valY = ((this.Size["Height"] / 20) + perlin);

                    this.Map.Add(("<g transform=\"rotate(0 0 0) translate(25 " + valY + ") skewX(-45) scale(1 0.5)\">").Replace(',', '.'));
                    this.Map.Add(
                        SvgFactory.BuildRectangle(
                                Position,
                                Size,
                                new Color(),
                                new Color("red")
                            )
                        );

                    this.Map.Add("</g>");
                }
            }

            this.Map.Add("</g>");
        }

        private new void AddGrid()
        {
            this.Map.Add("<g id=\"Grid\">");

            // --- horizontal lines ---
            this.Map.Add("<g id=\"Horizontal\">");
            this.Map.Add(SvgFactory.BuildLine(
                            new Dictionary<string, int>() { { "x1", 0 }, { "y1", (this.Size["Height"] / 2) }, { "x2", this.Size["Width"] }, { "y2", (this.Size["Height"] / 2) } },
                            new Color()
                            )
                         );
            for (int y = 0; y <= this.Size["Height"]; y += (this.Size["Height"] / 10))
            {
                this.Map.Add(SvgFactory.BuildLine(
                            new Dictionary<string, int>() { { "x1", 0 }, { "y1", y }, { "x2", this.Size["Width"] }, { "y2", y } },
                            new Color(),
                            "0.5"
                            )
                         );
            }
            this.Map.Add("</g>");

            // --- vertical lines ---
            this.Map.Add("<g id=\"Veritcal\">");
            this.Map.Add(SvgFactory.BuildLine(
                            new Dictionary<string, int>() { { "x1", (this.Size["Width"] / 2) }, { "y1", 0 }, { "x2", (this.Size["Width"] / 2) }, { "y2", this.Size["Height"] } },
                            new Color()
                            )
             );
            for (int x = 0; x <= this.Size["Width"]; x += (this.Size["Width"] / 10))
            {
                this.Map.Add(SvgFactory.BuildLine(
                            new Dictionary<string, int>() { { "x1", x }, { "y1", 0 }, { "x2", x }, { "y2", this.Size["Height"] } },
                            new Color(),
                            "0.5"
                            )
                         );
            }
            this.Map.Add("</g>");

            this.Map.Add("</g>");
        }
    }
}


Color.cs
Quellcode ausblenden C#-Code
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;

namespace Svg
{
    class Color
    {
        public Dictionary<string, int> Value;

        public Color(Dictionary<string, int> color)
        {
            if(!validateColorParameter(color))
            {
                throw new ArgumentException("Farbwerte haben ungültige Werte.");
            }

            this.Value = color;
        }

        public Color(string colorName = "")
        {
            this.Value = new Dictionary<string, int>();

            switch (colorName)
            {
                case "red":
                    this.Value.Add("R", 255);
                    this.Value.Add("G", 0);
                    this.Value.Add("B", 0);
                    break;
                case "green":
                    this.Value.Add("R", 0);
                    this.Value.Add("G", 255);
                    this.Value.Add("B", 0);
                    break;
                case "blue":
                    this.Value.Add("R", 0);
                    this.Value.Add("G", 0);
                    this.Value.Add("B", 255);
                    break;
                case "black":
                default:
                    this.Value.Add("R", 0);
                    this.Value.Add("G", 0);
                    this.Value.Add("B", 0);
                    break;
            }
        }

        private bool validateColorParameter(Dictionary<string, int> color)
        {
            foreach (int c in color.Values)
            {
                if(c < 0 || c > 255)
                {
                    return false;
                }
            }

            return true;
        }
    }
}


SvgFactory.cs
Quellcode ausblenden C#-Code
using System;
using System.Collections.Generic;

using Generator;
using System.Text.RegularExpressions;
using System.IO;

namespace Svg
{
    static class SvgFactory
    {
        private const string PATTERN_STROKE_WIDTH = @"^[0-9]{1,}(\.[0-9]{1,}|)$";

        public static List<string> BuildSvgOpening(Dictionary<string, int> Size)
        {
            List<string> start = new List<string>();

            start.Add("<!DOCTYPE html>");
            start.Add("<html>");
            start.Add("<body>");
            start.Add("<svg style=\"background-color:rgb(240,240,240);\" height=\"" + Size["Height"] + "\" width=\"" + Size["Width"] + "\" xmlns=\"http://www.w3.org/2000/svg\">");

            return start;
        }

        public static List<string> BuildSvgClosing()
        {
            List<string> end = new List<string>();

            end.Add("</svg>");
            end.Add("</body>");
            end.Add("</html>");

            return end;
        }

        private static string BuildColor(Color c)
        {
            return "rgb(" + c.Value["R"] + "," + c.Value["G"] + "," +c.Value["B"] + ")";
        }

        private static string BuildStroke(Color c, string strokeWidth)
        {
            if(!new Regex(PATTERN_STROKE_WIDTH).IsMatch(strokeWidth))
            {
                throw new ArgumentException("strokeWidth ist ungültig.");
            }

            return "stroke:" + BuildColor(c) + ";stroke-width:" + strokeWidth;
        }

        private static string BuildPositions(Dictionary<string, int> Positions)
        {
            if (Positions.Count == 2)
            {
                return "x=\"" + Positions["x"] + "\" y=\"" + Positions["y"] + "\"";
            }
            else if (Positions.Count == 4)
            {
                return "x1=\"" + Positions["x1"] + "\" y1=\"" + Positions["y1"] + "\" x2=\"" + Positions["x2"] + "\" y2=\"" + Positions["y2"] + "\"";
            }
            else
            {
                throw new ArgumentException("Positions muss zwei oder vier Positionsangaben enthalten.");
            }
        }

        private static string BuildPoints(double[] Points, Dictionary<string, int> Size, int Width)
        {
            string points = "points=\"";

            for (int index = 0; index < Points.Length; index++)
            {
                points += index * Width + "%" + Points[index] + " ";
            }

            points = points.Substring(0, points.Length - 1); // letztes Leerzeichen entfernen.

            // Zeichen ersetzen.
            points = points.Replace(',', '.');
            points = points.Replace('%', ',');

            points += "\""; // letztes Anfürhungszeichen hinzufügen

            return points;
        }

        public static string BuildText(Dictionary<string, int> Position, Color c, string Text)
        {
            return "<text " + BuildPositions(Position) + ">" + Text + "</text>";
        }

        public static string BuildLine(Dictionary<string, int> Positions, Color c, string strokeWidth = "1.0")
        {
            return "<line " + BuildPositions(Positions) + " style=\"" + BuildStroke(c, strokeWidth) + "\" />";
        }

        public static string BuldPolyline(double[] Points, Color c, string strokeWidth, Dictionary<string, int> Size, int Width)
        {
            string line = "<polyline "+ BuildPoints(Points, Size, Width) +" style=\"fill:none;"+BuildStroke(c, strokeWidth)+"\" />";

            return line;
        }

        public static string BuildRectangle(Dictionary<string, int> Position, Dictionary<string, int> Size, Color border, Color fill = null, string strokeWidth = "1.0")
        {
            string rectangle = "<rect ";

            rectangle += "x=\"" + Position["Width"] + "\" ";
            rectangle += "y=\"" + Position["Height"] + "\" ";
            rectangle += "width=\"" + Size["Width"] + "\" ";
            rectangle += "height=\"" + Size["Height"] + "\" ";
            rectangle += "style=\""+BuildStroke(border, strokeWidth)+";";
            rectangle += (fill != null) ? "fill:" + BuildColor(fill) + "\" />" : " />";

            return rectangle;
        }
    }
}
1800860

Du scheinst einen AdBlocker zu nutzen. Ich würde mich freuen, wenn du ihn auf dieser Seite deaktivierst und dich davon überzeugst, dass die Werbung hier nicht störend ist.