示例#1
0
        public static void test_convex_hull_2()
        {
            Random r = new Random(31337);

            //LocalProfiler p = new LocalProfiler();
            //p.Start("Hulls");

            QueryNumberType[] modes = new QueryNumberType[] { QueryNumberType.QT_DOUBLE, QueryNumberType.QT_INT64 };

            foreach (var queryMode in modes)
            {
                for (int k = 0; k < 1000; ++k)
                {
                    int    N     = 2500;
                    double scale = (r.NextDouble() + 0.1) * 1024.0;

                    Vector2d[] pts = TestUtil.RandomPoints2(N, r, Vector2d.Zero, scale);

                    double eps = MathUtil.Epsilonf;

                    ConvexHull2 hull     = new ConvexHull2(pts, eps, queryMode);
                    Polygon2d   hullPoly = hull.GetHullPolygon();

                    foreach (Vector2d v in pts)
                    {
                        if (hullPoly.Contains(v))
                        {
                            continue;
                        }
                        double d = hullPoly.DistanceSquared(v);
                        if (d < eps)
                        {
                            continue;
                        }
                        System.Console.WriteLine("test_convex_hull: Point {0} not contained!", v);
                    }
                }
            }

            //p.StopAll();
            //System.Console.WriteLine(p.AllTimes());

            //SVGWriter writer = new SVGWriter();
            //foreach (Vector2d v in pts) {
            //    writer.AddCircle(new Circle2d(v, 3.0), SVGWriter.Style.Outline("black", 1.0f));
            //}
            //writer.AddPolygon(hullPoly, SVGWriter.Style.Outline("red", 2.0f));
            //writer.Write(TestUtil.GetTestOutputPath("test.svg"));
        }
示例#2
0
        public void AddNumberQuery(string name, QueryNumberType type, double value)
        {
            string itemFilter;

            switch (type)
            {
            case QueryNumberType.LowerThan: itemFilter = "lt"; break;

            case QueryNumberType.LowerOrEqual: itemFilter = "le"; break;

            case QueryNumberType.Equals: itemFilter = "eq"; break;

            case QueryNumberType.GreaterThan: itemFilter = "gt"; break;

            case QueryNumberType.GreaterOrEqual: itemFilter = "ge"; break;

            default: throw new NotSupportedException($"The type {type} does not exist within the QueryNumberType enum.");
            }

            string item = $"{itemFilter}({value.ToUsNotation()})";

            SetParam(GetParamName(name), item);
        }
        /// <summary>
        /// 计算输入点的凸包。
        /// Compute convex hull of input points.
        /// epsilon仅用于检查点是否位于一条线(1d船体)上,而不用于其余计算。
        /// epsilon is only used for check if points lie on a line (1d hull), not for rest of compute.
        /// </summary>
        public ConvexHull2(IList <Vector2d> vertices, double epsilon, QueryNumberType queryType)
        {
            //mQueryType = queryType;
            mVertices     = vertices;
            mNumVertices  = vertices.Count;
            mDimension    = 0;
            mNumSimplices = 0;
            mIndices      = null;
            mSVertices    = null;

            mEpsilon = epsilon;

            mQuery = null;

            mLineOrigin    = Vector2d.Zero;
            mLineDirection = Vector2d.Zero;

            Vector2d.Information info;
            Vector2d.GetInformation(mVertices, mEpsilon, out info);
            if (info.mDimension == 0)
            {
                mDimension = 0;
                mIndices   = null;
                return;
            }

            if (info.mDimension == 1)
            {
                // The set is (nearly) collinear.  The caller is responsible for
                // creating a ConvexHull1 object.
                mDimension     = 1;
                mLineOrigin    = info.mOrigin;
                mLineDirection = info.mDirection0;
                return;
            }

            mDimension = 2;

            int i0 = info.mExtreme[0];
            int i1 = info.mExtreme[1];
            int i2 = info.mExtreme[2];

            mSVertices = new Vector2d[mNumVertices];

            if (queryType != QueryNumberType.QT_RATIONAL && queryType != QueryNumberType.QT_FILTERED)
            {
                // Transform the vertices to the square [0,1]^2.
                Vector2d minValue = new Vector2d(info.mMin[0], info.mMin[1]);
                double   scale    = ((double)1) / info.mMaxRange;
                for (int i = 0; i < mNumVertices; ++i)
                {
                    mSVertices[i] = (mVertices[i] - minValue) * scale;
                }

                double expand;
                if (queryType == QueryNumberType.QT_INT64)
                {
                    // Scale the vertices to the square [0,2^{20}]^2 to allow use of
                    // 64-bit integers.
                    expand = (double)(1 << 20);
                    mQuery = new Query2Int64(mSVertices);
                }
                else if (queryType == QueryNumberType.QT_INTEGER)
                {
                    throw new NotImplementedException("ConvexHull2: Query type QT_INTEGER not currently supported");
                    // Scale the vertices to the square [0,2^{24}]^2 to allow use of
                    // Integer.
                    //expand = (double)(1 << 24);
                    //mQuery = new Query2Integer(mNumVertices, mSVertices);
                }
                else
                {  // queryType == Query::QT_double
                    // No scaling for floating point.
                    expand = (double)1;
                    mQuery = new Query2d(mSVertices);
                }

                for (int i = 0; i < mNumVertices; ++i)
                {
                    mSVertices[i] *= expand;
                }
            }
            else
            {
                throw new NotImplementedException("ConvexHull2: Query type QT_RATIONAL/QT_FILTERED not currently supported");

                // No transformation needed for exact rational arithmetic or filtered
                // predicates.
                //for (int i = 0; i < mSVertices.Length; ++i)
                //    mSVertices[i] = mVertices[i];

                //if (queryType == Query::QT_RATIONAL) {
                //    mQuery = new Query2Rational(mNumVertices, mSVertices);
                //} else { // queryType == Query::QT_FILTERED
                //    mQuery = new Query2Filtered(mNumVertices, mSVertices,
                //        mEpsilon);
                //}
            }


            Edge edge0 = null;
            Edge edge1 = null;
            Edge edge2 = null;

            if (info.mExtremeCCW)
            {
                edge0 = new Edge(i0, i1);
                edge1 = new Edge(i1, i2);
                edge2 = new Edge(i2, i0);
            }
            else
            {
                edge0 = new Edge(i0, i2);
                edge1 = new Edge(i2, i1);
                edge2 = new Edge(i1, i0);
            }

            edge0.Insert(edge2, edge1);
            edge1.Insert(edge0, edge2);
            edge2.Insert(edge1, edge0);

            Edge hull = edge0;

            // ideally we insert points in random order. but instead of
            // generating a permutation, just insert them using modulo-indexing,
            // which is in the ballpark...
            int ii = 0;

            do
            {
                if (!Update(ref hull, ii))
                {
                    return;
                }
                ii = (ii + 31337) % mNumVertices;
            } while (ii != 0);

            // original code, vastly slower in pathological cases
            //for (int i = 0; i < mNumVertices; ++i) {
            //    if ( ! Update(ref hull, i) )
            //        return;
            //}

            hull.GetIndices(ref mNumSimplices, ref mIndices);
        }
