【问题标题】:Detecting Number of Intersections of a line with a polygon检测线与多边形的交点数
【发布时间】:2020-02-02 16:27:33
【问题描述】:

我已经在信号处理堆栈交换中问过这个问题,但没有得到任何答案。你是我唯一的希望 StackOverflow。请帮忙谢谢:

作为数学论文的一部分,我正在编写一个程序,用于检测绘制的线与输入的形状的交点。

例如,在下面的形状中,多边形是给定尺寸的输入图像。光线已从多边形内部开始绘制到多边形上。如何检测光线穿过多边形边界的次数。

此外,多边形始终是闭合多边形。

请看这里的图片:

我最初想到的一个想法是用与边框相同的颜色(黑色)填充多边形。然后数一数光线遇到黑色然后是白色的次数。

我对这种方法的问题是我真的不知道如何沿着一条线追踪并检查每个点,我猜想沿着这条线连续检查将是一个极其昂贵的过程。

我现在使用的是 Processing 3,但我可以使用除 MatLab 之外的任何其他软件/平台,因为我现在无法访问它。

【问题讨论】:

  • 你能轻松地将多边形“分解”成它的线条吗?因为如果可以的话,那么你可以对它们应用一些简单的线碰撞检测算法,你就是金子。
  • 嗨!好主意啊。我曾经有过这样的想法,我记得曾短暂地试图找到一些可以做到这一点的现有库或工具。我什么也找不到,但我仍在寻找。你知道什么可以做到这一点(最好是处理)?

标签: animation math image-processing processing


【解决方案1】:

如果您有一条由点 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

如果您有一个从l1p1l1p2 的行和从l2p1l2p2 的第二行,那么:

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类型。如果存在交叉点且线不平行,则属性validtruePVector 类型的属性point 是交点,如果有的话。


看例子:

<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>

【讨论】:

    【解决方案2】:

    在某些假设下,您可以做到这一点,而无需使用大量数学来获得近似的交点。

    假设是:

    • 您的输入多边形图像是用非反抗线绘制的
    • 你可以画一条非抗反射线(或射线)
    • 线条粗细为 1 像素

    获取您的多边形图像,我们称之为poly。设置此图像的阈值,使背景像素为 0,多边形像素为非零,例如 1。

    创建一个与poly 大小相同的空图像(所有像素值为0),我们称之为ray。在此图像上使用非零像素值(例如 1)绘制非抗噪射线。

    取这两张图片的总和,result = poly + ray。现在交叉点在result 图像中的像素值为 2,您可以轻松提取它们的坐标。

    这里假定所有图像都有一个通道。

    【讨论】:

      猜你喜欢
      • 2014-03-21
      • 1970-01-01
      • 1970-01-01
      • 2015-03-12
      • 1970-01-01
      • 2013-01-05
      • 1970-01-01
      • 1970-01-01
      • 2011-07-08
      相关资源
      最近更新 更多