Files
autoPlanner2025/main.py
T

361 lines
15 KiB
Python
Raw Normal View History

2024-07-02 14:43:48 -06:00
import sys
import os
2024-04-11 10:27:35 -06:00
import numpy as np
2024-07-03 14:15:25 -06:00
from PySide6.QtWidgets import QApplication, QLabel, QMainWindow, QPushButton, QVBoxLayout, QHBoxLayout, QWidget, QMessageBox
2024-07-09 07:18:37 -06:00
from PySide6.QtGui import QPixmap, QMouseEvent, QPainter, QPen, QColor, QPainterPath, QPolygon, QFont, QKeyEvent
2024-07-02 14:43:48 -06:00
from PySide6.QtCore import Qt, QPoint, QRect
2024-04-11 10:27:35 -06:00
2024-07-26 13:59:15 -04:00
from buttonEditor import ButtonEditor # Import the button editor
from about import AboutWindow
'''
This is the path planner window
The path planner lets the user add nodes and control points to let them change the bezier curves and the path of the robot during the auto
'''
2024-07-03 14:15:25 -06:00
class PathPlanner(QMainWindow):
2024-07-02 14:43:48 -06:00
def __init__(self):
super().__init__()
2024-07-26 13:59:15 -04:00
self.setWindowTitle("Path Planner") # Set the window title
2024-07-02 14:43:48 -06:00
2024-07-26 13:59:15 -04:00
# Set up the arrays
self.coordinates = np.empty((0, 2), dtype=int) # Make an empty array for the coordinates of objects
2024-07-03 17:44:46 -06:00
self.controlPoints = []
self.rotationHandles = []
self.nodeAngles = []
2024-07-03 15:12:25 -06:00
2024-07-26 13:59:15 -04:00
# Find the field png and then set the background to that png
2024-07-03 17:44:46 -06:00
self.imageLabel = QLabel(self)
scriptDir = os.path.dirname(os.path.abspath(__file__))
imagePath = os.path.join(scriptDir, "images", "Field.png")
self.pixmap = QPixmap(imagePath)
2024-07-26 13:59:15 -04:00
2024-07-02 14:43:48 -06:00
if self.pixmap.isNull():
2024-07-03 17:44:46 -06:00
self.imageLabel.setText(f"Image not found at: {imagePath}")
2024-07-02 14:43:48 -06:00
else:
2024-07-03 17:44:46 -06:00
self.imageLabel.setPixmap(self.pixmap)
2024-04-11 10:27:35 -06:00
2024-07-26 13:59:15 -04:00
# Buttons at the top of the screen
2024-07-03 17:44:46 -06:00
self.mainWindowButton = QPushButton("Main Window")
self.mainWindowButton.clicked.connect(self.showMainWindow)
self.buttonEditorButton = QPushButton("Button Editor")
self.buttonEditorButton.clicked.connect(self.showButtonEditor)
2024-07-26 13:59:15 -04:00
self.aboutButton = QPushButton("About")
self.aboutButton.clicked.connect(self.showAbout)
2024-07-03 14:15:25 -06:00
2024-07-26 13:59:15 -04:00
# Button layouts
2024-07-03 17:44:46 -06:00
buttonLayout = QHBoxLayout()
buttonLayout.addWidget(self.mainWindowButton)
buttonLayout.addWidget(self.buttonEditorButton)
2024-07-26 13:59:15 -04:00
buttonLayout.addWidget(self.aboutButton)
2024-04-11 10:27:35 -06:00
2024-07-26 13:59:15 -04:00
# Adding the button layout to the main layout
2024-07-02 14:43:48 -06:00
layout = QVBoxLayout()
2024-07-03 17:44:46 -06:00
layout.addLayout(buttonLayout)
layout.addWidget(self.imageLabel)
2024-04-11 10:27:35 -06:00
2024-07-26 13:59:15 -04:00
# Set the button editor to that script
2024-07-03 17:44:46 -06:00
self.buttonEditor = ButtonEditor(self)
2024-07-03 14:15:25 -06:00
2024-07-26 13:59:15 -04:00
# Defining the overall layout
2024-07-02 14:43:48 -06:00
container = QWidget()
container.setLayout(layout)
self.setCentralWidget(container)
2024-07-26 13:59:15 -04:00
# Resize the window to all of the layouts and widgets added
2024-07-02 14:43:48 -06:00
self.resize(self.pixmap.width(), self.pixmap.height() + 60)
2024-07-26 13:59:15 -04:00
# Let the app track the users mouse
self.setMouseTracking(True)
2024-07-03 17:44:46 -06:00
self.lastClickPos = QPoint()
2024-07-02 21:00:29 -06:00
2024-07-26 13:59:15 -04:00
# Variables
self.coordinates = np.empty((0, 2), dtype=int) # Define the coordinate array again
self.lastClickTime = 0 # The last time the user clicked on the sceen
self.nodeSize = 35 # How large to make the nodes
self.handleSize = 15 # How large to make the rotation/control handles
self.rotationHandleDistance = 35 # How far the rotation handles are from the node
self.controlPoints = [] # Stores all of the control points
self.rotationHandles = [] # Stores all of the rotation handles
self.nodeAngles = [] # Stores all of the node angles
# Tell what the user is currently dragging
self.draggingControlPoint = False
2024-07-03 17:44:46 -06:00
self.draggingNode = False
self.draggingRotationHandle = False
self.draggingControlPointIndex = (-1, -1)
self.draggingNodeIndex = -1
self.draggingRotationHandleIndex = -1
2024-07-26 13:59:15 -04:00
# Tell when the user presses a key down
2024-07-09 07:18:37 -06:00
def keyPressEvent(self, event: QKeyEvent):
if event.key() == Qt.Key_R:
2024-07-26 13:59:15 -04:00
# Clear the auto
2024-07-09 07:18:37 -06:00
self.showClearWarning()
2024-07-26 13:59:15 -04:00
2024-07-09 07:18:37 -06:00
2024-07-26 13:59:15 -04:00
# Tell when the user presses a mouse button
2024-07-02 14:43:48 -06:00
def mousePressEvent(self, event: QMouseEvent):
2024-07-26 13:59:15 -04:00
# Get the position of the mouse click
2024-07-03 17:44:46 -06:00
pos = self.imageLabel.mapFrom(self, event.position().toPoint())
2024-07-02 16:15:40 -06:00
x, y = pos.x(), pos.y()
2024-04-11 10:27:35 -06:00
2024-07-26 13:59:15 -04:00
# Tell if the mouse was click ed in the window
2024-07-02 14:43:48 -06:00
if 0 <= x < self.pixmap.width() and 0 <= y < self.pixmap.height():
2024-07-26 13:59:15 -04:00
# If right clicked, add a node
2024-07-02 16:32:53 -06:00
if event.button() == Qt.RightButton:
self.coordinates = np.vstack((self.coordinates, [x, y]))
2024-07-09 07:18:37 -06:00
self.nodeAngles.append(0)
2024-07-02 16:32:53 -06:00
if len(self.coordinates) >= 2:
2024-07-03 17:44:46 -06:00
self.calculateControlPoints()
self.calculateRotationHandlePos()
self.drawScene()
self.buttonEditor.updateScene()
2024-07-02 16:32:53 -06:00
elif event.button() == Qt.LeftButton:
2024-07-03 17:44:46 -06:00
currentTime = event.timestamp()
if (currentTime - self.lastClickTime < 300 and
(pos - self.lastClickPos).manhattanLength() < 5):
nodeIndex = self.isPointInNode(x, y)
if nodeIndex != -1:
self.deleteNode(nodeIndex)
2024-07-02 21:18:10 -06:00
else:
2024-07-03 17:44:46 -06:00
controlPointIndex = self.isPointInControlPoint(x, y)
if controlPointIndex != (-1, -1):
self.smoothPoints(controlPointIndex[0], controlPointIndex[1])
self.drawScene()
self.buttonEditor.updateScene()
2024-07-02 21:00:29 -06:00
else:
2024-07-03 17:44:46 -06:00
controlPointIndex = self.isPointInControlPoint(x, y)
if controlPointIndex != (-1, -1):
self.draggingControlPoint = True
self.draggingControlPointIndex = controlPointIndex
2024-07-03 15:12:25 -06:00
else:
2024-07-03 17:44:46 -06:00
nodeIndex = self.isPointInNode(x, y)
if nodeIndex != -1:
self.draggingNode = True
self.draggingNodeIndex = nodeIndex
2024-07-02 21:00:29 -06:00
else:
2024-07-03 17:44:46 -06:00
rotationHandleIndex = self.isPointInRotationHandle(x, y)
if rotationHandleIndex != -1:
self.draggingRotationHandle = True
self.draggingRotationHandleIndex = rotationHandleIndex
2024-07-03 15:12:25 -06:00
2024-07-03 17:44:46 -06:00
self.lastClickTime = currentTime
self.lastClickPos = pos
2024-07-02 21:00:29 -06:00
2024-07-03 17:44:46 -06:00
def deleteNode(self, index):
2024-07-02 21:00:29 -06:00
self.coordinates = np.delete(self.coordinates, index, axis=0)
2024-07-03 17:44:46 -06:00
if index < len(self.controlPoints):
2024-07-09 07:18:37 -06:00
self.controlPoints.pop(index)
2024-07-03 17:44:46 -06:00
if index > 0 and index < len(self.controlPoints):
2024-07-09 07:18:37 -06:00
self.controlPoints.pop(index - 1)
self.nodeAngles.pop(index)
self.calculateControlPoints()
self.calculateRotationHandlePos()
2024-07-03 17:44:46 -06:00
self.drawScene()
self.buttonEditor.updateScene()
2024-07-02 16:15:40 -06:00
def mouseMoveEvent(self, event: QMouseEvent):
2024-07-09 07:18:37 -06:00
pos = self.imageLabel.mapFrom(self, event.position().toPoint())
x, y = pos.x(), pos.y()
2024-07-03 17:44:46 -06:00
if self.draggingControlPoint:
2024-07-09 07:18:37 -06:00
self.controlPoints[self.draggingControlPointIndex[0]][self.draggingControlPointIndex[1]] = QPoint(x, y)
2024-07-03 17:44:46 -06:00
self.drawScene()
self.buttonEditor.updateScene()
elif self.draggingNode:
self.coordinates[self.draggingNodeIndex] = [x, y]
self.calculateControlPoints()
self.calculateRotationHandlePos()
self.drawScene()
self.buttonEditor.updateScene()
elif self.draggingRotationHandle:
2024-07-09 07:18:37 -06:00
nodePos = QPoint(self.coordinates[self.draggingRotationHandleIndex][0],
self.coordinates[self.draggingRotationHandleIndex][1])
angle = (np.degrees(np.arctan2(y - nodePos.y(), x - nodePos.x())) + 90) % 360
2024-07-03 17:44:46 -06:00
self.nodeAngles[self.draggingRotationHandleIndex] = angle
2024-07-09 07:18:37 -06:00
self.calculateRotationHandlePos()
2024-07-03 17:44:46 -06:00
self.drawScene()
self.buttonEditor.updateScene()
2024-07-02 16:15:40 -06:00
def mouseReleaseEvent(self, event: QMouseEvent):
if event.button() == Qt.LeftButton:
2024-07-03 17:44:46 -06:00
self.draggingControlPoint = False
self.draggingControlPointIndex = (-1, -1)
self.draggingNode = False
self.draggingNodeIndex = -1
self.draggingRotationHandle = False
self.draggingRotationHandleIndex = -1
self.buttonEditor.updateScene()
def isPointInRotationHandle(self, x, y):
for i, handle in enumerate(self.rotationHandles):
if (abs(x - handle.x()) <= self.handleSize // 2 and
abs(y - handle.y()) <= self.handleSize // 2):
2024-07-02 16:53:07 -06:00
return i
return -1
2024-07-02 16:15:40 -06:00
2024-07-03 17:44:46 -06:00
def isPointInControlPoint(self, x, y):
for i, controlPair in enumerate(self.controlPoints):
for j, controlPoint in enumerate(controlPair):
if (abs(x - controlPoint.x()) <= self.handleSize // 2 and
abs(y - controlPoint.y()) <= self.handleSize // 2):
2024-07-03 13:35:23 -06:00
return (i, j)
return (-1, -1)
2024-07-02 16:32:53 -06:00
2024-07-03 17:44:46 -06:00
def isPointInNode(self, x, y):
for i, (nodeX, nodeY) in enumerate(self.coordinates):
if (abs(x - nodeX) <= self.nodeSize // 2 and
abs(y - nodeY) <= self.nodeSize // 2):
2024-07-02 16:32:53 -06:00
return i
return -1
2024-07-02 16:15:40 -06:00
2024-07-03 17:44:46 -06:00
def calculateControlPoints(self):
2024-07-02 16:15:40 -06:00
if len(self.coordinates) >= 2:
x1, y1 = self.coordinates[-2]
x2, y2 = self.coordinates[-1]
2024-07-03 17:44:46 -06:00
midX, midY = (x1 + x2) // 2, (y1 + y2) // 2
self.controlPoints.append([
QPoint(midX - (x2 - x1) // 4, midY - (y2 - y1) // 4),
QPoint(midX + (x2 - x1) // 4, midY + (y2 - y1) // 4)
2024-07-03 13:35:23 -06:00
])
2024-07-02 16:15:40 -06:00
2024-07-03 17:44:46 -06:00
def calculateRotationHandlePos(self):
2024-07-09 07:18:37 -06:00
self.rotationHandles = []
2024-07-02 16:53:07 -06:00
for i, (x, y) in enumerate(self.coordinates):
2024-07-09 07:18:37 -06:00
angle = self.nodeAngles[i] if i < len(self.nodeAngles) else 0
radians = np.radians(angle)
handle_x = x + self.rotationHandleDistance * np.sin(radians)
handle_y = y - self.rotationHandleDistance * np.cos(radians)
self.rotationHandles.append(QPoint(int(handle_x), int(handle_y)))
2024-07-02 16:53:07 -06:00
2024-07-03 17:44:46 -06:00
def updateScene(self):
self.buttonEditor.updateScene(self.coordinates, self.controlPoints)
2024-07-03 15:12:25 -06:00
2024-07-03 17:44:46 -06:00
def drawScene(self):
2024-07-02 16:15:40 -06:00
self.pixmap = QPixmap(os.path.join(os.path.dirname(os.path.abspath(__file__)), "images", "Field.png"))
2024-07-02 14:43:48 -06:00
painter = QPainter(self.pixmap)
2024-07-02 16:15:40 -06:00
2024-07-03 17:44:46 -06:00
greyPen = QPen(QColor(127, 127, 127))
greyPen.setWidth(2)
painter.setPen(greyPen)
for i, controlPair in enumerate(self.controlPoints):
2024-07-03 13:35:23 -06:00
if i < len(self.coordinates) - 1:
2024-07-02 16:15:40 -06:00
start = QPoint(self.coordinates[i][0], self.coordinates[i][1])
end = QPoint(self.coordinates[i + 1][0], self.coordinates[i + 1][1])
2024-07-03 13:35:23 -06:00
pen = QPen(Qt.yellow)
pen.setWidth(2)
painter.setPen(pen)
path = QPainterPath()
path.moveTo(start)
2024-07-03 17:44:46 -06:00
path.cubicTo(controlPair[0], controlPair[1], end)
2024-07-03 13:35:23 -06:00
painter.drawPath(path)
painter.setBrush(Qt.NoBrush)
2024-07-03 17:44:46 -06:00
for controlPoint in controlPair:
2024-07-03 13:35:23 -06:00
painter.setPen(QPen(QColor(127, 127, 127), 1, Qt.DashLine))
2024-07-03 17:44:46 -06:00
painter.drawLine(start, controlPoint)
painter.drawLine(end, controlPoint)
2024-07-03 13:35:23 -06:00
painter.setPen(Qt.NoPen)
painter.setBrush(QColor(0, 255, 255))
2024-07-03 17:44:46 -06:00
for controlPoint in controlPair:
2024-07-02 21:00:29 -06:00
painter.drawEllipse(
2024-07-03 17:44:46 -06:00
controlPoint.x() - self.handleSize // 2,
controlPoint.y() - self.handleSize // 2,
self.handleSize,
self.handleSize
2024-07-02 16:15:40 -06:00
)
2024-07-03 13:35:23 -06:00
painter.setBrush(Qt.NoBrush)
2024-07-02 16:15:40 -06:00
for i, (x, y) in enumerate(self.coordinates):
2024-07-03 17:44:46 -06:00
if i < len(self.rotationHandles):
2024-07-09 07:18:37 -06:00
angle = self.nodeAngles[i] if i < len(self.nodeAngles) else 0
2024-07-02 21:00:29 -06:00
painter.save()
painter.translate(x, y)
2024-07-09 07:18:37 -06:00
painter.rotate(angle)
2024-07-02 21:00:29 -06:00
pen = QPen(QColor(127, 127, 127))
pen.setWidth(2)
painter.setPen(pen)
painter.setBrush(Qt.NoBrush)
2024-07-03 17:44:46 -06:00
painter.drawRect(-self.nodeSize // 2, -self.nodeSize // 2,
self.nodeSize, self.nodeSize)
2024-07-02 21:00:29 -06:00
painter.restore()
painter.setPen(Qt.white)
font = painter.font()
font.setPointSize(25)
painter.setFont(font)
2024-07-03 17:44:46 -06:00
painter.drawText(QRect(x - self.nodeSize // 2, y - self.nodeSize // 2,
self.nodeSize, self.nodeSize),
2024-07-02 21:00:29 -06:00
Qt.AlignCenter, str(i + 1))
2024-07-03 17:44:46 -06:00
for i, handle in enumerate(self.rotationHandles):
2024-07-02 21:00:29 -06:00
painter.setBrush(QColor(255, 0, 255))
2024-07-02 16:53:07 -06:00
painter.drawEllipse(
handle,
2024-07-03 17:44:46 -06:00
self.handleSize // 2,
self.handleSize // 2
2024-07-02 16:53:07 -06:00
)
painter.setBrush(Qt.NoBrush)
2024-07-03 17:44:46 -06:00
self.imageLabel.setPixmap(self.pixmap)
self.buttonEditor.updateScene()
2024-07-02 14:43:48 -06:00
2024-07-03 17:44:46 -06:00
def smoothPoints(self, curveIndex: int, pointIndex: int):
if curveIndex + 1 < len(self.controlPoints):
prevControlPair = self.controlPoints[curveIndex]
nextControlPair = self.controlPoints[curveIndex + 1]
node = QPoint(self.coordinates[curveIndex + 1][0], self.coordinates[curveIndex + 1][1])
2024-07-03 13:35:23 -06:00
2024-07-03 17:44:46 -06:00
newControl2 = QPoint(
2 * node.x() - nextControlPair[0].x(),
2 * node.y() - nextControlPair[0].y()
2024-07-02 21:18:10 -06:00
)
2024-07-03 17:44:46 -06:00
self.controlPoints[curveIndex][1] = newControl2
2024-07-02 21:18:10 -06:00
2024-07-03 17:44:46 -06:00
newControl1 = QPoint(
2 * node.x() - prevControlPair[1].x(),
2 * node.y() - prevControlPair[1].y()
2024-07-03 13:35:23 -06:00
)
2024-07-03 17:44:46 -06:00
self.controlPoints[curveIndex + 1][0] = newControl1
2024-07-02 21:18:10 -06:00
2024-07-03 17:44:46 -06:00
def clearPoints(self):
2024-07-02 14:43:48 -06:00
self.coordinates = np.empty((0, 2), dtype=int)
2024-07-03 17:44:46 -06:00
self.controlPoints.clear()
self.rotationHandles.clear()
self.nodeAngles.clear()
self.drawScene()
self.buttonEditor.updateScene()
def showClearWarning(self):
2024-07-03 13:35:23 -06:00
reply = QMessageBox.question(self, "Clear Auto",
"Are you sure you want to clear this auto?",
QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
if reply == QMessageBox.Yes:
2024-07-03 17:44:46 -06:00
self.clearPoints()
2024-07-02 14:43:48 -06:00
2024-07-03 17:44:46 -06:00
def showMainWindow(self):
2024-07-03 14:15:25 -06:00
self.show()
2024-07-03 17:44:46 -06:00
self.buttonEditor.hide()
2024-07-03 14:15:25 -06:00
2024-07-03 17:44:46 -06:00
def showButtonEditor(self):
2024-07-03 14:15:25 -06:00
self.hide()
2024-07-26 13:59:15 -04:00
self.buttonEditor.show()
def showAbout(self):
self.about_window = AboutWindow()
self.about_window.show()
2024-07-03 14:15:25 -06:00
2024-07-02 14:43:48 -06:00
if __name__ == "__main__":
app = QApplication(sys.argv)
2024-07-03 14:15:25 -06:00
window = PathPlanner()
2024-07-02 14:43:48 -06:00
window.show()
2024-07-03 17:44:46 -06:00
sys.exit(app.exec())