Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add referee display layer #3337

Open
wants to merge 30 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
4d608ca
Add new layer
Lmh-java Oct 4, 2024
de95603
Update format
Lmh-java Oct 4, 2024
07f17cb
[pre-commit.ci lite] apply automatic fixes
pre-commit-ci-lite[bot] Oct 4, 2024
b369777
Fix typo
Lmh-java Oct 5, 2024
8f58631
Add ball placement vis?
Lmh-java Oct 5, 2024
878112a
Migrate ball placement
Lmh-java Oct 5, 2024
160d347
Add ball placement visuals
Lmh-java Oct 6, 2024
ef0a030
[pre-commit.ci lite] apply automatic fixes
pre-commit-ci-lite[bot] Oct 6, 2024
f39a399
Refactor code
Lmh-java Oct 19, 2024
0d242ce
Fix refactor
Lmh-java Oct 19, 2024
db3f208
[pre-commit.ci lite] apply automatic fixes
pre-commit-ci-lite[bot] Oct 19, 2024
709db29
Rename methods
Lmh-java Oct 19, 2024
ecd314d
[pre-commit.ci lite] apply automatic fixes
pre-commit-ci-lite[bot] Oct 19, 2024
b7642cf
Merge branch 'master' into HEAD
Lmh-java Oct 25, 2024
4f3d93c
replace auto with optional
Lmh-java Oct 26, 2024
947428d
[pre-commit.ci lite] apply automatic fixes
pre-commit-ci-lite[bot] Oct 26, 2024
70ea39f
partially introduce constexpr
Lmh-java Oct 26, 2024
63ff6ed
use cpp library calls
Lmh-java Nov 9, 2024
494b833
[pre-commit.ci lite] apply automatic fixes
pre-commit-ci-lite[bot] Nov 9, 2024
f403762
nit
Lmh-java Nov 16, 2024
e08edd0
replace all font name with constant
Lmh-java Nov 16, 2024
8b1a616
[pre-commit.ci lite] apply automatic fixes
pre-commit-ci-lite[bot] Nov 16, 2024
2daae4f
update flag logic
Lmh-java Nov 23, 2024
207eae2
Merge branch 'minghao/referee-layer' of github.com:Lmh-java/Software …
Lmh-java Nov 23, 2024
4f94227
update flag logic
Lmh-java Nov 23, 2024
7368f8c
Merge branch 'master' into minghao/referee-layer
Lmh-java Nov 23, 2024
7f097b0
Merge branch 'master' into minghao/referee-layer
Lmh-java Jan 19, 2025
afaf9a9
remove font name instance
Lmh-java Jan 19, 2025
6b4fb32
fix
Lmh-java Jan 19, 2025
1f91332
[pre-commit.ci lite] apply automatic fixes
pre-commit-ci-lite[bot] Jan 19, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/software/thunderscope/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ py_library(
"//software/thunderscope/gl/layers:gl_obstacle_layer",
"//software/thunderscope/gl/layers:gl_passing_layer",
"//software/thunderscope/gl/layers:gl_path_layer",
"//software/thunderscope/gl/layers:gl_referee_info_layer",
"//software/thunderscope/gl/layers:gl_sandbox_world_layer",
"//software/thunderscope/gl/layers:gl_simulator_layer",
"//software/thunderscope/gl/layers:gl_tactic_layer",
Expand Down
9 changes: 9 additions & 0 deletions src/software/thunderscope/gl/graphics/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -106,3 +106,12 @@ py_library(
":gl_shape",
],
)

py_library(
name = "gl_label",
srcs = ["gl_label.py"],
deps = [
requirement("pyqtgraph"),
":gl_shape",
],
)
78 changes: 78 additions & 0 deletions src/software/thunderscope/gl/graphics/gl_label.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
from PyQt6.QtGui import QFont, QColor
from pyqtgraph.opengl.GLGraphicsItem import GLGraphicsItem
from pyqtgraph.Qt import QtCore, QtGui

from typing import Optional

from software.thunderscope.gl.graphics.gl_painter import GLPainter
from software.thunderscope.constants import Colors


