算法说明
该图为俯视图,所有信息均为三维空间在底面上的二维投影
算法的目的是检测起始点S到终止点N之间是否可以直线通过,并且求出射线SN所经过的多边形(下述称“路径多边形”),进而得到射线SN与路径多边形的相交边(相交边分为:“进入边”和“穿出边”),并且求出与每一条相交边的交点坐标(由于某些需求使用,该交点需要得到其三维信息)。如果点S到点N之间不存在直线通过,则需要求解出第一个不可通过的相交点。
例如,三角形ABC、BCD、CDE、DEF均为路径多边形,边BC、CD、DE、EF均为相交边,且EF为边界边(非连通,没有邻居),点K、I、J、L为相交点,且L为射线探测的终止点。其中对于射线SN和三角形BCD,边BC为进入边,边CD为穿出边。
算法流程详解
- 算法开始的时候,起点S所在的多边形为“初始多边形”,以该多边形作为第一次算法的“迭代多边形”。
- 遍历“迭代多边形”的所有边,分别计算每一条边的与“目标射线”的交点情况。
- 上述计算后,会得到射线SN与当前多边形最多两个交点,而沿着射线方向,进入多边形的那条交点所在的边称为“传入边”,另一条边就是“传出边”。例如:SN与多边形BDC相交于边CB和边DC,沿着SN的方向,CB就是“传入边”。
- 此时射线SN与当前迭代多边形的运算就结束了,继续寻找下一个需要迭代计算的多边形。寻找的方式很简单,上一个多边形“传出边”所连接的多边形,就是下一个要迭代进入的多边形。例如,计算完SN与多边形BCA的交点情况,会得到一条“穿出边BC”,而多边形BCA以BC作为连接的多边形就是BDC,所以BDC就是下一个迭代多边形。我们称BDC为BCA以BC为公共边的“邻居多边形”。
- 寻找“迭代多边形”会有一种特殊情况,就是当前多边形的“穿出边”为公共边没有邻居多边形。例如,多边形EDF在边EF上没有多边形,而EF又是射线SN在多边形EDF上的穿出边,此时说明从起点到终点没有射线可以直接到达,算法结束。
- 还有一种情况,也会结束算法,即线段SN与当前多边形的交点计算结果后,发现,只有“传入边”,没有“传出边”。此时表明找到了终点N,因为只有射线SN不断迭代进入到终点N所在的多边形,才会出现这种情况。
- 以下一个迭代多边形重复上述过程。
算法核心
- 该图为俯视图,所有信息均为三维空间在底面上的二维投影
- 点S、N分别为射线求解的起始点和终点
- 向量SL是向量BC沿BS的平移结果,二者空间意义一致。
- SI是点S向BC做的垂线,NJ是点N向SL做的垂线
- 核心计算如下:
d值的作用
- d小于0,当前边要么为“进入边”,要么无交点。
- d大于0,当前边要么为“穿出边”,要么无交点。
- d等于0,当前边与射线SN平行,直接忽略。
为什么d值可以决定“传入边”和“穿出边”
- 多边形是以逆时针存储顶点信息和边信息的。
- d值是“目标向量”SE和“边的逆时针向量”的叉乘结果。数学意义上,叉乘结果的正负关系,代表了两个向量的空间位置关系。
- 根据上图来判断空间中任意向量(下述简称“向量X”)和多边形任意一条边的“传入穿出关系”(图中以边HI作为例子,其他边理论一样)。
- 可以明显的看到,其实多边形自身的所有范围区域一定是在某条具体的边向量的一侧。并且,“传入边”的空间意义,其实就是从不属于多边形范围的空间,进入到属于多边形范围的空间。
- 根据上述的前置条件,会发现如果向量X与多边形的某条边是“穿入关系”。那向量X平移到与多边形边起点相同的时候(向量在空间中的任意平移,不会影响向量间的位置关系和数学运算结果),其向量X的终点指向一定是在边向量的同一侧。这就证明了,任意与当前多边形边HI构成“穿入”关系的空间向量,一定是在该边向量的同一侧,而这个空间关系恰好就是向量叉乘计算的空间意义。
- 如图中,很清晰的就可以看到。对于向量HJ和HJ1来说,边HI就是“穿入关系”。同理,对于HJ2来说,边HI就是“穿出关系”。
- 图中,向量HJ,H1J1,H2J2是同一个向量,可以相互平移得到。
- 构成“穿入穿出关系”不一定就是“穿入穿出边”。因为“穿入穿出边”还需要一个非常重要的信息,就是两个线段在空间中还必须有交点。
- 例如向量H2J2,其和向量H1J1是一样的,但是以线段的角度来看,H2J2与HI压根都不相交,所以也就不是“传入边”了。
- 到此,证明了运算值d在实际算法中的具体空间意义。
t值的作用
求解SK占SN的比例系数:
到此可以看出:t值代表的交点的一种特殊比例关系。
t的特殊情况
- 上图求解SN与多边形DCB的交点情况。S起点,N终点。
- 从实际情况来看,期望的算法结果为:边BC是“进入边”,点A是与其的交点。边CD是穿出边,点E是与其的交点。
- 上面对t的计算,实际空间意义是:两条向量所在两条直线的交点。例如,实际边BD与射线SN是没有交点的。但是直线BD与直线SN会交于点F,此时t值的实际意义就是SF占SN的比例。
多个“穿入穿出关系边”获取“穿入穿出边”
多边形出现多个边向量与射线向量有交点的时候,其t值大小决定了哪一个是真正的交点边。(这一步就把上面根据d值判断“穿入穿出边”时候无交点的边排除掉了)
其实上图就能明确的看到,在计算SN和多边形BDC的穿出边的时候,DC和BD两条边都具有“穿出关系”。且直线DC与SN实际交点与点E,直线BD与SN实际交点于点F,但是点F不在边BD上。
- 对边DC计算t值,结果是SE长度占SN长度的比例
- 对边BD计算t值,结果是SF长度占SN长度的比例
- 可以证明,对于多个“穿出关系”边,t值小的那个是真正的有交点的“穿出边”
- 同理,对于多个“穿入关系”边,t值大的那个是真正的有交点的“穿入边”
- SN是射线算法的起点和终点。
- AB是一条实际的穿出边,与SN交点与点G。
- 如果在多边形中还有其他边与SN有“穿出关系”。那么这条边一定满足以下条件:(例如,图中的边EA)
- 边是逆时针排序,所以EA一定在AB所在直线h的左侧。(此处是以EA去逆时针连接AB为例,AB逆时针连接BE证明思路一样)
- EA与射线SN存在“穿出关系”,所以EA一定由直线g的下侧穿向直线g的上侧。直线g是射线SN的平行线。因为:既然SN与EA存在“穿出关系”,那么SN一定是由EA的内侧穿向EA的外侧。反过来,EA就是由SN的外侧穿向SN的内侧。
- 所以证明任意“穿出关系”边EA,(注意,是任意的,图中只是EA做举例)。其起点,一定是在图中E所在的橙色区域内,这个区域是上述两个条件的空间交集区域。
- 而当在橙色区域内的任意点E向A所做的射线,与SN交点F,这个F在SN上的位置一定在点G后面。因为点F一定在直线h的右侧。
- 所以SF占SN的比例一定大于SG占SN的比例。
t值范围过滤“无效边”
多边形边向量与射线向量产生交点,t值的有效范围必须是有约束的。
交点不在线段SN上,且交点在SN的反向延长线上
- 上图中,BC是多边形的一条边,SN是射线的起点和终点。
- 这种计算BC和SN的t值,会得到负数。也就是说,如果交点不在线段SN上,且交点在SN方向的反方向上,得到的结果就是负数。(直观理解:就是中间向量BS和向量SN一定在向量BC的同一侧。因为BC在SN的反方向上,所以构建BS的时候,BS一定是顺着SN的方向)
- 射线算法中,具有“穿出关系”的边,不会出现t小于0的情况。除非整个多边形根本与SN无交点(这种情况被射线算法本身排除了,射线算法在迭代的过程中不会处理任何与射线没有交点的多边形。且射线算法中采用的逆时针存储节点的凸多边形)。反证法很容易证明这一点。
交点不在线段SN上,交点在SN的正向延长线上
- 这种情况t值计算之后为正值,因为,SN和BS相对于BC的空间位置正好相反,所以t值表达式的分子分母的正负关系相同,其结果必为正值。
- 因为交点在SN正向延长线上,相当于BC在SN方向的前方。而BS的方向,又是以BC的起点为起点,SN的起点为终点,相当于从BC往SN的方向,这个方向必然与SN方向相反。
- 虽然t值为正值,但是,可以发现此时SA与SN的比例关系大于1。这也就说明计算结果t值大于1时,也是需要过滤的无效值。
- 同样的,射线算法中,具有“穿入关系”的边也不会出现t大于1的情况,除非整个多边形与SN无交点。反证法很容易证明。
射线起点和终点的特殊情况:
- 射线SN与初始多边形BCA无“穿入边”。此时默认穿入边t为最小值0。其实还有一种情况,就是S恰好在边BA上,此时t也是0。也就是将起点作为了一个初始交点。
- 而对于SN与最后多边形(假如上述多边形FEG是联通的)FHG无“穿出边”。此时默认穿出边t为最大值1。同样的,N恰好在HG上,t也是1。也就是将终点作为了最后一个交点。
- 而射线算法结果中还保留了相交边的索引,如果没有“穿入穿出边”,会有一个默认常量值标识这种特殊情况。
总结d值和t值的实际意义:
- d值首先得到射线SN与多边形每条边的“穿入穿出关系”
- t值首先需要过滤有效范围,[0,1]。小于0,大于1的都是无效值。
- 在得到有效t值后,对于多个具有“穿入关系”的边,t值最大为“穿入边”。对于多个具有“穿出关系”的边,t值最小的为“穿出边”。
- 确定“穿入穿出边”后,对应的t值就是一种线段比例关系。
根据Detour测试工具实际例子验证结果
得到交点的高度信息
计算二维投影下的交点后,确定其在三维上的高度信息。这一步仅仅只是为了利用边交点的三维数据信息,来计算花销
解释说明:
- 点B、C是二维投影下多边形的边的端点
- 点B1、C1是三维空间中多边形的边的实际端点
- 点K是二维投影下,SN(射线起终点)与边的交点(该点不需要计算)
- 点P是二维点K在三维SN上的垂直对应点(是之前已经求解的)
- 点X是最终求解,二维点K在三维边上垂直对应点(含高度信息)
求解点X:
数据结构参考
射线算法的结果数据
射线与多边形计算相交关系的结果数据
算法思考
有重复计算
很明显的,算法不断的往前搜索需要与射线计算交点关系的多边形。而往前迭代的连接逻辑就是:找到射线与当前多边形形成的“穿出边”,那么与当前多边形以“穿出边”构成邻居关系的多边形就是,下一个要迭代的多边形。
这就证明了:下一个搜索多边形的“穿入边”就是上一个搜索多边形的“穿出边”。且是同一个交点,那么相应的t值就是一致的。这就是重复计算
如果算法中是分开两次分别计算的这个t值。还会引起一个问题,t值本身就是一个比值关系,最后会被转化成小数,但是这个小数绝大多数情况下都是近似值,因为分数多数都是不能整除的。那么分开两次计算,很有可能造成两次结果有细微的不一致。