Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/323.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 获取最小数量的点以创建相同的多边形_C#_Algorithm_Polygon - Fatal编程技术网

C# 获取最小数量的点以创建相同的多边形

C# 获取最小数量的点以创建相同的多边形,c#,algorithm,polygon,C#,Algorithm,Polygon,我想做什么: 我希望从多边形中获得最小数量的点,以创建相同的多边形: 例如,如果我有这个多边形: (0,0)、(1,0)、(2,0)、(3,0)、(4,0)、(0,4)、(4,4) 它将创建一个位置为0、0、宽度为4、高度为4的多边形 如果我将该多边形输入到假设的算法中,它将返回: (0,0)、(4,0)、(0,4)、(4,4) 我为什么要这样做: 我正在创建一个游戏,游戏有动画,每个动画都有自己的图像和多边形(图像的边界),我已经有了动画的图像,但是我没有多边形,当然,我可以自己创建多边形,但

我想做什么:

我希望从多边形中获得最小数量的点,以创建相同的多边形:

例如,如果我有这个多边形:

(0,0)、(1,0)、(2,0)、(3,0)、(4,0)、(0,4)、(4,4)

它将创建一个位置为0、0、宽度为4、高度为4的多边形

如果我将该多边形输入到假设的算法中,它将返回:

(0,0)、(4,0)、(0,4)、(4,4)

我为什么要这样做:

我正在创建一个游戏,游戏有动画,每个动画都有自己的图像和多边形(图像的边界),我已经有了动画的图像,但是我没有多边形,当然,我可以自己创建多边形,但是手动为100多个图像创建多边形会让人筋疲力尽,不要谈论添加/修改动画

我所尝试的:

我的想法是:

逐像素扫描图像,检查像素是否为空,如果不是空,则将其添加到列表中。完成后,使用某种算法获取最小数量的点以创建相同的多边形

我做了一些研究,我认为LLTS(Long Live the Square)算法就是我所需要的,所以我用C#编写了这段代码:

最后,我运行了这个程序,我得到的是:

这不完全是我的想法,至少可以说,我希望它是这样的:

有什么想法吗

编辑-最终解决了它

我使用的是的答案,然后,我使用我创建的函数最小化了结果中的点数:

    private static List<Point> MinimizePoints(List<Point> points)
    {
        if (points.Count < 3)
        {
            return points;
        }
        List<Point> minimumPoints = new List<Point>(points);

        for (int i = minimumPoints.Count - 1; i > 2; i -= 3)
        {
            List<Point> currentPoints = minimumPoints.GetRange(i - 3, 3);
            try
            {
                if ((currentPoints[2].X - currentPoints[0].X) / (currentPoints[1].X - currentPoints[0].X) ==
                    (currentPoints[2].Y - currentPoints[0].Y) / (currentPoints[1].Y - currentPoints[0].Y))
                {
                    minimumPoints.Remove(minimumPoints[i + 1]);
                }
            }
            catch (DivideByZeroException)
            {
                // Ignore
            }
        }
        return minimumPoints;
    }
私有静态列表最小化点(列表点)
{
如果(点数<3)
{
返回点;
}
列表最小点数=新列表(点数);
对于(int i=最小点数。计数-1;i>2;i-=3)
{
列出currentPoints=minimumPoints.GetRange(i-3,3);
尝试
{
if((currentPoints[2].X-currentPoints[0].X)/(currentPoints[1].X-currentPoints[0].X)==
(currentPoints[2].Y-currentPoints[0].Y)/(currentPoints[1].Y-currentPoints[0].Y))
{
最小点数。移除(最小点数[i+1]);
}
}
捕获(除零异常)
{
//忽略
}
}
返回最低点数;
}
我用的是的答案

第二次编辑-更优化的解决方案

我没有使用自己的meh算法来减少点,而是使用Ramer–Douglas–Peucker算法并将ε(公差)设置为0。以下是我使用的实现:

private static class DouglasPeuckerReduction
    {
        public static Point[] ReducePoints(Point[] existingPolygon)
        {
            if (existingPolygon == null || existingPolygon.Length < 3)
                return existingPolygon;

            int firstPoint = 0;
            int lastPoint = existingPolygon.Length - 1;
            List<int> pointIndexsToKeep = new List<int>();

            //Add the first and last index to the keepers
            pointIndexsToKeep.Add(firstPoint);
            pointIndexsToKeep.Add(lastPoint);

            //The first and the last point cannot be the same
            while (existingPolygon[firstPoint].Equals(existingPolygon[lastPoint]))
            {
                lastPoint--;
            }

            ReducePoints(existingPolygon, firstPoint, lastPoint,
                0, ref pointIndexsToKeep);

            pointIndexsToKeep.Sort();
            return pointIndexsToKeep.Select(index => existingPolygon[index]).ToArray();
        }

        /// <summary>
        /// Douglases the peucker reduction.
        /// </summary>
        /// <param name="points">The points.</param>
        /// <param name="firstPoint">The first point.</param>
        /// <param name="lastPoint">The last point.</param>
        /// <param name="tolerance">The tolerance.</param>
        /// <param name="pointIndexesToKeep">The point index to keep.</param>
        private static void ReducePoints(IReadOnlyList<Point> points, int firstPoint, int lastPoint, double tolerance,
            ref List<int> pointIndexesToKeep)
        {
            double maxDistance = 0;
            int indexFarthest = 0;

            for (int index = firstPoint; index < lastPoint; index++)
            {
                double distance = PerpendicularDistance
                    (points[firstPoint], points[lastPoint], points[index]);
                if (distance > maxDistance)
                {
                    maxDistance = distance;
                    indexFarthest = index;
                }
            }

            if (maxDistance > tolerance && indexFarthest != 0)
            {
                //Add the largest point that exceeds the tolerance
                pointIndexesToKeep.Add(indexFarthest);

                ReducePoints(points, firstPoint,
                    indexFarthest, tolerance, ref pointIndexesToKeep);
                ReducePoints(points, indexFarthest,
                    lastPoint, tolerance, ref pointIndexesToKeep);
            }
        }

        /// <summary>
        /// The distance of a point from a line made from point1 and point2.
        /// </summary>
        /// <param name="pt1">The PT1.</param>
        /// <param name="pt2">The PT2.</param>
        /// <param name="p">The p.</param>
        /// <returns></returns>
        private static double PerpendicularDistance
            (Point Point1, Point Point2, Point Point)
        {
            //Area = |(1/2)(x1y2 + x2y3 + x3y1 - x2y1 - x3y2 - x1y3)|   *Area of triangle
            //Base = v((x1-x2)²+(x1-x2)²)                               *Base of Triangle*
            //Area = .5*Base*H                                          *Solve for height
            //Height = Area/.5/Base

            double area = Math.Abs(.5 * (Point1.X * Point2.Y + Point2.X *
                                         Point.Y + Point.X * Point1.Y - Point2.X * Point1.Y - Point.X *
                                         Point2.Y - Point1.X * Point.Y));
            double bottom = Math.Sqrt(Math.Pow(Point1.X - Point2.X, 2) +
                                      Math.Pow(Point1.Y - Point2.Y, 2));
            double height = area / bottom * 2;

            return height;

        }
    }
