简体   繁体   English

快速几何接近谓词

[英]fast geometric proximity predicate

I have 3 points (A, B and X) and a distance (d). 我有3个点(A,B和X)和距离(d)。 I need to make a function that tests if point X is closer than distance d to any point on the line segment AB. 我需要创建一个函数来测试点X是否比距离d更接近线段AB上的任何点。

The question is firstly, is my solution correct and then to come up with a better (faster) solution. 问题首先是,我的解决方案是正确的,然后提出更好(更快)的解决方案。

My first pass is as follows 我的第一次通过如下

AX = X-A
BX = X-B
AB = A-B

    // closer than d to A (done squared to avoid needing to compute the sqrt in mag)
If d^2 > AX.mag^2  return true

    // closer than d to B
If d^2 > BX.mag^2 return true

    // "beyond"  B
If (dot(BX,AB) < 0) return false

    // "beyond"  A
If (dot(AX,AB) > 0) return false

    // find component of BX perpendicular to AB
Return (BX.mag)^2 - (dot(AB,BX)/AB.mag)^2 < d^2

This code will end up being run for a large set of P's and a large set of A/B/d triplets with the intent of finding all P's that pass for at least one A/B/d so I suspect that there is a way to reduce overall the cost based on that but I haven't looked into that yet. 这段代码最终将运行一大堆P和一大组A / B / d三元组,目的是找到所有通过至少一个A / B / d的P,所以我怀疑有一种方法在此基础上降低总体成本,但我还没有考虑过。

(BTW: I am aware that some reordering, some temporary values and some algebraic identities could decrease the cost of the above. I just omitted them for clarity.) (顺便说一句:我知道一些重新排序,一些临时值和一些代数身份可能会降低上述成本。为了清楚起见,我只是省略了它们。)


