Слияние двух выпуклых оболочек

В настоящее время я пишу версию алгоритма выпуклой оболочки «разделяй и властвуй», и она очень близка к работе, но у меня возникают проблемы с объединением двух выпуклых оболочек (для формирования общей выпуклой оболочки).

Я объединяюсь:

  • Вычисление верхнего и нижнего корпусов для каждого входного корпуса, A и B
  • Нахождение совмещенного верхнего корпуса путем обеспечения правых поворотов
  • Нахождение комбинированного нижнего корпуса путем обеспечения левых поворотов
  • Вычисление объединения двух комбинированных корпусов

Я не уверен на 100%, что это правильный способ сделать это - какое-либо руководство или псевдокод для поиска комбинированных верхних/нижних корпусов?


person Dave Clarke    schedule 20.11.2012    source источник


Ответы (3)


Проверь это; это даст вам очень умные подходы к манипулированию выпуклой оболочкой

https://web.archive.org/web/20120617005317/http://moais.imag.fr/membres/denis.trystram/SupportsDeCours/convexHull.pdf

person Gianluca Ghettini    schedule 20.11.2012
comment
Я полностью понимаю подход, но я борюсь с процессом поиска верхних и нижних касательных? - person Dave Clarke; 21.11.2012

Хотя это использует библиотеку XNA, вы определенно можете перенести это на Java:

public static class PolygonUnion
{
        public static bool PolyUnion(Vector2[] polya, Vector2[] polyb, out Vector2[] union, out Vector2[] intersection)
        {
            if (!Intersects(polya, polyb))
            {
                union = polya;
                intersection = polyb;
                return false;
            }

            LList a = new LList(polya), b = new LList(polyb);
            List<Intersection> intersections = new List<Intersection>();

            Vector2 vert;
            VNode aNode = a.First, bNode = b.First;
            //Find intersection points between the polygons
            do
            {
                do
                {
                    if (EdgeIntersects(aNode.Value, (aNode.Next == null) ? a.First.Value : aNode.Next.Value, bNode.Value,
(bNode.Next == null) ? b.First.Value : bNode.Next.Value, out vert))
                    {
                        //An intersection point has been found!
                        intersections.Add(new Intersection(vert, aNode, bNode, (aNode.Next == null) ? a.First : aNode.Next,
(bNode.Next == null) ? b.First : bNode.Next));
                    }
                    bNode = bNode.Next;
                }
                while (bNode != null);
                bNode = b.First;
                aNode = aNode.Next;
            }
            while (aNode != null);
            //Perform surgery on these intersections
            Intersection i;
            for (int j = 0; j < intersections.Count; j++)
            {
                i = intersections[j];
                i.aIn.Next = new VNode(i.Position, i.bOut, i.aIn);
                i.bOut.Prev = i.aIn.Next;

                i.bIn.Next = new VNode(i.Position, i.aOut, i.bIn);
                i.aOut.Prev = i.bIn.Next;
            }
            //Decompose and simplify polygons into arrays
            union = a.ToArray();
            intersection = b.ToArray();

            //Find exterior polygon
            if (union.Length < intersection.Length)
            {
                //Polygons need swapping!
                Vector2[] u = union;
                union = intersection;
                intersection = u;
            }
            return true;
        }

        private class Intersection
        {
            public Intersection(Vector2 position, VNode aIn, VNode bIn, VNode aOut, VNode bOut)
            {
                this.aIn = aIn;
                this.bIn = bIn;
                this.aOut = aOut;
                this.bOut = bOut;
                this.Position = position;
            }

            public VNode aIn, bIn, aOut, bOut;
            public Vector2 Position;
        }

        private class LList
        {
            public LList(Vector2[] poly)
            {
                First = new VNode(poly[0], null, null);

                current = First;
                for (int i = 1; i < poly.Length; i++)
                {
                    Add(current, poly[i]);
                    current = current.Next;
                }
                current = First;
            }

            private void Add(VNode prev, Vector2 pos)
            {
                prev.Next = new VNode(pos, null, prev);
            }

            public VNode First;
            private VNode current;

            public Vector2[] ToArray()
            {
                List<Vector2> ret = new List<Vector2>();
                current = First;
                int timeout = 1000;
                bool starting = true;

                while (current != null)
                {
                    if (current.Prev != null && current.Value == current.Prev.Value)
                    {
                        current = current.Next;
                        if (current.Value == current.Next.Value && current.Value == current.Prev.Value) break;
                        continue;
                    }
                    if (!starting && current.Value == First.Value) break;
                    starting = false;

                    ret.Add(current.Value);
                    current = current.Next;

                    timeout--;
                    if (timeout <= 0) break;
                }
                return Simplify(ret.ToArray());
            }
        }

        private class VNode
        {
            public VNode(Vector2 value, VNode next, VNode prev)
            {
                Value = value;
                Next = next;
                Prev = prev;
            }

            public VNode Next;
            public VNode Prev;
            public Vector2 Value;

            public override string ToString()
            {
                return Value.ToString();
            }
        }