私有静态类DouglasPeuckerReduction
{
公共静态点[]还原点(点[]现有多边形)
{
if(existingPolygon==null | | existingPolygon.Length<3)
返回现有多边形;
int firstPoint=0;
int lastPoint=existingPolygon.Length-1;
List POINTINDEXSTOKEP=新列表();
//将第一个和最后一个索引添加到keepers
pointIndexsToKeep.Add(firstPoint);
pointIndexsToKeep.Add(lastPoint);
//第一点和最后一点不能相同
while(existingPolygon[firstPoint]。等于(existingPolygon[lastPoint]))
{
最后一点--;
}
还原点(现有多边形、第一点、最后点、,
0,参考点IndexTokep);
pointIndexsToKeep.Sort();
返回pointIndexsToKeep.Select(index=>existingPolygon[index]).ToArray();
}
/// 
///道格拉斯的佩克还原。
/// 
///重点。
///第一点。
///最后一点。
///宽容。
///要保留的点索引。
私有静态void ReducePoints(IReadOnlyList点、int firstPoint、int lastPoint、双公差、,
参考列表点INDEXESTOKEP)
{
双最大距离=0;
int indexFarthest=0;
对于(int index=firstPoint;index最大距离)
{
最大距离=距离;
indexFarthest=索引;
}
}
如果(最大距离>公差和索引最短!=0)
{
//添加超出公差的最大点
pointIndexesToKeep.Add(indexforthest);
还原点(点、第一点、,
INDEX测试、公差、参考点INDEXESTOKEP);
还原点(点、索引、大地测量、,
最后一点,公差,参考点IndexeEP);
}
}
/// 
///点与点1和点2形成的直线之间的距离。
/// 
///PT1。
///PT2。
///p。
/// 
专用静态双垂直度距离
(点1、点2、点1)
{
//面积=|(1/2)(x1y2+x2y3+x3y1-x2y1-x3y2-x1y3)|*三角形面积
//底面=v((x1-x2)²+(x1-x2)²)*三角形底面*
//面积=.5*基准*H*高度求解
//高度=面积/.5/基底
双面积=Math.Abs(.5*(点1.X*点2.Y+点2.X*
Point.Y+Point.X*Point1.Y-Point2.X*Point1.Y-Point.X*
点2.Y-点1.X*
    private static List<Point> MinimizePoints(List<Point> points)
    {
        if (points.Count < 3)
        {
            return points;
        }
        List<Point> minimumPoints = new List<Point>(points);

        for (int i = minimumPoints.Count - 1; i > 2; i -= 3)
        {
            List<Point> currentPoints = minimumPoints.GetRange(i - 3, 3);
            try
            {
                if ((currentPoints[2].X - currentPoints[0].X) / (currentPoints[1].X - currentPoints[0].X) ==
                    (currentPoints[2].Y - currentPoints[0].Y) / (currentPoints[1].Y - currentPoints[0].Y))
                {
                    minimumPoints.Remove(minimumPoints[i + 1]);
                }
            }
            catch (DivideByZeroException)
            {
                // Ignore
            }
        }
        return minimumPoints;
    }
private static class DouglasPeuckerReduction
    {
        public static Point[] ReducePoints(Point[] existingPolygon)
        {
            if (existingPolygon == null || existingPolygon.Length < 3)
                return existingPolygon;

            int firstPoint = 0;
            int lastPoint = existingPolygon.Length - 1;
            List<int> pointIndexsToKeep = new List<int>();

            //Add the first and last index to the keepers
            pointIndexsToKeep.Add(firstPoint);
            pointIndexsToKeep.Add(lastPoint);

            //The first and the last point cannot be the same
            while (existingPolygon[firstPoint].Equals(existingPolygon[lastPoint]))
            {
                lastPoint--;
            }

            ReducePoints(existingPolygon, firstPoint, lastPoint,
                0, ref pointIndexsToKeep);

            pointIndexsToKeep.Sort();
            return pointIndexsToKeep.Select(index => existingPolygon[index]).ToArray();
        }

        /// <summary>
        /// Douglases the peucker reduction.
        /// </summary>
        /// <param name="points">The points.</param>
        /// <param name="firstPoint">The first point.</param>
        /// <param name="lastPoint">The last point.</param>
        /// <param name="tolerance">The tolerance.</param>
        /// <param name="pointIndexesToKeep">The point index to keep.</param>
        private static void ReducePoints(IReadOnlyList<Point> points, int firstPoint, int lastPoint, double tolerance,
            ref List<int> pointIndexesToKeep)
        {
            double maxDistance = 0;
            int indexFarthest = 0;

            for (int index = firstPoint; index < lastPoint; index++)
            {
                double distance = PerpendicularDistance
                    (points[firstPoint], points[lastPoint], points[index]);
                if (distance > maxDistance)
                {
                    maxDistance = distance;
                    indexFarthest = index;
                }
            }

            if (maxDistance > tolerance && indexFarthest != 0)
            {
                //Add the largest point that exceeds the tolerance
                pointIndexesToKeep.Add(indexFarthest);

                ReducePoints(points, firstPoint,
                    indexFarthest, tolerance, ref pointIndexesToKeep);
                ReducePoints(points, indexFarthest,
                    lastPoint, tolerance, ref pointIndexesToKeep);
            }
        }

        /// <summary>
        /// The distance of a point from a line made from point1 and point2.
        /// </summary>
        /// <param name="pt1">The PT1.</param>
        /// <param name="pt2">The PT2.</param>
        /// <param name="p">The p.</param>
        /// <returns></returns>
        private static double PerpendicularDistance
            (Point Point1, Point Point2, Point Point)
        {
            //Area = |(1/2)(x1y2 + x2y3 + x3y1 - x2y1 - x3y2 - x1y3)|   *Area of triangle
            //Base = v((x1-x2)²+(x1-x2)²)                               *Base of Triangle*
            //Area = .5*Base*H                                          *Solve for height
            //Height = Area/.5/Base

            double area = Math.Abs(.5 * (Point1.X * Point2.Y + Point2.X *
                                         Point.Y + Point.X * Point1.Y - Point2.X * Point1.Y - Point.X *
                                         Point2.Y - Point1.X * Point.Y));
            double bottom = Math.Sqrt(Math.Pow(Point1.X - Point2.X, 2) +
                                      Math.Pow(Point1.Y - Point2.Y, 2));
            double height = area / bottom * 2;

            return height;

        }
    }