2

I have two closed QPolygonFs and I need to find out whether their edges (that is their contours) intersect. As these polygons may be included in one another, looking simply at the intersection of the polygons does not work.

PyQt5 has a built-in function for checking if a point is on the contourline of the polygon, contains(QPointF(x,y)). Thus, it seemed obvious to use this method for each point in the QPolygonF:

def check_if_two_polygons_share_contour(polygon1,polygon2):
        for i in range(polygon1.size()):
            if polygon2.contains(QPointF(polygon1[i].x(),polygon1[i].y()))
                print("polygon contours touch!")
                return 1
        return 0

However, as my QPolygons are made up only of their cornerpoints, this does not work.

It seems like the logical next step to get all of the points that make up the QPolygonF's contour, and run the same function on it (iso polygon1.size()).

How should one go about this?

To illustrate the concept a bit better, the function should return 1 for the 1st, 4th and 5th illustration in the image below.

Illustration


Edit: Added some code here to show that some of the suggested answers do not work.

Input:

Function suggested by @alec:

def check_if_two_polygons_share_contour(polygon1,polygon2):
    polygon = polygon1.intersected(polygon2)
    return polygon and polygon not in (polygon1, polygon2)

Logic suggested by @Yves Daoust:

def line_intersection(line1, line2):
    xdiff = (line1[0][0] - line1[1][0], line2[0][0] - line2[1][0])
    ydiff = (line1[0][1] - line1[1][1], line2[0][1] - line2[1][1])

    def det(a, b):
        return a[0] * b[1] - a[1] * b[0]

    div = det(xdiff, ydiff)
    if div == 0:
        # print("Weak Exception 67892: Lines do not interact (function line_intersection). Return 9999999,9999999")
        return 9999999, 9999999
        # raise Exception('lines do not intersect')

    d = (det(*line1), det(*line2))
    x = det(d, xdiff) / div
    y = det(d, ydiff) / div
    return x, y


def check_if_two_polygons_share_contour(polygon1,polygon2):
    for i in range(polygon1.size()):
        if i != polygon1.size() - 1:
            j = i + 1
            point1 = [polygon1[i].x(), polygon1[i].y()]
            point2 = [polygon1[j].x(), polygon1[j].y()]
            for k in range(polygon2.size()):
                if k != polygon2.size() - 1:
                    l = k + 1
                    point3 = [polygon2[k].x(), polygon2[k].y()]
                    point4 = [polygon2[l].x(), polygon2[l].y()]
                    value_ = line_intersection([point1, point2], [point3, point4])
                    if value_ != (9999999, 9999999):
                        return 1

UI

import PyQt5
from PyQt5 import QtCore
import cv2
import numpy as np
import math
from scipy.ndimage.interpolation import rotate
import sys
import PyQt5
from PyQt5.QtCore import *#QPointF, QRectF
from PyQt5.QtGui import *#QPainterPath, QPolygonF, QBrush,QPen,QFont,QColor, QTransform
from PyQt5.QtWidgets import *#QApplication, QGraphicsScene, QGraphicsView, QGraphicsSimpleTextItem
import math


