Skip to content

Commit 3e96f98

Browse files
authored
Merge pull request The-OpenROAD-Project#9280 from gadfort/gui-legend
gui: add separate classes for rendering legends
2 parents 62b63f8 + d00d78f commit 3e96f98

File tree

8 files changed

+214
-12
lines changed

8 files changed

+214
-12
lines changed

src/gui/include/gui/gui.h

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -651,6 +651,54 @@ class SpectrumGenerator
651651
private:
652652
static const unsigned char kSpectrum[256][3];
653653
double scale_;
654+
static constexpr int kLegendColorIncrement = 2;
655+
};
656+
657+
class Legend
658+
{
659+
public:
660+
virtual ~Legend() = default;
661+
662+
virtual void draw(Painter& painter) const = 0;
663+
664+
protected:
665+
Legend() = default;
666+
};
667+
668+
// A legend for a linear spectrum of colors
669+
// The colors are specified as continuous from low to high
670+
// For example, a heat map legend
671+
class LinearLegend : public Legend
672+
{
673+
public:
674+
LinearLegend(const std::vector<Painter::Color>& colors);
675+
676+
// Set the legend key as a vector of (color, text) pairs
677+
void setLegendKey(
678+
const std::vector<std::pair<Painter::Color, std::string>>& legend_key);
679+
680+
void draw(Painter& painter) const override;
681+
682+
private:
683+
std::vector<Painter::Color> colors_;
684+
std::vector<std::pair<Painter::Color, std::string>> legend_key_;
685+
};
686+
687+
// A legend for discrete colors
688+
// Each color is associated with a text label
689+
// For example, a timing path legend
690+
class DiscreteLegend : public Legend
691+
{
692+
public:
693+
DiscreteLegend() = default;
694+
695+
// Add a (color, text) entry to the legend
696+
void addLegendKey(const Painter::Color& color, const std::string& text);
697+
698+
void draw(Painter& painter) const override;
699+
700+
private:
701+
std::vector<std::pair<Painter::Color, std::string>> color_key_;
654702
};
655703

656704
// A chart with a single X axis and potentially multiple Y axes

src/gui/src/gui.cpp

Lines changed: 113 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1183,6 +1183,8 @@ void Renderer::setSettings(const Renderer::Settings& settings)
11831183
}
11841184
}
11851185

