9

所以我试图翻译这里找到的凹壳算法:http ://repositorium.sdum.uminho.pt/bitstream/1822/6429/1/ConcaveHull_ACM_MYS.pdf

(第 65 页)

我已经通读了整篇文章,但我不知道如何实现sortByAngle,而且angle我不确定我应该在其中执行什么方法。这是我到目前为止所拥有的:

//Main method
public static Vertex[] ConcaveHull(Vertex[] points, int k = 3)
{
    if (k < 3)
        throw new ArgumentException("K is required to be 3 or more", "k");
    List<Vertex> hull = new List<Vertex>();
    //Clean first, may have lots of duplicates
    Vertex[] clean = RemoveDuplicates(points);
    if (clean.Length < 3)
        throw new ArgumentException("At least 3 dissimilar points reqired", "points");
    if (clean.Length == 3)//This is the hull, its already as small as it can be.
        return clean;
    if (clean.Length < k)
        throw new ArgumentException("K must be equal to or smaller then the amount of dissimilar points", "points");
    Vertex firstPoint = clean[0]; //TODO find mid point
    hull.Add(firstPoint);
    Vertex currentPoint = firstPoint;
    Vertex[] dataset = RemoveIndex(clean, 0);
    double previousAngle = 0;
    int step = 2;
    int i;
    while (((currentPoint != firstPoint) || (step == 2)) && (dataset.Length > 0))
    {
        if (step == 5)
            dataset = Add(dataset, firstPoint);
        Vertex[] kNearestPoints = nearestPoints(dataset, currentPoint, k);
        Vertex[] cPoints = sortByAngle(kNearestPoints, currentPoint, previousAngle);
        bool its = true;
        i = 0;
        while ((its) && (i < cPoints.Length))
        {
            i++;
            int lastPoint = 0;
            if (cPoints[0] == firstPoint)
                lastPoint = 1;
            int j = 2;
            its = false;
            while ((!its) && (j < hull.Count - lastPoint))
            {
                its = intersectsQ(hull[step - 1 - 1], cPoints[0], hull[step - i - j - 1], hull[step - j - 1]);
                j++;
            }
        }
        if (its)
        {
            return ConcaveHull(points, k + 1);
        }
        currentPoint = cPoints[0];
        hull.Add(currentPoint);
        previousAngle = angle(hull[step - 1], hull[step - 2]);
        dataset = RemoveIndex(dataset, 0);
        step++;
    }
    bool allInside = true;
    i = dataset.Length;
    while (allInside && i > 0)
    {
        allInside = new Polygon(dataset).Contains(currentPoint); //TODO havent finished ray casting yet.
        i--;
    }
    if (!allInside)
        return ConcaveHull(points, k + 1);
    return hull.ToArray();
}

private static Vertex[] Add(Vertex[] vs, Vertex v)
{
    List<Vertex> n = new List<Vertex>(vs);
    n.Add(v);
    return n.ToArray();
}

private static Vertex[] RemoveIndex(Vertex[] vs, int index)
{
    List<Vertex> removed = new List<Vertex>();
    for (int i = 0; i < vs.Length; i++)
        if (i != index)
            removed.Add(vs[i]);
    return removed.ToArray();
}

private static Vertex[] RemoveDuplicates(Vertex[] vs)
{
    List<Vertex> clean = new List<Vertex>();
    VertexComparer vc = new VertexComparer();
    foreach (Vertex v in vs)
    {
        if (!clean.Contains(v, vc))
            clean.Add(v);
    }
    return clean.ToArray();
}

private static Vertex[] nearestPoints(Vertex[] vs, Vertex v, int k)
{
    Dictionary<double, Vertex> lengths = new Dictionary<double, Vertex>();
    List<Vertex> n = new List<Vertex>();
    double[] sorted = lengths.Keys.OrderBy(d => d).ToArray();
    for (int i = 0; i < k; i++)
    {
        n.Add(lengths[sorted[i]]);
    }
    return n.ToArray();
}

