diff options
-rw-r--r-- | src/subway_map.cpp | 171 | ||||
-rw-r--r-- | src/subway_map.h | 15 |
2 files changed, 151 insertions, 35 deletions
diff --git a/src/subway_map.cpp b/src/subway_map.cpp index 5a4be4b..b02c616 100644 --- a/src/subway_map.cpp +++ b/src/subway_map.cpp | |||
@@ -12,11 +12,7 @@ | |||
12 | constexpr int AREA_ACTUAL_SIZE = 21; | 12 | constexpr int AREA_ACTUAL_SIZE = 21; |
13 | constexpr int OWL_ACTUAL_SIZE = 32; | 13 | constexpr int OWL_ACTUAL_SIZE = 32; |
14 | 14 | ||
15 | enum class ItemDrawType { | 15 | enum class ItemDrawType { kNone, kBox, kOwl }; |
16 | kNone, | ||
17 | kBox, | ||
18 | kOwl | ||
19 | }; | ||
20 | 16 | ||
21 | SubwayMap::SubwayMap(wxWindow *parent) : wxPanel(parent, wxID_ANY) { | 17 | SubwayMap::SubwayMap(wxWindow *parent) : wxPanel(parent, wxID_ANY) { |
22 | SetBackgroundStyle(wxBG_STYLE_PAINT); | 18 | SetBackgroundStyle(wxBG_STYLE_PAINT); |
@@ -50,8 +46,13 @@ SubwayMap::SubwayMap(wxWindow *parent) : wxPanel(parent, wxID_ANY) { | |||
50 | 46 | ||
51 | Redraw(); | 47 | Redraw(); |
52 | 48 | ||
49 | scroll_timer_ = new wxTimer(this); | ||
50 | |||
53 | Bind(wxEVT_PAINT, &SubwayMap::OnPaint, this); | 51 | Bind(wxEVT_PAINT, &SubwayMap::OnPaint, this); |
54 | Bind(wxEVT_MOTION, &SubwayMap::OnMouseMove, this); | 52 | Bind(wxEVT_MOTION, &SubwayMap::OnMouseMove, this); |
53 | Bind(wxEVT_MOUSEWHEEL, &SubwayMap::OnMouseScroll, this); | ||
54 | Bind(wxEVT_LEAVE_WINDOW, &SubwayMap::OnMouseLeave, this); | ||
55 | Bind(wxEVT_TIMER, &SubwayMap::OnTimer, this); | ||
55 | } | 56 | } |
56 | 57 | ||
57 | void SubwayMap::OnConnect() { | 58 | void SubwayMap::OnConnect() { |
@@ -67,7 +68,8 @@ void SubwayMap::OnConnect() { | |||
67 | tagged[tag].push_back(subway_item.id); | 68 | tagged[tag].push_back(subway_item.id); |
68 | } | 69 | } |
69 | 70 | ||
70 | if (!AP_IsSunwarpShuffle() && subway_item.sunwarp && subway_item.sunwarp->type != SubwaySunwarpType::kFinal) { | 71 | if (!AP_IsSunwarpShuffle() && subway_item.sunwarp && |
72 | subway_item.sunwarp->type != SubwaySunwarpType::kFinal) { | ||
71 | std::ostringstream tag; | 73 | std::ostringstream tag; |
72 | tag << "sunwarp" << subway_item.sunwarp->dots; | 74 | tag << "sunwarp" << subway_item.sunwarp->dots; |
73 | 75 | ||
@@ -108,7 +110,7 @@ void SubwayMap::UpdateIndicators() { | |||
108 | } | 110 | } |
109 | 111 | ||
110 | void SubwayMap::UpdateSunwarp(SubwaySunwarp from_sunwarp, | 112 | void SubwayMap::UpdateSunwarp(SubwaySunwarp from_sunwarp, |
111 | SubwaySunwarp to_sunwarp) { | 113 | SubwaySunwarp to_sunwarp) { |
112 | networks_.AddLink(GD_GetSubwayItemForSunwarp(from_sunwarp), | 114 | networks_.AddLink(GD_GetSubwayItemForSunwarp(from_sunwarp), |
113 | GD_GetSubwayItemForSunwarp(to_sunwarp)); | 115 | GD_GetSubwayItemForSunwarp(to_sunwarp)); |
114 | } | 116 | } |
@@ -119,7 +121,9 @@ void SubwayMap::OnPaint(wxPaintEvent &event) { | |||
119 | } | 121 | } |
120 | 122 | ||
121 | wxBufferedPaintDC dc(this); | 123 | wxBufferedPaintDC dc(this); |
122 | dc.DrawBitmap(rendered_, 0, 0); | 124 | dc.SetBackground(*wxWHITE_BRUSH); |
125 | dc.Clear(); | ||
126 | dc.DrawBitmap(rendered_, zoom_x_, zoom_y_); | ||
123 | 127 | ||
124 | if (hovered_item_) { | 128 | if (hovered_item_) { |
125 | const SubwayItem &subway_item = GD_GetSubwayItem(*hovered_item_); | 129 | const SubwayItem &subway_item = GD_GetSubwayItem(*hovered_item_); |
@@ -139,7 +143,7 @@ void SubwayMap::OnPaint(wxPaintEvent &event) { | |||
139 | col_width = item_extent.GetWidth(); | 143 | col_width = item_extent.GetWidth(); |
140 | } | 144 | } |
141 | } | 145 | } |
142 | 146 | ||
143 | int item_width = col_width + 10 + 32; | 147 | int item_width = col_width + 10 + 32; |
144 | int full_width = item_width + 20; | 148 | int full_width = item_width + 20; |
145 | 149 | ||
@@ -162,7 +166,7 @@ void SubwayMap::OnPaint(wxPaintEvent &event) { | |||
162 | 166 | ||
163 | int cur_height = 10; | 167 | int cur_height = 10; |
164 | 168 | ||
165 | for (const auto& [text, obtained] : report) { | 169 | for (const auto &[text, obtained] : report) { |
166 | wxBitmap *eye_ptr = obtained ? &unchecked_eye_ : &checked_eye_; | 170 | wxBitmap *eye_ptr = obtained ? &unchecked_eye_ : &checked_eye_; |
167 | 171 | ||
168 | dc.DrawBitmap(*eye_ptr, popup_pos + wxPoint{10, cur_height}); | 172 | dc.DrawBitmap(*eye_ptr, popup_pos + wxPoint{10, cur_height}); |
@@ -191,7 +195,7 @@ void SubwayMap::OnPaint(wxPaintEvent &event) { | |||
191 | {item1.x + AREA_ACTUAL_SIZE / 2, item1.y + AREA_ACTUAL_SIZE / 2}); | 195 | {item1.x + AREA_ACTUAL_SIZE / 2, item1.y + AREA_ACTUAL_SIZE / 2}); |
192 | wxPoint item2_pos = MapPosToRenderPos( | 196 | wxPoint item2_pos = MapPosToRenderPos( |
193 | {item2.x + AREA_ACTUAL_SIZE / 2, item2.y + AREA_ACTUAL_SIZE / 2}); | 197 | {item2.x + AREA_ACTUAL_SIZE / 2, item2.y + AREA_ACTUAL_SIZE / 2}); |
194 | 198 | ||
195 | int left = std::min(item1_pos.x, item2_pos.x); | 199 | int left = std::min(item1_pos.x, item2_pos.x); |
196 | int top = std::min(item1_pos.y, item2_pos.y); | 200 | int top = std::min(item1_pos.y, item2_pos.y); |
197 | int right = std::max(item1_pos.x, item2_pos.x); | 201 | int right = std::max(item1_pos.x, item2_pos.x); |
@@ -242,11 +246,11 @@ void SubwayMap::OnPaint(wxPaintEvent &event) { | |||
242 | } | 246 | } |
243 | 247 | ||
244 | dc.SetPen(*wxThePenList->FindOrCreatePen(*wxBLACK, 4)); | 248 | dc.SetPen(*wxThePenList->FindOrCreatePen(*wxBLACK, 4)); |
245 | dc.DrawEllipticArc(ellipse_x, ellipse_y, halfwidth * 2, halfheight * 2, | 249 | dc.DrawEllipticArc(ellipse_x, ellipse_y, halfwidth * 2, |
246 | start, end); | 250 | halfheight * 2, start, end); |
247 | dc.SetPen(*wxThePenList->FindOrCreatePen(*wxCYAN, 2)); | 251 | dc.SetPen(*wxThePenList->FindOrCreatePen(*wxCYAN, 2)); |
248 | dc.DrawEllipticArc(ellipse_x, ellipse_y, halfwidth * 2, halfheight * 2, | 252 | dc.DrawEllipticArc(ellipse_x, ellipse_y, halfwidth * 2, |
249 | start, end); | 253 | halfheight * 2, start, end); |
250 | } | 254 | } |
251 | } | 255 | } |
252 | } | 256 | } |
@@ -257,7 +261,7 @@ void SubwayMap::OnPaint(wxPaintEvent &event) { | |||
257 | 261 | ||
258 | void SubwayMap::OnMouseMove(wxMouseEvent &event) { | 262 | void SubwayMap::OnMouseMove(wxMouseEvent &event) { |
259 | wxPoint mouse_pos = RenderPosToMapPos(event.GetPosition()); | 263 | wxPoint mouse_pos = RenderPosToMapPos(event.GetPosition()); |
260 | 264 | ||
261 | std::vector<int> hovered = tree_->query( | 265 | std::vector<int> hovered = tree_->query( |
262 | {static_cast<float>(mouse_pos.x), static_cast<float>(mouse_pos.y), 2, 2}); | 266 | {static_cast<float>(mouse_pos.x), static_cast<float>(mouse_pos.y), 2, 2}); |
263 | std::optional<int> new_hovered_item; | 267 | std::optional<int> new_hovered_item; |
@@ -271,9 +275,65 @@ void SubwayMap::OnMouseMove(wxMouseEvent &event) { | |||
271 | Refresh(); | 275 | Refresh(); |
272 | } | 276 | } |
273 | 277 | ||
278 | int scroll_x; | ||
279 | int scroll_y; | ||
280 | if (event.GetPosition().x < GetSize().GetWidth() / 9) { | ||
281 | scroll_x = 20; | ||
282 | } else if (event.GetPosition().x < GetSize().GetWidth() / 6) { | ||
283 | scroll_x = 5; | ||
284 | } else if (event.GetPosition().x > 8 * GetSize().GetWidth() / 9) { | ||
285 | scroll_x = -20; | ||
286 | } else if (event.GetPosition().x > 5 * GetSize().GetWidth() / 6) { | ||
287 | scroll_x = -5; | ||
288 | } else { | ||
289 | scroll_x = 0; | ||
290 | } | ||
291 | if (event.GetPosition().y < GetSize().GetHeight() / 9) { | ||
292 | scroll_y = 20; | ||
293 | } else if (event.GetPosition().y < GetSize().GetHeight() / 6) { | ||
294 | scroll_y = 5; | ||
295 | } else if (event.GetPosition().y > 8 * GetSize().GetHeight() / 9) { | ||
296 | scroll_y = -20; | ||
297 | } else if (event.GetPosition().y > 5 * GetSize().GetHeight() / 6) { | ||
298 | scroll_y = -5; | ||
299 | } else { | ||
300 | scroll_y = 0; | ||
301 | } | ||
302 | |||
303 | SetScrollSpeed(scroll_x, scroll_y); | ||
304 | |||
274 | event.Skip(); | 305 | event.Skip(); |
275 | } | 306 | } |
276 | 307 | ||
308 | void SubwayMap::OnMouseScroll(wxMouseEvent &event) { | ||
309 | double new_zoom = zoom_; | ||
310 | if (event.GetWheelRotation() > 0) { | ||
311 | new_zoom = std::min(3.0, zoom_ + 0.25); | ||
312 | } else { | ||
313 | new_zoom = std::max(1.0, zoom_ - 0.25); | ||
314 | } | ||
315 | |||
316 | if (zoom_ != new_zoom) { | ||
317 | wxPoint map_pos = RenderPosToMapPos(event.GetPosition()); | ||
318 | zoom_ = new_zoom; | ||
319 | |||
320 | wxPoint virtual_pos = MapPosToVirtualPos(map_pos); | ||
321 | SetZoomPos(-(virtual_pos - event.GetPosition())); | ||
322 | |||
323 | Redraw(); | ||
324 | Refresh(); | ||
325 | } | ||
326 | |||
327 | event.Skip(); | ||
328 | } | ||
329 | |||
330 | void SubwayMap::OnMouseLeave(wxMouseEvent &event) { SetScrollSpeed(0, 0); } | ||
331 | |||
332 | void SubwayMap::OnTimer(wxTimerEvent &event) { | ||
333 | SetZoomPos({zoom_x_ + scroll_x_, zoom_y_ + scroll_y_}); | ||
334 | Refresh(); | ||
335 | } | ||
336 | |||
277 | void SubwayMap::Redraw() { | 337 | void SubwayMap::Redraw() { |
278 | wxSize panel_size = GetSize(); | 338 | wxSize panel_size = GetSize(); |
279 | wxSize image_size = map_image_.GetSize(); | 339 | wxSize image_size = map_image_.GetSize(); |
@@ -294,10 +354,10 @@ void SubwayMap::Redraw() { | |||
294 | render_x_ = (panel_size.GetWidth() - render_width_) / 2; | 354 | render_x_ = (panel_size.GetWidth() - render_width_) / 2; |
295 | } | 355 | } |
296 | 356 | ||
297 | rendered_ = wxBitmap( | 357 | rendered_ = wxBitmap(map_image_.Scale( |
298 | map_image_ | 358 | render_width_ * zoom_, render_height_ * zoom_, wxIMAGE_QUALITY_BILINEAR)); |
299 | .Scale(render_width_, render_height_, wxIMAGE_QUALITY_BILINEAR) | 359 | |
300 | .Size(panel_size, {render_x_, render_y_}, 255, 255, 255)); | 360 | SetZoomPos({zoom_x_, zoom_y_}); |
301 | 361 | ||
302 | wxMemoryDC dc; | 362 | wxMemoryDC dc; |
303 | dc.SelectObject(rendered_); | 363 | dc.SelectObject(rendered_); |
@@ -353,10 +413,10 @@ void SubwayMap::Redraw() { | |||
353 | } | 413 | } |
354 | } | 414 | } |
355 | 415 | ||
356 | wxPoint real_area_pos = MapPosToRenderPos({subway_item.x, subway_item.y}); | 416 | wxPoint real_area_pos = MapPosToVirtualPos({subway_item.x, subway_item.y}); |
357 | 417 | ||
358 | int real_area_size = | 418 | int real_area_size = |
359 | render_width_ * | 419 | render_width_ * zoom_ * |
360 | (draw_type == ItemDrawType::kOwl ? OWL_ACTUAL_SIZE : AREA_ACTUAL_SIZE) / | 420 | (draw_type == ItemDrawType::kOwl ? OWL_ACTUAL_SIZE : AREA_ACTUAL_SIZE) / |
361 | image_size.GetWidth(); | 421 | image_size.GetWidth(); |
362 | if (real_area_size == 0) { | 422 | if (real_area_size == 0) { |
@@ -366,32 +426,73 @@ void SubwayMap::Redraw() { | |||
366 | if (draw_type == ItemDrawType::kBox) { | 426 | if (draw_type == ItemDrawType::kBox) { |
367 | dc.SetPen(*wxThePenList->FindOrCreatePen(*wxBLACK, 1)); | 427 | dc.SetPen(*wxThePenList->FindOrCreatePen(*wxBLACK, 1)); |
368 | dc.SetBrush(*brush_color); | 428 | dc.SetBrush(*brush_color); |
369 | dc.DrawRectangle(real_area_pos, | 429 | dc.DrawRectangle(real_area_pos, {real_area_size, real_area_size}); |
370 | {real_area_size, real_area_size}); | ||
371 | } else if (draw_type == ItemDrawType::kOwl) { | 430 | } else if (draw_type == ItemDrawType::kOwl) { |
372 | wxBitmap owl_bitmap = wxBitmap( | 431 | wxBitmap owl_bitmap = wxBitmap(owl_image_.Scale( |
373 | owl_image_.Scale(real_area_size, real_area_size, | 432 | real_area_size, real_area_size, wxIMAGE_QUALITY_BILINEAR)); |
374 | wxIMAGE_QUALITY_BILINEAR)); | ||
375 | dc.DrawBitmap(owl_bitmap, real_area_pos); | 433 | dc.DrawBitmap(owl_bitmap, real_area_pos); |
376 | } | 434 | } |
377 | } | 435 | } |
378 | } | 436 | } |
379 | 437 | ||
380 | |||
381 | wxPoint SubwayMap::MapPosToRenderPos(wxPoint pos) const { | 438 | wxPoint SubwayMap::MapPosToRenderPos(wxPoint pos) const { |
382 | return {pos.x * render_width_ / map_image_.GetSize().GetWidth() + render_x_, | 439 | return {static_cast<int>(pos.x * render_width_ * zoom_ / |
383 | pos.y * render_width_ / map_image_.GetSize().GetWidth() + render_y_}; | 440 | map_image_.GetSize().GetWidth() + |
441 | zoom_x_), | ||
442 | static_cast<int>(pos.y * render_width_ * zoom_ / | ||
443 | map_image_.GetSize().GetWidth() + | ||
444 | zoom_y_)}; | ||
445 | } | ||
446 | |||
447 | wxPoint SubwayMap::MapPosToVirtualPos(wxPoint pos) const { | ||
448 | return {static_cast<int>(pos.x * render_width_ * zoom_ / | ||
449 | map_image_.GetSize().GetWidth()), | ||
450 | static_cast<int>(pos.y * render_width_ * zoom_ / | ||
451 | map_image_.GetSize().GetWidth())}; | ||
384 | } | 452 | } |
385 | 453 | ||
386 | wxPoint SubwayMap::RenderPosToMapPos(wxPoint pos) const { | 454 | wxPoint SubwayMap::RenderPosToMapPos(wxPoint pos) const { |
387 | return { | 455 | return { |
388 | std::clamp((pos.x - render_x_) * map_image_.GetWidth() / render_width_, 0, | 456 | std::clamp(static_cast<int>((pos.x - zoom_x_) * map_image_.GetWidth() / |
389 | map_image_.GetWidth() - 1), | 457 | render_width_ / zoom_), |
390 | std::clamp((pos.y - render_y_) * map_image_.GetWidth() / render_width_, 0, | 458 | 0, map_image_.GetWidth() - 1), |
391 | map_image_.GetHeight() - 1)}; | 459 | std::clamp(static_cast<int>((pos.y - zoom_y_) * map_image_.GetWidth() / |
460 | render_width_ / zoom_), | ||
461 | 0, map_image_.GetHeight() - 1)}; | ||
462 | } | ||
463 | |||
464 | void SubwayMap::SetZoomPos(wxPoint pos) { | ||
465 | if (render_width_ * zoom_ <= GetSize().GetWidth()) { | ||
466 | zoom_x_ = (GetSize().GetWidth() - render_width_ * zoom_) / 2; | ||
467 | } else { | ||
468 | zoom_x_ = std::clamp( | ||
469 | pos.x, GetSize().GetWidth() - static_cast<int>(render_width_ * zoom_), | ||
470 | 0); | ||
471 | } | ||
472 | if (render_height_ * zoom_ <= GetSize().GetHeight()) { | ||
473 | zoom_y_ = (GetSize().GetHeight() - render_height_ * zoom_) / 2; | ||
474 | } else { | ||
475 | zoom_y_ = std::clamp( | ||
476 | pos.y, GetSize().GetHeight() - static_cast<int>(render_height_ * zoom_), | ||
477 | 0); | ||
478 | } | ||
479 | } | ||
480 | |||
481 | void SubwayMap::SetScrollSpeed(int scroll_x, int scroll_y) { | ||
482 | bool should_timer = (scroll_x != 0 || scroll_y != 0); | ||
483 | if (should_timer != scroll_timer_->IsRunning()) { | ||
484 | if (should_timer) { | ||
485 | scroll_timer_->Start(1000 / 60); | ||
486 | } else { | ||
487 | scroll_timer_->Stop(); | ||
488 | } | ||
489 | } | ||
490 | |||
491 | scroll_x_ = scroll_x; | ||
492 | scroll_y_ = scroll_y; | ||
392 | } | 493 | } |
393 | 494 | ||
394 | quadtree::Box<float> SubwayMap::GetItemBox::operator()(const int& id) const { | 495 | quadtree::Box<float> SubwayMap::GetItemBox::operator()(const int &id) const { |
395 | const SubwayItem &subway_item = GD_GetSubwayItem(id); | 496 | const SubwayItem &subway_item = GD_GetSubwayItem(id); |
396 | return {static_cast<float>(subway_item.x), static_cast<float>(subway_item.y), | 497 | return {static_cast<float>(subway_item.x), static_cast<float>(subway_item.y), |
397 | AREA_ACTUAL_SIZE, AREA_ACTUAL_SIZE}; | 498 | AREA_ACTUAL_SIZE, AREA_ACTUAL_SIZE}; |
diff --git a/src/subway_map.h b/src/subway_map.h index 0d26d0b..9b0b43f 100644 --- a/src/subway_map.h +++ b/src/subway_map.h | |||
@@ -29,12 +29,19 @@ class SubwayMap : public wxPanel { | |||
29 | private: | 29 | private: |
30 | void OnPaint(wxPaintEvent &event); | 30 | void OnPaint(wxPaintEvent &event); |
31 | void OnMouseMove(wxMouseEvent &event); | 31 | void OnMouseMove(wxMouseEvent &event); |
32 | void OnMouseScroll(wxMouseEvent &event); | ||
33 | void OnMouseLeave(wxMouseEvent &event); | ||
34 | void OnTimer(wxTimerEvent &event); | ||
32 | 35 | ||
33 | void Redraw(); | 36 | void Redraw(); |
34 | 37 | ||
35 | wxPoint MapPosToRenderPos(wxPoint pos) const; | 38 | wxPoint MapPosToRenderPos(wxPoint pos) const; |
39 | wxPoint MapPosToVirtualPos(wxPoint pos) const; | ||
36 | wxPoint RenderPosToMapPos(wxPoint pos) const; | 40 | wxPoint RenderPosToMapPos(wxPoint pos) const; |
37 | 41 | ||
42 | void SetZoomPos(wxPoint pos); | ||
43 | void SetScrollSpeed(int scroll_x, int scroll_y); | ||
44 | |||
38 | wxImage map_image_; | 45 | wxImage map_image_; |
39 | wxImage owl_image_; | 46 | wxImage owl_image_; |
40 | wxBitmap unchecked_eye_; | 47 | wxBitmap unchecked_eye_; |
@@ -46,6 +53,14 @@ class SubwayMap : public wxPanel { | |||
46 | int render_width_ = 0; | 53 | int render_width_ = 0; |
47 | int render_height_ = 0; | 54 | int render_height_ = 0; |
48 | 55 | ||
56 | double zoom_ = 1.0; | ||
57 | int zoom_x_ = 0; // in render space | ||
58 | int zoom_y_ = 0; | ||
59 | |||
60 | wxTimer* scroll_timer_; | ||
61 | int scroll_x_ = 0; | ||
62 | int scroll_y_ = 0; | ||
63 | |||
49 | struct GetItemBox { | 64 | struct GetItemBox { |
50 | quadtree::Box<float> operator()(const int &id) const; | 65 | quadtree::Box<float> operator()(const int &id) const; |
51 | }; | 66 | }; |