Example #1
0
 /**
  *
  * @param _project A functor which converts vertices to a 2d
  *                 projection.
  * @param _loop The polygon loop which indices address.
  * @param _vert The vertex from which distance is measured.
  *
  */
 public heap_ordering(Project <T> _project, FList <T> _loop, T _vert, int _axis)
 {
     project = _project;
     loop    = _loop;
     p       = _project(_vert);
     axis    = _axis;
 }
Example #2
0
        static bool splitAndResume(vertex_info begin, FList <TriIdx> result)
        {
            vertex_info v1, v2;

            if (!findDiagonal(begin, out v1, out v2))
            {
                return(false);
            }

            vertex_info v1_copy = v1.Clone();
            vertex_info v2_copy = v2.Clone();

            v1.next = v2;
            v2.prev = v1;

            v1_copy.next.prev = v1_copy;
            v2_copy.prev.next = v2_copy;

            v1_copy.prev = v2_copy;
            v2_copy.next = v1_copy;

            bool r1 = doTriangulate(v1, result);
            bool r2 = doTriangulate(v1_copy, result);

            return(r1 && r2);
        }
Example #3
0
 /// <summary>
 /// コンストラクタ、比較インターフェースと初期項目を指定して初期化する
 /// </summary>
 /// <param name="comparer">比較インターフェース</param>
 /// <param name="capacity">初期キャパシティ</param>
 /// <param name="initialValues">初期項目</param>
 public PriorityQueue(IComparer <T> comparer, int capacity, IEnumerable <T> initialValues)
 {
     _List     = capacity != 0 ? new FList <T>(capacity) : new FList <T>();
     _Comparer = comparer;
     if (initialValues != null)
     {
         _List.AddRange(initialValues);
     }
     UpdateHeap();
 }
Example #4
0
 /// <summary>
 /// 指定されたバイナリヒープリストに値を追加する
 /// </summary>
 /// <param name="list">追加先リスト、バイナリヒープになっている必要がある</param>
 /// <param name="hole">追加先リスト内でのバイナリヒープを満たしていない可能性がある項目インデックス</param>
 /// <param name="top">追加先リスト内での順序並び替え開始インデックス</param>
 /// <param name="val">追加値</param>
 /// <param name="comparer">比較インターフェース</param>
 static void PushHeapByIndex(FList <T> list, int hole, int top, T val, IComparer <T> comparer)
 {
     // hole を親へ移動していく
     for (var idx = (hole - 1) / 2; top < hole && comparer.Compare(list[idx], val) < 0; idx = (hole - 1) / 2)
     {
         list[hole] = list[idx];
         hole       = idx;
     }
     // 最後の hole に追加値を設定
     list[hole] = val;
 }
Example #5
0
        /// <summary>
        /// 指定されたバイナリヒープリストへ値を追加する
        /// </summary>
        /// <param name="list">追加先リスト、バイナリヒープになっている必要がある</param>
        /// <param name="value">追加値</param>
        /// <param name="comparer">比較インターフェース</param>
        public static void PushHeap(FList <T> list, T value, IComparer <T> comparer)
        {
            list.Add(value);
            var last = list.Count;

            if (2 <= last)
            {
                --last;
                PushHeapByIndex(list, last, 0, list[last], comparer);
            }
        }
Example #6
0
        public static FList <T> incorporateHolesIntoPolygon <T>(Project <T> project, FList <FList <T> > loops)
        {
            if (loops.Count <= 1)
            {
                return(loops[0]);
            }
            var holes = new FList <FList <T> >(loops);

            holes.RemoveAt(0);
            return(incorporateHolesIntoPolygon(project, loops[0], holes));
        }
Example #7
0
        /// <summary>
        /// 指定されたバイナリヒープリストから優先順位が最大の値を取り出す
        /// </summary>
        /// <param name="list">削除元リスト、バイナリヒープになっている必要がある</param>
        /// <param name="comparer">比較インターフェース</param>
        /// <returns>取り出された値</returns>
        public static T PopHeap(FList <T> list, IComparer <T> comparer)         // pop *_First to *(_Last - 1) and reheap, using _Pred
        {
            var top  = list[0];
            var last = list.Count - 1;

            if (1 <= last)
            {
                PopHeapHoleByIndex(list, 0, last, list[last], comparer);
            }
            list.RemoveAt(last);
            return(top);
        }