示例#4
0
        public ContMinBox2(IList <Vector2d> points, double epsilon, QueryNumberType queryType, bool isConvexPolygon)
        {
            // Get the convex hull of the points.
            IList <Vector2d> hullPoints;
            int numPoints;

            if (isConvexPolygon)
            {
                hullPoints = points;
                numPoints  = hullPoints.Count;
            }
            else
            {
                ConvexHull2 hull             = new ConvexHull2(points, epsilon, queryType);
                int         hullDim          = hull.Dimension;
                int         hullNumSimplices = hull.NumSimplices;
                int[]       hullIndices      = hull.HullIndices;

                if (hullDim == 0)
                {
                    mMinBox.Center    = points[0];
                    mMinBox.AxisX     = Vector2d.AxisX;
                    mMinBox.AxisY     = Vector2d.AxisY;
                    mMinBox.Extent[0] = (double)0;
                    mMinBox.Extent[1] = (double)0;
                    return;
                }

                if (hullDim == 1)
                {
                    throw new NotImplementedException("ContMinBox2: Have not implemented 1d case");
                    //ConvexHull1 hull1 = hull.GetConvexHull1();
                    //hullIndices = hull1->GetIndices();

                    //mMinBox.Center = ((double)0.5) * (points[hullIndices[0]] +
                    //    points[hullIndices[1]]);
                    //Vector2d diff =
                    //    points[hullIndices[1]] - points[hullIndices[0]];
                    //mMinBox.Extent[0] = ((double)0.5) * diff.Normalize();
                    //mMinBox.Extent[1] = (double)0.0;
                    //mMinBox.Axis[0] = diff;
                    //mMinBox.Axis[1] = -mMinBox.Axis[0].Perp();
                    //return;
                }

                numPoints = hullNumSimplices;
                Vector2d[] pointsArray = new Vector2d[numPoints];
                for (int i = 0; i < numPoints; ++i)
                {
                    pointsArray[i] = points[hullIndices[i]];
                }
                hullPoints = pointsArray;
            }

            // The input points are V[0] through V[N-1] and are assumed to be the
            // vertices of a convex polygon that are counterclockwise ordered.  The
            // input points must not contain three consecutive collinear points.

            // Unit-length edge directions of convex polygon.  These could be
            // precomputed and passed to this routine if the application requires it.
            int numPointsM1 = numPoints - 1;

            Vector2d[] edges   = new Vector2d[numPoints];
            bool[]     visited = new bool[numPoints];
            for (int i = 0; i < numPointsM1; ++i)
            {
                edges[i] = hullPoints[i + 1] - hullPoints[i];
                edges[i].Normalize();
                visited[i] = false;
            }
            edges[numPointsM1] = hullPoints[0] - hullPoints[numPointsM1];
            edges[numPointsM1].Normalize();
            visited[numPointsM1] = false;

            // Find the smallest axis-aligned box containing the points.  Keep track
            // of the extremum indices, L (left), R (right), B (bottom), and T (top)
            // so that the following constraints are met:
            //   V[L].x <= V[i].x for all i and V[(L+1)%N].x > V[L].x
            //   V[R].x >= V[i].x for all i and V[(R+1)%N].x < V[R].x
            //   V[B].y <= V[i].y for all i and V[(B+1)%N].y > V[B].y
            //   V[T].y >= V[i].y for all i and V[(T+1)%N].y < V[T].y
            double xmin = hullPoints[0].x, xmax = xmin;
            double ymin = hullPoints[0].y, ymax = ymin;
            int    LIndex = 0, RIndex = 0, BIndex = 0, TIndex = 0;

            for (int i = 1; i < numPoints; ++i)
            {
                if (hullPoints[i].x <= xmin)
                {
                    xmin   = hullPoints[i].x;
                    LIndex = i;
                }
                if (hullPoints[i].x >= xmax)
                {
                    xmax   = hullPoints[i].x;
                    RIndex = i;
                }

                if (hullPoints[i].y <= ymin)
                {
                    ymin   = hullPoints[i].y;
                    BIndex = i;
                }
                if (hullPoints[i].y >= ymax)
                {
                    ymax   = hullPoints[i].y;
                    TIndex = i;
                }
            }

            // Apply wrap-around tests to ensure the constraints mentioned above are
            // satisfied.
            if (LIndex == numPointsM1)
            {
                if (hullPoints[0].x <= xmin)
                {
                    xmin   = hullPoints[0].x;
                    LIndex = 0;
                }
            }

            if (RIndex == numPointsM1)
            {
                if (hullPoints[0].x >= xmax)
                {
                    xmax   = hullPoints[0].x;
                    RIndex = 0;
                }
            }

            if (BIndex == numPointsM1)
            {
                if (hullPoints[0].y <= ymin)
                {
                    ymin   = hullPoints[0].y;
                    BIndex = 0;
                }
            }

            if (TIndex == numPointsM1)
            {
                if (hullPoints[0].y >= ymax)
                {
                    ymax   = hullPoints[0].y;
                    TIndex = 0;
                }
            }

            // The dimensions of the axis-aligned box.  The extents store width and
            // height for now.
            mMinBox.Center.x  = ((double)0.5) * (xmin + xmax);
            mMinBox.Center.y  = ((double)0.5) * (ymin + ymax);
            mMinBox.AxisX     = Vector2d.AxisX;
            mMinBox.AxisY     = Vector2d.AxisY;
            mMinBox.Extent[0] = ((double)0.5) * (xmax - xmin);
            mMinBox.Extent[1] = ((double)0.5) * (ymax - ymin);
            double minAreaDiv4 = mMinBox.Extent[0] * mMinBox.Extent[1];

            // The rotating calipers algorithm.
            Vector2d U = Vector2d.AxisX;
            Vector2d V = Vector2d.AxisY;

            bool done = false;

            while (!done)
            {
                // Determine the edge that forms the smallest angle with the current
                // box edges.
                RCFlags flag   = RCFlags.F_NONE;
                double  maxDot = (double)0;

                double dot = U.Dot(edges[BIndex]);
                if (dot > maxDot)
                {
                    maxDot = dot;
                    flag   = RCFlags.F_BOTTOM;
                }

                dot = V.Dot(edges[RIndex]);
                if (dot > maxDot)
                {
                    maxDot = dot;
                    flag   = RCFlags.F_RIGHT;
                }

                dot = -U.Dot(edges[TIndex]);
                if (dot > maxDot)
                {
                    maxDot = dot;
                    flag   = RCFlags.F_TOP;
                }

                dot = -V.Dot(edges[LIndex]);
                if (dot > maxDot)
                {
                    maxDot = dot;
                    flag   = RCFlags.F_LEFT;
                }

                switch (flag)
                {
                case RCFlags.F_BOTTOM:
                    if (visited[BIndex])
                    {
                        done = true;
                    }
                    else
                    {
                        // Compute box axes with E[B] as an edge.
                        U = edges[BIndex];
                        V = -U.Perp;
                        UpdateBox(hullPoints[LIndex], hullPoints[RIndex],
                                  hullPoints[BIndex], hullPoints[TIndex], ref U, ref V,
                                  ref minAreaDiv4);

                        // Mark edge visited and rotate the calipers.
                        visited[BIndex] = true;
                        if (++BIndex == numPoints)
                        {
                            BIndex = 0;
                        }
                    }
                    break;

                case RCFlags.F_RIGHT:
                    if (visited[RIndex])
                    {
                        done = true;
                    }
                    else
                    {
                        // Compute box axes with E[R] as an edge.
                        V = edges[RIndex];
                        U = V.Perp;
                        UpdateBox(hullPoints[LIndex], hullPoints[RIndex],
                                  hullPoints[BIndex], hullPoints[TIndex], ref U, ref V,
                                  ref minAreaDiv4);

                        // Mark edge visited and rotate the calipers.
                        visited[RIndex] = true;
                        if (++RIndex == numPoints)
                        {
                            RIndex = 0;
                        }
                    }
                    break;

                case RCFlags.F_TOP:
                    if (visited[TIndex])
                    {
                        done = true;
                    }
                    else
                    {
                        // Compute box axes with E[T] as an edge.
                        U = -edges[TIndex];
                        V = -U.Perp;
                        UpdateBox(hullPoints[LIndex], hullPoints[RIndex],
                                  hullPoints[BIndex], hullPoints[TIndex], ref U, ref V,
                                  ref minAreaDiv4);

                        // Mark edge visited and rotate the calipers.
                        visited[TIndex] = true;
                        if (++TIndex == numPoints)
                        {
                            TIndex = 0;
                        }
                    }
                    break;

                case RCFlags.F_LEFT:
                    if (visited[LIndex])
                    {
                        done = true;
                    }
                    else
                    {
                        // Compute box axes with E[L] as an edge.
                        V = -edges[LIndex];
                        U = V.Perp;
                        UpdateBox(hullPoints[LIndex], hullPoints[RIndex],
                                  hullPoints[BIndex], hullPoints[TIndex], ref U, ref V,
                                  ref minAreaDiv4);

                        // Mark edge visited and rotate the calipers.
                        visited[LIndex] = true;
                        if (++LIndex == numPoints)
                        {
                            LIndex = 0;
                        }
                    }
                    break;

                case RCFlags.F_NONE:
                    // The polygon is a rectangle.
                    done = true;
                    break;
                }
            }
        }