polycoords1_main_object=[PyQt5.QtCore.QPointF(1162.12, 302.37), PyQt5.QtCore.QPointF(1141.65, 304.13), PyQt5.QtCore.QPointF(1133.45, 307.05), PyQt5.QtCore.QPointF(1124.1, 315.83), PyQt5.QtCore.QPointF(1116.5, 332.2), PyQt5.QtCore.QPointF(1109.48, 365.54), PyQt5.QtCore.QPointF(1099.53, 396.53), PyQt5.QtCore.QPointF(1096.02, 419.93), PyQt5.QtCore.QPointF(1096.62, 457.94), PyQt5.QtCore.QPointF(1100.7, 464.96), PyQt5.QtCore.QPointF(1110.06, 471.98), PyQt5.QtCore.QPointF(1123.51, 471.4), PyQt5.QtCore.QPointF(1129.36, 467.3), PyQt5.QtCore.QPointF(1137.55, 466.13), PyQt5.QtCore.QPointF(1146.91, 466.72), PyQt5.QtCore.QPointF(1155.1, 470.23), PyQt5.QtCore.QPointF(1163.88, 464.38), PyQt5.QtCore.QPointF(1170.3, 450.93), PyQt5.QtCore.QPointF(1170.89, 431.62), PyQt5.QtCore.QPointF(1165.63, 414.07), PyQt5.QtCore.QPointF(1215.0, 448.0), PyQt5.QtCore.QPointF(1227.0, 443.0), PyQt5.QtCore.QPointF(1249.0, 388.0), PyQt5.QtCore.QPointF(1249.0, 362.0), PyQt5.QtCore.QPointF(1240.0, 336.0), PyQt5.QtCore.QPointF(1234.0, 310.0), PyQt5.QtCore.QPointF(1226.0, 288.0), PyQt5.QtCore.QPointF(1227.0, 275.0), PyQt5.QtCore.QPointF(1220.0, 257.0), PyQt5.QtCore.QPointF(1197.0, 247.0), PyQt5.QtCore.QPointF(1174.0, 249.0), PyQt5.QtCore.QPointF(1168.0, 260.0), PyQt5.QtCore.QPointF(1162.0, 278.0), PyQt5.QtCore.QPointF(1179.0, 273.0), PyQt5.QtCore.QPointF(1161.0, 287.0), PyQt5.QtCore.QPointF(1159.0, 291.0)]
polycoords2_secondary_not_touching=[PyQt5.QtCore.QPointF(1234.0, 395.0), PyQt5.QtCore.QPointF(1228.0, 411.0), PyQt5.QtCore.QPointF(1229.0, 417.0), PyQt5.QtCore.QPointF(1209.0, 408.0), PyQt5.QtCore.QPointF(1212.0, 392.0), PyQt5.QtCore.QPointF(1214.0, 390.0)]
polycoords3_secondary_touching = [PyQt5.QtCore.QPointF(1179.0, 401.0), PyQt5.QtCore.QPointF(1169.0, 407.0), PyQt5.QtCore.QPointF(1157.0, 424.0), PyQt5.QtCore.QPointF(1170.0, 448.0), PyQt5.QtCore.QPointF(1178.0, 446.0), PyQt5.QtCore.QPointF(1184.0, 442.0), PyQt5.QtCore.QPointF(1193.0, 434.0), PyQt5.QtCore.QPointF(1193.0, 421.0), PyQt5.QtCore.QPointF(1189.0, 412.0)]
polycoords4_secondary_touching_barely = [PyQt5.QtCore.QPointF(1116.0, 335.0), PyQt5.QtCore.QPointF(1115.0, 346.0), PyQt5.QtCore.QPointF(1111.0, 355.0), PyQt5.QtCore.QPointF(1107.0, 378.0), PyQt5.QtCore.QPointF(1129.0, 381.0), PyQt5.QtCore.QPointF(1130.0, 375.0), PyQt5.QtCore.QPointF(1139.0, 358.0), PyQt5.QtCore.QPointF(1139.0, 347.0), PyQt5.QtCore.QPointF(1141.0, 341.0), PyQt5.QtCore.QPointF(1141.0, 341.0)]

polygon1_main_object= QPolygonF(polycoords1_main_object)
polygon2_secondary_not_touching=QPolygonF(polycoords2_secondary_not_touching)
polygon3_secondary_touching = QPolygonF(polycoords3_secondary_touching)
polygon4_secondary_touching_barely = QPolygonF(polycoords4_secondary_touching_barely)


def main():
    app = QApplication(sys.argv)



    scene = QGraphicsScene()
    view = QGraphicsView(scene)

    a = check_if_two_polygons_share_contour(polygon1_main_object,polygon2_secondary_not_touching)
    if a == 1:
        print("polygon1_main_object and polygon2 touch")
    else:
        print("polygon1_main_object and polygon2 do not touch")

    a = check_if_two_polygons_share_contour(polygon1_main_object, polygon3_secondary_touching)
    if a == 1:
        print("polygon1_main_object and polygon3 touch")
    else:
        print("polygon1_main_object and polygon3do not touch")

    a = check_if_two_polygons_share_contour(polygon1_main_object, polygon4_secondary_touching_barely)
    if a == 1:
        print("polygon1_main_object and polygon4 touch")
    else:
        print("polygon1_main_object and polygon4  do not touch")



    scene.addPolygon(polygon1_main_object,QPen(QColor(0, 20, 0)))

    scene.addPolygon(polygon2_secondary_not_touching, QPen(QColor(240, 20, 0)))

    scene.addPolygon(polygon3_secondary_touching, QPen(QColor(0, 20, 250)))

    scene.addPolygon(polygon4_secondary_touching_barely, QPen(QColor(0, 250, 8)))





    view.show()

    sys.exit(app.exec_())


if __name__ == "__main__":
    main()

The solution as suggested by musicamante:

def return_path_from_points(poly_coords_in):
    path = QPainterPath()

    path.moveTo(poly_coords_in[0])
    for points in poly_coords_in:
        path.lineTo(points)
    path.lineTo(poly_coords_in[0])

    return path


def check_if_two_polygons_share_contour(polygon_coords_a,polygon_coords_b):
    path_a = return_path_from_points(polygon_coords_a)
    path_b = return_path_from_points(polygon_coords_b)

    if path_a.intersects(path_b):
        return 1
    else:
        return 0

and ofcourse one needs to edit check_if_two_polygons_share_contour in the UI file to include polycoords iso polygons check_if_two_polygons_share_contour(polygon1_main_object,polygon2_secondary_not_touching) ---> check_if_two_polygons_share_contour(polycoords1_main_object, polycoords2_secondary_not_touching)

output for all cases:

polygon1_main_object and polygon2 touch
polygon1_main_object and polygon3 touch
polygon1_main_object and polygon4 touch

