我已经在信号处理堆栈交换中问了这个问题,但是没有得到任何答案。您是我唯一的希望StackOverflow。请帮忙谢谢:
作为数学论文的一部分,我正在编写一个程序,该程序可检测绘制的线条与输入的形状的交点。
例如,在下面的形状中,多边形是给定尺寸的输入图像。光线已从多边形内部开始绘制到多边形上。如何检测射线穿过多边形边界的次数。
此外,多边形始终是封闭的多边形。
请在此处查看图片:
最初想到的是用与边框相同的颜色(黑色)填充多边形。然后计算射线遇到黑色,然后遇到白色的次数。
这种方法的问题是我真的不知道如何沿着一条线进行跟踪并检查每个点,而且我猜想,沿着一条线进行连续检查将是非常昂贵的过程。
我现在正在使用Processing 3,但是我可以使用MatLab以外的任何其他软件/平台,因为我现在无法访问它。
答案 0 :(得分:5)
如果您有一条由点P
和归一化方向R
定义的无尽线和另一条由点Q
和一个方向定义的无尽线S
,则环形线X
的交点为:
alpha ... angle between Q-P and R
beta ... angle between R and S
gamma = 180° - alpha - beta
h = | Q - P | * sin(alpha)
u = h / sin(beta)
t = | Q - P | * sin(gamma) / sin(beta)
t = dot(Q-P, (S.y, -S.x)) / dot(R, (S.y, -S.x)) = determinant(mat2(Q-P, S)) / determinant(mat2(R, S))
u = dot(Q-P, (R.y, -R.x)) / dot(R, (S.y, -S.x)) = determinant(mat2(Q-P, R)) / determinant(mat2(R, S))
X = P + R * t = Q + S * u
另请参见find intersection point of two vectors independent from direction
如果您有一个从l1p1
到l1p2
的正弦,并且从l2p1
到l2p2
的第二行,那么:
P = l1p1
Q = l2p1;
R = normalize(l1p2 - l1p1)
S = normalize(l2p2 - l2p1)
normalize
计算向量的Unit vector。单位向量的长度为1。
由于线不是无止境的,因此您必须评估交点是否在线段上。计算线的长度,以及从留置线起点到相交点的距离。验证距离(由Dot product计算)是否大于等于0且小于等于线的长度。
在下面
len1 = | l1p2 - l1p1 |
len2 = | l2p2 - l2p1 |
distOnL1 = dot(X - P, R);
distOnL2 = dot(X - Q, S);
intersecting = distOnL1 >= 0 AND distOnL1 <= len1 AND distOnL2 >= 0 AND distOnL2 <= len2
这可以通过使用PVector
来计算,如下所示:
class TIntersection {
boolean valid = false;
PVector point = new PVector(0.0, 0.0);
}
// Intersect 2 endless lines
// line 1: line segment from `l1p1` to `l1p2`
// line 2: line segment from `l2p1` to `l2p2`
TIntersection Intersect(PVector l1p1, PVector l1p2, PVector l2p1, PVector l2p2) {
PVector P = l1p1;
PVector Q = l2p1;
PVector R = PVector.sub(l1p2, l1p1);
PVector S = PVector.sub(l2p2, l2p1);
float len1 = R.mag();
float len2 = S.mag();
R.normalize();
S.normalize();
PVector QP = PVector.sub(Q, P);
PVector SNV = new PVector(S.y, -S.x);
TIntersection isect = new TIntersection();
float t = QP.dot(SNV) / R.dot(SNV);
isect.point = PVector.add(P, PVector.mult(R, t));
if (!Float.isInfinite(isect.point.x) || !Float.isInfinite(isect.point.y)) {
float distOnL1 = PVector.sub(isect.point, P).dot(R);
float distOnL2 = PVector.sub(isect.point, Q).dot(S);
isect.valid = distOnL1 >= 0.0 && distOnL1 <= len1 && distOnL2 >= 0.0 && distOnL2 <= len2;
} else {
isect.valid = false;
}
return isect;
}
该函数的返回值类型为TIntersection
。如果存在相交且直线不平行,则属性valid
为true
。
类型为point
的属性PVector
是交点(如果有的话)。
查看示例:
<script src="https://cdnjs.cloudflare.com/ajax/libs/processing.js/1.6.6/processing.min.js"></script>
<canvas id="pjs"></canvas>
<script type="application/processing" data-processing-target="pjs">
class TIntersection {
boolean valid = false;
PVector point = new PVector(0.0, 0.0);
}
// Intersect 2 endless lines
// line 1: line segment from `l1p1` to `l1p2`
// line 2: line segment from `l2p1` to `l2p2`
TIntersection Intersect(PVector l1p1, PVector l1p2, PVector l2p1, PVector l2p2) {
PVector P = l1p1;
PVector Q = l2p1;
PVector R = PVector.sub(l1p2, l1p1);
PVector S = PVector.sub(l2p2, l2p1);
float len1 = R.mag();
float len2 = S.mag();
R.normalize();
S.normalize();
PVector QP = PVector.sub(Q, P);
PVector SNV = new PVector(S.y, -S.x);
TIntersection isect = new TIntersection();
float t = QP.dot(SNV) / R.dot(SNV);
isect.point = PVector.add(P, PVector.mult(R, t));
//if (!Float.isInfinite(isect.point.x) || !Float.isInfinite(isect.point.y)) {
float distOnL1 = PVector.sub(isect.point, P).dot(R);
float distOnL2 = PVector.sub(isect.point, Q).dot(S);
isect.valid = distOnL1 >= 0.0 && distOnL1 <= len1 && distOnL2 >= 0.0 && distOnL2 <= len2;
//} else {
// isect.valid = false;
//}
return isect;
}
ArrayList<PVector> poly;
PVector[] line_p, move;
void setup() {
size(500,500);
poly = new ArrayList();
poly.add(new PVector(175, 100));
poly.add(new PVector(175, 300));
poly.add(new PVector(200, 300));
poly.add(new PVector(225, 400));
poly.add(new PVector(275, 350));
poly.add(new PVector(275, 200));
poly.add(new PVector(325, 200));
poly.add(new PVector(325, 100));
line_p = new PVector[2];
line_p[0] = new PVector(150, 400);
line_p[1] = new PVector(380, 100);
move = new PVector[2];
move[0] = new PVector(random(2)-1, random(2)-1);
move[1] = new PVector(random(2)-1, random(2)-1);
}
void draw() {
// randomize points
for (int i=0; i < line_p.length; ++i ) {
line_p[i] = PVector.add(line_p[i], move[i]);
if (line_p[i].x < 50 || line_p[i].x > width-50)
move[i].x *= -1;
if (line_p[i].y < 50 || line_p[i].y > height-50)
move[i].y *= -1;
move[i].x = max(-1, min(1, move[i].x+random(0.2)-0.1));
move[i].y = max(-1, min(1, move[i].y+random(0.2)-0.1));
}
// clear background
background(0, 0, 0);
stroke(255);
fill(255, 0, 0);
// draw line
line(line_p[0].x, line_p[0].y, line_p[1].x, line_p[1].y);
// draw polygon and intersections
int intersections = 0;
for (int i = 0; i < poly.size(); i++) {
PVector poly_p1 = poly.get(i);
PVector poly_p2 = poly.get((i+1) % poly.size());
line(poly_p1.x, poly_p1.y, poly_p2.x, poly_p2.y);
TIntersection x = Intersect(line_p[0], line_p[1], poly_p1, poly_p2);
if (x.valid) {
ellipse(x.point.x, x.point.y, 10, 10);
intersections ++;
}
}
// draw intersection count
fill(255);
textSize(24);
text("intersections: " + str(intersections), 20, 40);
}
</script>
答案 1 :(得分:0)
在某些假设下,您无需进行大量数学运算即可获得近似的相交点。
假设是:
获取多边形图像,我们将其命名为poly
。对此图像进行阈值处理,以使背景像素为0,多边形像素为非零,例如1。
创建一个空白图像(所有像素值均为0),其大小与poly
相同,我们将其称为ray
。用非零像素值(例如1)在此图像上绘制非抗光线。
取总和或这两个图像result = poly + ray
。现在,交集在result
图像中的像素值为2,您可以轻松提取其坐标。
在这里,假定所有图像都具有一个通道。