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