The output that would be correct:

polygon1_main_object and polygon2 do not touch
polygon1_main_object and polygon3 touch
polygon1_main_object and polygon4 touch

Output

  • Have you already tried [`intersects()`](https://doc.qt.io/qt-5/qpolygonf.html#intersects)? – musicamante Dec 02 '21 at 20:14
  • 1
    See your outlines as sequences of line segments and test the pairwise intersections. –  Dec 02 '21 at 22:05
  • @musicamante intersects will treat the polygons as areas, and thus return the wrong answer if one polygon is inside the other (as their areas intersect, but not their edges); – usario30032021 Dec 03 '21 at 02:22
  • @YvesDaoust Seems like a logical way to solve this, if the answer suggested by Alec does not work, I'll give this a try. Thanks :)! – usario30032021 Dec 03 '21 at 02:24
  • @usario30032021 the concept of "intersection" is that a polygon can have *any* common point with another polygon: whether any of those is inside of the other is pointless. If a polygon is inside another, both of them *obviously* intersects the other. If what you want is to check if polygons intercept but ***none*** of them is inside the other, then follow what `intersects` does: create a QPainterPath for both polygons, and then use [`contains()`](https://doc.qt.io/qt-5/qpainterpath.html#contains) against each other. – musicamante Dec 03 '21 at 03:11
  • @musicamante Thanks for the clarification :) The issue is that one of the polygons may or be inside of the other one (although that is not necessarily always the case), see example 5 in the illustration above. Thus I cannot rule out via contains, or am I missing something? – usario30032021 Dec 03 '21 at 03:21
  • @usario30032021 your question states that examples 1, 4 and 5 should return `True`. `intersects()` does that. – musicamante Dec 03 '21 at 03:29
  • @YvesDaoust Tried implementing the solution that you suggested, however couldn't get it work. Where am I going wrong? – usario30032021 Dec 03 '21 at 14:15
  • @musicamante Tried implementing the solution that you suggested, but I think I misunderstood some part of it. Can you help me out? – usario30032021 Dec 03 '21 at 14:39
  • Perhaps my english let me down here. As the title of the question is intersection of qpolygons' edges (emphasis on the word edges) - I am looking for a way to see whether the edges of the polygons intersect. As illustratd in the example, examples 1,4 and 5 should return True, whereas example 3 should not (although it intersects, its contour or edge does not intersect). As illustrated further by the edit, the green and blue polygon intersect the edges of the black polygon, whereas the contours of the red polygon do not intersect the edge of the black polygon. – usario30032021 Dec 03 '21 at 14:49
  • @usario30032021 Sorry, I think I get it now. So, you need to know if two polygons intercept each other, but none of them is *enclosed* in the other, am I right? – musicamante Dec 03 '21 at 14:51
  • @musicamante no worries, I appreciate the help nevertheless :) I am not a hundred percent sure what is meant by the word enclosed. The polygons are always atleast partially inside one another, yet I need to know if their contours intercept. That is: their areas almost always intercept, but their edges or outlines do not necessarily cross / intercept. – usario30032021 Dec 03 '21 at 14:57

2 Answers2

2

Use QPolygonF.intersected to get the intersection of the two polygons. Assuming both polygons are closed as you said, if one is fully contained inside the other then the returned intersection polygon will be equal to the smaller one. So you can check that it does not equal either of the given polygons.

def check_if_two_polygons_share_contour(polygon1, polygon2):
    for x in (polygon1, polygon2):
        if not x.isClosed():
            x << x[0]
    polygon = polygon1.intersected(polygon2)
    return bool(polygon) and polygon not in (polygon1, polygon2)
alec
  • 5,799
  • 1
  • 7
  • 20
  • The issue with intersected() is that it will treat the polygons as areas, this means that it only checks whether they have an area in common. Thus, in the case of illustration 3, the function will return the wrong answer. Or am I misunderstanding the documentation? – usario30032021 Dec 03 '21 at 02:31
  • @usario30032021 That's correct, which is why my answer also checks if `polygon not in (polygon1, polygon2)` – alec Dec 03 '21 at 04:54
  • Tried it out, yet couldn't get it working. Edited the original question to include your function with an example. What am I missing? – usario30032021 Dec 03 '21 at 14:02
  • @usario30032021 You said the polygons were "closed" so I assumed you meant by the [Qt definition](https://doc.qt.io/qt-5/qpolygonf.html#isClosed), i.e. that the start and end points are equal, but now I see that's not the case. See revised function. – alec Dec 04 '21 at 01:15
2

If you need to verify the interception but not the containement, you have to check for both; the following should suffice:

def check_if_two_polygons_share_contour(p1, p2):
    path1 = QPainterPath()
    path1.addPolygon(p1)
    path2 = QPainterPath()
    path2.addPolygon(p2)
    return path1.intersects(path2) and not (
        path1.contains(path2) or path2.contains(path1))
musicamante
  • 41,230
  • 6
  • 33
  • 58