        private static Vector2[] Simplify(Vector2[] poly)
        {
            const float tolerance = 25f;//5 pixels tolerance. (5^2 to save sqrt)
            if (poly.Length == 0) return poly;

            List<Vector2> ret = new List<Vector2>(1);
            ret.Add(poly[0]);
            float dist;

            for (int i = 1; i < poly.Length; i++)
            {
                dist = (poly[ret.Count - 1] - poly[i]).LengthSquared();
                if (dist < tolerance) continue;
                if (ret.Contains(poly[i])) continue;

                ret.Add(poly[i]);
            }
            return ret.ToArray();
        }

        /// <summary>
        /// Checks if the line segments intersect.
        /// </summary>
        private static bool EdgeIntersects(Vector2 x, Vector2 y, Vector2 a, Vector2 b, out Vector2 i)
        {
            i = Vector2.Zero;
            float dx, dy, da, db, t, s;

            dx = y.X - x.X;
            dy = y.Y - x.Y;
            da = b.X - a.X;
            db = b.Y - a.Y;
            if ((da * dy - db * dx) == 0) return false;

            s = (dx * (a.Y - x.Y) + dy * (x.X - a.X)) / (da * dy - db * dx);
            t = (da * (x.Y - a.Y) + db * (a.X - x.X)) / (db * dx - da * dy);
            i = new Vector2(x.X + t * dx, x.Y + t * dy);
            return (bool)(s >= 0 && s <= 1 && t >= 0 && t <= 1);
        }

        #region Intersection Test
        /// <summary>
        /// Checks if two polygons intersect.
        /// </summary>
        public static bool Intersects(Vector2[] aPoints, Vector2[] bPoints)
        {
            Vector2 edge, axis;
            float minA, maxA, minB, maxB, overlap;
            //Loop through all edges until a seperating axis is found
            for (int x = 0; x < aPoints.Length + bPoints.Length; x++)
            {
                //Calculate the current edge
                if (x < aPoints.Length) edge = aPoints[(x == aPoints.Length - 1 ? 0 : x + 1)] - aPoints[x];
                else
                {
                    x -= aPoints.Length;
                    edge = bPoints[(x == bPoints.Length - 1 ? 0 : x + 1)] - bPoints[x];
                    x += aPoints.Length;
                }
                //Find the axis perpendicular to current edge
                axis = new Vector2(-edge.Y, edge.X);
                axis.Normalize();
                //Project the two shapes onto this axis
                //Project this
                ProjectPoly(aPoints, axis, out maxA, out minA);
                ProjectPoly(bPoints, axis, out maxB, out minB);
                //Find the overlap between them
                if (minA < minB) overlap = minB - maxA;
                else overlap = minA - maxB;
                //If the overlap is negative then they are overlapping and the smallest
                //overlap must be found to find the minumum translation.
                //If it is bigger than 0 then they won't overlap at all.
                if (overlap < 0) return true;
            }
            return false;
        }
        /// <summary>
        /// Projects a polygon onto the axis, giving the maximum and minimum positions.
        /// </summary>
        /// <param name="points">Points that are in world space with origin at the centre</param>
        private static void ProjectPoly(Vector2[] points, Vector2 axis, out float max, out float min)
        {
            //Projecting a point onto an axis uses a dot product.
            float dotProduct = Vector2.Dot(axis, points[0]);
            min = dotProduct;
            max = dotProduct;
            //Now project the rest of the polygon...
            for (int i = 1; i < points.Length; i++)
            {
                dotProduct = Vector2.Dot(axis, points[i]);
                if (dotProduct < min) min = dotProduct;
                else if (dotProduct > max) max = dotProduct;
            }
        }
        #endregion
}
person Josh C.    schedule 20.11.2012

Я не уверен, что понимаю, что вы подразумеваете под нижним и верхним, и было бы хорошо указать, является ли это 2D или 3D.

Для двумерной выпуклой оболочки. Я сделал алгоритм, который, согласно моим тестам, является самым быстрым (по крайней мере, в два раза быстрее, чем Чан), и оба они находятся в O (n log h).

Но что еще отличает меня, так это то, что поддерживается «онлайн» кормление. Я имею в виду, что вы можете динамически добавлять одну точку за раз (удаление пока не поддерживается). Все остается в O (log h) на точку, что на самом деле является самым быстрым, который вы можете получить. Я думаю, что это также единственный, который находится в сети и находится в этом диапазоне производительности.

Статья о выпуклой оболочке: Быстрый и улучшенный алгоритм 2D Convex Hull и его реализация за O(n log h). Но «онлайн» часть еще не задокументирована. Я только что завершил его сегодня (2018-01-25). Но весь код доступен на GitHub.

Для простоты вы можете использовать онлайн-часть только для динамического объединения чего-либо с этим кодом (вы можете добавить точку выпуклой оболочки или любую точку):

OuelletConvexHullAvl2Online.ConvexHullOnline convexHullOnline = new OuelletConvexHullAvl2Online.ConvexHullOnline();
                    foreach (Point pt in points)
                    {
                        convexHullOnline.DynamicallyAddAnotherPointToConvexHullIfAppropriate(pt);
                    }

                    return convexHullOnline.GetResultsAsArrayOfPoint();
person Eric Ouellet    schedule 26.01.2018