class GLLabel(GLPainter):
"""Displays a 2D text label on the viewport"""

def __init__(
self,
parent_item: Optional[GLGraphicsItem] = None,
font: QFont = QFont("Roboto", 8),
text_color: QColor = Colors.PRIMARY_TEXT_COLOR,
offset: tuple[int, int] = (0, 0),
text: str = "",
) -> None:
"""Initialize the GLGradientLegend
Lmh-java marked this conversation as resolved.
Show resolved Hide resolved

:param parent_item: The parent item of the graphic
:param font: The font using to render the text
:param text_color: The color for rendering the text.
:param offset: The offset (x, y) from the viewport left and top edge
to use when positioning the label.
If x is negative then the x offset is |x| pixels from
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: is this necessary? feels like its a bit clearer to just require the offsets to be positive numbers always. from the perspective of someone using this method in the future, the sign flipping feels a bit roundabout

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not a huge deal tho

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lol I followed the design of other components on the same level for consistency. But sure, I will clean this up to make it more understandable.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After some testing here, I found out that the easier way to place the label on the top right corner is just to keep this wired sign flipping, because it takes some code to fetch the width of the viewport before passing into this function. Instead, we can get viewport by whatever calls this function, so we probably need to use a negative sign to denote starting from the right side.

the viewport right edge.
If y is negative then the y offset is |y| pixels from
the viewport bottom edge.
:param text: The optional title to display above the legend
"""
super().__init__(parent_item=parent_item)

self.text_pen = QtGui.QPen(text_color)
self.font = font
self.offset = offset
self.text = text

self.add_draw_function(self.draw_label)

def draw_label(self, painter: QtGui.QPainter, viewport_rect: QtCore.QRect) -> None:
"""Draw the label

:param painter: The QPainter to perform drawing operations with
:param viewport_rect: The QRect indicating the viewport dimensions
"""
# calculate width and height of the label
painter.setFont(self.font)
bounds = painter.boundingRect(
QtCore.QRectF(0, 0, 0, 0),
QtCore.Qt.AlignmentFlag.AlignLeft | QtCore.Qt.AlignmentFlag.AlignVCenter,
str(self.text),
)

width = round(bounds.width())
height = round(bounds.height())

# Determine x and y coordinates of the label
if self.offset[0] < 0:
x = viewport_rect.right() + self.offset[0] - width
else:
x = viewport_rect.left() + self.offset[0]
if self.offset[1] < 0:
y = viewport_rect.bottom() + self.offset[1] - height
else:
y = viewport_rect.top() + self.offset[1]

if self.text:
painter.drawText(QtCore.QPoint(x, y), self.text)

def set_text(self, new_text: str) -> None:
"""Update the text being displayed
:param new_text: new text being displayed
"""
self.text = new_text
10 changes: 10 additions & 0 deletions src/software/thunderscope/gl/layers/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -156,3 +156,13 @@ py_library(
requirement("pyqtgraph"),
],
)

py_library(
name = "gl_referee_info_layer",
srcs = ["gl_referee_info_layer.py"],
deps = [
":gl_layer",
"//software/thunderscope/gl/graphics:gl_label",
requirement("pyqtgraph"),
],
)
68 changes: 68 additions & 0 deletions src/software/thunderscope/gl/layers/gl_referee_info_layer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
from typing import Optional

from google.protobuf.json_format import MessageToDict

from proto.import_all_protos import *
from software.thunderscope.constants import DepthValues
from software.thunderscope.gl.graphics.gl_label import GLLabel
from software.thunderscope.gl.helpers.observable_list import ObservableList
from software.thunderscope.gl.layers.gl_layer import GLLayer
from software.thunderscope.thread_safe_buffer import ThreadSafeBuffer


class GLRefereeInfoLayer(GLLayer):
"""GLLayer that visualizes referee info"""

REFEREE_COMMAND_PREFIX = "Command: "
GAMESTATE_PREFIX = "Game State: "

