about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--src/subway_map.cpp171
-rw-r--r--src/subway_map.h15
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 @@
12constexpr int AREA_ACTUAL_SIZE = 21; 12constexpr int AREA_ACTUAL_SIZE = 21;
13constexpr int OWL_ACTUAL_SIZE = 32; 13constexpr int OWL_ACTUAL_SIZE = 32;
14 14
15enum class ItemDrawType { 15enum class ItemDrawType { kNone, kBox, kOwl };
16 kNone,
17 kBox,
18 kOwl
19};
20 16
21SubwayMap::SubwayMap(wxWindow *parent) : wxPanel(parent, wxID_ANY) { 17SubwayMap::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
57void SubwayMap::OnConnect() { 58void 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
110void SubwayMap::UpdateSunwarp(SubwaySunwarp from_sunwarp, 112void 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
258void SubwayMap::OnMouseMove(wxMouseEvent &event) { 262void 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
308void 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
330void SubwayMap::OnMouseLeave(wxMouseEvent &event) { SetScrollSpeed(0, 0); }
331
332void SubwayMap::OnTimer(wxTimerEvent &event) {
333 SetZoomPos({zoom_x_ + scroll_x_, zoom_y_ + scroll_y_});
334 Refresh();
335}
336
277void SubwayMap::Redraw() { 337void 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
381wxPoint SubwayMap::MapPosToRenderPos(wxPoint pos) const { 438wxPoint 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
447wxPoint 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
386wxPoint SubwayMap::RenderPosToMapPos(wxPoint pos) const { 454wxPoint 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
464void 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
481void 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
394quadtree::Box<float> SubwayMap::GetItemBox::operator()(const int& id) const { 495quadtree::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 };