【发布时间】:2011-12-21 05:26:27
【问题描述】:
我正在调试我编写的一种算法,该算法通过识别所有交点并绕着外围走动,将复杂的自相交多边形转换为简单的多边形。
我写了一系列随机数据生成压力测试,在这一次中,我遇到了一个有趣的情况,导致我的算法在正确运行数千次后失败。
double fRand(double fMin, double fMax)
{
double f = (double)rand() / RAND_MAX; // On my machine RAND_MAX = 2147483647
return fMin + f * (fMax - fMin);
}
// ... testing code below:
srand(1);
for(int j=3;j<5000;j+=5) {
std::vector<E_Point> geometry;
for(int i=0;i<j;i++) {
double radius = fRand(0.6,1.0);
double angle = fRand(0,2*3.1415926535);
E_Point pt(cos(angle),sin(angle));
pt *= radius;
geometry.push_back(pt); // sending in a pile of shit
}
// run algorithm on this geometry
这是对它的相关性以及我如何达到现在的位置的概述。还有很多细节我要省略。
我能够做的是将问题缩小到我正在使用的段-段交叉代码:
bool intersect(const E_Point& a0, const E_Point& a1,
const E_Point& b0, const E_Point& b1,
E_Point& intersectionPoint) {
if (a0 == b0 || a0 == b1 || a1 == b0 || a1 == b1) return false;
double x1 = a0.x; double y1 = a0.y;
double x2 = a1.x; double y2 = a1.y;
double x3 = b0.x; double y3 = b0.y;
double x4 = b1.x; double y4 = b1.y;
//AABB early exit
if (b2Max(x1,x2) < b2Min(x3,x4) || b2Max(x3,x4) < b2Min(x1,x2) ) return false;
if (b2Max(y1,y2) < b2Min(y3,y4) || b2Max(y3,y4) < b2Min(y1,y2) ) return false;
float ua = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3));
float ub = ((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3));
float denom = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1);
// check against epsilon (lowest normalized double value)
if (fabs(denom) < DBL_EPSILON) {
//Lines are too close to parallel to call
return false;
}
ua /= denom;
ub /= denom;
if ((0 < ua) && (ua < 1) && (0 < ub) && (ub < 1)) {
intersectionPoint.x = (x1 + ua * (x2 - x1));
intersectionPoint.y = (y1 + ua * (y2 - y1));
return true;
}
return false;
}
发生的情况是我有两个交点,此函数为其返回完全相同的交点值。这是相关几何的放大视图:
垂直线由点定义
(0.3871953044519425, -0.91857980824611341), (0.36139704793723609, 0.91605957361605106)
由(0.8208980020500205, 0.52853407296583088), (0.36178501611208552, 0.88880385168617226) 点组成的几乎水平的绿色线
和(0.36178501611208552, 0.88880385168617226), (-0.43211245441046209, 0.68034202227710472)的白线
如您所见,最后两行确实有一个共同点。
我的函数给了我(0.36178033094571277, 0.88880245640159794)的解决方案
对于这两个交叉口(您在图片中看到一个红点)。
这是一个大问题的原因是我的周界算法依赖于对每条边上的交点进行排序。由于这两个交点都被计算为具有相同的精确值,因此排序将它们置于错误的方向。周界的路径来自上方,沿着左边的白线,而不是左边的绿线,这意味着我不再沿着多边形的外周长。
要解决这个问题,我可能可以做很多事情,但我不想搜索所有交集列表来检查其他点以查看位置是否相等。更好的解决方案是尝试提高我的交集函数的准确性。
所以我要问的是,为什么解点如此不准确?是因为其中一条线几乎是垂直的吗?我应该先进行某种转换吗?在这两种情况下,几乎垂直的线都以a0 和a1 传递。
更新:嘿,看看这个:
TEST(intersection_precision_test) {
E_Point problem[] = {
{0.3871953044519425, -0.91857980824611341}, // 1559
{0.36139704793723609, 0.91605957361605106}, // 1560
{-0.8208980020500205, 0.52853407296583088}, // 1798
{0.36178501611208552, 0.88880385168617226}, // 1799
{-0.43211245441046209, 0.6803420222771047} // 1800
};
std::cout.precision(16);
E_Point result;
intersect(problem[0],problem[1],problem[2],problem[3],result);
std::cout << "1: " << result << std::endl;
intersect(problem[0],problem[1],problem[3],problem[2],result);
std::cout << "2: " << result << std::endl;
intersect(problem[1],problem[0],problem[2],problem[3],result);
std::cout << "3: " << result << std::endl;
intersect(problem[1],problem[0],problem[3],problem[2],result);
std::cout << "4: " << result << std::endl;
intersect(problem[2],problem[3],problem[0],problem[1],result);
std::cout << "rev: " << result << std::endl;
intersect(problem[3],problem[2],problem[0],problem[1],result);
std::cout << "revf1: " << result << std::endl;
intersect(problem[2],problem[3],problem[1],problem[0],result);
std::cout << "revf2: " << result << std::endl;
intersect(problem[3],problem[2],problem[1],problem[0],result);
std::cout << "revfboth: " << result << std::endl;
}
输出:
Starting Test intersection_precision_test, at Polygon.cpp:1830
1: <0.3617803309457128,0.8888024564015979>
2: <0.3617803309457128,0.8888024564015979>
3: <0.3617803314022162,0.8888024239374175>
4: <0.3617803314022162,0.8888024239374175>
rev: <0.3617803635476076,0.8888024344185281>
revf1: <0.3617803313928456,0.8888024246235207>
revf2: <0.3617803635476076,0.8888024344185281>
revfboth: <0.3617803313928456,0.8888024246235207>
我是否真的用完了尾数位,或者我可以使用更智能的算法做得更好吗?
这里的问题是我没有简单的方法来确定何时将顶点设置为非常接近另一条线。我不介意移动它,甚至完全摧毁它,因为这些都不会搞砸我的排序!
【问题讨论】:
标签: geometry floating-point precision computational-geometry