凹壳实现 [英] Concave Hull implementation

查看:69
本文介绍了凹壳实现的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试实施以下 http://repositorium.sdum.uminho.pt/bitstream/1822/6429/1/ConcaveHull_ACM_MYS.pdf

我正在使用以下类库。 Loyc库来自 http://core.loyc.net/

I am using the following class libraries. Loyc libs come from http://core.loyc.net/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Device.Location;
using Loyc.Collections;
using Loyc.Geometry;
using Loyc;

这是基本课程

public class Hulls
{
    private static List<Point<double>> KNearestNeighbors(List<Point<double>> points, Point<double> currentPoint, int k, out int kk)
    {
        kk = Math.Min(k, points.Count - 1);
        var ret = points
            .OrderBy(x => PointMath.Length(new Vector<double>(currentPoint.X - x.X, currentPoint.Y - x.Y)))
            .Take(k)
            .ToList();
        return ret;
    }
    private static double Angle(Point<double> a, Point<double> b)
    {
        var ret = -Math.Atan2(b.Y - a.Y, b.X - a.X);
        return NormaliseAngle(ret);
    }
    private static double NormaliseAngle(double a)
    {
        //while (a < b - Math.PI) a += Math.PI * 2;
        //while (b < a - Math.PI) b += Math.PI * 2;
        if (a < 0.0) { return a + Math.PI + Math.PI; }
        return a;
    }
    private static List<Point<double>> SortByAngle(List<Point<double>> kNearest, Point<double> currentPoint, double angle)
    {
        //kNearest
        //    .Sort((v1, v2) => AngleDifference(angle, Angle(currentPoint, v1)).CompareTo(AngleDifference(angle, Angle(currentPoint, v2))));
        //return kNearest.ToList();
        kNearest = kNearest.OrderByDescending(x => NormaliseAngle(Angle(currentPoint, x) - angle)).ToList();
        return kNearest;
    }

    private static bool CCW(Point<double> p1, Point<double> p2, Point<double> p3)
    {
        var cw = ((p3.Y - p1.Y) * (p2.X - p1.X)) - ((p2.Y - p1.Y) * (p3.X - p1.X));
        return cw > 0 ? true : cw < 0 ? false : true; // colinear 
    }

    private static bool _Intersect(LineSegment<double> seg1, LineSegment<double> seg2)
    {
        return CCW(seg1.A, seg2.A, seg2.B) != CCW(seg1.B, seg2.A, seg2.B) && CCW(seg1.A, seg1.B, seg2.A) != CCW(seg1.A, seg1.B, seg2.B);
    }

    private static bool Intersect(LineSegment<double> seg1, LineSegment<double> seg2)
    {
        if ((seg1.A.X == seg2.A.X && seg1.A.Y == seg2.A.Y) 
            || (seg1.B.X == seg2.B.X && seg1.B.Y == seg2.B.Y))
        {
            return false;
        }
        if (_Intersect(seg1, seg2))
        {
            return true;
        }
        return false;
    }