1186+
//////////////////////////////////////////////////////////////////
1187+
11861188
SpectrumGenerator::SpectrumGenerator(double max_value) : scale_(1.0 / max_value)
11871189
{
11881190
}
@@ -1209,6 +1211,40 @@ Painter::Color SpectrumGenerator::getColor(double value, int alpha) const
12091211
void SpectrumGenerator::drawLegend(
12101212
Painter& painter,
12111213
const std::vector<std::pair<int, std::string>>& legend_key) const
1214+
{
1215+
const int color_count = getColorCount();
1216+
std::vector<Painter::Color> colors;
1217+
colors.reserve(color_count);
1218+
for (int i = 0; i < color_count; i += kLegendColorIncrement) {
1219+
const double color_idx = (color_count - 1 - i) / scale_;
1220+
colors.push_back(getColor(color_idx / color_count));
1221+
}
1222+
std::vector<std::pair<Painter::Color, std::string>> legend_key_colors;
1223+
for (const auto& [legend_value, legend_text] : legend_key) {
1224+
const int idx = std::clamp(legend_value / kLegendColorIncrement,
1225+
0,
1226+
static_cast<int>(colors.size()) - 1);
1227+
legend_key_colors.push_back({colors[idx], legend_text});
1228+
}
1229+
LinearLegend legend(colors);
1230+
legend.setLegendKey(legend_key_colors);
1231+
legend.draw(painter);
1232+
}
1233+
1234+
/////////////////////////////////////////////////
1235+
1236+
LinearLegend::LinearLegend(const std::vector<Painter::Color>& colors)
1237+
: colors_(colors)
1238+
{
1239+
}
1240+
1241+
void LinearLegend::setLegendKey(
1242+
const std::vector<std::pair<Painter::Color, std::string>>& legend_key)
1243+
{
1244+
legend_key_ = legend_key;
1245+
}
1246+
1247+
void LinearLegend::draw(Painter& painter) const
12121248
{
12131249
const odb::Rect& bounds = painter.getBounds();
12141250
const double pixel_per_dbu = painter.getPixelsPerDBU();
@@ -1224,14 +1260,20 @@ void SpectrumGenerator::drawLegend(
12241260
odb::Rect legend_bounds(
12251261
legend_left, legend_top, legend_right + text_offset, legend_top);
12261262

1227-
const int color_count = getColorCount();
1228-
const int color_incr = 2;
1263+
const int color_count = colors_.size();
12291264

12301265
std::vector<std::pair<odb::Point, std::string>> legend_key_points;
1231-
for (const auto& [legend_value, legend_text] : legend_key) {
1266+
for (const auto& [legend_color, legend_text] : legend_key_) {
1267+
const auto find_color = std::ranges::find(colors_, legend_color);
1268+
if (find_color == colors_.end()) {
1269+
continue;
1270+
}
1271+
const int legend_value
1272+
= std::distance(colors_.begin(), find_color); // index in colors_
1273+
12321274
const int text_right = legend_left - text_offset;
12331275
const int box_top
1234-
= legend_top - ((color_count - legend_value) * box_height) / color_incr;
1276+
= legend_top - ((color_count - legend_value) * box_height);
12351277

12361278
legend_key_points.push_back({{text_right, box_top}, legend_text});
12371279
const odb::Rect text_bounds = painter.stringBoundaries(
@@ -1247,10 +1289,8 @@ void SpectrumGenerator::drawLegend(
12471289

12481290
// draw color map
12491291
double box_top = legend_top;
1250-
for (int i = 0; i < color_count; i += color_incr) {
1251-
const double color_idx = (color_count - 1 - i) / scale_;
1252-
1253-
painter.setPen(getColor(color_idx / color_count), true);
1292+
for (int i = 0; i < color_count; i++) {
1293+
painter.setPen(colors_[i], true);
12541294
painter.drawLine(odb::Point(legend_left, box_top),
12551295
odb::Point(legend_right, box_top));
12561296
box_top -= box_height;
@@ -1265,6 +1305,71 @@ void SpectrumGenerator::drawLegend(
12651305
painter.drawRect(odb::Rect(legend_left, box_top, legend_right, legend_top));
12661306
}
12671307

1308+
/////////////////////////////////////////////////
1309+
1310+
void DiscreteLegend::addLegendKey(const Painter::Color& color,
1311+
const std::string& text)
1312+
{
1313+
color_key_.emplace_back(color, text);
1314+
}
1315+
1316+
void DiscreteLegend::draw(Painter& painter) const
1317+
{
1318+
const odb::Rect& bounds = painter.getBounds();
1319+
const double pixel_per_dbu = painter.getPixelsPerDBU();
1320+
const int legend_offset = 20 / pixel_per_dbu; // 20 pixels
1321+
const int legend_width = 20 / pixel_per_dbu; // 20 pixels
1322+
const int text_offset = 2 / pixel_per_dbu;
1323+
const int color_offset = 2 * text_offset;
1324+
const int legend_top = bounds.yMax() - legend_offset;
1325+
const int legend_right = bounds.xMax() - legend_offset;
1326+
const int legend_left = legend_right - legend_width;
1327+
1328+
odb::Rect legend_bounds(
1329+
legend_left, legend_top, legend_right + text_offset, legend_top);
1330+
1331+
std::vector<std::pair<odb::Rect, std::string>> legend_key_rects;
1332+
std::vector<std::pair<odb::Rect, Painter::Color>> legend_color_rects;
1333+
int last_text_top = legend_top;
1334+
for (const auto& [legend_color, legend_text] : color_key_) {
1335+
const int text_right = legend_left - text_offset;
1336+
1337+
const odb::Rect key_rect = painter.stringBoundaries(
1338+
text_right, last_text_top, Painter::Anchor::kTopRight, legend_text);
1339+
1340+
last_text_top = key_rect.yMin();
1341+
1342+
legend_key_rects.push_back({key_rect, legend_text});
1343+
legend_color_rects.push_back({{legend_left + color_offset,
1344+
key_rect.yMin() + color_offset,
1345+
legend_right - color_offset,
1346+
key_rect.yMax() - color_offset},
1347+
legend_color});
1348+
legend_bounds.merge(key_rect);
1349+
}
1350+
1351+
// draw background
1352+
painter.setPen(Painter::kDarkGray, true);
1353+
painter.setBrush(Painter::kDarkGray);
1354+
painter.drawRect(legend_bounds, 10, 10);
1355+
1356+
// draw color map
1357+
painter.setPen(Painter::kBlack, true);
1358+
for (const auto& [rect, color] : legend_color_rects) {
1359+
painter.setBrush(color);
1360+
painter.drawRect(rect);
1361+
}
1362+
1363+
// draw key values
1364+
painter.setBrush(Painter::kTransparent);
1365+
for (const auto& [rect, text] : legend_key_rects) {
1366+
painter.drawString(
1367+
rect.xMax(), rect.yCenter(), Painter::Anchor::kRightCenter, text);
1368+
}
1369+
}
1370+
1371+
/////////////////////////////////////////////////
1372+
12681373
void Gui::fit()
12691374
{
12701375
main_window->fit();

src/gui/src/staGui.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,7 @@ TimingPathRenderer::TimingPathRenderer() : path_(nullptr)
485485
addDisplayControl(kDataPathLabel, true);
486486
addDisplayControl(kLaunchClockLabel, true);
487487
addDisplayControl(kCaptureClockLabel, true);
488+
addDisplayControl(kLegendLabel, true);
488489
}
489490

490491
void TimingPathRenderer::highlight(TimingPath* path)
@@ -607,6 +608,16 @@ void TimingPathRenderer::drawObjects(gui::Painter& painter)
607608
checkDisplayControl(kDataPathLabel));
608609

609610
highlightStage(painter, net_descriptor, inst_descriptor);
611+
612+
if (checkDisplayControl(kLegendLabel)) {
613+
DiscreteLegend legend;
614+
legend.addLegendKey(kCaptureClockColor, "Capture");
615+
legend.addLegendKey(kClockColor, "Launch");
616+
legend.addLegendKey(kSignalColor, "Signal");
617+
legend.addLegendKey(kPathInstColor, "Inst");
618+
legend.addLegendKey(kTermColor, "Term");
619+
legend.draw(painter);
620+
}
610621
}
611622

612623
void TimingPathRenderer::highlightStage(gui::Painter& painter,

src/gui/src/staGui.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,7 @@ class TimingPathRenderer : public gui::Renderer
246246
static constexpr const char* kDataPathLabel = "Data path";
247247
static constexpr const char* kLaunchClockLabel = "Launch clock";
248248
static constexpr const char* kCaptureClockLabel = "Capture clock";
249+
static constexpr const char* kLegendLabel = "Legend";
249250
};
250251

251252
class TimingConeRenderer : public gui::Renderer

src/gui/src/stub.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,15 @@ SpectrumGenerator::SpectrumGenerator(double scale) : scale_(scale)
9898
{
9999
}
100100

101+
void DiscreteLegend::addLegendKey(const Painter::Color& color,
102+
const std::string& text)
103+
{
104+
}
105+
106+
void DiscreteLegend::draw(Painter& painter) const
107+
{
108+
}
109+
101110
bool Renderer::checkDisplayControl(const std::string& /* name */)
102111
{
103112
return false;

src/pad/src/RDLGui.cpp

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,9 @@ void RDLGui::drawObjects(gui::Painter& painter)
5050
}
5151

5252
const bool draw_obs = draw_detail && checkDisplayControl(kDrawObs);
53+
gui::Painter::Color obs_color = gui::Painter::kCyan;
54+
obs_color.a = 127;
5355
if (draw_obs) {
54-
gui::Painter::Color obs_color = gui::Painter::kCyan;
55-
obs_color.a = 127;
5656
painter.setPenAndBrush(obs_color, true);
5757

5858
for (const auto& [rect, poly, ptr, src] : router_->getObstructions()) {
@@ -88,9 +88,9 @@ void RDLGui::drawObjects(gui::Painter& painter)
8888
}
8989
}
9090

91+
gui::Painter::Color edge_color = gui::Painter::kGreen;
92+
edge_color.a = 127;
9193
if (draw_edge) {
92-
gui::Painter::Color edge_color = gui::Painter::kGreen;
93-
edge_color.a = 127;
9494
painter.setPenAndBrush(edge_color, true);
9595

9696
for (const auto& v : vertex) {
@@ -215,6 +215,15 @@ void RDLGui::drawObjects(gui::Painter& painter)
215215
for (const auto& [pt0, pt1] : snap_) {
216216
painter.drawLine(pt0, pt1);
217217
}
218+
219+
gui::DiscreteLegend legend;
220+
legend.addLegendKey(gui::Painter::kRed, "Vertex");
221+
legend.addLegendKey(edge_color, "Edge");
222+
legend.addLegendKey(obs_color, "Obstruction");
223+
legend.addLegendKey(gui::Painter::kBlue, "Target");
224+
legend.addLegendKey(gui::Painter::kYellow, "Flywire");
225+
legend.addLegendKey(gui::Painter::kGreen, "Route");
226+
legend.draw(painter);
218227
}
219228

220229
void RDLGui::addSnap(const odb::Point& pt0, const odb::Point& pt1)

src/pdn/src/renderer.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,15 @@ void PDNRenderer::drawLayer(odb::dbTechLayer* layer, gui::Painter& painter)
288288

289289
void PDNRenderer::drawObjects(gui::Painter& painter)
290290
{
291+
gui::DiscreteLegend legend;
292+
legend.addLegendKey(ring_color_, "Ring");
293+
legend.addLegendKey(strap_color_, "Strap");
294+
legend.addLegendKey(followpin_color_, "Followpin");
295+
legend.addLegendKey(via_color_, "Via");
296+
legend.addLegendKey(obstruction_color_, "Obstruction");
297+
legend.addLegendKey(repair_color_, "Repair Area");
298+
legend.addLegendKey(repair_outline_color_, "Repair Area Outline");
299+
legend.draw(painter);
291300
}
292301

293302
void PDNRenderer::pause()

src/psm/src/debug_gui.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -611,6 +611,16 @@ void DebugGui::drawLayer(odb::dbTechLayer* layer, gui::Painter& painter)
611611
drawSource(*node_itr, painter);
612612
}
613613
}
614+
615+
gui::DiscreteLegend legend;
616+
legend.addLegendKey(shape_color_, "Shape");
617+
legend.addLegendKey(node_color_, "Node");
618+
legend.addLegendKey(iterm_node_color_, "ITerm Node");
619+
legend.addLegendKey(bpin_node_color_, "BPin Node");
620+
legend.addLegendKey(connection_color_, "Connection");
621+
legend.addLegendKey(term_connection_color_, "Terminal Connection");
622+
legend.addLegendKey(src_node_color_, "Source Node/Shape");
623+
legend.draw(painter);
614624
}
615625

616626
gui::SelectionSet DebugGui::select(odb::dbTechLayer* layer,

0 commit comments

Comments
 (0)