private static Vertex[] sortByAngle(Vertex[] vs, Vertex v, double angle)
{
    //TODO
    return new Vertex[]{};
}

private static bool intersectsQ(Vertex v1, Vertex v2, Vertex v3, Vertex v4)
{
    return intersectsQ(new Edge(v1, v2), new Edge(v3, v4));
}

private static bool intersectsQ(Edge e1, Edge e2)
{
    double x1 = e1.A.X;
    double x2 = e1.B.X;
    double x3 = e2.A.X;
    double x4 = e2.B.X;

    double y1 = e1.A.Y;
    double y2 = e1.B.Y;
    double y3 = e2.A.Y;
    double y4 = e2.B.Y;

    var x = ((x1 * y2 - y1 * x2) * (x3 - x4) - (x1 - x2) * (x3 * y4 - y3 * x4)) / ((x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4));
    var y = ((x1 * y2 - y1 * x2) * (y3 - y4) - (y1 - y2) * (x3 * y4 - y3 * x4)) / ((x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4));
    if (double.IsNaN(x) || double.IsNaN(y))
    {
        return false;
    }
    else
    {
        if (x1 >= x2)
        {
            if (!(x2 <= x && x <= x1)) { return false; }
        }
        else
        {
            if (!(x1 <= x && x <= x2)) { return false; }
        }
        if (y1 >= y2)
        {
            if (!(y2 <= y && y <= y1)) { return false; }
        }
        else
        {
            if (!(y1 <= y && y <= y2)) { return false; }
        }
        if (x3 >= x4)
        {
            if (!(x4 <= x && x <= x3)) { return false; }
        }
        else
        {
            if (!(x3 <= x && x <= x4)) { return false; }
        }
        if (y3 >= y4)
        {
            if (!(y4 <= y && y <= y3)) { return false; }
        }
        else
        {
            if (!(y3 <= y && y <= y4)) { return false; }
        }
    }
    return true;
}

private static double angle(Vertex v1, Vertex v2)
{
    // TODO fix
    Vertex v3 = new Vertex(v1.X, 0);
    if (Orientation(v3, v1, v2) == 0)
        return 180;

    double b = EuclideanDistance(v3, v1);
    double a = EuclideanDistance(v1, v2);
    double c = EuclideanDistance(v3, v2);
    double angle = Math.Acos((Math.Pow(a, 2) + Math.Pow(b, 2) - Math.Pow(c, 2)) / (2 * a * b));

    if (Orientation(v3, v1, v2) < 0)
        angle = 360 - angle;

    return angle;
}

private static double EuclideanDistance(Vertex v1, Vertex v2)
{
    return Math.Sqrt(Math.Pow((v1.X - v2.X), 2) + Math.Pow((v1.Y - v2.Y), 2));
}

public static double Orientation(Vertex p1, Vertex p2, Vertex p)
{
    double Orin = (p2.X - p1.X) * (p.Y - p1.Y) - (p.X - p1.X) * (p2.Y - p1.Y);
    if (Orin > 0)
        return -1;//Left
    if (Orin < 0)
        return 1;//Right
    return 0;//Colinier
}

我知道这里有很多代码。但我不确定我是否可以展示上下文以及没有它的情况。

其他类:

public class Polygon
{

    private Vertex[] vs;

    public Polygon(Vertex[] Vertexes)
    {
        vs = Vertexes;
    }

    public Polygon(Bounds bounds)
    {
        vs = bounds.ToArray();
    }

    public Vertex[] ToArray()
    {
        return vs;
    }

    public IEnumerable<Edge> Edges()
    {
        if (vs.Length > 1)
        {
            Vertex P = vs[0];
            for (int i = 1; i < vs.Length; i++)
            {
                yield return new Edge(P, vs[i]);
                P = vs[i];
            }
            yield return new Edge(P, vs[0]);
        }
    }

