10 #include "../stdafx.h"
11 #include "../window_gui.h"
12 #include "../window_func.h"
13 #include "../company_base.h"
14 #include "../company_gui.h"
15 #include "../timer/timer_game_tick.h"
16 #include "../timer/timer_game_calendar.h"
17 #include "../viewport_func.h"
18 #include "../zoom_func.h"
19 #include "../smallmap_gui.h"
20 #include "../core/geometry_func.hpp"
21 #include "../widgets/link_graph_legend_widget.h"
22 #include "../strings_func.h"
25 #include "table/strings.h"
27 #include "../safeguards.h"
35 0x0f, 0xd1, 0xd0, 0x57,
36 0x55, 0x53, 0xbf, 0xbd,
37 0xba, 0xb9, 0xb7, 0xb5
40 0x0f, 0xd1, 0xd0, 0x57,
41 0x55, 0x53, 0x96, 0x95,
42 0x94, 0x93, 0x92, 0x91
45 0x0f, 0x0b, 0x09, 0x07,
46 0x05, 0x03, 0xbf, 0xbd,
47 0xba, 0xb9, 0xb7, 0xb5
50 0x0f, 0x0b, 0x0a, 0x09,
51 0x08, 0x07, 0x06, 0x05,
52 0x04, 0x03, 0x02, 0x01
63 dpi->left = dpi->top = 0;
81 if (sta->rect.IsEmpty())
continue;
85 StationID from = sta->index;
94 ConstNode &from_node = lg[sta->goods[c].node];
95 supply += lg.
Monthly(from_node.supply);
96 for (
const Edge &edge : from_node.edges) {
97 StationID to = lg[edge.
dest_node].station;
107 if (stb->
rect.IsEmpty())
continue;
130 return pt.x > dpi->left - padding && pt.y > dpi->top - padding &&
131 pt.x < dpi->left + dpi->width + padding &&
132 pt.y < dpi->top + dpi->height + padding;
145 const int left = dpi->left - padding;
146 const int right = dpi->left + dpi->width + padding;
147 const int top = dpi->top - padding;
148 const int bottom = dpi->top + dpi->height + padding;
155 const uint8_t INSIDE = 0;
156 const uint8_t LEFT = 1;
157 const uint8_t RIGHT = 2;
158 const uint8_t BOTTOM = 4;
159 const uint8_t TOP = 8;
166 auto out_code = [&](
int x,
int y) -> uint8_t {
167 uint8_t out = INSIDE;
170 }
else if (x > right) {
175 }
else if (y > bottom) {
181 uint8_t c0 = out_code(x0, y0);
182 uint8_t c1 = out_code(x1, y1);
185 if (c0 == 0 || c1 == 0)
return true;
186 if ((c0 & c1) != 0)
return false;
189 x0 = x0 + (int)(((int64_t) (x1 - x0)) * ((int64_t) (top - y0)) / ((int64_t) (y1 - y0)));
191 }
else if (c0 & BOTTOM) {
192 x0 = x0 + (int)(((int64_t) (x1 - x0)) * ((int64_t) (bottom - y0)) / ((int64_t) (y1 - y0)));
194 }
else if (c0 & RIGHT) {
195 y0 = y0 + (int)(((int64_t) (y1 - y0)) * ((int64_t) (right - x0)) / ((int64_t) (x1 - x0)));
197 }
else if (c0 & LEFT) {
198 y0 = y0 + (int)(((int64_t) (y1 - y0)) * ((int64_t) (left - x0)) / ((int64_t) (x1 - x0)));
202 c0 = out_code(x0, y0);
248 cargo.
Usage() * 32 / (cargo.
capacity + 1) < std::max(new_usg, new_plan) * 32 / (new_cap + 1)) {
249 cargo.
cargo = new_cargo;
251 cargo.
usage = new_usg;
255 if (new_shared) cargo.
shared =
true;
282 for (
const auto &j : i.second) {
285 if (!this->
IsLinkVisible(pta, ptb, dpi, width + 2))
continue;
299 uint usage_or_plan = std::min(cargo.
capacity * 2 + 1, cargo.
Usage());
302 int dash = cargo.
shared ? width * 4 : 0;
307 if (
abs(pta.x - ptb.x) <
abs(pta.y - ptb.y)) {
308 int offset_x = (pta.y > ptb.y ? 1 : -1) * side * width;
309 GfxDrawLine(pta.x + offset_x, pta.y, ptb.x + offset_x, ptb.y, colour, width, dash);
311 int offset_y = (pta.x < ptb.x ? 1 : -1) * side * width;
312 GfxDrawLine(pta.x, pta.y + offset_y, ptb.x, ptb.y + offset_y, colour, width, dash);
315 GfxDrawLine(pta.x, pta.y, ptb.x, ptb.y,
_colour_gradient[COLOUR_GREY][1], width);
327 if (st ==
nullptr)
continue;
331 uint r = width * 2 + width * 2 * std::min(200U, i.second) / 200;
352 int w2 = size / 2 + size % 2;
354 GfxFillRect(x - w1, y - w1, x + w2, y + w2, colour);
358 GfxDrawLine(x - w1, y - w1, x + w2, y - w1, border_colour);
359 GfxDrawLine(x - w1, y + w2, x + w2, y + w2, border_colour);
360 GfxDrawLine(x - w1, y - w1, x - w1, y + w2, border_colour);
361 GfxDrawLine(x + w2, y - w1, x + w2, y + w2, border_colour);
364 bool LinkGraphOverlay::ShowTooltip(
Point pt, TooltipCloseCondition close_cond)
366 for (
auto i(this->
cached_links.crbegin()); i != this->cached_links.crend(); ++i) {
369 for (
auto j(i->second.crbegin()); j != i->second.crend(); ++j) {
371 if (i->first == j->first)
continue;
375 float dist =
std::abs((int64_t)(ptb.x - pta.x) * (int64_t)(pta.y - pt.y) - (int64_t)(pta.x - pt.x) * (int64_t)(ptb.y - pta.y)) /
376 std::sqrt((int64_t)(ptb.x - pta.x) * (int64_t)(ptb.x - pta.x) + (int64_t)(ptb.y - pta.y) * (int64_t)(ptb.y - pta.y));
377 const auto &link = j->second;
378 if (dist <= 4 && link.Usage() > 0 &&
379 pt.x + 2 >= std::min(pta.x, ptb.x) &&
380 pt.x - 2 <= std::max(pta.x, ptb.x) &&
381 pt.y + 2 >= std::min(pta.y, ptb.y) &&
382 pt.y - 2 <= std::max(pta.y, ptb.y)) {
383 static std::string tooltip_extension;
384 tooltip_extension.clear();
386 uint32_t back_time = 0;
389 const auto &back = k->second;
390 back_time = back.time;
391 if (back.Usage() > 0) {
394 SetDParam(2, back.Usage() * 100 / (back.capacity + 1));
395 tooltip_extension =
GetString(STR_LINKGRAPH_STATS_TOOLTIP_RETURN_EXTENSION);
399 const auto time = link.time ? back_time ? ((link.time + back_time) / 2) : link.time : back_time;
402 tooltip_extension +=
GetString(STR_LINKGRAPH_STATS_TOOLTIP_TIME_EXTENSION);
408 SetDParam(4, link.Usage() * 100 / (link.capacity + 1));
464 std::unique_ptr<NWidgetBase> MakeSaturationLegendLinkGraphGUI()
466 auto panel = std::make_unique<NWidgetVertical>(
NC_EQUALSIZE);
468 auto wid = std::make_unique<NWidgetBackground>(
WWT_PANEL, COLOUR_DARK_GREEN, i + WID_LGL_SATURATION_FIRST);
469 wid->SetMinimalSize(50, 0);
470 wid->SetMinimalTextLines(1, 0,
FS_SMALL);
472 wid->SetResize(0, 0);
473 panel->Add(std::move(wid));
478 std::unique_ptr<NWidgetBase> MakeCargoesLegendLinkGraphGUI()
481 static const uint ENTRIES_PER_COL = 5;
482 auto panel = std::make_unique<NWidgetHorizontal>(
NC_EQUALSIZE);
483 std::unique_ptr<NWidgetVertical> col =
nullptr;
485 for (uint i = 0; i < num_cargo; ++i) {
486 if (i % ENTRIES_PER_COL == 0) {
487 if (col !=
nullptr) panel->Add(std::move(col));
490 auto wid = std::make_unique<NWidgetBackground>(
WWT_PANEL, COLOUR_GREY, i + WID_LGL_CARGO_FIRST);
491 wid->SetMinimalSize(25, 0);
492 wid->SetMinimalTextLines(1, 0,
FS_SMALL);
494 wid->SetResize(0, 0);
495 col->Add(std::move(wid));
498 for (uint i = num_cargo; i <
Ceil(num_cargo, ENTRIES_PER_COL); ++i) {
499 auto spc = std::make_unique<NWidgetSpacer>(25, 0);
500 spc->SetMinimalTextLines(1, 0,
FS_SMALL);
502 spc->SetResize(0, 0);
503 col->Add(std::move(spc));
506 if (col !=
nullptr) panel->Add(std::move(col));
511 static constexpr
NWidgetPart _nested_linkgraph_legend_widgets[] = {
541 static_assert(WID_LGL_SATURATION_LAST - WID_LGL_SATURATION_FIRST ==
544 static WindowDesc _linkgraph_legend_desc(__FILE__, __LINE__,
545 WDP_AUTO,
"toolbar_linkgraph", 0, 0,
548 std::begin(_nested_linkgraph_legend_widgets), std::end(_nested_linkgraph_legend_widgets)
556 AllocateWindowDescFront<LinkGraphLegendWindow>(&_linkgraph_legend_desc, 0);
559 LinkGraphLegendWindow::LinkGraphLegendWindow(
WindowDesc *desc,
int window_number) :
Window(desc)
563 this->InitNested(window_number);
564 this->InvalidateData(0);
574 this->overlay = overlay;
575 CompanyMask companies = this->overlay->GetCompanyMask();
581 CargoTypes cargoes = this->overlay->GetCargoMask();
582 for (uint c = 0; c < this->num_cargo; c++) {
589 if (
IsInsideMM(widget, WID_LGL_SATURATION_FIRST, WID_LGL_SATURATION_LAST + 1)) {
591 if (widget == WID_LGL_SATURATION_FIRST) {
592 str = STR_LINKGRAPH_LEGEND_UNUSED;
593 }
else if (widget == WID_LGL_SATURATION_LAST) {
594 str = STR_LINKGRAPH_LEGEND_OVERLOADED;
595 }
else if (widget == (WID_LGL_SATURATION_LAST + WID_LGL_SATURATION_FIRST) / 2) {
596 str = STR_LINKGRAPH_LEGEND_SATURATED;
598 if (str != STR_NULL) {
600 dim.width += padding.width;
601 dim.height += padding.height;
602 *size =
maxdim(*size, dim);
605 if (
IsInsideMM(widget, WID_LGL_CARGO_FIRST, WID_LGL_CARGO_LAST + 1)) {
608 dim.width += padding.width;
609 dim.height += padding.height;
610 *size =
maxdim(*size, dim);
614 void LinkGraphLegendWindow::DrawWidget(
const Rect &r,
WidgetID widget)
const
617 if (
IsInsideMM(widget, WID_LGL_COMPANY_FIRST, WID_LGL_COMPANY_LAST + 1)) {
623 if (
IsInsideMM(widget, WID_LGL_SATURATION_FIRST, WID_LGL_SATURATION_LAST + 1)) {
627 if (widget == WID_LGL_SATURATION_FIRST) {
628 str = STR_LINKGRAPH_LEGEND_UNUSED;
629 }
else if (widget == WID_LGL_SATURATION_LAST) {
630 str = STR_LINKGRAPH_LEGEND_OVERLOADED;
631 }
else if (widget == (WID_LGL_SATURATION_LAST + WID_LGL_SATURATION_FIRST) / 2) {
632 str = STR_LINKGRAPH_LEGEND_SATURATED;
634 if (str != STR_NULL) {
638 if (
IsInsideMM(widget, WID_LGL_CARGO_FIRST, WID_LGL_CARGO_LAST + 1)) {
641 DrawString(br.left, br.right,
CenterBounds(br.top, br.bottom,
GetCharacterHeight(
FS_SMALL)), cargo->
abbrev,
GetContrastColour(cargo->legend_colour, 73),
SA_HOR_CENTER,
false,
FS_SMALL);
645 bool LinkGraphLegendWindow::OnTooltip([[maybe_unused]]
Point,
WidgetID widget, TooltipCloseCondition close_cond)
647 if (
IsInsideMM(widget, WID_LGL_COMPANY_FIRST, WID_LGL_COMPANY_LAST + 1)) {
649 GuiShowTooltips(
this, STR_LINKGRAPH_LEGEND_SELECT_COMPANIES, close_cond);
651 SetDParam(0, STR_LINKGRAPH_LEGEND_SELECT_COMPANIES);
653 GuiShowTooltips(
this, STR_LINKGRAPH_LEGEND_COMPANY_TOOLTIP, close_cond, 2);
657 if (
IsInsideMM(widget, WID_LGL_CARGO_FIRST, WID_LGL_CARGO_LAST + 1)) {
676 this->overlay->SetCompanyMask(mask);
685 for (uint c = 0; c < num_cargo; c++) {
689 this->overlay->SetCargoMask(mask);
692 void LinkGraphLegendWindow::OnClick([[maybe_unused]]
Point pt,
WidgetID widget, [[maybe_unused]]
int click_count)
695 if (
IsInsideMM(widget, WID_LGL_COMPANY_FIRST, WID_LGL_COMPANY_LAST + 1)) {
700 }
else if (widget == WID_LGL_COMPANIES_ALL || widget == WID_LGL_COMPANIES_NONE) {
707 }
else if (
IsInsideMM(widget, WID_LGL_CARGO_FIRST, WID_LGL_CARGO_LAST + 1)) {
710 }
else if (widget == WID_LGL_CARGOES_ALL || widget == WID_LGL_CARGOES_NONE) {
711 for (uint c = 0; c < this->num_cargo; c++) {