    public IListSource<Point<double>> KNearestConcaveHull(List<Point<double>> points, int k)
    {
        points.Sort((a, b) => a.Y == b.Y ? (a.X > b.X ? 1 : -1) : (a.Y >= b.Y ? 1 : -1));
        Console.WriteLine("Starting with size {0}", k.ToString());

        DList<Point<double>> hull = new DList<Point<double>>();
        var len = points.Count;

        if (len < 3) { return null; }
        if (len == 3) { return hull; }

        var kk = Math.Min(Math.Max(k, 3), len);

        var dataset = new List<Point<double>>();
        dataset.AddRange(points.Distinct());

        var firstPoint = dataset[0];
        hull.PushFirst(firstPoint);

        var currentPoint = firstPoint;
        dataset.RemoveAt(0);

        double previousAngle = 0;
        int step = 2;
        int i;
        while ((currentPoint != firstPoint || step == 2) && dataset.Count > 0)
        {
            if (step == 5) { dataset.Add(firstPoint); }
            var kNearest = KNearestNeighbors(dataset, currentPoint, k, out kk);
            var cPoints = SortByAngle(kNearest, currentPoint, previousAngle);
            var its = true;
            i = 0;
            while (its == true && i < cPoints.Count)
            {
                i++;
                int lastPoint = 0;
                if (cPoints[i - 1] == firstPoint)
                {
                    lastPoint = 1;
                }
                int j = 2;
                its = false;
                while (its == false && j < hull.Count - lastPoint)
                {
                    LineSegment<double> line1 = new LineSegment<double>(hull[step - 2], cPoints[i - 1]);
                    LineSegment<double> line2 = new LineSegment<double>(hull[step - 2 - j], hull[step - 1 - j]);

                    //its = LineMath.ComputeIntersection(line1, line2, out pfrac, LineType.Segment);
                    its = Intersect(line1, line2);
                    j++;
                }
            }
            if (its == true)
            {
                return KNearestConcaveHull(points, kk + 1);
            }
            currentPoint = cPoints[i - 1];
            hull.PushLast(currentPoint);
            previousAngle = Angle(hull[step - 1], hull[step - 2]);
            dataset.Remove(currentPoint); 
            step++;
        }
        bool allInside = true;
        i = dataset.Count;
        while (allInside == true && i > 0)
        {
            allInside = PolygonMath.IsPointInPolygon(hull, dataset[i - 1]);
            i--;
        }
        if (allInside == false) { return KNearestConcaveHull(points, kk + 1); }
        return hull;
    }
}

以上内容应该为边界基于从上一个边缘向右逆时针旋转最远的角度。该代码似乎从初始顶点中选取了具有y值最低的正确的第一条边,但是当偏移角度为非零时,然后没有正确地选择下一条边。我认为问题是SortByAngle或Angle。 -atan2将返回顺时针旋转,对吗?

The above is supposed to pick a new edge for the boundary based on the furthest right-hand turn from the previous edge going around the point set counterclockwise. The code seems to pick the correct first edge from the initial vertex which has the lowest y-value, but then does not pick the next edge correctly when the offset angle is nonzero. I think the issue is the SortByAngle or Angle. -atan2 would return the clockwise turn, correct? Possibly I should be adding the offset angle?

编辑(解决方案):在按照问题的第一条评论中Eric的有用建议后,发现了问题。这是SortByAngle和Angle:

EDIT (SOLUTION): Found the issue after following Eric's helpful advice provided in the first comment to the question. It was SortByAngle and Angle:

private static double Angle(Point<double> a, Point<double> b)
    {
        var ret = Math.Atan2(b.Y - a.Y, b.X - a.X);
        return NormaliseAngle(ret);
    }

    private static double NormaliseAngle(double a)
    {
        if (a < 0.0) { return a + Math.PI + Math.PI; }
        return a;
    }

    private static List<Point<double>> SortByAngle(List<Point<double>> kNearest, Point<double> currentPoint, double angle)
    {
        //kNearest = kNearest.OrderByDescending(x => NormaliseAngle(Angle(currentPoint, x) - angle)).ToList();
        kNearest.Sort((a, b) => NormaliseAngle(Angle(currentPoint, a) - angle) > NormaliseAngle(Angle(currentPoint, b) - angle) ? 1 : -1);
        return kNearest;
    }


推荐答案

您有一些错误:

var kNearest = KNearestNeighbors(dataset, currentPoint, k, out kk);

将kk更改为一些变量。覆盖该 kk值的增量,然后得到StackOverflow异常。

Change the kk to just some var. You override the incrementation of that "kk" value, and then you're getting StackOverflow exceptions.

将代码更改为以下内容:

Change your code to the following:

int someVal;    
var kNearest = KNearestNeighbors(dataset, currentPoint, k, out someVal);

这篇关于凹壳实现的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