From 47128adc99b2c848656b95cfe65b2dc4f187fdbd Mon Sep 17 00:00:00 2001 From: Philippe Babin Date: Thu, 11 Apr 2019 15:15:32 -0400 Subject: [PATCH 1/2] Add an option to switch the field at the vertical --- Controller/DrawingObject/FieldLineDrawing.py | 47 +++++++++++++------- Controller/FieldController.py | 35 +++++++++++---- Controller/MainController.py | 16 +++++++ 3 files changed, 74 insertions(+), 24 deletions(-) diff --git a/Controller/DrawingObject/FieldLineDrawing.py b/Controller/DrawingObject/FieldLineDrawing.py index ca2eeea..5b1ed7a 100644 --- a/Controller/DrawingObject/FieldLineDrawing.py +++ b/Controller/DrawingObject/FieldLineDrawing.py @@ -1,4 +1,5 @@ # Under MIT License, see LICENSE.txt +import math from PyQt5 import QtCore from PyQt5.QtGui import QPixmap, QPainter @@ -18,16 +19,9 @@ def __init__(self): def draw(self, painter): if self.isVisible() and QtToolBox.field_ctrl.need_redraw: QtToolBox.field_ctrl.need_redraw = False + # Grass drawing - self.field_painter.setPen(QtToolBox.create_pen(is_hide=True)) - self.field_painter.setBrush(QtToolBox.create_brush(color=Color.DARK_GREEN_FIELD)) - self.field_painter.drawRect(0, 0, 9500, 6500) - x, y = QtToolBox.field_ctrl.get_top_left_to_screen() - width, height = QtToolBox.field_ctrl.get_size_to_screen() - width /= 10 - self.field_painter.setBrush(QtToolBox.create_brush(color=Color.GREEN_FIELD)) - for i in range(0, 10, 2): - self.field_painter.drawRect(x + width * i, y, width, height) + self.drawGrass() self.field_painter.setBrush(QtToolBox.create_brush(is_visible=False)) @@ -56,16 +50,39 @@ def draw(self, painter): painter.drawPixmap(0, 0, self.field_cache) + def drawGrass(self, nb_lines = 10): + self.field_painter.setPen(QtToolBox.create_pen(is_hide=True)) + self.field_painter.setBrush(QtToolBox.create_brush(color=Color.DARK_GREEN_FIELD)) + self.field_painter.drawRect(0, 0, 9500, 6500) + self.field_painter.setBrush(QtToolBox.create_brush(color=Color.GREEN_FIELD)) + + width, height = QtToolBox.field_ctrl.field_length, QtToolBox.field_ctrl.field_width + xo, yo = width / 2, height / 2 + width_line = width / nb_lines + for i in range(0, nb_lines, 2): + # Top left + ax, ay, _ = QtToolBox.field_ctrl.convert_real_to_scene_pst(i * width_line - xo, -yo) + # Bot right + bx, by, _ = QtToolBox.field_ctrl.convert_real_to_scene_pst((i + 1) * width_line -xo, height -yo) + self.field_painter.drawRect(ax, ay, bx-ax, by-ay) + + def drawArc(self, painter, arc): - x, y, _ = QtToolBox.field_ctrl.convert_real_to_scene_pst(arc.center[0] - arc.radius, - arc.center[1] + arc.radius) - width = arc.radius * 2 * QtToolBox.field_ctrl.ratio_screen + # Top left + ax, ay, theta_off = QtToolBox.field_ctrl.convert_real_to_scene_pst(arc.center[0] - arc.radius, + arc.center[1] + arc.radius) + # Lower right + bx, by, _ = QtToolBox.field_ctrl.convert_real_to_scene_pst(arc.center[0] + arc.radius, + arc.center[1] - arc.radius) # Qt is weird and it require a rectangle to be defined to draw an arc - rect = QtCore.QRect(x, y, width, width) + rect = QtCore.QRect(ax, ay, bx-ax, by-ay) + + # If the screen is rotate, the angle is also rotated + theta_off_deg = theta_off * 180 / math.pi # Angle are calculate in 1/16th of a degree painter.drawArc(rect, - int(arc.start_angle) * 16, - int(arc.end_angle - arc.start_angle) * 16) + int(arc.start_angle - theta_off_deg) * 16, + int(arc.end_angle - arc.start_angle - theta_off_deg) * 16) def drawLine(self, painter, line): x1, y1, _ = QtToolBox.field_ctrl.convert_real_to_scene_pst(line.p1[0], line.p1[1]) diff --git a/Controller/FieldController.py b/Controller/FieldController.py index 6c50acc..58973ac 100644 --- a/Controller/FieldController.py +++ b/Controller/FieldController.py @@ -1,5 +1,5 @@ # Under MIT License, see LICENSE.txt - +import math from math import cos, sin, atan2, sqrt, pi from Communication.messages_robocup_ssl_geometry_pb2 import SSL_GeometryFieldSize @@ -51,6 +51,7 @@ def __init__(self): self.ratio_screen = 1 / 10 self.is_x_axe_flipped = False self.is_y_axe_flipped = True + self.is_horizontal = True # Dimension du terrain self.margin = 250 # Marge au tour du terrain pour l'écran @@ -128,27 +129,43 @@ def ratio_field_mobs(self, new_ratio): def convert_real_to_scene_pst(self, x, y, theta=0.0): """ Convertit les coordonnées réelles en coordonnées du terrain """ + + if not self.is_horizontal: + x, y = -y, x + theta += math.pi / 2 rot_x = cos(theta) rot_y = sin(theta) + if self.is_x_axe_flipped: x *= -1 rot_x *= -1 if self.is_y_axe_flipped: y *= -1 rot_y *= -1 - x = (x + self.field_length / 2 + self.margin) * self.ratio_screen + self._camera_position[0] - y = (y + self.field_width / 2 + self.margin) * self.ratio_screen + self._camera_position[1] - return x, y, atan2(rot_y, rot_x) + + x_screen = (x + self.field_length / 2 + self.margin) * self.ratio_screen + self._camera_position[0] + y_screen = (y + self.field_width / 2 + self.margin) * self.ratio_screen + self._camera_position[1] + return x_screen, y_screen, atan2(rot_y, rot_x) def convert_screen_to_real_pst(self, x, y): """ Convertir les coordonnées du terrain en coordonnées réelles """ - x_2 = (x - self._camera_position[0]) / self.ratio_screen - self.field_length / 2 - self.margin - y_2 = (y - self._camera_position[1]) / self.ratio_screen - self.field_width / 2 - self.margin + x_real = (x - self._camera_position[0]) / self.ratio_screen - self.field_length / 2 - self.margin + y_real = (y - self._camera_position[1]) / self.ratio_screen - self.field_width / 2 - self.margin + if self.is_x_axe_flipped: - x_2 *= -1 + x_real *= -1 if self.is_y_axe_flipped: - y_2 *= -1 - return x_2, y_2 + y_real *= -1 + + if self.is_horizontal: + return x_real, y_real + else: + return y_real, -x_real + + def set_horizontal(self, val: bool): + if self.is_horizontal != val: + self.need_redraw = True + self.is_horizontal = val def flip_x_axe(self): """ Retourne l'axe des X du terrain """ diff --git a/Controller/MainController.py b/Controller/MainController.py index 4196f24..1d94540 100644 --- a/Controller/MainController.py +++ b/Controller/MainController.py @@ -185,6 +185,14 @@ def init_menubar(self): fieldMenu.addSeparator() + horiAction = QAction("Changer à l'horizontal", self) + horiAction.triggered.connect(self.set_screen_to_horizontal) + fieldMenu.addAction(horiAction) + + vertAction = QAction("Changer à la vertical", self) + vertAction.triggered.connect(self.set_screen_to_vertical) + fieldMenu.addAction(vertAction) + flipXAction = QAction("Changer l'axe des X", self, checkable=True) flipXAction.triggered.connect(self.flip_screen_x_axe) fieldMenu.addAction(flipXAction) @@ -352,6 +360,14 @@ def toggle_full_screen(self): else: self.setWindowState(Qt.WindowActive) + def set_screen_to_horizontal(self): + """ Met l'écran à l'horizontal """ + QtToolBox.field_ctrl.set_horizontal(True) + + def set_screen_to_vertical(self): + """ Met l'écran à la vertical """ + QtToolBox.field_ctrl.set_horizontal(False) + def flip_screen_x_axe(self): """ Bascule l'axe des X de l'écran """ QtToolBox.field_ctrl.flip_x_axe() From 9bcf021e3e3bd4490c7db6eda3d917b4d51ecc68 Mon Sep 17 00:00:00 2001 From: Philippe Babin Date: Sat, 4 May 2019 16:12:08 -0400 Subject: [PATCH 2/2] Add trail to the ball --- Controller/DrawingObject/color.py | 4 +++- Controller/MobileObject/BallMob.py | 30 +++++++++++++++++++++++++++--- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/Controller/DrawingObject/color.py b/Controller/DrawingObject/color.py index 60f0f36..db49a75 100644 --- a/Controller/DrawingObject/color.py +++ b/Controller/DrawingObject/color.py @@ -12,4 +12,6 @@ class Color: WHITE = ColorTuple(red=255, green=255, blue=255) DARK_GREEN_FIELD = ColorTuple(red=0, green=150, blue=0) GREEN_FIELD = ColorTuple(red=0, green=125, blue=0) - ORANGE = ColorTuple(red=255, green=100, blue=0) \ No newline at end of file + CLEAR_ORANGE = ColorTuple(red=238, green=239, blue=168) + ORANGE = ColorTuple(red=255, green=100, blue=0) + DARK_ORANGE = ColorTuple(red=125, green=69, blue=25) \ No newline at end of file diff --git a/Controller/MobileObject/BallMob.py b/Controller/MobileObject/BallMob.py index a147571..43e05d3 100644 --- a/Controller/MobileObject/BallMob.py +++ b/Controller/MobileObject/BallMob.py @@ -1,8 +1,11 @@ # Under MIT License, see LICENSE.txt +from PyQt5.QtCore import Qt +from PyQt5.QtGui import QPainterPath from Controller.DrawingObject.color import Color from Controller.MobileObject.BaseMobileObject import BaseMobileObject from Controller.QtToolBox import QtToolBox +from collections import deque __author__ = 'RoboCupULaval' @@ -12,23 +15,44 @@ def __init__(self, x=0, y=0): BaseMobileObject.__init__(self, x, y) self._radius = 43 / 2 + NB_BALL_POSE_TO_KEEP = 50 + self.queue = deque(maxlen=NB_BALL_POSE_TO_KEEP) + @property def radius(self): return self._radius * QtToolBox.field_ctrl.ratio_field_mobs def draw(self, painter): if self.isVisible(): + bx, by, _ = QtToolBox.field_ctrl.convert_real_to_scene_pst(self._x, self._y) + + self.queue.appendleft((self._x, self._y)) + + # Draw ball's trail + path_painter = QPainterPath() + path_painter.moveTo(bx, by) + for rx, ry in self.queue: + x, y, _ = QtToolBox.field_ctrl.convert_real_to_scene_pst(rx, ry) + path_painter.lineTo(x, y) + + painter.setBrush(Qt.NoBrush) + painter.setPen(QtToolBox.create_pen(color=Color.CLEAR_ORANGE, + style='SolidLine', + width=2)) + painter.drawPath(path_painter) + + # Draw ball painter.setBrush(QtToolBox.create_brush(color=Color.ORANGE)) painter.setPen(QtToolBox.create_pen(color=Color.BLACK, style='SolidLine', width=1)) - x, y, _ = QtToolBox.field_ctrl.convert_real_to_scene_pst(self._x, self._y) radius = self.radius * QtToolBox.field_ctrl.ratio_screen - painter.drawEllipse(x - radius, - y - radius, + painter.drawEllipse(bx - radius, + by - radius, radius * 2, radius * 2) + @staticmethod def get_datain_associated(): return 'ball'