from PyQt5 import QtWidgets, uic from PyQt5.QtWidgets import QTableWidgetItem, QMessageBox from PyQt5.QtGui import QPen, QColor, QImage, QPixmap, QPainter, QTransform from PyQt5.QtCore import Qt, QTime, QCoreApplication, QEventLoop, QPointF red = Qt.red blue = Qt.blue black = Qt.black now = None class Window(QtWidgets.QMainWindow): def __init__(self): QtWidgets.QWidget.__init__(self) uic.loadUi("window.ui", self) self.scene = Scene(0, 0, 561, 581) self.scene.win = self self.view.setScene(self.scene) self.image = QImage(561, 581, QImage.Format_ARGB32_Premultiplied) self.image.fill(Qt.white) self.bars.clicked.connect(lambda : set_bars(self)) self.erase.clicked.connect(lambda: clean_all(self)) self.paint.clicked.connect(lambda: clipping(self)) self.rect.clicked.connect(lambda: set_rect(self)) self.ect.clicked.connect(lambda: add_bars(self)) self.lock.clicked.connect(lambda: lock(self)) self.lines = [] self.edges = [] self.clip = None self.point_now_rect = None self.point_now_bars = None self.point_lock = None self.input_bars = False self.input_rect = False self.pen = QPen(black) class Scene(QtWidgets.QGraphicsScene): def mousePressEvent(self, event): add_point(event.scenePos()) def sign(x): if not x: return 0 else: return x / abs(x) def set_bars(win): if win.input_bars: win.input_bars = False win.rect.setDisabled(False) win.erase.setDisabled(False) win.paint.setDisabled(False) win.ect.setDisabled(False) win.lock.setDisabled(False) else: win.input_bars = True win.rect.setDisabled(True) win.erase.setDisabled(True) win.paint.setDisabled(True) win.ect.setDisabled(True) win.lock.setDisabled(True) def set_rect(win): if win.input_rect: win.input_rect = False win.bars.setDisabled(False) win.erase.setDisabled(False) win.paint.setDisabled(False) win.ect.setDisabled(False) win.lock.setDisabled(False) else: win.input_rect = True win.bars.setDisabled(True) win.erase.setDisabled(True) win.paint.setDisabled(True) win.ect.setDisabled(True) def lock(win): win.edges.append(win.point_lock) win.scene.addLine(win.point_now_rect.x(), win.point_now_rect.y(), win.point_lock.x(), win.point_lock.y(), w.pen) win.point_now_rect = None def add_bars(win): if len(win.edges) == 0: QMessageBox.warning(win, "Внимание!", "Не введен отсекатель!") return win.pen.setColor(red) w.lines.append([[win.edges[0].x() - 15, win.edges[0].y() - 15], [win.edges[1].x() - 15, win.edges[1].y() - 15]]) add_row(w.table_bars) i = w.table_bars.rowCount() - 1 item_b = QTableWidgetItem("[{0}, {1}]".format(win.edges[0].x() - 15 , win.edges[0].y() - 15)) item_e = QTableWidgetItem("[{0}, {1}]".format(win.edges[1].x() - 15, win.edges[1].y() - 15)) w.table_bars.setItem(i, 0, item_b) w.table_bars.setItem(i, 1, item_e) w.scene.addLine(win.edges[0].x() - 15, win.edges[0].y() - 15, win.edges[1].x() - 15, win.edges[1].y() - 15, w.pen) win.pen.setColor(red) w.lines.append([[win.edges[0].x() + 15, win.edges[0].y() + 15], [win.edges[1].x() + 15, win.edges[1].y() + 15]]) add_row(w.table_bars) i = w.table_bars.rowCount() - 1 item_b = QTableWidgetItem("[{0}, {1}]".format(win.edges[0].x() + 15, win.edges[0].y() + 15)) item_e = QTableWidgetItem("[{0}, {1}]".format(win.edges[1].x() + 15, win.edges[1].y() + 15)) w.table_bars.setItem(i, 0, item_b) w.table_bars.setItem(i, 1, item_e) w.scene.addLine(win.edges[0].x() + 15, win.edges[0].y() + 15, win.edges[1].x() + 15, win.edges[1].y() + 15, w.pen) def clean_all(win): win.scene.clear() win.table_rect.clear() win.table_bars.clear() win.lines = [] win.edges = [] win.point_now_rect = None win.point_now_bars = None win.point_lock = None win.image.fill(Qt.white) r = win.table_rect.rowCount() for i in range(r, -1, -1): win.table_rect.removeRow(i) r = win.table_bars.rowCount() for i in range(r, -1, -1): win.table_bars.removeRow(i) def add_row(win_table): win_table.insertRow(win_table.rowCount()) def add_point(point): global w if w.input_rect: w.pen.setColor(black) if w.point_now_rect is None: w.point_now_rect = point w.point_lock = point add_row(w.table_rect) i = w.table_rect.rowCount() - 1 item_x = QTableWidgetItem("{0}".format(point.x())) item_y = QTableWidgetItem("{0}".format(point.y())) w.table_rect.setItem(i, 0, item_x) w.table_rect.setItem(i, 1, item_y) else: w.edges.append(point) w.point_now_rect = point add_row(w.table_rect) i = w.table_rect.rowCount() - 1 item_x = QTableWidgetItem("{0}".format(point.x())) item_y = QTableWidgetItem("{0}".format(point.y())) w.table_rect.setItem(i, 0, item_x) w.table_rect.setItem(i, 1, item_y) item_x = w.table_rect.item(i-1, 0) item_y = w.table_rect.item(i-1, 1) w.scene.addLine(point.x(), point.y(), float(item_x.text()), float(item_y.text()), w.pen) if w.input_bars: w.pen.setColor(red) if w.point_now_bars is None: w.point_now_bars = point else: w.lines.append([[w.point_now_bars.x(), w.point_now_bars.y()], [point.x(), point.y()]]) add_row(w.table_bars) i = w.table_bars.rowCount() - 1 item_b = QTableWidgetItem("[{0}, {1}]".format(w.point_now_bars.x(), w.point_now_bars.y())) item_e = QTableWidgetItem("[{0}, {1}]".format(point.x(), point.y())) w.table_bars.setItem(i, 0, item_b) w.table_bars.setItem(i, 1, item_e) w.scene.addLine(w.point_now_bars.x(), w.point_now_bars.y(), point.x(), point.y(), w.pen) w.point_now_bars = None def clipping(win): norm = isConvex(win.edges) if not norm: QMessageBox.warning(win, "Ошибка!", "Отсекатель не выпуклый!Операция не может быть проведена!") for b in win.lines: win.pen.setColor(blue) cyrus_beck(b, win.edges, norm, win.scene, win.pen) win.pen.setColor(red) def isConvex(edges): flag = 1 # начальные вершины vo = edges[0] # iая вершина vi = edges[1] # i+1 вершина vn = edges[2] # i+2 вершина и все остальные # векторное произведение двух векторов x1 = vi.x() - vo.x() y1 = vi.y() - vo.y() x2 = vn.x() - vi.x() y2 = vn.y() - vi.y() # определяем знак ординаты r = x1 * y2 - x2 * y1 prev = sign(r) for i in range(2, len(edges) - 1): if not flag: break vo = edges[i - 1] vi = edges[i] vn = edges[i + 1] # векторное произведение двух векторов x1 = vi.x() - vo.x() y1 = vi.y() - vo.y() x2 = vn.x() - vi.x() y2 = vn.y() - vi.y() r = x1 * y2 - x2 * y1 curr = sign(r) # если знак предыдущей координаты не совпадает, то возможно многоугольник невыпуклый if curr != prev: flag = 0 prev = curr # не забываем проверить последнюю с первой вершины vo = edges[len(edges) - 1] vi = edges[0] vn = edges[1] # векторное произведение двух векторов x1 = vi.x() - vo.x() y1 = vi.y() - vo.y() x2 = vn.x() - vi.x() y2 = vn.y() - vi.y() r = x1 * y2 - x2 * y1 curr = sign(r) if curr != prev: flag = 0 return flag * curr def scalar(v1, v2): return v1.x() * v2.x() + v1.y() * v2.y() def cyrus_beck(r, edges, n, scene, p): # инициализируем пределы значений параметра, предполагая, что весь отрезок полностью видимый # максимизируем t нижнее и t верхнее, исходя из того что 0 <= t <= 1 tb = 0 te = 1 # вычисляем директрису(определяет направление/ориентацию отрезка) D= p1 - p2 D = QPointF() D.setX(r[1][0] - r[0][0]) D.setY(r[1][1] - r[0][1]) # главный цикл по сторонам отсекателя for i in range(len(edges)): # вычисляем wi, D * ni, wi * n # весовой множитель удаленности гранничной точки от р1(берем граничную точку равной вершине) W = QPointF() W.setX(r[0][0] - edges[i].x()) W.setY(r[0][1] - edges[i].y()) # определяем нормаль N = QPointF() if i == len(edges) - 1: N.setX(-n * (edges[0].y() - edges[i].y())) N.setY(n * (edges[0].x() - edges[i].x())) else: N.setX(-n * (edges[i + 1].y() - edges[i].y())) N.setY(n * (edges[i + 1].x() - edges[i].x())) # определяем скалярные произведения Dscalar = scalar(D, N) Wscalar = scalar(W, N) if Dscalar == 0: # если отрезок параллелен ребру отсекателю if Wscalar < 0: # виден ли? return else: # отрезок невырожден, определяем t t = - Wscalar / Dscalar # поиск верхнего и нижнего предела t if Dscalar > 0: # поиск нижнего предела # верно ли, что t <= 1 if t > 1: return else: tb = max(tb, t) elif Dscalar < 0: # поиск верхнего предела # верно ли, что t >= 0 if t < 0: return else: te = min(te, t) # проверка фактической видимости отрезка if tb <= te: scene.addLine(r[0][0] + (r[1][0] - r[0][0]) * te, r[0][1] + (r[1][1] - r[0][1]) * te, r[0][0] + (r[1][0] - r[0][0]) * tb, r[0][1] + (r[1][1] - r[0][1]) * tb, p) if __name__ == "__main__": import sys app = QtWidgets.QApplication(sys.argv) w = Window() w.show() sys.exit(app.exec_())