Example #8
0
        /// <summary>
        /// 指定されたリスト内要素をバイナリヒープになるよう並び替える
        /// </summary>
        /// <param name="list">並び替えられるリスト</param>
        /// <param name="comparer">比較インターフェース</param>
        public static void MakeHeap(FList <T> list, IComparer <T> comparer)
        {
            var last = list.Count;

            if (2 <= last)
            {
                for (var hole = last / 2; 0 < hole;)
                {
                    --hole;
                    PopHeapHoleByIndex(list, hole, last, list[hole], comparer);
                }
            }
        }
Example #9
0
        /**
         * \brief Given a polygon loop and a hole loop, and attachment
         * points, insert the hole loop vertices into the polygon loop.
         *
         * @param[in,out] f_loop The polygon loop to incorporate the
         *                       hole into.
         * @param f_loop_attach[in] The index of the vertex of the
         *                          polygon loop that the hole is to be
         *                          attached to.
         * @param hole_attach[in] A pair consisting of a pointer to a
         *                        hole container and an iterator into
         *                        that container reflecting the point of
         *                        attachment of the hole.
         */
        static void patchHoleIntoPolygon <T>(FList <T> f_loop, int f_loop_attach, Iterator <T> hole_attach)
        {
            // join the vertex curr of the polygon loop to the hole at
            // h_loop_connect
            var hole_temp = new T[hole_attach.list.Count + 2];

            for (int i = 0, n = hole_attach.list.Count; i <= n; i++)
            {
                hole_temp[i] = hole_attach.list[(hole_attach.index + i) % n];
            }
            hole_temp[hole_temp.Length - 1] = f_loop[f_loop_attach];
            f_loop.InsertRange(f_loop_attach + 1, hole_temp);
        }
Example #10
0
 public FList(FList <T> list)
 {
     if (list._Items == EmptyArray)
     {
         _Items = EmptyArray;
     }
     else
     {
         var count = list._Count;
         var items = new T[count];
         Array.Copy(list._Items, items, count);
         _Items = items;
         _Count = count;
     }
 }
Example #11
0
        static int MinElementIndex <T>(FList <T> list, IComparer <T> comparer)
        {
            T   min   = list[0];
            int index = 0;

            for (int i = list.Count - 1; i != 0; i--)
            {
                var t = list[i];
                if (comparer.Compare(t, min) < 0)
                {
                    min   = t;
                    index = i;
                }
            }
            return(index);
        }
Example #12
0
        public static void triangulate <T>(Project <T> project, FList <T> poly, FList <TriIdx> result)
        {
            var N = poly.Count;

            result.Clear();
            if (N < 3)
            {
                return;
            }

            result.Capacity = poly.Count - 2;

            if (N == 3)
            {
                result.Add(new TriIdx(0, 1, 2));
                return;
            }

            var vinfo = new vertex_info[N];

            vinfo[0] = new vertex_info(project(poly[0]), 0);
            for (int i = 1; i < N - 1; ++i)
            {
                vinfo[i]          = new vertex_info(project(poly[i]), i);
                vinfo[i].prev     = vinfo[i - 1];
                vinfo[i - 1].next = vinfo[i];
            }
            vinfo[N - 1]      = new vertex_info(project(poly[N - 1]), N - 1);
            vinfo[N - 1].prev = vinfo[N - 2];
            vinfo[N - 1].next = vinfo[0];
            vinfo[0].prev     = vinfo[N - 1];
            vinfo[N - 2].next = vinfo[N - 1];

            for (int i = 0; i < N; ++i)
            {
                vinfo[i].recompute();
            }

            var begin = vinfo[0];

            removeDegeneracies(ref begin, result);

            doTriangulate(begin, result);
        }
