Le risque

Added most of the functionality to the button editor
This commit is contained in:
Daniel Carta
2024-07-09 07:18:37 -06:00
parent ae8894a751
commit d0fc8d8014
3 changed files with 144 additions and 83 deletions
+6 -2
View File
@@ -16,9 +16,13 @@ python3 ./main.py
##### "Path Editor" Tab: ##### "Path Editor" Tab:
- Right click to add nodes - Right click to add nodes
- Left click on specific points to manipulate paths and nodes - Left click on specific points to manipulate paths and nodes:
- Drag cyan circles (Control points) to change the shape of the path
- Drag magenta nodes change the rotation of the robot
- Drag white numbers (Nodes) to change the position of the robot
- Double click on nodes to delete them - Double click on nodes to delete them
- Double click on control points to make path, and robot movment continuous, while keeping the node clicked the at the same location (Smooth the path) - Double click on control points to make path, and robot movment continuous, while keeping the node clicked the at the same location (Smooth the path)
- Press the 'r' key to delete the whole auto
##### "Button editor" Tab: ##### "Button editor" Tab:
@@ -34,5 +38,5 @@ python3 ./main.py
### Known Bugs: ### Known Bugs:
- Smoothing function is janky (I want to completely redo it) - Smoothing function is janky sometimes
- Sometimes the control points spawn in random locations but I cant seem to replicate it? - Sometimes the control points spawn in random locations but I cant seem to replicate it?
+110 -45
View File
@@ -1,8 +1,8 @@
import sys import sys
import os import os
import numpy as np import numpy as np
from PySide6.QtWidgets import QApplication, QLabel, QMainWindow, QPushButton, QVBoxLayout, QHBoxLayout, QWidget, QMessageBox from PySide6.QtWidgets import QApplication, QLabel, QMainWindow, QPushButton, QVBoxLayout, QHBoxLayout, QWidget, QScrollArea
from PySide6.QtGui import QPixmap, QMouseEvent, QPainter, QPen, QColor, QPainterPath, QPolygon, QFont from PySide6.QtGui import QPixmap, QPainter, QPainterPath, QColor, QPen
from PySide6.QtCore import Qt, QPoint, QRect from PySide6.QtCore import Qt, QPoint, QRect
class ButtonEditor(QMainWindow): class ButtonEditor(QMainWindow):
@@ -12,7 +12,6 @@ class ButtonEditor(QMainWindow):
self.pathPlanner = pathPlanner self.pathPlanner = pathPlanner
self.imageLabel = QLabel(self) self.imageLabel = QLabel(self)
scriptDir = os.path.dirname(os.path.abspath(__file__)) scriptDir = os.path.dirname(os.path.abspath(__file__))
imagePath = os.path.join(scriptDir, "images", "Field.png") imagePath = os.path.join(scriptDir, "images", "Field.png")
self.pixmap = QPixmap(imagePath) self.pixmap = QPixmap(imagePath)
@@ -35,75 +34,141 @@ class ButtonEditor(QMainWindow):
layout.addLayout(buttonLayout) layout.addLayout(buttonLayout)
layout.addWidget(self.imageLabel) layout.addWidget(self.imageLabel)
self.scrollArea = QScrollArea()
self.scrollArea.setWidgetResizable(True)
self.scrollArea.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
self.scrollArea.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
layout.addWidget(self.scrollArea)
self.rectanglesWidget = QWidget()
self.scrollArea.setWidget(self.rectanglesWidget)
container = QWidget() container = QWidget()
container.setLayout(layout) container.setLayout(layout)
self.setCentralWidget(container) self.setCentralWidget(container)
self.resize(self.pixmap.width(), self.pixmap.height() + 60) self.resize(self.pixmap.width(), self.pixmap.height() + 200)
# Variables # VariablesssszczadasdadawsadsaaDSdasas
self.matchLength = 15 self.matchLength = 15
self.TPS = 50 self.TPS = 50
self.matchTicks = self.matchLength * self.TPS self.matchTicks = self.matchLength * self.TPS
self.displayTickResolution = 4 self.displayTickResolution = 18
self.displayTicks = round(self.matchTicks / self.displayTickResolution) self.displayTicks = round(self.matchTicks / self.displayTickResolution)
self.keyFrames = [] self.keyFrames = []
self.displayFrames = []
def addKeyFrames(self): self.setupFrames()
currentFrame = 0
for i in range(0, self.displayTicks): def setupFrames(self):
self.keyFrames.append(currentFrame) self.keyFrames = list(range(1, self.matchTicks + 1))
self.displayFrames = list(range(1, self.displayTicks + 1))
print(len(self.keyFrames))
print(len(self.displayFrames))
def showButtonEditor(self): def showButtonEditor(self):
self.show() self.show()
self.pathPlanner.hide() if self.pathPlanner:
self.pathPlanner.hide()
def showPathPlanner(self): def showPathPlanner(self):
self.hide() self.hide()
self.pathPlanner.show() if self.pathPlanner:
self.pathPlanner.show()
def updateScene(self): def updateScene(self):
self.pixmap = QPixmap(os.path.join(os.path.dirname(os.path.abspath(__file__)), "images", "Field.png")) self.pixmap = QPixmap(os.path.join(os.path.dirname(os.path.abspath(__file__)), "images", "Field.png"))
painter = QPainter(self.pixmap) painter = QPainter(self.pixmap)
greyPen = QPen(QColor(127, 127, 127))
greyPen.setWidth(2)
painter.setPen(greyPen)
# Draw the Bezier curve segments
if self.pathPlanner.coordinates.size > 0:
for i in range(len(self.pathPlanner.coordinates) - 1):
start = QPoint(self.pathPlanner.coordinates[i][0], self.pathPlanner.coordinates[i][1])
end = QPoint(self.pathPlanner.coordinates[i + 1][0], self.pathPlanner.coordinates[i + 1][1])
if i < len(self.pathPlanner.controlPoints):
controlPair = self.pathPlanner.controlPoints[i]
pen = QPen(Qt.yellow)
pen.setWidth(2)
painter.setPen(pen)
path = QPainterPath()
path.moveTo(start)
path.cubicTo(controlPair[0], controlPair[1], end)
painter.drawPath(path)
# Draw the nodes
painter.setPen(Qt.white)
font = painter.font()
font.setPointSize(25)
painter.setFont(font)
painter.setPen(Qt.NoPen) painter.setPen(Qt.NoPen)
painter.setBrush(Qt.white) painter.setBrush(Qt.white)
for i, (x, y) in enumerate(self.pathPlanner.coordinates): if self.pathPlanner and hasattr(self.pathPlanner, 'coordinates'):
nodeRect = QRect(x - self.pathPlanner.nodeSize // 6, y - self.pathPlanner.nodeSize // 6, for i, (x, y) in enumerate(self.pathPlanner.coordinates):
self.pathPlanner.nodeSize // 3, self.pathPlanner.nodeSize // 3) nodeRect = QRect(x - self.pathPlanner.nodeSize // 6, y - self.pathPlanner.nodeSize // 6,
painter.drawEllipse(nodeRect) self.pathPlanner.nodeSize // 3, self.pathPlanner.nodeSize // 3)
painter.drawText(nodeRect, Qt.AlignCenter, str(i + 1)) painter.drawEllipse(nodeRect)
painter.drawText(nodeRect, Qt.AlignCenter, str(i + 1))
angle = self.pathPlanner.nodeAngles[i] if i < len(self.pathPlanner.nodeAngles) else 0
self.drawRobot(painter, QPoint(x, y), angle)
if len(self.pathPlanner.coordinates) > 1:
for i in range(len(self.pathPlanner.coordinates) - 1):
start = QPoint(self.pathPlanner.coordinates[i][0], self.pathPlanner.coordinates[i][1])
end = QPoint(self.pathPlanner.coordinates[i + 1][0], self.pathPlanner.coordinates[i + 1][1])
if i < len(self.pathPlanner.controlPoints):
controlPair = self.pathPlanner.controlPoints[i]
start_angle = self.pathPlanner.nodeAngles[i] if i < len(self.pathPlanner.nodeAngles) else 0
end_angle = self.pathPlanner.nodeAngles[i + 1] if i + 1 < len(self.pathPlanner.nodeAngles) else 0
for t in np.linspace(0, 1, self.displayTicks)[1:-1]:
point = self.pointOnBezierCurve(start, controlPair[0], controlPair[1], end, t)
current_angle = self.interpolateAngle(start_angle, end_angle, t)
self.drawRobot(painter, point, current_angle)
painter.end() painter.end()
self.imageLabel.setPixmap(self.pixmap) self.imageLabel.setPixmap(self.pixmap)
def interpolateAngle(self, start_angle, end_angle, t):
diff = (end_angle - start_angle + 180) % 360 - 180
return start_angle + diff * t
def drawRobot(self, painter, position, angle):
side_length = self.pathPlanner.nodeSize
half_side = side_length / 2
painter.save()
painter.translate(position)
painter.rotate(angle - 90)
painter.setBrush(Qt.NoBrush)
painter.setPen(QPen(QColor(127, 127, 127), 2))
painter.drawRect(-half_side, -half_side, side_length, side_length)
painter.setPen(QPen(QColor(255, 0, 0), 2))
painter.drawLine(0, 0, half_side, 0)
painter.drawLine(half_side, 0, half_side - 5, -5)
painter.drawLine(half_side, 0, half_side - 5, 5)
painter.restore()
def pointOnBezierCurve(self, start, control1, control2, end, t):
x = (1-t)**3 * start.x() + 3*(1-t)**2*t * control1.x() + 3*(1-t)*t**2 * control2.x() + t**3 * end.x()
y = (1-t)**3 * start.y() + 3*(1-t)**2*t * control1.y() + 3*(1-t)*t**2 * control2.y() + t**3 * end.y()
return QPoint(int(x), int(y))
def updateRectangles(self):
if self.rectanglesWidget.layout():
QWidget().setLayout(self.rectanglesWidget.layout())
layout = QHBoxLayout()
layout.setSpacing(2)
rect_width = 20
rect_height = 50
for frame in self.displayFrames:
rectWidget = QWidget()
rectWidget.setFixedSize(rect_width, rect_height)
rectWidget.setAutoFillBackground(True)
rectWidget.setStyleSheet("background-color: white; border: 1px solid black;")
layout.addWidget(rectWidget)
self.rectanglesWidget.setLayout(layout)
self.rectanglesWidget.setFixedHeight(rect_height + 10)
self.rectanglesWidget.setMinimumWidth(len(self.displayFrames) * (rect_width + 2))
self.scrollArea.setWidget(self.rectanglesWidget)
self.scrollArea.setFixedHeight(rect_height + 30)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = ButtonEditor(None)
window.updateScene()
window.show()
sys.exit(app.exec())
+28 -36
View File
@@ -2,7 +2,7 @@ import sys
import os import os
import numpy as np import numpy as np
from PySide6.QtWidgets import QApplication, QLabel, QMainWindow, QPushButton, QVBoxLayout, QHBoxLayout, QWidget, QMessageBox from PySide6.QtWidgets import QApplication, QLabel, QMainWindow, QPushButton, QVBoxLayout, QHBoxLayout, QWidget, QMessageBox
from PySide6.QtGui import QPixmap, QMouseEvent, QPainter, QPen, QColor, QPainterPath, QPolygon, QFont from PySide6.QtGui import QPixmap, QMouseEvent, QPainter, QPen, QColor, QPainterPath, QPolygon, QFont, QKeyEvent
from PySide6.QtCore import Qt, QPoint, QRect from PySide6.QtCore import Qt, QPoint, QRect
from buttonEditor import ButtonEditor from buttonEditor import ButtonEditor
@@ -69,6 +69,10 @@ class PathPlanner(QMainWindow):
self.draggingNodeIndex = -1 self.draggingNodeIndex = -1
self.draggingRotationHandleIndex = -1 self.draggingRotationHandleIndex = -1
def keyPressEvent(self, event: QKeyEvent):
if event.key() == Qt.Key_R:
self.showClearWarning()
def mousePressEvent(self, event: QMouseEvent): def mousePressEvent(self, event: QMouseEvent):
pos = self.imageLabel.mapFrom(self, event.position().toPoint()) pos = self.imageLabel.mapFrom(self, event.position().toPoint())
x, y = pos.x(), pos.y() x, y = pos.x(), pos.y()
@@ -76,6 +80,7 @@ class PathPlanner(QMainWindow):
if 0 <= x < self.pixmap.width() and 0 <= y < self.pixmap.height(): if 0 <= x < self.pixmap.width() and 0 <= y < self.pixmap.height():
if event.button() == Qt.RightButton: if event.button() == Qt.RightButton:
self.coordinates = np.vstack((self.coordinates, [x, y])) self.coordinates = np.vstack((self.coordinates, [x, y]))
self.nodeAngles.append(0)
if len(self.coordinates) >= 2: if len(self.coordinates) >= 2:
self.calculateControlPoints() self.calculateControlPoints()
self.calculateRotationHandlePos() self.calculateRotationHandlePos()
@@ -116,43 +121,36 @@ class PathPlanner(QMainWindow):
def deleteNode(self, index): def deleteNode(self, index):
self.coordinates = np.delete(self.coordinates, index, axis=0) self.coordinates = np.delete(self.coordinates, index, axis=0)
if index < len(self.controlPoints): if index < len(self.controlPoints):
del self.controlPoints[index] self.controlPoints.pop(index)
if index > 0 and index < len(self.controlPoints): if index > 0 and index < len(self.controlPoints):
del self.controlPoints[index - 1] self.controlPoints.pop(index - 1)
if index < len(self.rotationHandles): self.nodeAngles.pop(index)
del self.rotationHandles[index] self.calculateControlPoints()
self.calculateRotationHandlePos()
self.drawScene() self.drawScene()
self.buttonEditor.updateScene() self.buttonEditor.updateScene()
def mouseMoveEvent(self, event: QMouseEvent): def mouseMoveEvent(self, event: QMouseEvent):
pos = self.imageLabel.mapFrom(self, event.position().toPoint())
x, y = pos.x(), pos.y()
if self.draggingControlPoint: if self.draggingControlPoint:
pos = self.imageLabel.mapFrom(self, event.position().toPoint()) self.controlPoints[self.draggingControlPointIndex[0]][self.draggingControlPointIndex[1]] = QPoint(x, y)
x, y = pos.x(), pos.y()
curveIndex, pointIndex = self.draggingControlPointIndex
self.controlPoints[curveIndex][pointIndex] = QPoint(x, y)
self.drawScene() self.drawScene()
self.buttonEditor.updateScene() self.buttonEditor.updateScene()
elif self.draggingNode: elif self.draggingNode:
pos = self.imageLabel.mapFrom(self, event.position().toPoint())
x, y = pos.x(), pos.y()
self.coordinates[self.draggingNodeIndex] = [x, y] self.coordinates[self.draggingNodeIndex] = [x, y]
self.calculateControlPoints() self.calculateControlPoints()
self.calculateRotationHandlePos() self.calculateRotationHandlePos()
self.drawScene() self.drawScene()
self.buttonEditor.updateScene() self.buttonEditor.updateScene()
elif self.draggingRotationHandle: elif self.draggingRotationHandle:
pos = self.imageLabel.mapFrom(self, event.position().toPoint()) nodePos = QPoint(self.coordinates[self.draggingRotationHandleIndex][0],
x, y = pos.x(), pos.y() self.coordinates[self.draggingRotationHandleIndex][1])
nodeX, nodeY = self.coordinates[self.draggingRotationHandleIndex] angle = (np.degrees(np.arctan2(y - nodePos.y(), x - nodePos.x())) + 90) % 360
dx = x - nodeX
dy = y - nodeY
angle = np.arctan2(dy, dx)
self.nodeAngles[self.draggingRotationHandleIndex] = angle self.nodeAngles[self.draggingRotationHandleIndex] = angle
self.calculateRotationHandlePos()
rotationHandleX = int(nodeX + self.rotationHandleDistance * np.cos(angle))
rotationHandleY = int(nodeY + self.rotationHandleDistance * np.sin(angle))
self.rotationHandles[self.draggingRotationHandleIndex] = QPoint(rotationHandleX, rotationHandleY)
self.drawScene() self.drawScene()
self.buttonEditor.updateScene() self.buttonEditor.updateScene()
@@ -199,19 +197,13 @@ class PathPlanner(QMainWindow):
]) ])
def calculateRotationHandlePos(self): def calculateRotationHandlePos(self):
self.rotationHandles = []
for i, (x, y) in enumerate(self.coordinates): for i, (x, y) in enumerate(self.coordinates):
if i >= len(self.nodeAngles): angle = self.nodeAngles[i] if i < len(self.nodeAngles) else 0
self.nodeAngles.append(np.pi) # Default angle for new nodes radians = np.radians(angle)
handle_x = x + self.rotationHandleDistance * np.sin(radians)
angle = self.nodeAngles[i] handle_y = y - self.rotationHandleDistance * np.cos(radians)
self.rotationHandles.append(QPoint(int(handle_x), int(handle_y)))
rotationHandleX = int(x + self.rotationHandleDistance * np.cos(angle))
rotationHandleY = int(y + self.rotationHandleDistance * np.sin(angle))
if i >= len(self.rotationHandles):
self.rotationHandles.append(QPoint(rotationHandleX, rotationHandleY))
else:
self.rotationHandles[i] = QPoint(rotationHandleX, rotationHandleY)
def updateScene(self): def updateScene(self):
self.buttonEditor.updateScene(self.coordinates, self.controlPoints) self.buttonEditor.updateScene(self.coordinates, self.controlPoints)
@@ -256,11 +248,11 @@ class PathPlanner(QMainWindow):
for i, (x, y) in enumerate(self.coordinates): for i, (x, y) in enumerate(self.coordinates):
if i < len(self.rotationHandles): if i < len(self.rotationHandles):
angle = self.nodeAngles[i] angle = self.nodeAngles[i] if i < len(self.nodeAngles) else 0
painter.save() painter.save()
painter.translate(x, y) painter.translate(x, y)
painter.rotate(np.degrees(angle)) painter.rotate(angle)
pen = QPen(QColor(127, 127, 127)) pen = QPen(QColor(127, 127, 127))
pen.setWidth(2) pen.setWidth(2)