def __init__(self, name: str, buffer_size: int = 1) -> None:
"""Initialize the GLRefereeInfoLayer

:param name: The displayed name of the layer
:param buffer_size: the buffer size, set higher for smoother plots.
Set lower for more realtime plots. Default is arbitrary.
"""
super().__init__(name)
self.setDepthValue(DepthValues.OVERLAY_DEPTH)
self.referee_vis_buffer = ThreadSafeBuffer(buffer_size, Referee, False)

self.referee_text_graphics = ObservableList(self._graphics_changed)

# initialize the two text items to display
self.gamestate_type_text: Optional[GLLabel] = None
self.command_type_text: Optional[GLLabel] = None

def refresh_graphics(self) -> None:
"""Update displays in the layer"""
referee_proto = self.referee_vis_buffer.get(block=False, return_cached=False)
if not referee_proto:
return

referee_msg_dict = MessageToDict(referee_proto)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You actually don't need MessageToDict (I'm not sure why we use it in some of our code). You can just access the fields directly e.g. referee_proto.stage

Copy link
Contributor Author

@Lmh-java Lmh-java Oct 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After testing, I think in this case I might want to keep MessageToDict, since directly acessing referee_proto.stage only gives me the index number (instead of the text)

if not referee_msg_dict:
return

if not self.gamestate_type_text:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: these 2 if statements are pretty identical, could probably pull them out to a new method

Copy link
Contributor Author

@Lmh-java Lmh-java Oct 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These two if statements are writting to class fields, I can parametrize relevant parameters, but I don't know how to parametrize class level field so everytime we call this method we write to a different file. It seems taking a lot of code...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can probably parametrize a class-level member with a lambda but that might be overkill since there's only 2

self.gamestate_type_text = GLLabel(
parent_item=self,
offset=(-10, 50),
text=GLRefereeInfoLayer.GAMESTATE_PREFIX + referee_msg_dict["stage"],
)
self.referee_text_graphics.append(self.gamestate_type_text)
else:
self.gamestate_type_text.set_text(
GLRefereeInfoLayer.GAMESTATE_PREFIX + referee_msg_dict["stage"]
)

if not self.command_type_text:
self.command_type_text = GLLabel(
parent_item=self,
offset=(-10, 70),
text=GLRefereeInfoLayer.GAMESTATE_PREFIX + referee_msg_dict["command"],
)
self.referee_text_graphics.append(self.command_type_text)
else:
self.command_type_text.set_text(
GLRefereeInfoLayer.REFEREE_COMMAND_PREFIX + referee_msg_dict["command"]
)
6 changes: 6 additions & 0 deletions src/software/thunderscope/widget_setup_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
gl_tactic_layer,
gl_cost_vis_layer,
gl_trail_layer,
gl_referee_info_layer,
)


Expand Down Expand Up @@ -129,6 +130,9 @@ def setup_gl_widget(
)
tactic_layer = gl_tactic_layer.GLTacticLayer("Tactics", visualization_buffer_size)
trail_layer = gl_trail_layer.GLTrailLayer("Trail", visualization_buffer_size)
referee_layer = gl_referee_info_layer.GLRefereeInfoLayer(
"Referee Info", visualization_buffer_size
)

gl_widget.add_layer(world_layer)
gl_widget.add_layer(simulator_layer, False)
Expand All @@ -141,6 +145,7 @@ def setup_gl_widget(
gl_widget.add_layer(validation_layer)
gl_widget.add_layer(trail_layer, False)
gl_widget.add_layer(debug_shapes_layer, True)
gl_widget.add_layer(referee_layer, False)
Lmh-java marked this conversation as resolved.
Show resolved Hide resolved

simulation_control_toolbar = gl_widget.get_sim_control_toolbar()
simulation_control_toolbar.set_speed_callback(world_layer.set_simulation_speed)
Expand Down Expand Up @@ -189,6 +194,7 @@ def setup_gl_widget(
(CostVisualization, cost_vis_layer.cost_visualization_buffer),
(World, trail_layer.world_buffer),
(DebugShapes, debug_shapes_layer.debug_shapes_buffer),
(Referee, referee_layer.referee_vis_buffer),
]:
full_system_proto_unix_io.register_observer(*arg)

Expand Down
Loading