Example #13
0
        static bool findDiagonal(vertex_info begin, out vertex_info v1, out vertex_info v2)
        {
            vertex_info t;
            var         heap = new FList <vertex_info>();

            v1 = begin;
            do
            {
                heap.Clear();

                for (v2 = v1.next.next; v2 != v1.prev; v2 = v2.next)
                {
                    if (!internalToAngle(v1.next, v1, v1.prev, v2.p) ||
                        !internalToAngle(v2.next, v2, v2.prev, v1.p))
                    {
                        continue;
                    }

                    PriorityQueue <vertex_info> .PushHeap(heap, v2, new vertex_info_l2norm_inc_ordering(v1));
                }

                while (heap.Count != 0)
                {
                    v2 = PriorityQueue <vertex_info> .PopHeap(heap, new vertex_info_l2norm_inc_ordering(v1));

                    // test whether v1-v2 is a valid diagonal.
                    var v_min_x = Math.Min(v1.p.X, v2.p.X);
                    var v_max_x = Math.Max(v1.p.X, v2.p.X);

                    bool intersected = false;

                    for (t = v1.next; !intersected && t != v1.prev; t = t.next)
                    {
                        vertex_info u = t.next;
                        if (t == v2 || u == v2)
                        {
                            continue;
                        }

                        var l1 = orient2d(v1.p, v2.p, t.p);
                        var l2 = orient2d(v1.p, v2.p, u.p);

                        if ((l1 > 0.0 && l2 > 0.0) || (l1 < 0.0 && l2 < 0.0))
                        {
                            // both on the same side; no intersection
                            continue;
                        }

                        var dx13 = v1.p.X - t.p.X;
                        var dy13 = v1.p.Y - t.p.Y;
                        var dx43 = u.p.X - t.p.X;
                        var dy43 = u.p.Y - t.p.Y;
                        var dx21 = v2.p.X - v1.p.X;
                        var dy21 = v2.p.Y - v1.p.Y;
                        var ua_n = dx43 * dy13 - dy43 * dx13;
                        var ub_n = dx21 * dy13 - dy21 * dx13;
                        var u_d  = dy43 * dx21 - dx43 * dy21;

                        if (Math.Abs(u_d) < element.Epsilon)
                        {
                            // parallel
                            if (Math.Abs(ua_n) < element.Epsilon)
                            {
                                // colinear
                                if (Math.Max(t.p.X, u.p.X) >= v_min_x && Math.Min(t.p.X, u.p.X) <= v_max_x)
                                {
                                    // colinear and intersecting
                                    intersected = true;
                                }
                            }
                        }
                        else
                        {
                            // not parallel
                            var ua = ua_n / u_d;
                            var ub = ub_n / u_d;

                            if (0 <= ua && ua <= 1 && 0 <= ub && ub <= 1)
                            {
                                intersected = true;
                            }
                        }
                    }

                    if (!intersected)
                    {
                        // test whether midpoint winding == 1

                        var mid = (v1.p + v2.p) / 2;
                        if (windingNumber(begin, mid) == 1)
                        {
                            // this diagonal is ok
                            return(true);
                        }
                    }
                }

                // couldn't find a diagonal from v1 that was ok.
                v1 = v1.next;
            } while (v1 != begin);
            return(false);
        }
Example #14
0
 public Iterator(FList <T> list, int index)
 {
     this.list  = list;
     this.index = index;
 }