示例#5
0
        public static void test_min_box_2()
        {
            Random r = new Random(31337);

            bool write_svg           = false;
            int  contained_circles_N = 100;
            int  test_iters          = 1000;

            //LocalProfiler p = new LocalProfiler();
            //p.Start("Hulls");

            QueryNumberType[] modes = new QueryNumberType[] { QueryNumberType.QT_DOUBLE, QueryNumberType.QT_INT64 };
            //QueryNumberType[] modes = new QueryNumberType[] { QueryNumberType.QT_DOUBLE };

            foreach (var queryMode in modes)
            {
                for (int k = 0; k < test_iters; ++k)
                {
                    int        N        = contained_circles_N;
                    double     scale    = (r.NextDouble() + 0.1) * 1024.0;
                    Interval1d radRange = new Interval1d(10, 100);

                    Vector2d[] pts    = TestUtil.RandomPoints2(N, r, Vector2d.Zero, scale);
                    double[]   radius = TestUtil.RandomScalars(N, r, new Interval1d(radRange));

                    double eps = MathUtil.Epsilonf;

                    SVGWriter svg = (write_svg) ? new SVGWriter() : null;

                    List <Vector2d> accumPts = new List <Vector2d>();
                    for (int i = 0; i < pts.Length; ++i)
                    {
                        Polygon2d circ = Polygon2d.MakeCircle(radius[i], 16, radius[i]);
                        circ.Translate(pts[i]);
                        accumPts.AddRange(circ.Vertices);

                        if (svg != null)
                        {
                            svg.AddPolygon(circ, SVGWriter.Style.Outline("black", 1.0f));
                        }
                    }

                    ContMinBox2 contbox = new ContMinBox2(accumPts, 0.001, queryMode, false);
                    Box2d       box     = contbox.MinBox;

                    if (svg != null)
                    {
                        svg.AddPolygon(new Polygon2d(box.ComputeVertices()), SVGWriter.Style.Outline("red", 2.0f));
                        svg.Write(TestUtil.GetTestOutputPath("contbox.svg"));
                    }

                    foreach (Vector2d v in accumPts)
                    {
                        if (box.Contains(v))
                        {
                            continue;
                        }
                        double d = box.DistanceSquared(v);
                        if (d < eps)
                        {
                            continue;
                        }
                        System.Console.WriteLine("test_min_box_2: Point {0} not contained!", v);
                    }
                }
            }

            //p.StopAll();
            //System.Console.WriteLine(p.AllTimes());
        }