从伪代码到C#的凹面船体算法

时间:2014-11-28 09:11:39

标签: c# wpf computational-geometry convex concave-hull

我正在尝试将here(第12页)中描述的算法从伪代码转换为工作C#代码。该算法描述了如何对凸包进行转换'通过将被认为太长的边缘分成较小的边缘而进入凹形船体。我理解作者提出的一般想法,但无法将其转换为工作代码。请看下面我到目前为止的代码,包括每个伪代码行开头的注释(//)。我遇到的问题不在于特定的一行 - 尽管我确信当前的计算方法是使用localMaximumDistance'是不正确的。如果有人对如何处理这个有任何指示,我真的很想听到这些。 (在伪代码中,这是表示'计算边缘的局部最大距离d的行;')

提前感谢您的时间和反馈! :)

List<Line> concaveLineList = new List<Line>();
List<Line> sortedList = lineList.OrderByDescending(CalculateLength).ToList();
const int concaveTreshhold = 40;

PointCollection concavePointCollection = new PointCollection();
while (sortedList.Count > 0) {
    Console.WriteLine(concaveLineList.Count.ToString());
    //select the longest edge e from list A
    Line longestLine = sortedList[0];
    //remove edge e from list A
    sortedList.RemoveAt(0);
    //calculate local maximum distance d for edges - ???
    //double localMaximumDistance = CalculateLength(longestLine);
    List<Point<double>> nearbyPoints = new List<Point<double>>();

    foreach (BallUc ballUc in ballUcList) {
        if (Math.Abs(ballUc .CurrentPosition.X - longestLine.X1) > concaveTreshhold &&
        Math.Abs(ballUc .CurrentPosition.Y - longestLine.Y1) > concaveTreshhold) { 
            nearbyPoints.Add(new Point<double>(ballUc.CurrentPosition.X, ballUc.CurrentPosition.Y));
            }
        }

        double lineLenght = CalculateLength(longestLine);
        double localMaximumDistance = lineLenght / nearbyPoints.Count + concaveTreshhold * 4; //this value is based on nothing currently..

        if (lineLenght > localMaximumDistance) {
            //find the point p with the smallest maximum angle a
            Point smallestAnglePoint = new Point();
            double? smallestAngle = null;
            foreach (Point p in pointCollection) {
                if ((p.X == longestLine.X1 && p.Y == longestLine.Y1) ||
                (p.X == longestLine.X2 && p.Y == longestLine.Y2)) {
                    //these are the points already in the line.
                }
                else {
                    Line tempLine1 = new Line {X1 = p.X, X2 = longestLine.X1, Y1 = p.Y, Y2 = longestLine.Y1};
                    Line tempLine2 = new Line {X1 = p.X, X2 = longestLine.X2, Y1 = p.Y, Y2 = longestLine.Y2};

                    //calculate angle between the longest edge and the new edges
                    double angleInRadians1 = Math.Atan2(p.Y, p.X) - Math.Atan2(tempLine1.Y2, tempLine1.X2);
                    double angleInRadians2 = Math.Atan2(p.Y, p.X) - Math.Atan2(tempLine2.Y2, tempLine2.X2);
                    //select the largest angle of the two angles
                    double largestLocalAngle = Math.Max(angleInRadians1, angleInRadians2);

                    //in case of first calculation, smallestAngle is still null - in this case it should be assigned the value
                    //(this is probably not very elegant code)
                    if (smallestAngle == null) {
                        smallestAngle = largestLocalAngle;
                        smallestAnglePoint = p;
                    }
                    //we have to find the smallest angle.
                    else if (largestLocalAngle < smallestAngle) {
                        smallestAngle = largestLocalAngle;
                        smallestAnglePoint = p;
                    }
                    //double angleinDegrees = angleInRadians * 180 / Math.PI;
                }
            }
            //TODO if angle a is small enough and point p is not on the boundary

            //create edges e2 and e3 between point p and endpoints of edge e
            Line e2 = new Line {
                X1 = smallestAnglePoint.X,
                Y1 = smallestAnglePoint.Y,
                X2 = longestLine.X1,
                Y2 = longestLine.Y1
            };
            sortedList.Add(e2);
            Line e3 = new Line {
                X1 = smallestAnglePoint.X,
                Y1 = smallestAnglePoint.Y,
                X2 = longestLine.X2,
                Y2 = longestLine.Y2
            };
            sortedList.Add(e3);

            //if edge e2 and e3 don't intersect any other edge
            foreach (Line line in sortedList) {
                Point lineInitialPoint = new Point(line.X1, line.Y1);
                Point lineTerminalPoint = new Point(line.X2, line.Y2);

                Point line2InitialPoint = new Point(e2.X1, e2.Y1);
                Point line2TerminalPoint = new Point(e2.X2, e2.Y2);

                Point line3InitialPoint = new Point(e2.X1, e2.Y1);
                Point line3TerminalPoint = new Point(e2.X2, e2.Y2);

                Point intersectionPoint = GetIntersection(line2InitialPoint, line2TerminalPoint, lineInitialPoint, lineTerminalPoint);
                Point intersectionPoint2 = GetIntersection(line3InitialPoint, line3TerminalPoint, lineInitialPoint, lineTerminalPoint);

                 if ((Double.IsNaN(intersectionPoint.X) && Double.IsNaN(intersectionPoint.Y)) &&
                    (Double.IsNaN(intersectionPoint2.X) && Double.IsNaN(intersectionPoint2.Y))) {
                     //no intersection found, keep rolling..
                     //Console.WriteLine("no intersection found");
                 }
                 else {
                    //intersection found, lines no longer valid
                    Console.WriteLine("intersection found");
                    break;
                 }
                 concaveLineList.Add(e2);
                 concaveLineList.Add(e3);
            }
        }
        //if edge e2 and e3 was not added to list A
        else {
            //add edge e to list B
            concaveLineList.Add(longestLine);
            concavePointCollection.Add(new Point(longestLine.X1, longestLine.Y1));
            concavePointCollection.Add(new Point(longestLine.X2, longestLine.Y2));
        }
    }

1 个答案:

答案 0 :(得分:1)

我已实施此algorithm via JavaScript,可能会对您有所帮助。 There你可以看到将凸包改为凹壳的功能。我对算法的实现与论文中的完全不同,但它基于它。

在我的实现中,我跳过了localMaximumDistance计算:-)并决定首先我需要查看用例,其中没有localMaximumDistance的算法会出错。