Example #15
0
        /**
         * \brief Merge a set of holes into a polygon. (templated)
         *
         * Take a polygon loop and a collection of hole loops, and patch
         * the hole loops into the polygon loop, returning a vector of
         * vertices from the polygon and holes, which describes a new
         * polygon boundary with no holes. The new polygon boundary is
         * constructed via the addition of edges * joining the polygon
         * loop to the holes.
         *
         * This may be applied to arbitrary vertex data (generally
         * carve::geom3d::Vertex pointers), but a projection function must
         * be supplied to convert vertices to coordinates in 2-space, in
         * which the work is performed.
         *
         * @tparam project_t A functor which converts vertices to a 2d
         *                   projection.
         * @tparam vert_t    The vertex type.
         * @param project The projection functor.
         * @param f_loop The polygon loop into which holes are to be
         *               incorporated.
         * @param h_loops The set of hole loops to be incorporated.
         *
         * @return A vector of vertex pointers.
         */
        public static FList <T> incorporateHolesIntoPolygon <T>(Project <T> project, FList <T> f_loop, FList <FList <T> > h_loops)
        {
            var N = f_loop.Count;

            // work out how much space to reserve for the patched in holes.
            for (int i = h_loops.Count - 1; i != -1; i--)
            {
                N += 2 + h_loops[i].Count;
            }

            // this is the vector that we will build the result in.
            var current_f_loop = new FList <T>(N);

            current_f_loop.AddRange(f_loop);

            var h_loop_min_vertex = new FList <Iterator <T> >(h_loops.Count);

            // find the major axis for the holes - this is the axis that we
            // will sort on for finding vertices on the polygon to join
            // holes up to.
            //
            // it might also be nice to also look for whether it is better
            // to sort ascending or descending.
            //
            // another trick that could be used is to modify the projection
            // by 90 degree rotations or flipping about an axis. just as
            // long as we keep the carve::geom3d::Vector pointers for the
            // real data in sync, everything should be ok. then we wouldn't
            // need to accomodate axes or sort order in the main loop.

            // find the bounding box of all the holes.
            bool    first = true;
            element min_x = element.MaxValue, min_y = element.MaxValue, max_x = element.MinValue, max_y = element.MinValue;

            for (int i = h_loops.Count - 1; i != -1; i--)
            {
                var hole = h_loops[i];
                for (int j = hole.Count - 1; j != -1; j--)
                {
                    var curr = project(hole[j]);
                    if (first)
                    {
                        min_x = max_x = curr.X;
                        min_y = max_y = curr.Y;
                        first = false;
                    }
                    else
                    {
                        if (curr.X < min_x)
                        {
                            min_x = curr.X;
                        }
                        if (curr.Y < min_y)
                        {
                            min_y = curr.Y;
                        }
                        if (max_x < curr.X)
                        {
                            max_x = curr.X;
                        }
                        if (max_y < curr.Y)
                        {
                            max_y = curr.Y;
                        }
                    }
                }
            }

            // choose the axis for which the bbox is largest.
            int axis = (max_x - min_x) > (max_y - min_y) ? 0 : 1;

            // for each hole, find the minimum vertex in the chosen axis.
            for (int i = 0, n = h_loops.Count; i < n; i++)
            {
                var hole   = h_loops[i];
                var best_i = MinElementIndex(hole, new order_h_loops <T>(project, axis));
                h_loop_min_vertex.Add(new Iterator <T>(hole, best_i));
            }

            // sort the holes by the minimum vertex.
            h_loop_min_vertex.Sort(new order_h_loops_iterator <T>(project, axis));

            // now, for each hole, find a vertex in the current polygon loop that it can be joined to.
            for (int i = 0; i < h_loop_min_vertex.Count; ++i)
            {
                var N_f_loop = current_f_loop.Count;

                // the index of the vertex in the hole to connect.
                var h_loop_connect = h_loop_min_vertex[i].value;

                var hole_min = project(h_loop_connect);

                // we order polygon loop vertices that may be able to be connected
                // to the hole vertex by their distance to the hole vertex
                var f_loop_heap = new PriorityQueue <int>(new heap_ordering <T>(project, current_f_loop, h_loop_connect, axis), N);

                for (int j = 0; j < N_f_loop; ++j)
                {
                    // it is guaranteed that there exists a polygon vertex with
                    // coord < the min hole coord chosen, which can be joined to
                    // the min hole coord without crossing the polygon
                    // boundary. also, because we merge holes in ascending
                    // order, it is also true that this join can never cross
                    // another hole (and that doesn't need to be tested for).
                    if (project(current_f_loop[j])[axis] <= hole_min[axis])
                    {
                        f_loop_heap.Push(j);
                    }
                }

                // we are going to test each potential (according to the
                // previous test) polygon vertex as a candidate join. we order
                // by closeness to the hole vertex, so that the join we make
                // is as small as possible. to test, we need to check the
                // joining line segment does not cross any other line segment
                // in the current polygon loop (excluding those that have the
                // vertex that we are attempting to join with as an endpoint).
                var attachment_point = current_f_loop.Count;

                while (f_loop_heap.Count != 0)
                {
                    var curr = f_loop_heap.Pop();
                    // test the candidate join from current_f_loop[curr] to hole_min

                    if (!testCandidateAttachment(project, current_f_loop, curr, hole_min))
                    {
                        continue;
                    }

                    attachment_point = curr;
                    break;
                }

                if (attachment_point == current_f_loop.Count)
                {
                    throw new ApplicationException("didn't manage to link up hole!");
                }

                patchHoleIntoPolygon(current_f_loop, attachment_point, h_loop_min_vertex[i]);
            }

            return(current_f_loop);
        }