    public bool Contains(Vertex v)
    {
        return RayCasting.RayCast(this, v);
    }
}

public class Edge
{
    public Vertex A = new Vertex(0, 0);
    public Vertex B = new Vertex(0, 0);
    public Edge() { }
    public Edge(Vertex a, Vertex b)
    {
        A = a;
        B = b;
    }
    public Edge(double ax, double ay, double bx, double by)
    {
        A = new Vertex(ax, ay);
        B = new Vertex(bx, by);
    }
}

public class Bounds
{
    public Vertex TopLeft;
    public Vertex TopRight;
    public Vertex BottomLeft;
    public Vertex BottomRight;
    public Bounds() { }

    public Bounds(Vertex TL, Vertex TR, Vertex BL, Vertex BR)
    {
        TopLeft = TL;
        TopRight = TR;
        BottomLeft = BL;
        BottomRight = BR;
    }

    public Vertex[] ToArray()
    {
        return new Vertex[] { TopLeft, TopRight, BottomRight, BottomLeft };
    }

}

public class Vertex
{
    public double X = 0;
    public double Y = 0;
    public Vertex() { }
    public Vertex(double x, double y)
    {
        X = x;
        Y = y;
    }

    public static Vertex[] Convert(string vs)
    {
        vs = vs.Replace("[", "");
        vs = vs.Replace("]", "");
        string[] spl = vs.Split(';');
        List<Vertex> nvs = new List<Vertex>();
        foreach (string s in spl)
        {
            try
            {
                nvs.Add(new Vertex(s));
            }
            catch
            {

            }
        }
        return nvs.ToArray();
    }

    public static string Stringify(Vertex[] vs)
    {
        string res = "[";
        foreach (Vertex v in vs)
        {
            res += v.ToString();
            res += ";";
        }
        res = res.RemoveLastCharacter();
        res += "]";
        return res;
    }

    public static string ToString(Vertex[] array)
    {
        string res = "[";
        foreach (Vertex v in array)
            res += v.ToString() + ",";
        return res.RemoveLastCharacter() + "]";
    }

    /*
    //When x < y return -1
    //When x == y return 0
    //When x > y return 1
    public static int Compare(Vertex x, Vertex y)
    {
        //To find lowest
        if (x.X < y.X)
        {
            return -1;
        }
        else if (x.X == y.X)
        {
            if (x.Y < y.Y)
            {
                return -1;
            }
            else if (x.Y == y.Y)
            {
                return 0;
            }
            else
            {
                return 1;
            }
        }
        else
        {
            return 1;
        }
    }
    */
    public static int CompareY(Vertex a, Vertex b)
    {
        if (a.Y < b.Y)
            return -1;
        if (a.Y == b.Y)
            return 0;
        return 1;
    }

    public static int CompareX(Vertex a, Vertex b)
    {
        if (a.X < b.X)
            return -1;
        if (a.X == b.X)
            return 0;
        return 1;
    }

    public double distance (Vertex b){
        double dX = b.X - this.X;
        double dY = b.Y - this.Y;
        return Math.Sqrt((dX*dX) + (dY*dY));
    }

    public double slope (Vertex b){
        double dX = b.X - this.X;
        double dY = b.Y - this.Y;
        return dY / dX;
    }

    public static int Compare(Vertex u, Vertex a, Vertex b)
    {
        if (a.X == b.X && a.Y == b.Y) return 0;

        Vertex upper = new Vertex();
        Vertex p1 = new Vertex();
        Vertex p2 = new Vertex();
        upper.X = (u.X + 180) * 360;
        upper.Y = (u.Y + 90) * 180;
        p1.X = (a.X + 180) * 360;
        p1.Y = (a.Y + 90) * 180;
        p2.X = (b.X + 180) * 360;
        p2.Y = (b.Y + 90) * 180;
        if(p1 == upper) return -1;
        if(p2 == upper) return 1;

        double m1 = upper.slope(p1);
        double m2 = upper.slope(p2);

        if (m1 == m2)
        {
            return p1.distance(upper) < p2.distance(upper) ? -1 : 1;
        }

        if (m1 <= 0 && m2 > 0) return -1;

        if (m1 > 0 && m2 <= 0) return -1;

        return m1 > m2 ? -1 : 1;
    }

