2010-11-02 34 views
2

我一直在努力研究线段 - 圆筒交叉口例程,我的大脑已经融化。试图优化线条与圆柱体相交

/// Line segment VS <cylinder> 
// - cylinder (A, B, r) (start point, end point, radius) 
// - line has starting point (x0, y0, z0) and ending point (x0+ux, y0+uy, z0+uz) ((ux, uy, uz) is "direction") 
// => start = (x0, y0, z0) 
// dir = (ux, uy, uz) 
// A 
// B 
// r 
// optimize? (= don't care for t > 1) 
// <= t = "time" of intersection 
// norm = surface normal of intersection point 
void CollisionExecuter::cylinderVSline(const Ogre::Vector3& start, const Ogre::Vector3& dir, const Ogre::Vector3& A, const Ogre::Vector3& B, const double r, 
      const bool optimize, double& t, Ogre::Vector3& normal) { 
    t = NaN; 

    // Solution : http://www.gamedev.net/community/forums/topic.asp?topic_id=467789 
    double cxmin, cymin, czmin, cxmax, cymax, czmax; 
    if (A.z < B.z) { czmin = A.z - r; czmax = B.z + r; } else { czmin = B.z - r; czmax = A.z + r; } 
    if (A.y < B.y) { cymin = A.y - r; cymax = B.y + r; } else { cymin = B.y - r; cymax = A.y + r; } 
    if (A.x < B.x) { cxmin = A.x - r; cxmax = B.x + r; } else { cxmin = B.x - r; cxmax = A.x + r; } 
    if (optimize) { 
    if (start.z >= czmax && (start.z + dir.z) > czmax) return; 
    if (start.z <= czmin && (start.z + dir.z) < czmin) return; 
    if (start.y >= cymax && (start.y + dir.y) > cymax) return; 
    if (start.y <= cymin && (start.y + dir.y) < cymin) return; 
    if (start.x >= cxmax && (start.x + dir.x) > cxmax) return; 
    if (start.x <= cxmin && (start.x + dir.x) < cxmin) return; 
    } 

    Ogre::Vector3 AB = B - A; 
    Ogre::Vector3 AO = start - A; 
    Ogre::Vector3 AOxAB = AO.crossProduct(AB); 
    Ogre::Vector3 VxAB = dir.crossProduct(AB); 
    double ab2 = AB.dotProduct(AB); 
    double a = VxAB.dotProduct(VxAB); 
    double b = 2 * VxAB.dotProduct(AOxAB); 
    double c = AOxAB.dotProduct(AOxAB) - (r*r * ab2); 
    double d = b * b - 4 * a * c; 
    if (d < 0) return; 
    double time = (-b - sqrt(d))/(2 * a); 
    if (time < 0) return; 

    Ogre::Vector3 intersection = start + dir * time;  /// intersection point 
    Ogre::Vector3 projection = A + (AB.dotProduct(intersection - A)/ab2) * AB; /// intersection projected onto cylinder axis 
    if ((projection - A).length() + (B - projection).length() > AB.length()) return; /// THIS IS THE SLOW SAFE WAY 
    //if (projection.z > czmax - r || projection.z < czmin + r || 
    // projection.y > cymax - r || projection.y < cymin + r || 
    // projection.x > cxmax - r || projection.x < cxmin + r) return; /// THIS IS THE FASTER BUGGY WAY 

    normal = (intersection - projection); 
    normal.normalise(); 
    t = time; /// at last 
} 

我曾想过这样的方式来加速发现交点投影是否位于圆柱体长度内。然而,它不起作用,我无法真正了解它,因为它看起来很合逻辑: 如果投影点的x,y或z坐标不在圆柱体的极限范围内,则应该在外部考虑。看来虽然这在实践中并不奏效。

任何帮助将不胜感激!

干杯,

比尔Kotsias

编辑:看来,问题上升与边界的情况下,即当气缸是平行于轴线的一个。舍入误差进入公式,“优化”停止正常工作。