Example #16
0
        static bool testCandidateAttachment <T>(Project <T> project, FList <T> current_f_loop, int curr, vector hole_min)
        {
            var SZ = current_f_loop.Count;

            int prev, next;

            if (curr == 0)
            {
                prev = SZ - 1; next = 1;
            }
            else if (curr == SZ - 1)
            {
                prev = curr - 1; next = 0;
            }
            else
            {
                prev = curr - 1; next = curr + 1;
            }

            if (!internalToAngle(project(current_f_loop[next]), project(current_f_loop[curr]), project(current_f_loop[prev]), hole_min))
            {
                return(false);
            }

            if (hole_min == project(current_f_loop[curr]))
            {
                return(true);
            }

            var test = new LineSegment2(hole_min, project(current_f_loop[curr]));

            var     v1      = current_f_loop.Count - 1;
            int     v2      = 0;
            var     v1_side = orient2d(test.v1, test.v2, project(current_f_loop[v1]));
            element v2_side = 0;

            while (v2 != current_f_loop.Count)
            {
                v2_side = orient2d(test.v1, test.v2, project(current_f_loop[v2]));

                if (v1_side != v2_side)
                {
                    // XXX: need to test vertices, not indices, because they may
                    // be duplicated.
                    if (project(current_f_loop[v1]) != project(current_f_loop[curr]) &&

                        project(current_f_loop[v2]) != project(current_f_loop[curr]))
                    {
                        var test2 = new LineSegment2(project(current_f_loop[v1]), project(current_f_loop[v2]));
                        if (lineSegmentIntersection_simple(test, test2))
                        {
                            // intersection; failed.
                            return(false);
                        }
                    }
                }

                v1      = v2;
                v1_side = v2_side;
                ++v2;
            }
            return(true);
        }
Example #17
0
        static int removeDegeneracies(ref vertex_info begin, FList <TriIdx> result)
        {
            vertex_info v;
            vertex_info n;
            int         count  = 0;
            int         remain = 0;

            v = begin;
            do
            {
                v = v.next;
                ++remain;
            } while (v != begin);

            v = begin;
            do
            {
                if (remain < 4)
                {
                    break;
                }

                bool remove = false;
                if (v.p == v.next.p)
                {
                    remove = true;
                }
                else if (v.p == v.next.next.p)
                {
                    if (v.next.p == v.next.next.next.p)
                    {
                        // a 'z' in the loop: z (a) b a b c . remove a-b-a . z (a) a b c . remove a-a-b (next loop) . z a b c
                        // z --(a)-- b
                        //         /
                        //        /
                        //      a -- b -- d
                        remove = true;
                    }
                    else
                    {
                        // a 'shard' in the loop: z (a) b a c d . remove a-b-a . z (a) a b c d . remove a-a-b (next loop) . z a b c d
                        // z --(a)-- b
                        //         /
                        //        /
                        //      a -- c -- d
                        // n.b. can only do this if the shard is pointing out of the polygon. i.e. b is outside z-a-c
                        remove = !internalToAngle(v.next.next.next, v, v.prev, v.next.p);
                    }
                }

                if (remove)
                {
                    result.Add(new TriIdx(v.idx, v.next.idx, v.next.next.idx));
                    n = v.next;
                    if (n == begin)
                    {
                        begin = n.next;
                    }
                    n.remove();
                    count++;
                    remain--;
                }
                else
                {
                    v = v.next;
                }
            } while (v != begin);

            return(count);
        }
Example #18
0
        static bool doTriangulate(vertex_info begin, FList <TriIdx> result)
        {
            var vq = new EarQueue();

            var v      = begin;
            int remain = 0;

            do
            {
                if (v.isCandidate())
                {
                    vq.push(v);
                }
                v = v.next;
                remain++;
            } while (v != begin);

            while (remain > 3 && vq.size() != 0)
            {
                var v2 = vq.pop();
                if (!v2.isClipable())
                {
                    v2.failed = true;
                    continue;
                }

continue_clipping:
                var n = v2.next;
                var p = v2.prev;

                result.Add(new TriIdx(v2.prev.idx, v2.idx, v2.next.idx));

                v2.remove();
                if (v2 == begin)
                {
                    begin = v2.next;
                }

                if (--remain == 3)
                {
                    break;
                }

                vq.updateVertex(n);
                vq.updateVertex(p);

                if (n.score < p.score)
                {
                    var t = n;
                    n = p;
                    p = t;
                }

                if (n.score > 0.25 && n.isCandidate() && n.isClipable())
                {
                    vq.remove(n);
                    v2 = n;
                    goto continue_clipping;
                }

                if (p.score > 0.25 && p.isCandidate() && p.isClipable())
                {
                    vq.remove(p);
                    v2 = p;
                    goto continue_clipping;
                }
            }


            if (remain > 3)
            {
                remain -= removeDegeneracies(ref begin, result);

                if (remain > 3)
                {
                    return(splitAndResume(begin, result));
                }
            }

            if (remain == 3)
            {
                result.Add(new TriIdx(begin.idx, begin.next.idx, begin.next.next.idx));
            }

            var d = begin;

            do
            {
                var n = d.next;
                d = n;
            } while (d != begin);

            return(true);
        }