在计算机图形学中,Inside Outside是用来测试一个给定的点是否位于一个封闭的多边形内。主要有两种方法来确定一个点是否在多边形的内部/外部:
- 偶数-奇数/奇数-偶数规则或奇数平分规则
- 绕数法
偶数规则/奇数奇偶规则
它也被称为交叉数和射线铸造算法。该算法遵循一个基本的观察,即如果一条来自无穷大的射线穿过多边形的边界,那么它就会从外到内和从外到内交替进行。对于每两个交叉点,点位于多边形的外面。
算法
- 构建一条从待测点到多边形外侧点的线段。
- 计算线段与多边形边界的交叉点的数量。
- 如果交点数量为奇数,则点位于多边形内。
- 否则,点位于多边形之外。
伪代码如下:
count = 0
foreach side in polygon:
if line_intersect_side(P,side):
count = count + 1
if count % 2 == 1:
return inside
else
return outside
在线段与顶点相交的情况下,该测试失败。为了处理这个问题,我们做了一些修改。看一下多边形的两条线段的其他端点:
- 如果端点位于所建线段的同一侧,则考虑该交点的偶数交点
- 如果端点位于它的对面,则考虑奇数的交点
例子
在图(a)中,可以看出,有奇数交点的点位于多边形内,反之亦然。在图(b)中,代表特殊情况,奇数/偶数是根据边的方向来计算的。
偶数-奇数规则/奇数奇偶规则的实施样本:
// crossing number and ray casting algorithm
// iq.opengenus.org
import static java.lang.Math.*;
public class RayCasting {
static boolean intersects(int[] A, int[] B, double[] P) {
if (A[1] > B[1])
return intersects(B, A, P);
if (P[1] == A[1] || P[1] == B[1])
P[1] += 0.0001;
if (P[1] > B[1] || P[1] < A[1] || P[0] >= max(A[0], B[0]))
return false;
if (P[0] < min(A[0], B[0]))
return true;
double red = (P[1] - A[1]) / (double) (P[0] - A[0]);
double blue = (B[1] - A[1]) / (double) (B[0] - A[0]);
return red >= blue;
}
static boolean inside_out(int[][] shape, double[] pnt) {
boolean inside = false;
int len = shape.length;
for (int i = 0; i < len; i++) {
if (intersects(shape[i], shape[(i + 1) % len], pnt))
inside = !inside;
}
return inside;
}
public static void main(String[] a) {
double[][] testPoints = {{10, 10}, {10, 16}, {-20, 10}, {0, 10},
{20, 10}, {16, 10}, {20, 20}};
for (int[][] shape : shapes) {
for (double[] pnt : testPoints)
System.out.printf("%7s ", inside_out(shape, pnt));
System.out.println();
}
}
final static int[][] square = {{0, 0}, {20, 0}, {20, 20}, {0, 20}};
final static int[][] hexagon = {{6, 0}, {14, 0}, {20, 10}, {14, 20},
{6, 20}, {0, 10}};
final static int[][][] shapes = {square, hexagon};
}
时间复杂度:
- O(S),其中S是多边形中的边数。
绕数/非零算法
进行测试的另一种算法是缠绕数算法。对于多边形的给定点,将计算出一个绕行数。如果缠绕数为非零,则点位于多边形内。否则,它就位于多边形之外。
绕行数的计算
从概念上讲,要检查一个点P,构建一条从P开始到边界点的线段,将线段视为钉在P处的弹性。检查松紧带在P点周围缠绕了多少次,如果计数为非零,则点位于多边形内。否则,就在多边形的外面。
另一种评分方法是为每个与多边形边界相交的点分配一个分数,并将这些数字相加。分数是通过考虑多边形的边缘相对于所建线段的方向而给出的。因此,多边形的每条边都以逆时针的方式分配方向。如果边从所建线的下方开始,则得分-1。如果边从构造线的上方开始,那么得分是1。否则,得分是0。
实施样本:
# Winding Number / Non-Zero Algorithm
# iq.opengenus.org
def winding_number_util(P, V):
winding_number = 0 # the winding number counter
# repeat the first vertex at end
V = tuple(V[:]) + (V[0],)
# loop through all edges of the polygon
# edge from V[i] to V[i+1]
for i in range(len(V)-1):
# start y <= P[1]
if V[i][1] <= P[1]:
# an upward crossing
if V[i+1][1] > P[1]:
# P left of edge
if is_left(V[i], V[i+1], P) > 0:
# have a valid up intersect
winding_number += 1
# start y > P[1] (no test needed)
else:
# a downward crossing
if V[i+1][1] <= P[1]:
# P right of edge
if is_left(V[i], V[i+1], P) < 0:
# have a valid down intersect
winding_number -= 1
return wn
时间复杂度:
- O(S),其中S是多边形中的边数。
例子
对于一个给定的图形,
1. 对于最上面的点,绕数=(-1)+(1)+(-1)+(1)=0,位于外面
2. 对于最下面的点,绕数=-1,位于内部。
通过OpenGenus的这篇文章,你一定对Inside Outside测试有了很好的了解。