C# 生成隨機地圖的一些可能需要的函數

之前在研究隨機地圖的生成的時候用了一部分點和線之間的關係,部分算法還沒有優化。

        List<Line> RandomLines(int count, int x, int y, bool allowIntersect,int bezierControl)
        {
            List<Line> lines = new List<Line>();
            while (lines.Count < count)
            {
                Line line = RandomLine(x, y, bezierControl);
                if ((!isIntersect(line, lines) || allowIntersect) && line.length > 2)
                {
                    lines.Add(line);
                }
            }
            return lines;
        }


        /// <summary>
        /// 獲得一個點周圍的點座標
        /// </summary>
        /// <param name="center"></param>
        /// <param name="bitmap"></param>
        /// <param name="distance"></param>
        /// <returns></returns>
        List<Point> getPointsAround(Point center, Bitmap bitmap, double distance = 1)
        {
            return getPointsAround(center, bitmap.Width, bitmap.Height, distance);
        }

        /// <summary>
        /// 獲得一個點周圍的點座標
        /// </summary>
        /// <param name="center"></param>
        /// <param name="width"></param>
        /// <param name="height"></param>
        /// <param name="distance"></param>
        /// <returns></returns>
        List<Point> getPointsAround(Point center, int width, int height, double distance = 1)
        {
            int x = center.X;
            int y = center.Y;
            List<Point> pointsAround = new List<Point>();
            pointsAround.Add(new Point(x - 1, y));
            pointsAround.Add(new Point(x + 1, y));
            pointsAround.Add(new Point(x, y - 1));
            pointsAround.Add(new Point(x, y + 1));

            pointsAround.RemoveAll(p => p.X < 0 || p.X >= width || p.Y < 0 || p.Y >= height);

            return pointsAround;
        }
        
        /// <summary>
        /// 獲得點到點羣間的距離
        /// </summary>
        /// <param name="point"></param>
        /// <param name="pointList"></param>
        /// <returns></returns>
        double getDistanceBetweenPointAndPoints(Point point,List<Point> pointList)
        {
            double minDistance = 9999999999;
            foreach(Point tmp in pointList)
            {
                minDistance = Math.Min(minDistance, getDistance(point, tmp));
            }
            return minDistance;
        }

        /// <summary>
        /// 建立一個隨機點
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns></returns>
        Point RandomPoint(int x, int y)
        {
            return new Point(random.Next(0, x - 1), random.Next(0, y - 1));
        }

        /// <summary>
        /// 添加貝塞爾控制點
        /// </summary>
        /// <param name="line"></param>
        void AddBezierControlPoint(ref Line line,int distance)
        {
            float k = (line.start.X - (float)line.end.X) / (line.start.Y - (float)line.end.Y);
            int xLength = line.end.X - line.start.X;
            int yLength = line.end.Y - line.start.Y;
            if (Math.Abs(xLength) > Math.Abs(yLength))
            {
                int randomXLength = xLength > 0 ? random.Next(0, xLength) : random.Next(xLength, 0);
                Point controlStart = new Point
                {
                    X = randomXLength + line.start.X,
                    Y = (int)(randomXLength / k + random.Next(-distance, distance) + line.start.Y)
                };
                randomXLength = xLength > 0 ? random.Next(0, xLength) : random.Next(xLength, 0);
                Point controlEnd = new Point
                {
                    X = randomXLength + line.start.X,
                    Y = (int)(randomXLength / k + random.Next(-distance, distance) + line.start.Y)
                };

                line.controlStart = controlStart;
                line.controlEnd = controlEnd;
            }
            else
            {
                int randomYLength = yLength > 0 ? random.Next(0, yLength) : random.Next(yLength, 0);
                line.controlStart = new Point((int)(randomYLength * k + random.Next(-distance, distance) + line.start.X), randomYLength+ line.start.Y);
                randomYLength = xLength > 0 ? random.Next(0, xLength) : random.Next(xLength, 0);
                line.controlEnd = new Point((int)(randomYLength * k + random.Next(-distance, distance) + line.start.X), randomYLength + line.start.Y);
            }
        }

        /// <summary>
        /// 畫一條隨機直線
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns></returns>
        Line RandomLine(int x, int y,int bezierControl)
        {
            Line line = new Line();
            line.start = RandomPoint(x, y);
            line.end = RandomPoint(x, y);
            AddBezierControlPoint(ref line, bezierControl);
            return line;
        }

        /// <summary>
        /// 將貝塞爾曲線畫到圖像上
        /// </summary>
        /// <param name="line"></param>
        void DrawLine(Line line,Bitmap bitmap)
        {
            GraphicsPath path = new GraphicsPath();
            path.AddBezier(line.start, line.controlStart, line.controlEnd, line.end);
            Graphics.FromImage(bitmap).DrawPath(Pens.White, path);
        }
        
        /// <summary>
        /// 將多條貝塞爾曲線畫到圖像上
        /// </summary>
        /// <param name="lines"></param>
        /// <param name="bitmap"></param>
        void DrawLines(List<Line> lines,Bitmap bitmap)
        {
            foreach(Line line in lines)
            {
                DrawLine(line,bitmap);
            }
        }
        
        private bool isIntersect(Line line,List<Line> lines)
        {
            foreach(Line tmpLine in lines)
            {
                if(isIntersect(line, tmpLine))
                {
                    return true;
                }
            }
            return false;
        }

        public class Line
        {
            public Point start;
            public Point end;
            public Point controlStart;
            public Point controlEnd;
            public double length
            {
                get
                {
                    return Math.Sqrt((start.X - end.X) ^ 2 + (start.Y - end.Y) ^ 2);
                }
            }
        }

        private double getDistance(Point point1, Point point2)
        {
            double lineLength = 0;
            lineLength = Math.Sqrt((point1.X - point2.X) * (point1.X - point2.X) + (point1.Y - point2.Y) * (point1.Y - point2.Y));
            return lineLength;
        }

        private double pointToLines(List<Line> lines, Point point)
        {
            double distance = 9999999999;
            foreach(Line line in lines)
            {
                distance = pointToLine(line, point) < distance ? pointToLine(line, point) : distance;
            }
            return distance;
        }

        private double pointToLine(Line line, Point point)
        {
            double space = 0;
            double a, b, c;
            a = getDistance(line.start, line.end);// 線段的長度      
            b = getDistance(line.start, point);// (x1,y1)到點的距離      
            c = getDistance(line.end, point);// (x2,y2)到點的距離      
            if (c <= 0.000001 || b <= 0.000001)
            {
                space = 0;
                return space;
            }
            if (a <= 0.000001)
            {
                space = b;
                return space;
            }
            if (c * c >= a * a + b * b)
            {
                space = b;
                return space;
            }
            if (b * b >= a * a + c * c)
            {
                space = c;
                return space;
            }
            double p = (a + b + c) / 2;// 半周長      
            double s = Math.Sqrt(p * (p - a) * (p - b) * (p - c));// 海倫公式求面積      
            space = 2 * s / a;// 返回點到線的距離(利用三角形面積公式求高)      
            return space;
        }

        /// <summary>
        /// 判斷兩條線段是否相交
        ///   參考: https://www.cnblogs.com/Kconnie/p/4311745.html
        /// </summary>
        /// <param name="line1"></param>
        /// <param name="line2"></param>
        /// <param name="intersection"></param>
        /// <returns></returns>
        private bool isIntersect(Line line1, Line line2)
        {
            Point a = line1.start;
            Point b = line1.end;
            Point c = line2.start;
            Point d = line2.end;

            Point intersection = new Point();

            //判斷異常
            if (Math.Abs(b.X - a.Y) + Math.Abs(b.X - a.X) + Math.Abs(d.Y - c.Y) + Math.Abs(d.X - c.X) == 0)
            {
                if (c.X - a.X == 0)
                {
                    return true;//ABCD是同一個點
                }
                else
                {
                    return false;//AB是一個點,CD是一個點,且AC不同
                }
            }

            if (Math.Abs(b.Y - a.Y) + Math.Abs(b.X - a.X) == 0)
            {
                if ((a.X - d.X) * (c.Y - d.Y) - (a.Y - d.Y) * (c.X - d.X) == 0)
                {
                    return true;//A、B是一個點,且在CD線段上
                }
                else
                {
                    return false;//A、B是一個點,且不在CD線段上
                }
            }

            if (Math.Abs(d.Y - c.Y) + Math.Abs(d.X - c.X) == 0)
            {
                if ((d.X - b.X) * (a.Y - b.Y) - (d.Y - b.Y) * (a.X - b.X) == 0)
                {
                    return true;//C、D是一個點,且在AB線段上
                }
                else
                {
                    return false;//C、D是一個點,且不在AB線段上
                }
            }


            if ((b.Y - a.Y) * (c.X - d.X) - (b.X - a.X) * (c.Y - d.Y) == 0)
            {
                return false;//平行
            }

            intersection.X = ((b.X - a.X) * (c.X - d.X) * (c.Y - a.Y) - c.X * (b.X - a.X) * (c.Y - d.Y) + a.X * (b.Y - a.Y) * (c.X - d.X)) / ((b.Y - a.Y) * (c.X - d.X) - (b.X - a.X) * (c.Y - d.Y));
            intersection.Y = ((b.Y - a.Y) * (c.Y - d.Y) * (c.X - a.X) - c.Y * (b.Y - a.Y) * (c.X - d.X) + a.Y * (b.X - a.X) * (c.Y - d.Y)) / ((b.X - a.X) * (c.Y - d.Y) - (b.Y - a.Y) * (c.X - d.X));


            if ((intersection.X - a.X) * (intersection.X - b.X) <= 0 && (intersection.X - c.X) * (intersection.X - d.X) <= 0 && (intersection.Y - a.Y) * (intersection.Y - b.Y) <= 0 && (intersection.Y - c.Y) * (intersection.Y - d.Y) <= 0)
            {
                return true; //相交
            }
            else
            {
                return false; //相交但不在線段上
            }
        }

        private Color getColor(int height,int waterHeight = 35)
        {
            height = Math.Min(255, height);
            height = Math.Max(0, height);
            if (height > waterHeight)
                return Color.FromArgb(height, height, height);
            else
                return Color.DarkBlue;
        }

        /// <summary>
        /// 獲取周圍點的平均高度
        /// </summary>
        /// <param name="center"></param>
        /// <param name="bitmap"></param>
        /// <param name="minHeight"></param>
        /// <param name="distance"></param>
        /// <returns></returns>
        int getPointsAroundHeight(Point center, Bitmap bitmap, int minHeight, double distance = 1)
        {
            int height = 0;
            List<Point> points = getPointsAround(center, bitmap, distance);
            for (int index = 0; index < points.Count; index++)
            {
                height += bitmap.GetPixel(points[index].X, points[index].Y).R;
            }
            if (height / points.Count < minHeight)
                return height / points.Count;
            else
                return bitmap.GetPixel(center.X, center.Y).R;
        }

        /// <summary>
        /// 獲取周圍點的平均高度
        /// </summary>
        /// <param name="center"></param>
        /// <param name="bitmap"></param>
        /// <param name="distance"></param>
        /// <returns></returns>
        int getPointsAroundHeight(Point center, Bitmap bitmap, double distance = 1)
        {
            int height = 0;
            List<Point> points = getPointsAround(center, bitmap, distance);
            for (int index = 0; index < points.Count; index++)
            {
                height += bitmap.GetPixel(points[index].X, points[index].Y).R;
            }
            return height / points.Count;
        }

等我過年回來補充註釋,最後祝大家身體健康

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章