EDIT: this is a 2D problem (but solution that generalizes to 3D would be cool 编辑:这是一个2D问题(但推广到3D的解决方案很酷

Edit: On further reflection, I expect the hit rate to be around 50%. 编辑:进一步反思,我预计命中率约为50%。 The X point can be formed in a nested hierarchy so I expect to be able to prune large subtrees as all-pass and all-fail. X点可以在嵌套层次结构中形成,因此我希望能够将大型子树修剪为全通和全失败。 The A/B/d limiting the triplets will be more of a trick. 限制三胞胎的A / B / D将更具诀窍。

Edit: d is in the same order of magnitude as AB. 编辑:d与AB的数量级相同。


edit: Artelius posted a nice solution. 编辑:Artelius发布了一个很好的解决方案。 I'm not sure I understand exactly what he's getting at as I got off on a tangent before I fully understood it. 在我完全理解它之前,我不确定我是否正确理解了他所得到的东西。 Anyway another thought came to mind as a result: 无论如何,我想到了另一个想法:

  • First Artelius' bit, pre-cacluate a matrix that will place AB centered ate the origin and aligned with the X-axis. 首先是Artelius的位,预先确定一个矩阵,它将AB放置在原点的中心并与X轴对齐。 (2 adds, 4 muls and 2 adds) (2加,4 muls和2加)
  • fold it all into the 1st quadrant (2 abs) 将它全部折叠到第一象限(2 abs)
  • scale in X&Y to make the central portion of the zone into a unit square (2 mul) 在X和Y中缩放以使区域的中心部分成单位正方形(2 mul)
  • test if the point is in that square (2 test) is so quit 测试点是否在那个方块(2测试)是如此退出
  • test the end cap (go back to the unscaled values 测试端盖(返回未缩放的值
    • translate in x to place the end at the origin (1 add) 在x中翻译以将结尾放在原点(1添加)
    • square and add (2 mul, 1 add) 方和添加(2 mul,1 add)
    • compare to d^2 (1 cmp) 与d ^ 2(1 cmp)比较

I'm fairly sure this beats my solution. 我很确定这会打败我的解决方案。

(if nothing better comes along sone Artelius gets the "prize" :) (如果没有什么比这更好的了,Artelius获得了“奖品”:)

Hmmmmmmm.... What's the hit-rate? 嗯......什么是命中率? How often does point "X" meet the proximity requirements? 点“X”多久符合接近要求?

I think your existing algorithm is good, and any additional optimizations will come from tuning to the real data. 我认为您现有的算法很好,任何其他优化都将来自调整到真实数据。 For example, if the "X" point meets the proximity test 99% of the time, then I think your optimization strategy should be considerably different than if it only passes the test 1% of the time. 例如,如果“X”点在99%的时间内符合接近度测试,那么我认为您的优化策略应该与仅在1%的时间内通过测试的情况大不相同。


Incidentally, when you get to the point where you're running this algorithm with thousands of points, you should organize all the points into a K-Dimensional Tree (or KDTree ). 顺便提一下,当你到达运行此算法的点数为数千个点时,你应该将所有点组织成一个K维树(或KDTree )。 It makes the calculation of "nearest-neighbor" much simpler. 它使“最近邻居”的计算更加简单。

Your problem is a little more complex than a basic nearest-neighbor (because you're checking proximity-to-a-line-segment rather than just proximity-to-a-point) but I still think the KDTree will be handy. 你的问题比基本的最近邻居更复杂(因为你正在检查接近线段而不仅仅是接近点)但我仍然认为KDTree会很方便。

If I read this correctly, then this is almost the same as the classic ray/sphere intersection test as used in 3D ray-tracing. 如果我正确读取了这个,那么这几乎与3D光线追踪中使用的经典光线/球体相交测试相同。

In this case you have a sphere at location X of radius d, and you're trying to find out whether the line AB intersects the sphere. 在这种情况下,您在半径为d的位置X处有一个球体,并且您试图找出AB线是否与球体相交。 The one difference with ray-tracing is that in this case you've got a specific line AB, whereas in ray-tracing the ray is normally generalised as as origin + distance * direction , and you don't care how far along the infinite line AB+ it is. 与光线追踪的一个区别在于,在这种情况下,你有一条特定的线AB,而在光线追踪中,光线通常被概括为origin + distance * direction ,而你并不关心无限远的距离线AB+它是。

In pseudo-code from my own ray-tracer (based on the algorithm given in "An Introduction to Ray-tracing" (ed. Glassner): 在我自己的光线跟踪器的伪代码中(基于“光线跟踪简介”(编辑Glassner)中给出的算法:

Vector v = X - A
Vector d = normalise(B - A)  // unit direction vector of AB
double b = dot(v, B - A)
double discrim = b^2 - dot(v, v) + d^2
if (discrim < 0)
     return false            // definitely no intersection

If you've got this far, then there's some chance that your condition is met. 如果你走了这么远,那么有一些机会,你的条件得到满足。 You just have to figure out whether the intersection(s) is on the line AB: 你只需要弄清楚交叉点是否在AB线上:

discrim = sqrt(discrim)
double t2 = b + discrim
if (t2 <= 0)
    return false             // intersection is before A

double t1 = b  - discrim

result = (t1 < length(AB) || (t2 < length(AB))

If your set of (A,B,d) in fixed, you can calculate a pair of matrices for each to translate the co-ordinate system, so that the line AB becomes the X axis, and the midpoint of AB is the origin. 如果你的(A,B,d)集是固定的,你可以为每个矩阵计算一对矩阵来平移坐标系,这样AB线成为X轴,AB的中点就是原点。

I think this is a simple way to construct the matrices: 认为这是构造矩阵的一种简单方法:

trans = - ((A + B) / 2)        // translate midpoint of AB to origin

rot.col1 = AB / AB.mag         // unit vector in AB direction

                        0 -1    
rot.col2 = rot.col1 * (      ) // unit vector perp to AB
                        1  0 

rot = rot.inverse()            // but it needs to be done in reverse

Then you just take each X and do rot * (X + trans) . 然后你只需要每个X并做rot * (X + trans)

The region in question is actually symmetric in both the x and y axes now, so you can take the absolute value of the x co-ordinate, and of the y co-ordinate. 所讨论的区域现在在x和y轴上实际上是对称的,因此您可以获取x坐标和y坐标的绝对值。

Then you just need to check: 然后你只需要检查:

y < d && x < AB.mag/2            //"along" the line segment
|| (x - AB.mag/2)^2 + y^2 < d^2  // the "end cap".

You can do another trick; 你可以做另一招; the matrix can scale down by a factor of AB.mag/2 (remember the matrices are only calculated once per (A,B) meaning that it's better that finding them is slower, than the actual check itself). 矩阵可以按比例缩小AB.mag/2 (记住矩阵只计算一次(A,B),这意味着找到它们比实际检查本身更慢)。 This means your check becomes: 这意味着您的支票变为:

y < 2*d/AB.mag && x < 1
|| (x - 1)^2 + y^2 < (2*d/AB.mag)^2

Having replaced two instances of AB.mag/2 with the constant 1, it might be a touch faster. 用常数1替换了AB.mag / 2的两个实例,可能触摸得更快。 And of course you can precalculate 2*d/AB.mag and (2*d/AB.mag)^2 as well. 当然,您也可以预先计算2*d/AB.mag(2*d/AB.mag)^2

Whether this will work out faster than other methods depends on the inputs you give it. 这是否会比其他方法更快地取决于您提供的输入。 But if the length of AB is long compared to d, I think it will turn out considerably faster than the method you posted. 但是如果AB的长度与d相比很长,我认为它会比你发布的方法快得多。

This code will end up being run for a large set of P's and a large set of A/B/d triplets with the intent of finding all P's that pass for at least one A/B/d so I suspect that there is a way to reduce overall the cost based on that but I haven't looked into that yet. 这段代码最终将运行一大堆P和一大组A / B / d三元组,目的是找到所有通过至少一个A / B / d的P,所以我怀疑有一种方法在此基础上降低总体成本,但我还没有考虑过。

In the case when d ~ AB, for a given point X, you can first test whether X belongs to one of the many spheres of radius d and center Ai or Bi. 在d~AB的情况下,对于给定的点X,您可以首先测试X是否属于半径d和中心Ai或Bi的许多球体之一。 Look at the picture: 看图片:

     ......        .....
  ...........   ...........
 ...........................
.......A-------------B.......
 ...........................
  ...........   ...........
     .....         .....

The first two tests 前两个测试

If d^2 > AX.mag^2 return true
If d^2 > BX.mag^2 return true

are the fastest ones, and if d ~ AB they are also the ones with highest probability to succeed (given that the test succeeds at all). 是最快的,如果d~AB,它们也是成功概率最高的那些(考虑到测试成功)。 Given X, you can do all the "sphere tests" first, and then all the final ones: 给定X,您可以先进行所有“球体测试”,然后进行所有最终测试:

Return (BX.mag)^2 - (dot(AB,BX)/AB.mag)^2

If the # of sets of A/B/d are large, and you're definitely in 2D, consider using R-trees (or their octagonal equivalents) where each entry in the R-tree is the minimum bounding box of the A/B/d triple. 如果A / B / d的集合数量很大,并且您肯定是2D,请考虑使用R树 (或它们的八边形等价物),其中R树中的每个条目都是A /的最小边界框。 B / D三联。 This would let you eliminate having to test a lot of the A/B/d triples & focus your CPU power only on the few ones where each point X is within the bounding boxes of the A/B/d triple. 这样就可以省去测试很多A / B / d三元组并将CPU功率仅集中在每个点X在A / B / d三元组的边界框内的少数几个。 Then do a more detailed test like the one you mention. 然后像你提到的那样做一个更详细的测试。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM