I made an object with three points of view. one for forward, one for left, and one for right.
these POVs are some path like a slice of a circle
I want to detect intersections or collisions between these POVs with rectangles to set the color of each POV
POVs rotate with the object in any direction but rectangles are always oriented
here is my code
from random import randint
from sys import argv
from PyQt6.QtCore import QRectF, Qt, QTimer, QPoint
from PyQt6.QtGui import QColor, QKeyEvent, QMouseEvent, QPainter, QPen, QPaintEvent, QPainterPath, QBrush
from PyQt6.QtWidgets import QApplication, QVBoxLayout, QWidget
class Window(QWidget):
def __init__(self, parent=None) -> None:
super().__init__(parent)
screenWidth = 1920
screenHeight = 1080
self.gX = []
self.gY = []
self.framesShowPerSecond = 30
self.age = 0
self.maxAge = 500
self.windowWidth = 1920
self.windowHeight = 1080
self.isRunning = True
self.angle = -90
self.clockCounterVariable = 0
self.milliseconds = 0
self.seconds = 0
self.minutes = 0
self.hours = 0
self.setWindowTitle("test")
self.setGeometry((screenWidth - self.windowWidth) // 2, (screenHeight - self.windowHeight) // 2, self.windowWidth, self.windowHeight)
self.setLayout(QVBoxLayout())
self.showFullScreen()
self.setStyleSheet("background-color:rgb(20, 20, 20);font-size:20px;")
self.xCounter = 0
self.clock = QTimer(self)
self.graphicTimer = QTimer(self)
self.clock.timeout.connect(self.clockCounter)
self.graphicTimer.timeout.connect(self.update)
self.graphicTimer.start(round((1/self.framesShowPerSecond)*1000))
self.clock.start(10)
self.show()
def clockCounter(self) -> None:
if self.clockCounterVariable % 10 == 0:
x = self.xCounter
y = randint(0, 100)
self.xCounter += 1
self.gX.append(x - 0.5)
self.gX.append(x + 0.5)
self.gY.append(y)
self.gY.append(y)
self.clockCounterVariable += 1
def keyPressEvent(self, event: QKeyEvent) -> super:
key = QKeyEvent.key(event)
if key == 112 or key == 80: # P/p
if self.isRunning:
print("pause process")
self.isRunning = False
self.clock.stop()
self.graphicTimer.stop()
else:
print("continue process")
self.isRunning = True
self.clock.start(1000)
self.graphicTimer.start(round((1/self.framesShowPerSecond)*1000))
elif (key == 115) or (key == 83): # S/s
self.closeWindow()
self.update()
return super().keyPressEvent(event)
def mousePressEvent(self, event: QMouseEvent) -> super:
if event.buttons() == Qt.MouseButton.LeftButton:
if self.isRunning:
print("pause process")
self.isRunning = False
self.clock.stop()
self.graphicTimer.stop()
else:
print("continue process")
self.isRunning = True
self.clock.start(1000)
self.graphicTimer.start(round((1/self.framesShowPerSecond)*1000))
return super().mousePressEvent(event)
def paintEvent(self, event: QPaintEvent) -> super:
self.milliseconds = self.clockCounterVariable
self.seconds, self.milliseconds = divmod(self.milliseconds, 100)
self.minutes, self.seconds = divmod(self.seconds, 60)
self.hours, self.minutes = divmod(self.minutes, 60)
painter = QPainter()
painter.begin(self)
painter.setPen(QPen(QColor(255, 128, 20), 1, Qt.PenStyle.SolidLine))
painter.drawText(QRectF(35, 30, 400, 30), Qt.AlignmentFlag.AlignLeft, "{:02d} : {:02d} : {:02d} : {:02d}".format(self.hours, self.minutes, self.seconds, self.milliseconds))
painter.setPen(QPen(QColor(20, 20, 20), -1, Qt.PenStyle.SolidLine))
painter.setBrush(QBrush(QColor(20, 20, 160), Qt.BrushStyle.SolidPattern))
barrier = QRectF(1920//2-25, 1080//2-25-40, 50, 20)
painter.drawRect(barrier)
painter.translate(QPoint(1920//2, 1080//2))
painter.rotate(self.angle)
painter.setBrush(QBrush(QColor(200, 200, 200, 50), Qt.BrushStyle.SolidPattern))
r = 200
a = 40
b = a * 2
rect = QRectF(-r/2, -r/2, r, r)
path = QPainterPath()
path.arcTo(rect, -a, b)
path.closeSubpath()
if path.contains(barrier):
painter.setBrush(QBrush(QColor(200, 20, 20, 50), Qt.BrushStyle.SolidPattern))
else:
painter.setBrush(QBrush(QColor(20, 200, 20, 50), Qt.BrushStyle.SolidPattern))
painter.drawPath(path)
path = QPainterPath()
path.arcTo(rect, -a+90, b)
path.closeSubpath()
if path.contains(barrier):
painter.setBrush(QBrush(QColor(200, 20, 20, 50), Qt.BrushStyle.SolidPattern))
else:
painter.setBrush(QBrush(QColor(20, 200, 20, 50), Qt.BrushStyle.SolidPattern))
painter.drawPath(path)
path = QPainterPath()
path.arcTo(rect, -a-90, b)
path.closeSubpath()
if path.contains(barrier):
painter.setBrush(QBrush(QColor(200, 20, 20, 50), Qt.BrushStyle.SolidPattern))
else:
painter.setBrush(QBrush(QColor(20, 200, 20, 50), Qt.BrushStyle.SolidPattern))
painter.drawPath(path)
painter.setBrush(QBrush(QColor(160, 20, 20), Qt.BrushStyle.SolidPattern))
path = QPainterPath()
path.moveTo(30, 0)
path.lineTo(-30, -15)
path.lineTo(-10, 0)
path.lineTo(-30, 15)
path.closeSubpath()
painter.drawPath(path)
painter.end()
self.angle += 1
if self.angle == 360:
self.angle = 0
return super().paintEvent(event)
def closeWindow(self) -> None:
print("closing window ...")
self.close()
if __name__ == "__main__":
App = QApplication(argv)
window = Window()
exit(App.exec())
how should I do this purpose?
I want to detect collisions between some slices of a circle and rectangles.
Your code has many issues, but your main problem is caused by two factors:
contains()
, which "Returns true
if the given rectangle is inside the path"; you should use intersects()
instead;The result is that even using contains()
, it will never work, since the objects are too far apart:
barrier = QRectF(1920//2-25, 1080//2-25-40, 50, 20)
which is 935, 475, 50, 20
;rect = QRectF(-r/2, -r/2, r, r)
which is -100, -100, 200, 200
;As you can see, they are not even close.
Not only: it wouldn't work anyway because the paths never consider the rotation, as you applied it to the painter.
The proper solution requires to:
In order to do that, we can use Qtransform along with its map()
function, which returns a new transformed path.
Note that QTransform is always aligned to the 0, 0
coordinate, so in order to properly rotate around a different reference point, you must:
Here is an improved version of your code:
def paintEvent(self, event: QPaintEvent) -> super:
secs, ms = divmod(self.clockCounterVariable, 100)
mins, secs = divmod(secs, 60)
hours, mins = divmod(mins, 60)
painter = QPainter(self)
painter.setPen(QPen(QColor(255, 128, 20), 1, Qt.SolidLine))
painter.drawText(QRectF(35, 30, 400, 30), Qt.AlignLeft,
"{:02d} : {:02d} : {:02d} : {:02d}".format(hours, mins, secs, ms))
painter.setPen(QPen(QColor(20, 20, 20), -1, Qt.SolidLine))
painter.setBrush(QBrush(QColor(20, 20, 160), Qt.SolidPattern))
reference = QPointF(1920 / 2, 1080 / 2)
barrier = QRectF(reference.x() - 25, reference.y() - 65, 50, 20)
painter.drawRect(barrier)
r = 200
a = 40
b = a * 2
rect = QRectF(-r/2, -r/2, r, r).translated(reference)
collideBrush = QBrush(QColor(200, 20, 20, 50))
normalBrush = QBrush(QColor(20, 200, 20, 50))
reference = rect.center()
transform = QTransform()
transform.translate(reference.x(), reference.y())
transform.rotate(self.angle)
transform.translate(-reference.x(), -reference.y())
for deltaAngle in (0, 90, -90):
path = QPainterPath(reference)
path.arcTo(rect, -a + deltaAngle, b)
path.closeSubpath()
path = transform.map(path)
if path.intersects(barrier):
painter.setBrush(collideBrush)
else:
painter.setBrush(normalBrush)
painter.drawPath(path)
painter.setBrush(QBrush(QColor(160, 20, 20)))
path = QPainterPath()
path.moveTo(30, 0)
path.lineTo(-30, -15)
path.lineTo(-10, 0)
path.lineTo(-30, 15)
path.closeSubpath()
path.translate(rect.center())
painter.drawPath(transform.map(path))
self.angle = (self.angle + 1) % 360
Note that there are other issues with your code, for instance:
event.key()
, not QKeyEvent.key(event)
;112
and 115
values for the keys, but they are wrong: event.key()
returns an enum that is always the same for letter keys, no matter the modifiers (which you must check with event.modifier()
); just check the key against Qt.Key.Key_P
or Qt.Key.Key_S
and it will work for both lower and upper cases;return
when calling the base implementations are useless;self.milliseconds
, self.seconds
etc. is quite pointless for this case; if you do need those values outside of that function, they should certainly not be computed in the paintEvent()
, because if the window is hidden it will not be called: instead, compute those values in clockCounter
and call self.update()
;Finally, when dealing with complex graphics, implementing everything with the basic QPainter is not really effective, and normally results in making things more complex (and prone to errors and bugs) than necessary. Consider using the Graphics View Framework instead.