|
参考ObjectARX(VC)开发基础与实例教程P254
//************************************
// Author: WangHongFeng
// Summary: 点与闭合多段线的位置关系
// Method: PtRelationToPoly
// Access: public
// Returns: int -1表示在多段线外部,0表示在多段线上,1表示在多段线内部
// Parameter: AcDbPolyline * pPoly
// Parameter: const AcGePoint2d & pt
// Parameter: double tol
//************************************
int PtRelationToPoly(AcDbPolyline *pPoly, const AcGePoint2d &pt, double tol = 1.0E-7);
bool PointIsPolyVert(AcDbPolyline *pPoly, const AcGePoint2d &pt, double tol);
void IntersectWithGeRay(AcDbPolyline *pPoly, const AcGeRay2d &geRay, AcGePoint3dArray &intPoints, double tol = 1.0E-7);
void FilterEqualPoints(AcGePoint3dArray &points, double tol = 1.0E-7);
void FilterEqualPoints(AcGePoint3dArray &points, const AcGePoint2d &pt, double tol = 1.0E-7);
int FindPoint(const AcGePoint2dArray &points, const AcGePoint2d &point, double tol = 1.0E-7);
int PubFuc::PtRelationToPoly(AcDbPolyline *pPoly, const AcGePoint2d &pt, double tol /*= 1.0E-7*/)
{
assert(pPoly);
// 1.如果点到多段线的最近点和给定的点重合,表示点在多段线上
AcGePoint3d closestPoint;
pPoly->getClosestPointTo(ToPoint3d(pt, pPoly->elevation()), closestPoint); // 多段线上与给定点距离最近的点
if (fabs(closestPoint.x - pt.x) < tol && fabs(closestPoint.y - pt.y) < tol) // 点在多段线上
{
return 0;
}
// 2.第一个射线的方向是从最近点到当前点,起点是当前点
// 射线的起点是pt,方向为从最近点到pt,如果反向做判断,则最近点距离pt太近的时候,最近点也会被作为一个交点(这个交点不太容易被排除掉)
// 此外,这样的射线方向很容易判断出点不在内部的情况
AcGeVector3d vec(-(closestPoint[X] - pt[X]), -(closestPoint[Y] - pt[Y]), 0);
AcGeRay2d geRay(AcGePoint2d(pt.x, pt.y), AcGePoint2d(pt.x + vec.x, pt.y + vec.y));
// 3.射线与多段线计算交点
AcGePoint3dArray intPoints;
IntersectWithGeRay(pPoly, geRay, intPoints, 1.0E-4);
// IntersectWith函数经常会得到很近的交点,这些点必须进行过滤
PubFuc::FilterEqualPoints(intPoints, 1.0E-4);
// 4.判断点和多段线的位置关系
RETRY:
// 4.1 如果射线和多段线没有交点,表示点在多段线的外部
if (intPoints.length() == 0)
{
return -1;
}
else
{
// 3.1 过滤掉由于射线被反向延长带来的影响
PubFuc::FilterEqualPoints(intPoints, ToPoint2d(closestPoint)); // 2008-0907修订记录:当pt距离最近点比较近的时候,最近点竟然被作为一个交点!
// 3.2 如果某个交点与最近点在给定点的同一方向,要去掉这个点(这个点明显不是交点,还是由于intersectwith函数的Bug)
for (int i = intPoints.length() - 1; i >= 0; i--)
{
if ((intPoints[i][X] - pt[X]) * (closestPoint[X] - pt[X]) >= 0 &&
(intPoints[i][Y] - pt[Y]) * (closestPoint[Y] - pt[Y]) >= 0)
{
intPoints.removeAt(i);
}
}
int count = intPoints.length();
int i;
for (i = 0; i < intPoints.length(); i++)
{
if (PointIsPolyVert(pPoly, ToPoint2d(intPoints[i]), 1.0E-4)) // 只要有交点是多段线的顶点就重新进行判断
{
// 处理给定点很靠近多段线顶点的情况(如果与顶点距离很近,就认为这个点在多段线上,因为这种情况没有什么好的判断方法)
if (PointIsPolyVert(pPoly, AcGePoint2d(pt.x, pt.y), 1.0E-4))
{
return 0;
}
// 将射线旋转一个极小的角度(2度)再次判断(假定这样不会再通过上次判断到的顶点)
vec = vec.rotateBy(0.035, AcGeVector3d::kZAxis);
geRay.set(AcGePoint2d(pt.x, pt.y), AcGePoint2d(pt.x + vec.x, pt.y + vec.y));
intPoints.setLogicalLength(0);
IntersectWithGeRay(pPoly, geRay, intPoints, 1.0E-4);
goto RETRY; // 继续判断结果
}
}
if (count % 2 == 0)
{
return -1;
}
else
{
return 1;
}
}
}
bool PubFuc::PointIsPolyVert(AcDbPolyline *pPoly, const AcGePoint2d &pt, double tol)
{
for (int i = 0; i < (int)pPoly->numVerts(); i++)
{
AcGePoint3d vert;
pPoly->getPointAt(i, vert);
AcGeTol gtol;
gtol.setEqualPoint(tol);
if (ToPoint2d(vert).isEqualTo(pt, gtol))
{
return true;
}
}
return false;
}
// 几何类射线和多段线计算交点
void PubFuc::IntersectWithGeRay(AcDbPolyline *pPoly, const AcGeRay2d &geRay, AcGePoint3dArray &intPoints, double tol /*= 1.0E-7*/)
{
intPoints.setLogicalLength(0);
AcGePoint2dArray intPoints2d;
// 多段线的每一段分别与射线计算交点
AcGeTol geTol;
geTol.setEqualPoint(tol);
for (int i = 0; i < pPoly->numVerts(); i++)
{
if (i < pPoly->numVerts() - 1 || pPoly->isClosed() == Adesk::kTrue)
{
double bulge = 0;
pPoly->getBulgeAt(i, bulge);
if (fabs(bulge) < 1.0E-7)
{
// 构建几何类的线段来计算交点
AcGeLineSeg2d geLine;
Acad::ErrorStatus es = pPoly->getLineSegAt(i, geLine);
AcGePoint2d intPoint;
if (geLine.intersectWith(geRay, intPoint, geTol) == Adesk::kTrue)
{
if (PubFuc::FindPoint(intPoints2d, intPoint, tol) < 0)
{
intPoints2d.append(intPoint);
}
}
}
else
{
// 构建几何类的圆弧来计算交点
AcGeCircArc2d geArc;
pPoly->getArcSegAt(i, geArc);
AcGePoint2d pt1, pt2;
int count = 0;
if (geArc.intersectWith(geRay, count, pt1, pt2, geTol) == Adesk::kTrue)
{
if (PubFuc::FindPoint(intPoints2d, pt1, tol) < 0)
{
intPoints2d.append(pt1);
}
if (count > 1 && PubFuc::FindPoint(intPoints2d, pt2, tol) < 0)
{
intPoints2d.append(pt2);
}
}
}
}
}
double z = pPoly->elevation();
int i;
for (i = 0; i < intPoints2d.length(); i++)
{
intPoints.append(AcGePoint3d(intPoints2d[i].x, intPoints2d[i].y, z));
}
}
void PubFuc::FilterEqualPoints(AcGePoint3dArray &points, double tol /*= 1.0E-7*/)
{
for (int i = points.length() - 1; i > 0; i--)
{
for (int j = 0; j < i; j++)
{
if (MathUtils::IsEqual(points[i].x, points[j].x, tol) && MathUtils::IsEqual(points[i].y, points[j].y, tol))
{
points.removeAt(i);
break;
}
}
}
}
// 在数组中查找某个点,返回点在数组中的索引,未找到则返回-1
int PubFuc::FindPoint(const AcGePoint2dArray &points, const AcGePoint2d &point, double tol /*= 1.0E-7*/)
{
for (int i = 0; i < points.length(); i++)
{
AcGeTol gtol;
gtol.setEqualPoint(tol);
if (points[i].isEqualTo(point, gtol))
{
return i;
}
}
return -1;
}
// 从数组中过滤掉重复点
void PubFuc::FilterEqualPoints(AcGePoint3dArray &points, const AcGePoint2d &pt, double tol /*= 1.0E-7*/)
{
AcGePoint3dArray tempPoints;
for (int i = 0; i < points.length(); i++)
{
if (ToPoint2d(points[i]).distanceTo(pt) > tol)
{
tempPoints.append(points[i]);
}
}
points = tempPoints;
} |
|