    public static Vertex UpperLeft(Vertex[] vs)
    {
        Vertex top = vs[0];
        for (int i = 1; i < vs.Length; i++)
        {
            Vertex temp = vs[i];
            if (temp.Y > top.Y || (temp.Y == top.Y && temp.X < top.X))
            {
                top = temp;
            }
        }
        return top;
    }

}
4

4 回答 4

3

只是关于约定的注意事项:您应该以大写开头的函数名称和以小写开头的变量。在函数sortByAngle中,您可以同时引用参数angle和函数angle

假设Angle(...)只是为了计算两点之间的角度:

private static double Angle(Vertex v1, Vertex v2)
{
    return Math.Atan2(v2.Y - v1.Y, v2.X - v1.X);
}

将为您提供从v1到的角度,以v2-pi 和 +pi 之间的弧度表示。不要混合度数和弧度。我的建议是始终使用弧度,并且仅在必要时转换为度数以实现人类可读的输出。

private static Vertex[] SortByAngle(Vertex[] vs, Vertex v, double angle)
{
    List<Vertex> vertList = new List<Vertex>(vs);
    vertList.Sort((v1, v2) => AngleDifference(angle, Angle(v, v1)).CompareTo(AngleDifference(angle, Angle(v, v2))));
    return vertList.ToArray();
}

用于从顶点与自身List.Sort之间的角度差最大到最小对顶点进行排序,并且. 和的顺序在输入元组中交换以降序排序,即最大差在前。角度之间的差异计算如下:pointanglev1v2

private static double AngleDifference(double a, double b)
{
    while (a < b - Math.PI) a += Math.PI * 2;
    while (b < a - Math.PI) b += Math.PI * 2;

    return Math.Abs(a - b);
}

前两条线确保角度不超过 180 度。

于 2013-05-14T08:50:47.373 回答
2

你有错误

private static Vertex[] nearestPoints(Vertex[] vs, Vertex v, int k)
{
    Dictionary<double, Vertex> lengths = new Dictionary<double, Vertex>();
    List<Vertex> n = new List<Vertex>();
    double[] sorted = lengths.Keys.OrderBy(d => d).ToArray();
    for (int i = 0; i < k; i++)
    {
        n.Add(lengths[sorted[i]]);
    }
    return n.ToArray();
}

根据代码,如果您在相同距离处有多个顶点,则函数仅返回一个。由于 Dictionary 使用唯一键。

顺便说一句,有人完成了吗?

于 2013-07-19T13:46:20.550 回答
0

我现在没有时间阅读这篇论文,但根据我对 conVEX 船体算法的了解,我假设您正在围绕特定方向的点寻找下一个要链接的点。

如果是这种情况,“角度”将是船体最近线段的角度,并且您希望按与该线的角度对点进行排序。因此,您想要计算一条线(在船体上)和一组线(从当前点到正在考虑的其他点)之间的角度。计算出的角度是正还是负取决于你是顺时针还是逆时针。要计算角度,请查看以下内容:

计算两条线之间的角度而不必计算斜率?(爪哇)

然后按角度排序。

于 2013-05-06T22:27:58.850 回答
0

那个怎么样?

private List<Vector> sortClockwiseFromCentroid(List<Vector> points, Vector center)
    {
        points = points.OrderBy(x => Math.Atan2(x.X - center.X, x.Y - center.Y)).ToList();
        return points;
    }
于 2015-07-17T10:40:09.773 回答