/// <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);
        }