Freeciv21
Develop your civilization from humble roots to a global empire
minimap.cpp
Go to the documentation of this file.
1 /*
2  Copyright (c) 1996-2023 Freeciv21 and Freeciv contributors. This file is
3  part of Freeciv21. Freeciv21 is free software: you can redistribute it
4  and/or modify it under the terms of the GNU General Public License as
5  published by the Free Software Foundation, either version 3 of the
6  License, or (at your option) any later version. You should have received
7  a copy of the GNU General Public License along with Freeciv21. If not,
8  see https://www.gnu.org/licenses/.
9  */
10 
11 #include "minimap.h"
12 
13 // Qt
14 #include <QMouseEvent>
15 #include <QPainter>
16 #include <QPixmap>
17 #include <QToolTip>
18 
19 // client
20 #include "client_main.h"
21 #include "fc_client.h"
22 #include "minimap_panel.h"
23 #include "overview_common.h"
24 #include "page_game.h"
25 #include "qtg_cxxside.h"
26 #include "views/view_map.h"
27 
28 namespace {
29 const auto always_visible_margin = 15;
30 }
31 
35 minimap_view::minimap_view(QWidget *parent) : fcwidget(parent)
36 {
37  setAttribute(Qt::WA_OpaquePaintEvent, true);
38  setCursor(Qt::CrossCursor);
39 }
40 
44 void minimap_view::paintEvent(QPaintEvent *event)
45 {
46  QPainter painter;
47 
48  painter.begin(this);
49  paint(&painter, event);
50  painter.end();
51 }
52 
57 {
58  ::king()->menu_bar->minimap_status->setChecked(false);
59 }
60 
61 namespace {
62 
63 void overview_pos_nowrap(const struct tileset *t, int *ovr_x, int *ovr_y,
64  int gui_x, int gui_y)
65 {
66  double ntl_x, ntl_y;
67  gui_to_natural_pos(t, &ntl_x, &ntl_y, gui_x, gui_y);
68 
69  // Now convert straight to overview coordinates.
70  *ovr_x = std::round((ntl_x - gui_options->overview.map_x0)
72  *ovr_y = std::round((ntl_y - gui_options->overview.map_y0)
74 }
75 
76 } // namespace
77 
81 void minimap_view::draw_viewport(QPainter *painter)
82 {
83  int x[4], y[4];
84 
85  if (!gui_options->overview.map) {
86  return;
87  }
88 
89  overview_pos_nowrap(tileset, &x[0], &y[0], mapview.gui_x0, mapview.gui_y0);
90  overview_pos_nowrap(tileset, &x[1], &y[1], mapview.gui_x0 + mapview.width,
91  mapview.gui_y0);
92  overview_pos_nowrap(tileset, &x[2], &y[2], mapview.gui_x0 + mapview.width,
94  overview_pos_nowrap(tileset, &x[3], &y[3], mapview.gui_x0,
96 
97  if ((current_topo_has_flag(TF_WRAPX)
98  && (x[2] - x[0] > NATURAL_WIDTH * OVERVIEW_TILE_SIZE))
99  || (current_topo_has_flag(TF_WRAPY)
100  && (y[2] - y[0] > NATURAL_HEIGHT * OVERVIEW_TILE_SIZE))) {
101  // Don't draw viewport lines if the view wraps around the map.
102  return;
103  }
104 
105  painter->setPen(QColor(Qt::white));
106 
107  auto w_ratio = static_cast<double>(width()) / gui_options->overview.width;
108  auto h_ratio =
109  static_cast<double>(height()) / gui_options->overview.height;
110 
112  for (int i = 0; i < 4; i++) {
113  lines.append(QLineF(x[i] * w_ratio, y[i] * h_ratio,
114  x[(i + 1) % 4] * w_ratio, y[(i + 1) % 4] * h_ratio));
115 
116  // Add another line segment if this one wraps around.
117  int wrap_src_x = current_topo_has_flag(TF_WRAPX)
119  : x[i];
120  int wrap_src_y = current_topo_has_flag(TF_WRAPY)
122  : y[i];
123 
124  if (wrap_src_x != x[i] || wrap_src_y != y[i]) {
125  int projected_dst_x = x[(i + 1) % 4] + wrap_src_x - x[i];
126  int projected_dst_y = y[(i + 1) % 4] + wrap_src_y - y[i];
127  lines.append(QLineF(wrap_src_x * w_ratio, wrap_src_y * h_ratio,
128  projected_dst_x * w_ratio,
129  projected_dst_y * h_ratio));
130  }
131 
132  int wrap_dst_x =
133  current_topo_has_flag(TF_WRAPX)
134  ? FC_WRAP(x[(i + 1) % 4], NATURAL_WIDTH * OVERVIEW_TILE_SIZE)
135  : x[(i + 1) % 4];
136  int wrap_dst_y =
137  current_topo_has_flag(TF_WRAPY)
138  ? FC_WRAP(y[(i + 1) % 4], NATURAL_HEIGHT * OVERVIEW_TILE_SIZE)
139  : y[(i + 1) % 4];
140  if (wrap_dst_x != x[(i + 1) % 4] || wrap_dst_y != y[(i + 1) % 4]) {
141  int projected_src_x = x[i] + wrap_dst_x - x[(i + 1) % 4];
142  int projected_src_y = y[i] + wrap_dst_y - y[(i + 1) % 4];
143  lines.append(QLineF(projected_src_x * w_ratio,
144  projected_src_y * h_ratio, wrap_dst_x * w_ratio,
145  wrap_dst_y * h_ratio));
146  }
147  }
148  painter->drawLines(lines);
149 }
150 
155 {
156  if (isHidden()) {
157  return;
158  }
159  update();
160 }
161 
165 int minimap_view::heightForWidth(int width) const
166 {
167  const auto size = gui_options->overview.map->size();
169  // Traditional iso tilesets have more or less this aspect ratio
170  return size.height() * width / size.width() / 2;
171  } else {
172  return size.height() * width / size.width();
173  }
174 }
175 
180 {
181  // The default size is a bit too small...
182  return 5 * gui_options->overview.map->size();
183 }
184 
188 void minimap_view::paint(QPainter *painter, QPaintEvent *event)
189 {
190  painter->drawPixmap(1, 1, width() - 1, height() - 1,
192 
193  painter->setPen(QColor(palette().color(QPalette::HighlightedText)));
194  painter->drawRect(0, 0, width() - 1, height() - 1);
195  draw_viewport(painter);
196 }
197 
201 void minimap_view::resizeEvent(QResizeEvent *event)
202 {
203  auto size = event->size();
204 
205  if (x() + size.width() < always_visible_margin) {
206  size.setWidth(always_visible_margin - x());
207  resize(size);
208  }
209  if (y() + size.height() < always_visible_margin) {
210  size.setHeight(always_visible_margin - y());
211  resize(size);
212  }
213 
214  if (C_S_RUNNING <= client_state() && size.width() > 0
215  && size.height() > 0) {
217  static_cast<float>(size.width()) / mapview.width;
219  static_cast<float>(size.height()) / mapview.height;
220  }
221  update_image();
222 }
223 
231 {
232  if (event->button() == Qt::RightButton) {
233  auto fx = event->pos().x();
234  auto fy = event->pos().y();
235  fx = qRound(fx * gui_options->overview.width
236  / static_cast<double>(width()));
237  fy = qRound(fy * gui_options->overview.height
238  / static_cast<double>(height()));
239  fx = qMax(fx, 1);
240  fy = qMax(fy, 1);
241  fx = qMin(fx, gui_options->overview.width - 1);
242  fy = qMin(fy, gui_options->overview.height - 1);
243  int x, y;
244  overview_to_map_pos(&x, &y, fx, fy);
245  auto *ptile = map_pos_to_tile(&(wld.map), x, y);
246  fc_assert_ret(ptile);
247  queen()->mapview_wdg->center_on_tile(ptile);
248  update_image();
249  }
250  event->setAccepted(true);
251 }
252 
256 void update_minimap() { queen()->minimap_panel->minimap()->update_image(); }
Definition: shared.h:24
fc_settings qt_settings
Definition: fc_client.h:126
mr_menu * menu_bar
Definition: fc_client.h:127
void center_on_tile(tile *tile, bool animate=true)
Centers the view on a tile.
Definition: view_map.cpp:197
auto minimap()
Retrieves the minimap widget.
Definition: minimap_panel.h:36
void paint(QPainter *painter, QPaintEvent *event)
Redraws visible map using stored pixmap.
Definition: minimap.cpp:188
void paintEvent(QPaintEvent *event) override
Paint event for minimap.
Definition: minimap.cpp:44
void update_menu() override
Called by close widget, cause widget has been hidden.
Definition: minimap.cpp:56
minimap_view(QWidget *parent)
Constructor for minimap.
Definition: minimap.cpp:35
int heightForWidth(int width) const override
Reimplements QWidget::heightForWidth.
Definition: minimap.cpp:165
QSize sizeHint() const override
Reimplements QWidget::sizeHint.
Definition: minimap.cpp:179
void mousePressEvent(QMouseEvent *event) override
Mouse Handler for minimap_view Left button - moves minimap Right button - recenters on some point For...
Definition: minimap.cpp:230
void resizeEvent(QResizeEvent *event) override
Called when minimap has been resized.
Definition: minimap.cpp:201
void draw_viewport(QPainter *painter)
Draws viewport on minimap.
Definition: minimap.cpp:81
void update_image()
Updates minimap's pixmap.
Definition: minimap.cpp:154
QAction * minimap_status
Definition: menu.h:185
::minimap_panel * minimap_panel
Definition: page_game.h:82
map_view * mapview_wdg
Definition: page_game.h:81
enum client_states client_state()
Return current client state.
@ C_S_RUNNING
Definition: client_main.h:43
enum event_type event
Definition: events.cpp:68
class fc_client * king()
Return fc_client instance.
Definition: gui_main.cpp:58
struct world wld
Definition: game.cpp:48
#define fc_assert_ret(condition)
Definition: log.h:112
struct tile * map_pos_to_tile(const struct civ_map *nmap, int map_x, int map_y)
Return the tile for the given cartesian (map) position.
Definition: map.cpp:391
#define current_topo_has_flag(flag)
Definition: map.h:37
#define NATURAL_HEIGHT
Definition: map.h:158
#define NATURAL_WIDTH
Definition: map.h:157
void update_minimap()
Return a canvas that is the overview window.
Definition: minimap.cpp:256
client_options * gui_options
Definition: options.cpp:74
void overview_to_map_pos(int *map_x, int *map_y, int overview_x, int overview_y)
Finds the map coordinates for a given overview (canvas) position.
int OVERVIEW_TILE_SIZE
void gui_to_natural_pos(const struct tileset *t, double *ntl_x, double *ntl_y, int gui_x, int gui_y)
Translate from gui to natural coordinate systems.
char * lines
Definition: packhand.cpp:131
pageGame * queen()
Return game instandce.
Definition: page_game.cpp:557
#define FC_WRAP(value, range)
Definition: shared.h:59
size_t size
Definition: specvec.h:64
struct overview overview
Definition: options.h:179
float minimap_height
Definition: fc_client.h:72
float minimap_width
Definition: fc_client.h:71
double map_y0
Definition: options.h:35
double map_x0
Definition: options.h:35
QPixmap * map
Definition: options.h:39
int width
Definition: options.h:36
int height
Definition: options.h:36
int height
float gui_x0
float gui_y0
int width
struct civ_map map
Definition: world_object.h:21
bool tileset_is_isometric(const struct tileset *t)
Return whether the current tileset is isometric.
Definition: tilespec.cpp:336
struct view mapview