Skip to content

Commit 90a026a

Browse files
Lmh-javapre-commit-ci-lite[bot]williamckha
authored
Add referee display layer (UBC-Thunderbots#3337)
* Add new layer * Update format * [pre-commit.ci lite] apply automatic fixes * Fix typo Co-authored-by: William Ha <[email protected]> * Add ball placement vis? * Migrate ball placement * Add ball placement visuals * [pre-commit.ci lite] apply automatic fixes * Refactor code * Fix refactor * [pre-commit.ci lite] apply automatic fixes * Rename methods * [pre-commit.ci lite] apply automatic fixes * replace auto with optional * [pre-commit.ci lite] apply automatic fixes * partially introduce constexpr * use cpp library calls * [pre-commit.ci lite] apply automatic fixes * nit * replace all font name with constant * [pre-commit.ci lite] apply automatic fixes * update flag logic * update flag logic * remove font name instance * fix * [pre-commit.ci lite] apply automatic fixes --------- Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> Co-authored-by: William Ha <[email protected]>
1 parent 23ff0f6 commit 90a026a

18 files changed

+429
-16
lines changed

src/proto/visualization.proto

+5
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,11 @@ message AttackerVisualization
4141
optional Point chip_target = 4;
4242
}
4343

44+
message BallPlacementVisualization
45+
{
46+
Point ball_placement_point = 1;
47+
}
48+
4449
message CostVisualization
4550
{
4651
uint32 num_rows = 1;

src/shared/constants.h

+8
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,14 @@ static const double BALL_MAX_RADIUS_METERS = 0.0215;
8181
// cover more than 20% of the ball
8282
static const double MAX_FRACTION_OF_BALL_COVERED_BY_ROBOT = 0.2;
8383

84+
// The radius of a circle region where ball placement is acceptable (in meters).
85+
constexpr double BALL_PLACEMENT_TOLERANCE_RADIUS_METERS = 0.15;
86+
// The radius of the outer region where robots are not allowed to be during ball
87+
// placement (in meters)
88+
constexpr double BALL_PLACEMENT_ROBOT_AVOID_RADIUS_METERS = 0.5;
89+
// The time limit for ball placement in seconds
90+
constexpr int BALL_PLACEMENT_TIME_LIMIT_S = 30;
91+
8492
// The mass of a standard golf ball, as defined by https://en.wikipedia.org/wiki/Golf_ball
8593
constexpr double BALL_MASS_KG = 0.004593;
8694
// The max allowed speed of the robot when the stop command is issued, in meters per

src/software/ai/hl/stp/play/ball_placement/ball_placement_play.cpp

+14-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include "software/ai/hl/stp/play/ball_placement/ball_placement_play.h"
22

3+
#include "proto/message_translation/tbots_geometry.h"
34
#include "software/util/generic_factory/generic_factory.h"
45

56

@@ -17,7 +18,19 @@ void BallPlacementPlay::getNextTactics(TacticCoroutine::push_type &yield,
1718

1819
void BallPlacementPlay::updateTactics(const PlayUpdate &play_update)
1920
{
20-
fsm.process_event(BallPlacementPlayFSM::Update(control_params, play_update));
21+
auto event = BallPlacementPlayFSM::Update(control_params, play_update);
22+
fsm.process_event(event);
23+
24+
std::optional<Point> placement_point =
25+
event.common.world_ptr->gameState().getBallPlacementPoint();
26+
if (placement_point.has_value())
27+
{
28+
TbotsProto::BallPlacementVisualization ball_placement_vis_msg;
29+
*(ball_placement_vis_msg.mutable_ball_placement_point()) =
30+
*createPointProto(placement_point.value());
31+
32+
LOG(VISUALIZE) << ball_placement_vis_msg;
33+
}
2134
}
2235

2336
std::vector<std::string> BallPlacementPlay::getState()

src/software/py_constants.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@ PYBIND11_MODULE(py_constants, m)
1717
m.attr("BALL_MAX_RADIUS_METERS") = BALL_MAX_RADIUS_METERS;
1818
m.attr("BALL_MAX_RADIUS_MILLIMETERS") =
1919
BALL_MAX_RADIUS_METERS * MILLIMETERS_PER_METER;
20+
m.attr("BALL_PLACEMENT_TOLERANCE_RADIUS_METERS") =
21+
BALL_PLACEMENT_TOLERANCE_RADIUS_METERS;
22+
m.attr("BALL_PLACEMENT_ROBOT_AVOID_RADIUS_METERS") =
23+
BALL_PLACEMENT_ROBOT_AVOID_RADIUS_METERS;
24+
m.attr("BALL_PLACEMENT_TIME_LIMIT_S") = BALL_PLACEMENT_TIME_LIMIT_S;
2025

2126
m.attr("MAX_FRACTION_OF_BALL_COVERED_BY_ROBOT") =
2227
MAX_FRACTION_OF_BALL_COVERED_BY_ROBOT;

src/software/thunderscope/BUILD

+1
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ py_library(
9191
"//software/thunderscope/gl/layers:gl_obstacle_layer",
9292
"//software/thunderscope/gl/layers:gl_passing_layer",
9393
"//software/thunderscope/gl/layers:gl_path_layer",
94+
"//software/thunderscope/gl/layers:gl_referee_info_layer",
9495
"//software/thunderscope/gl/layers:gl_sandbox_world_layer",
9596
"//software/thunderscope/gl/layers:gl_simulator_layer",
9697
"//software/thunderscope/gl/layers:gl_tactic_layer",

src/software/thunderscope/binary_context_managers/full_system.py

+1
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ def setup_proto_unix_io(self, proto_unix_io: ProtoUnixIO) -> None:
184184
PlayInfo,
185185
ObstacleList,
186186
DebugShapes,
187+
BallPlacementVisualization,
187188
]:
188189
proto_unix_io.attach_unix_receiver(
189190
runtime_dir=self.full_system_runtime_dir,

src/software/thunderscope/constants.py

+3
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,8 @@ class EstopMode(IntEnum):
185185
"""
186186
)
187187

188+
THUNDERSCOPE_UI_FONT_NAME = "Roboto"
189+
188190

189191
def is_field_message_empty(field: Field) -> bool:
190192
"""Checks if a field message is empty
@@ -277,6 +279,7 @@ class Colors:
277279
ROBOT_MIDDLE_BLUE = QtGui.QColor(0, 0, 255, 255)
278280
PINK = QtGui.QColor(255, 0, 255)
279281
GREEN = QtGui.QColor(0, 255, 0)
282+
RED = QtGui.QColor(255, 0, 0, 255)
280283

281284
# Creates a default vision pattern lookup with the actual colors used on the robots
282285
VISION_PATTERN_LOOKUP = create_vision_pattern_lookup(PINK, GREEN)

src/software/thunderscope/gl/graphics/BUILD

+9
Original file line numberDiff line numberDiff line change
@@ -106,3 +106,12 @@ py_library(
106106
":gl_shape",
107107
],
108108
)
109+
110+
py_library(
111+
name = "gl_label",
112+
srcs = ["gl_label.py"],
113+
deps = [
114+
requirement("pyqtgraph"),
115+
":gl_shape",
116+
],
117+
)

src/software/thunderscope/gl/graphics/gl_gradient_legend.py

+5-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from typing import Optional
55

66
from software.thunderscope.gl.graphics.gl_painter import GLPainter
7-
from software.thunderscope.constants import Colors
7+
from software.thunderscope.constants import Colors, THUNDERSCOPE_UI_FONT_NAME
88

99

1010
class GLGradientLegend(GLPainter):
@@ -47,8 +47,10 @@ def __init__(
4747
self.title = title
4848

4949
self.text_pen = QtGui.QPen(Colors.PRIMARY_TEXT_COLOR)
50-
self.labels_font = QtGui.QFont("Roboto", 8)
51-
self.title_font = QtGui.QFont("Roboto", 9, QtGui.QFont.Weight.Bold)
50+
self.labels_font = QtGui.QFont(THUNDERSCOPE_UI_FONT_NAME, 8)
51+
self.title_font = QtGui.QFont(
52+
THUNDERSCOPE_UI_FONT_NAME, 9, QtGui.QFont.Weight.Bold
53+
)
5254

5355
self.add_draw_function(self.draw_gradient_legend)
5456

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
from PyQt6.QtGui import QFont, QColor
2+
from pyqtgraph.opengl.GLGraphicsItem import GLGraphicsItem
3+
from pyqtgraph.Qt import QtCore, QtGui
4+
5+
from typing import Optional
6+
7+
from software.thunderscope.gl.graphics.gl_painter import GLPainter
8+
from software.thunderscope.constants import Colors, THUNDERSCOPE_UI_FONT_NAME
9+
10+
11+
class GLLabel(GLPainter):
12+
"""Displays a 2D text label on the viewport"""
13+
14+
def __init__(
15+
self,
16+
parent_item: Optional[GLGraphicsItem] = None,
17+
font: QFont = QFont(THUNDERSCOPE_UI_FONT_NAME, 8),
18+
text_color: QColor = Colors.PRIMARY_TEXT_COLOR,
19+
offset: tuple[int, int] = (0, 0),
20+
text: str = "",
21+
) -> None:
22+
"""Initialize the GLLabel
23+
24+
:param parent_item: The parent item of the graphic
25+
:param font: The font using to render the text
26+
:param text_color: The color for rendering the text.
27+
:param offset: The offset (x, y) from the viewport left and top edge
28+
to use when positioning the label.
29+
If x is negative then the x offset is |x| pixels from
30+
the viewport right edge.
31+
If y is negative then the y offset is |y| pixels from
32+
the viewport bottom edge.
33+
:param text: The optional title to display above the legend
34+
"""
35+
super().__init__(parent_item=parent_item)
36+
37+
self.text_pen = QtGui.QPen(text_color)
38+
self.font = font
39+
self.offset = offset
40+
self.text = text
41+
42+
self.add_draw_function(self.draw_label)
43+
44+
def draw_label(self, painter: QtGui.QPainter, viewport_rect: QtCore.QRect) -> None:
45+
"""Draw the label
46+
:param painter: The QPainter to perform drawing operations with
47+
:param viewport_rect: The QRect indicating the viewport dimensions
48+
"""
49+
# calculate width and height of the label
50+
painter.setFont(self.font)
51+
bounds = painter.boundingRect(
52+
QtCore.QRectF(0, 0, 0, 0),
53+
QtCore.Qt.AlignmentFlag.AlignLeft | QtCore.Qt.AlignmentFlag.AlignVCenter,
54+
str(self.text),
55+
)
56+
57+
width = round(bounds.width())
58+
height = round(bounds.height())
59+
60+
# Determine x and y coordinates of the label
61+
if self.offset[0] < 0:
62+
x = viewport_rect.right() + self.offset[0] - width
63+
else:
64+
x = viewport_rect.left() + self.offset[0]
65+
if self.offset[1] < 0:
66+
y = viewport_rect.bottom() + self.offset[1] - height
67+
else:
68+
y = viewport_rect.top() + self.offset[1]
69+
70+
if self.text:
71+
painter.drawText(QtCore.QPoint(x, y), self.text)
72+
73+
def set_text(self, new_text: str) -> None:
74+
"""Update the text being displayed
75+
:param new_text: new text being displayed
76+
"""
77+
self.text = new_text

src/software/thunderscope/gl/layers/BUILD

+10
Original file line numberDiff line numberDiff line change
@@ -168,3 +168,13 @@ py_library(
168168
requirement("pyqtgraph"),
169169
],
170170
)
171+
172+
py_library(
173+
name = "gl_referee_info_layer",
174+
srcs = ["gl_referee_info_layer.py"],
175+
deps = [
176+
":gl_layer",
177+
"//software/thunderscope/gl/graphics:gl_label",
178+
requirement("pyqtgraph"),
179+
],
180+
)

src/software/thunderscope/gl/layers/gl_attacker_layer.py

+7-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,11 @@
66

77
from proto.visualization_pb2 import AttackerVisualization
88

9-
from software.thunderscope.constants import Colors, DepthValues
9+
from software.thunderscope.constants import (
10+
Colors,
11+
DepthValues,
12+
THUNDERSCOPE_UI_FONT_NAME,
13+
)
1014
from software.thunderscope.thread_safe_buffer import ThreadSafeBuffer
1115
from software.thunderscope.gl.layers.gl_layer import GLLayer
1216
from software.thunderscope.gl.graphics.gl_circle import GLCircle
@@ -97,7 +101,8 @@ def refresh_graphics(self) -> None:
97101
self.shot_open_angle_graphics.resize(
98102
1,
99103
lambda: GLTextItem(
100-
font=QtGui.QFont("Roboto", 8), color=Colors.SHOT_VISUALIZATION_COLOR
104+
font=QtGui.QFont(THUNDERSCOPE_UI_FONT_NAME, 8),
105+
color=Colors.SHOT_VISUALIZATION_COLOR,
101106
),
102107
)
103108

src/software/thunderscope/gl/layers/gl_debug_shapes_layer.py

+11-4
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,11 @@
66

77
from proto.visualization_pb2 import DebugShapes
88

9-
from software.thunderscope.constants import Colors, DepthValues
9+
from software.thunderscope.constants import (
10+
Colors,
11+
DepthValues,
12+
THUNDERSCOPE_UI_FONT_NAME,
13+
)
1014
from software.thunderscope.thread_safe_buffer import ThreadSafeBuffer
1115
from software.thunderscope.gl.layers.gl_layer import GLLayer
1216
from software.thunderscope.gl.graphics.gl_circle import GLCircle
@@ -89,7 +93,8 @@ def refresh_graphics(self) -> None:
8993
self.poly_shape_name_graphics.resize(
9094
len(poly_named_shapes),
9195
lambda: GLTextItem(
92-
font=QtGui.QFont("Roboto", 8), color=Colors.DEBUG_SHAPES_COLOR
96+
font=QtGui.QFont(THUNDERSCOPE_UI_FONT_NAME, 8),
97+
color=Colors.DEBUG_SHAPES_COLOR,
9398
),
9499
)
95100

@@ -100,7 +105,8 @@ def refresh_graphics(self) -> None:
100105
self.circle_shape_name_graphics.resize(
101106
len(circle_named_shapes),
102107
lambda: GLTextItem(
103-
font=QtGui.QFont("Roboto", 8), color=Colors.DEBUG_SHAPES_COLOR
108+
font=QtGui.QFont(THUNDERSCOPE_UI_FONT_NAME, 8),
109+
color=Colors.DEBUG_SHAPES_COLOR,
104110
),
105111
)
106112

@@ -111,7 +117,8 @@ def refresh_graphics(self) -> None:
111117
self.stadium_shape_name_graphics.resize(
112118
len(stadium_named_shapes),
113119
lambda: GLTextItem(
114-
font=QtGui.QFont("Roboto", 8), color=Colors.DEBUG_SHAPES_COLOR
120+
font=QtGui.QFont(THUNDERSCOPE_UI_FONT_NAME, 8),
121+
color=Colors.DEBUG_SHAPES_COLOR,
115122
),
116123
)
117124

src/software/thunderscope/gl/layers/gl_measure_layer.py

+8-4
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,11 @@
55
import numpy as np
66

77
from software.py_constants import *
8-
from software.thunderscope.constants import Colors, DepthValues
8+
from software.thunderscope.constants import (
9+
Colors,
10+
DepthValues,
11+
THUNDERSCOPE_UI_FONT_NAME,
12+
)
913

1014
from software.thunderscope.gl.layers.gl_layer import GLLayer
1115
from software.thunderscope.gl.graphics.gl_sphere import GLSphere
@@ -80,7 +84,7 @@ def mouse_in_scene_pressed(self, event: MouseInSceneEvent) -> None:
8084

8185
self.measurement_text_graphics.append(
8286
GLTextItem(
83-
font=QtGui.QFont("Roboto", 8),
87+
font=QtGui.QFont(THUNDERSCOPE_UI_FONT_NAME, 8),
8488
color=Colors.PRIMARY_TEXT_COLOR,
8589
text=f"{distance:.2f} m",
8690
pos=np.array([midpoint.x(), midpoint.y(), 0]),
@@ -111,7 +115,7 @@ def mouse_in_scene_pressed(self, event: MouseInSceneEvent) -> None:
111115

112116
self.measurement_text_graphics.append(
113117
GLTextItem(
114-
font=QtGui.QFont("Roboto", 8),
118+
font=QtGui.QFont(THUNDERSCOPE_UI_FONT_NAME, 8),
115119
color=Colors.PRIMARY_TEXT_COLOR,
116120
text=f"{angle:.1f}°",
117121
pos=np.array([placement_point.x(), placement_point.y(), 0]),
@@ -142,7 +146,7 @@ def refresh_graphics(self) -> None:
142146
if not self.cursor_coords_graphic:
143147
self.cursor_coords_graphic = GLTextItem(
144148
parentItem=self,
145-
font=QtGui.QFont("Roboto", 10),
149+
font=QtGui.QFont(THUNDERSCOPE_UI_FONT_NAME, 10),
146150
color=Colors.PRIMARY_TEXT_COLOR,
147151
)
148152

0 commit comments

Comments
 (0)