if (projection.z > czmax - r + 0.001 || projection.z < czmin + r - 0.001 || ... etc... 

回答

3

气缸是圆形,右:

也许,如果逻辑正确,问题将通过插入有点像宽容走吗?您可以转换坐标,使圆柱的中心线作为Z轴。然后你有一个与圆相交的2D问题。交点将根据沿线长度从0到1的参数表示,因此您可以计算它们在该坐标系中的位置并与圆柱体的顶部和底部进行比较。

你应该能够以封闭的形式做到这一切。没有容差。当然,你会得到奇点和想象的解决方案。你似乎想到了这一切,所以我想我不确定问题是什么。

2

迈克的回答很好。对于任何棘手的形状,你最好找到转换矩阵T,它将它映射成一个不错的直立版本,在这种情况下,半径为1的直径圆柱体将很好地完成这项工作。在这个新空间中找出你的新线路,执行计算,转换回来。然而,如果你正在寻找优化(听起来像你),那么你可能会做负载。

例如,您可以计算两条线之间的最短距离 - 可能使用点积规则 - 想象一个线程连接两条线。那么如果这个线程是所有可能线程中最短的线程,那么它将垂直于两条线,所以Thread.LineA = Thread.LineB = 0

如果最短距离大于圆柱半径,一个小姐。

你可以使用x,y,z定义圆柱体的轨迹,并将整个事物作为一些可怕的二次方程式进行排列,并通过首先计算判别式进行优化,如果是负数则返回无打击。

要定义轨迹,取任意点P =(x,y,z)。将它垂直放置在圆柱体的中心线上,并观察其大小的平方。如果这等于该点所在的R^2。

然后你把你的线{s = U + lamda * V}放入那个混乱中,最后你会在lamda中得到一些屁股丑陋的二次方。但是这可能比摆弄矩阵要快,除非你可以让硬件做到这一点(我猜OpenGL有一些功能可以让硬件做到这一点)。

这一切都取决于你想要多少优化;个人而言,除非有充分理由不这样做,否则我会和迈克的答案一起去。

PS如果你解释你使用的技术而不是仅仅倾销代码,你可能会得到更多的帮助,让读者去弄清楚你在做什么。

3

这是我使用的东西,它可以帮助:

bool d3RayCylinderIntersection(const DCylinder &cylinder,const DVector3 &org,const DVector3 &dir,float &lambda,DVector3 &normal,DVector3 &newPosition) 
// Ray and cylinder intersection 
// If hit, returns true and the intersection point in 'newPosition' with a normal and distance along 
// the ray ('lambda') 
{ 
    DVector3 RC; 
    float d; 
    float t,s; 
    DVector3 n,D,O; 
    float ln; 
    float in,out; 

    RC=org; RC.Subtract(&cylinder.position); 
    n.Cross(&dir,&cylinder.axis); 

    ln=n.Length(); 

    // Parallel? (?) 
    if((ln<D3_EPSILON)&&(ln>-D3_EPSILON)) 
    return false; 

    n.Normalize(); 

    d=fabs(RC.Dot(n)); 

    if (d<=cylinder.radius) 
    { 
    O.Cross(&RC,&cylinder.axis); 
    //TVector::cross(RC,cylinder._Axis,O); 
    t=-O.Dot(n)/ln; 
    //TVector::cross(n,cylinder._Axis,O); 
    O.Cross(&n,&cylinder.axis); 
    O.Normalize(); 
    s=fabs(sqrtf(cylinder.radius*cylinder.radius-d*d)/dir.Dot(O)); 

    in=t-s; 
    out=t+s; 

    if (in<-D3_EPSILON) 
    { 
     if(out<-D3_EPSILON) 
     return false; 
     else lambda=out; 
    } else if(out<-D3_EPSILON) 
    { 
     lambda=in; 
    } else if(in<out) 
    { 
     lambda=in; 
    } else 
    { 
     lambda=out; 
    } 

    // Calculate intersection point 
    newPosition=org; 
    newPosition.x+=dir.x*lambda; 
    newPosition.y+=dir.y*lambda; 
    newPosition.z+=dir.z*lambda; 
    DVector3 HB; 
    HB=newPosition; 
    HB.Subtract(&cylinder.position); 

    float scale=HB.Dot(&cylinder.axis); 
    normal.x=HB.x-cylinder.axis.x*scale; 
    normal.y=HB.y-cylinder.axis.y*scale; 
    normal.z=HB.z-cylinder.axis.z*scale; 
    normal.Normalize(); 
    return true; 
    } 

    return false; 
} 
1

你有没有想过这样说?

圆柱体本质上是一个“粗”线段,因此一种方法是找到线段(圆柱体中心线)到线段(您正在测试交点的线段)上的最近点。

从那里检查此最近点与其他线段之间的距离,并将其与半径进行比较。

在这一点上,你有一个“药丸vs线段”测试,但你可以做一些飞机测试来“切断”药丸上的瓶盖来制作一个圆柱体。

从臀部拍摄有点虽然所以希望它有帮助(: