OpenTTD Source 20241224-master-gf74b0cf984
linkgraph_gui.cpp
Go to the documentation of this file.
1/*
2 * This file is part of OpenTTD.
3 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
6 */
7
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"
23#include "linkgraph_gui.h"
24
25#include "table/strings.h"
26
27#include "../safeguards.h"
28
33const uint8_t LinkGraphOverlay::LINK_COLOURS[][12] = {
34{
35 0x0f, 0xd1, 0xd0, 0x57,
36 0x55, 0x53, 0xbf, 0xbd,
37 0xba, 0xb9, 0xb7, 0xb5
38},
39{
40 0x0f, 0xd1, 0xd0, 0x57,
41 0x55, 0x53, 0x96, 0x95,
42 0x94, 0x93, 0x92, 0x91
43},
44{
45 0x0f, 0x0b, 0x09, 0x07,
46 0x05, 0x03, 0xbf, 0xbd,
47 0xba, 0xb9, 0xb7, 0xb5
48},
49{
50 0x0f, 0x0b, 0x0a, 0x09,
51 0x08, 0x07, 0x06, 0x05,
52 0x04, 0x03, 0x02, 0x01
53}
54};
55
61{
62 const NWidgetBase *wi = this->window->GetWidget<NWidgetBase>(this->widget_id);
63 dpi->left = dpi->top = 0;
64 dpi->width = wi->current_x;
65 dpi->height = wi->current_y;
66}
67
72{
73 this->cached_links.clear();
74 this->cached_stations.clear();
75 if (this->company_mask == 0) return;
76
77 DrawPixelInfo dpi;
78 this->GetWidgetDpi(&dpi);
79
80 for (const Station *sta : Station::Iterate()) {
81 if (sta->rect.IsEmpty()) continue;
82
83 Point pta = this->GetStationMiddle(sta);
84
85 StationID from = sta->index;
86 StationLinkMap &seen_links = this->cached_links[from];
87
88 uint supply = 0;
89 for (CargoID c : SetCargoBitIterator(this->cargo_mask)) {
90 if (!CargoSpec::Get(c)->IsValid()) continue;
91 if (!LinkGraph::IsValidID(sta->goods[c].link_graph)) continue;
92 const LinkGraph &lg = *LinkGraph::Get(sta->goods[c].link_graph);
93
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;
98 assert(from != to);
99 if (!Station::IsValidID(to) || seen_links.find(to) != seen_links.end()) {
100 continue;
101 }
102 const Station *stb = Station::Get(to);
103 assert(sta != stb);
104
105 /* Show links between stations of selected companies or "neutral" ones like oilrigs. */
106 if (stb->owner != OWNER_NONE && sta->owner != OWNER_NONE && !HasBit(this->company_mask, stb->owner)) continue;
107 if (stb->rect.IsEmpty()) continue;
108
109 if (!this->IsLinkVisible(pta, this->GetStationMiddle(stb), &dpi)) continue;
110
111 this->AddLinks(sta, stb);
112 seen_links[to]; // make sure it is created and marked as seen
113 }
114 }
115 if (this->IsPointVisible(pta, &dpi)) {
116 this->cached_stations.emplace_back(from, supply);
117 }
118 }
119}
120
128inline bool LinkGraphOverlay::IsPointVisible(Point pt, const DrawPixelInfo *dpi, int padding) const
129{
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;
133}
134
143inline bool LinkGraphOverlay::IsLinkVisible(Point pta, Point ptb, const DrawPixelInfo *dpi, int padding) const
144{
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;
149
150 /*
151 * This method is an implementation of the Cohen-Sutherland line-clipping algorithm.
152 * See: https://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm
153 */
154
155 const uint8_t INSIDE = 0; // 0000
156 const uint8_t LEFT = 1; // 0001
157 const uint8_t RIGHT = 2; // 0010
158 const uint8_t BOTTOM = 4; // 0100
159 const uint8_t TOP = 8; // 1000
160
161 int x0 = pta.x;
162 int y0 = pta.y;
163 int x1 = ptb.x;
164 int y1 = ptb.y;
165
166 auto out_code = [&](int x, int y) -> uint8_t {
167 uint8_t out = INSIDE;
168 if (x < left) {
169 out |= LEFT;
170 } else if (x > right) {
171 out |= RIGHT;
172 }
173 if (y < top) {
174 out |= TOP;
175 } else if (y > bottom) {
176 out |= BOTTOM;
177 }
178 return out;
179 };
180
181 uint8_t c0 = out_code(x0, y0);
182 uint8_t c1 = out_code(x1, y1);
183
184 while (true) {
185 if (c0 == 0 || c1 == 0) return true;
186 if ((c0 & c1) != 0) return false;
187
188 if (c0 & TOP) { // point 0 is above the clip window
189 x0 = x0 + (int)(((int64_t) (x1 - x0)) * ((int64_t) (top - y0)) / ((int64_t) (y1 - y0)));
190 y0 = top;
191 } else if (c0 & BOTTOM) { // point 0 is below the clip window
192 x0 = x0 + (int)(((int64_t) (x1 - x0)) * ((int64_t) (bottom - y0)) / ((int64_t) (y1 - y0)));
193 y0 = bottom;
194 } else if (c0 & RIGHT) { // point 0 is to the right of clip window
195 y0 = y0 + (int)(((int64_t) (y1 - y0)) * ((int64_t) (right - x0)) / ((int64_t) (x1 - x0)));
196 x0 = right;
197 } else if (c0 & LEFT) { // point 0 is to the left of clip window
198 y0 = y0 + (int)(((int64_t) (y1 - y0)) * ((int64_t) (left - x0)) / ((int64_t) (x1 - x0)));
199 x0 = left;
200 }
201
202 c0 = out_code(x0, y0);
203 }
204
205 NOT_REACHED();
206}
207
213void LinkGraphOverlay::AddLinks(const Station *from, const Station *to)
214{
215 for (CargoID c : SetCargoBitIterator(this->cargo_mask)) {
216 if (!CargoSpec::Get(c)->IsValid()) continue;
217 const GoodsEntry &ge = from->goods[c];
219 ge.link_graph != to->goods[c].link_graph) {
220 continue;
221 }
222 const LinkGraph &lg = *LinkGraph::Get(ge.link_graph);
223 if (lg[ge.node].HasEdgeTo(to->goods[c].node)) {
224 ConstEdge &edge = lg[ge.node][to->goods[c].node];
225 this->AddStats(c, lg.Monthly(edge.capacity), lg.Monthly(edge.usage),
226 ge.flows.GetFlowVia(to->index),
228 from->owner == OWNER_NONE || to->owner == OWNER_NONE,
229 this->cached_links[from->index][to->index]);
230 }
231 }
232}
233
244/* static */ void LinkGraphOverlay::AddStats(CargoID new_cargo, uint new_cap, uint new_usg, uint new_plan, uint32_t time, bool new_shared, LinkProperties &cargo)
245{
246 /* multiply the numbers by 32 in order to avoid comparing to 0 too often. */
247 if (cargo.capacity == 0 ||
248 cargo.Usage() * 32 / (cargo.capacity + 1) < std::max(new_usg, new_plan) * 32 / (new_cap + 1)) {
249 cargo.cargo = new_cargo;
250 cargo.capacity = new_cap;
251 cargo.usage = new_usg;
252 cargo.planned = new_plan;
253 cargo.time = time;
254 }
255 if (new_shared) cargo.shared = true;
256}
257
263{
264 if (this->dirty) {
265 this->RebuildCache();
266 this->dirty = false;
267 }
268 this->DrawLinks(dpi);
269 this->DrawStationDots(dpi);
270}
271
277{
278 int width = ScaleGUITrad(this->scale);
279 for (const auto &i : this->cached_links) {
280 if (!Station::IsValidID(i.first)) continue;
281 Point pta = this->GetStationMiddle(Station::Get(i.first));
282 for (const auto &j : i.second) {
283 if (!Station::IsValidID(j.first)) continue;
284 Point ptb = this->GetStationMiddle(Station::Get(j.first));
285 if (!this->IsLinkVisible(pta, ptb, dpi, width + 2)) continue;
286 this->DrawContent(pta, ptb, j.second);
287 }
288 }
289}
290
297void LinkGraphOverlay::DrawContent(Point pta, Point ptb, const LinkProperties &cargo) const
298{
299 uint usage_or_plan = std::min(cargo.capacity * 2 + 1, cargo.Usage());
301 int width = ScaleGUITrad(this->scale);
302 int dash = cargo.shared ? width * 4 : 0;
303
304 /* Move line a bit 90° against its dominant direction to prevent it from
305 * being hidden below the grey line. */
306 int side = _settings_game.vehicle.road_side ? 1 : -1;
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);
310 } else {
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);
313 }
314
315 GfxDrawLine(pta.x, pta.y, ptb.x, ptb.y, GetColourGradient(COLOUR_GREY, SHADE_DARKEST), width);
316}
317
323{
324 int width = ScaleGUITrad(this->scale);
325 for (const auto &i : this->cached_stations) {
326 const Station *st = Station::GetIfValid(i.first);
327 if (st == nullptr) continue;
328 Point pt = this->GetStationMiddle(st);
329 if (!this->IsPointVisible(pt, dpi, 3 * width)) continue;
330
331 uint r = width * 2 + width * 2 * std::min(200U, i.second) / 200;
332
333 LinkGraphOverlay::DrawVertex(pt.x, pt.y, r,
335 Company::Get(st->owner)->colour : COLOUR_GREY, SHADE_LIGHT),
336 GetColourGradient(COLOUR_GREY, SHADE_DARKEST));
337 }
338}
339
348/* static */ void LinkGraphOverlay::DrawVertex(int x, int y, int size, int colour, int border_colour)
349{
350 size--;
351 int w1 = size / 2;
352 int w2 = size / 2 + size % 2;
353 int borderwidth = ScaleGUITrad(1);
354
355 GfxFillRect(x - w1 - borderwidth, y - w1 - borderwidth, x + w2 + borderwidth, y + w2 + borderwidth, border_colour);
356 GfxFillRect(x - w1, y - w1, x + w2, y + w2, colour);
357}
358
359bool LinkGraphOverlay::ShowTooltip(Point pt, TooltipCloseCondition close_cond)
360{
361 for (auto i(this->cached_links.crbegin()); i != this->cached_links.crend(); ++i) {
362 if (!Station::IsValidID(i->first)) continue;
363 Point pta = this->GetStationMiddle(Station::Get(i->first));
364 for (auto j(i->second.crbegin()); j != i->second.crend(); ++j) {
365 if (!Station::IsValidID(j->first)) continue;
366 if (i->first == j->first) continue;
367
368 /* Check the distance from the cursor to the line defined by the two stations. */
369 Point ptb = this->GetStationMiddle(Station::Get(j->first));
370 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)) /
371 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));
372 const auto &link = j->second;
373 if (dist <= 4 && link.Usage() > 0 &&
374 pt.x + 2 >= std::min(pta.x, ptb.x) &&
375 pt.x - 2 <= std::max(pta.x, ptb.x) &&
376 pt.y + 2 >= std::min(pta.y, ptb.y) &&
377 pt.y - 2 <= std::max(pta.y, ptb.y)) {
378 static std::string tooltip_extension;
379 tooltip_extension.clear();
380 /* Fill buf with more information if this is a bidirectional link. */
381 uint32_t back_time = 0;
382 auto k = this->cached_links[j->first].find(i->first);
383 if (k != this->cached_links[j->first].end()) {
384 const auto &back = k->second;
385 back_time = back.time;
386 if (back.Usage() > 0) {
387 SetDParam(0, back.cargo);
388 SetDParam(1, back.Usage());
389 SetDParam(2, back.Usage() * 100 / (back.capacity + 1));
390 tooltip_extension = GetString(STR_LINKGRAPH_STATS_TOOLTIP_RETURN_EXTENSION);
391 }
392 }
393 /* Add information about the travel time if known. */
394 const auto time = link.time ? back_time ? ((link.time + back_time) / 2) : link.time : back_time;
395 if (time > 0) {
396 SetDParam(0, time);
397 AppendStringInPlace(tooltip_extension, STR_LINKGRAPH_STATS_TOOLTIP_TIME_EXTENSION);
398 }
399 SetDParam(0, link.cargo);
400 SetDParam(1, link.Usage());
401 SetDParam(2, i->first);
402 SetDParam(3, j->first);
403 SetDParam(4, link.Usage() * 100 / (link.capacity + 1));
404 SetDParamStr(5, tooltip_extension);
406 TimerGameEconomy::UsingWallclockUnits() ? STR_LINKGRAPH_STATS_TOOLTIP_MINUTE : STR_LINKGRAPH_STATS_TOOLTIP_MONTH,
407 close_cond, 7);
408 return true;
409 }
410 }
411 }
412 GuiShowTooltips(this->window, STR_NULL, close_cond);
413 return false;
414}
415
422{
423 if (this->window->viewport != nullptr) {
424 return GetViewportStationMiddle(this->window->viewport, st);
425 } else {
426 /* assume this is a smallmap */
427 return GetSmallMapStationMiddle(this->window, st);
428 }
429}
430
435void LinkGraphOverlay::SetCargoMask(CargoTypes cargo_mask)
436{
437 this->cargo_mask = cargo_mask;
438 this->RebuildCache();
439 this->window->GetWidget<NWidgetBase>(this->widget_id)->SetDirty(this->window);
440}
441
446void LinkGraphOverlay::SetCompanyMask(CompanyMask company_mask)
447{
448 this->company_mask = company_mask;
449 this->RebuildCache();
450 this->window->GetWidget<NWidgetBase>(this->widget_id)->SetDirty(this->window);
451}
452
454std::unique_ptr<NWidgetBase> MakeCompanyButtonRowsLinkGraphGUI()
455{
456 return MakeCompanyButtonRows(WID_LGL_COMPANY_FIRST, WID_LGL_COMPANY_LAST, COLOUR_GREY, 3, STR_NULL);
457}
458
459std::unique_ptr<NWidgetBase> MakeSaturationLegendLinkGraphGUI()
460{
461 auto panel = std::make_unique<NWidgetVertical>(NC_EQUALSIZE);
462 for (uint i = 0; i < lengthof(LinkGraphOverlay::LINK_COLOURS[0]); ++i) {
463 auto wid = std::make_unique<NWidgetBackground>(WWT_PANEL, COLOUR_DARK_GREEN, i + WID_LGL_SATURATION_FIRST);
464 wid->SetMinimalSize(50, 0);
465 wid->SetMinimalTextLines(1, 0, FS_SMALL);
466 wid->SetFill(1, 1);
467 wid->SetResize(0, 0);
468 panel->Add(std::move(wid));
469 }
470 return panel;
471}
472
473std::unique_ptr<NWidgetBase> MakeCargoesLegendLinkGraphGUI()
474{
475 uint num_cargo = static_cast<uint>(_sorted_cargo_specs.size());
476 static const uint ENTRIES_PER_COL = 5;
477 auto panel = std::make_unique<NWidgetHorizontal>(NC_EQUALSIZE);
478 std::unique_ptr<NWidgetVertical> col = nullptr;
479
480 for (uint i = 0; i < num_cargo; ++i) {
481 if (i % ENTRIES_PER_COL == 0) {
482 if (col != nullptr) panel->Add(std::move(col));
483 col = std::make_unique<NWidgetVertical>(NC_EQUALSIZE);
484 }
485 auto wid = std::make_unique<NWidgetBackground>(WWT_PANEL, COLOUR_GREY, i + WID_LGL_CARGO_FIRST);
486 wid->SetMinimalSize(25, 0);
487 wid->SetMinimalTextLines(1, 0, FS_SMALL);
488 wid->SetFill(1, 1);
489 wid->SetResize(0, 0);
490 col->Add(std::move(wid));
491 }
492 /* Fill up last row */
493 for (uint i = num_cargo; i < Ceil(num_cargo, ENTRIES_PER_COL); ++i) {
494 auto spc = std::make_unique<NWidgetSpacer>(25, 0);
495 spc->SetMinimalTextLines(1, 0, FS_SMALL);
496 spc->SetFill(1, 1);
497 spc->SetResize(0, 0);
498 col->Add(std::move(spc));
499 }
500 /* If there are no cargo specs defined, then col won't have been created so don't add it. */
501 if (col != nullptr) panel->Add(std::move(col));
502 return panel;
503}
504
505
506static constexpr NWidgetPart _nested_linkgraph_legend_widgets[] = {
508 NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN),
509 NWidget(WWT_CAPTION, COLOUR_DARK_GREEN, WID_LGL_CAPTION), SetDataTip(STR_LINKGRAPH_LEGEND_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
510 NWidget(WWT_SHADEBOX, COLOUR_DARK_GREEN),
511 NWidget(WWT_STICKYBOX, COLOUR_DARK_GREEN),
512 EndContainer(),
513 NWidget(WWT_PANEL, COLOUR_DARK_GREEN),
515 NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_LGL_SATURATION),
516 NWidgetFunction(MakeSaturationLegendLinkGraphGUI),
517 EndContainer(),
518 NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_LGL_COMPANIES),
521 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_LGL_COMPANIES_ALL), SetDataTip(STR_LINKGRAPH_LEGEND_ALL, STR_NULL),
522 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_LGL_COMPANIES_NONE), SetDataTip(STR_LINKGRAPH_LEGEND_NONE, STR_NULL),
523 EndContainer(),
524 EndContainer(),
525 NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_LGL_CARGOES),
527 NWidgetFunction(MakeCargoesLegendLinkGraphGUI),
528 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_LGL_CARGOES_ALL), SetDataTip(STR_LINKGRAPH_LEGEND_ALL, STR_NULL),
529 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_LGL_CARGOES_NONE), SetDataTip(STR_LINKGRAPH_LEGEND_NONE, STR_NULL),
530 EndContainer(),
531 EndContainer(),
532 EndContainer(),
534};
535
536static_assert(WID_LGL_SATURATION_LAST - WID_LGL_SATURATION_FIRST ==
538
539static WindowDesc _linkgraph_legend_desc(
540 WDP_AUTO, "toolbar_linkgraph", 0, 0,
542 0,
543 _nested_linkgraph_legend_widgets
544);
545
550{
551 AllocateWindowDescFront<LinkGraphLegendWindow>(_linkgraph_legend_desc, 0);
552}
553
554LinkGraphLegendWindow::LinkGraphLegendWindow(WindowDesc &desc, int window_number) : Window(desc)
555{
556 this->num_cargo = _sorted_cargo_specs.size();
557
558 this->InitNested(window_number);
559 this->InvalidateData(0);
560 this->SetOverlay(GetMainWindow()->viewport->overlay);
561}
562
567void LinkGraphLegendWindow::SetOverlay(std::shared_ptr<LinkGraphOverlay> overlay)
568{
569 this->overlay = overlay;
570 CompanyMask companies = this->overlay->GetCompanyMask();
571 for (uint c = 0; c < MAX_COMPANIES; c++) {
572 if (!this->IsWidgetDisabled(WID_LGL_COMPANY_FIRST + c)) {
573 this->SetWidgetLoweredState(WID_LGL_COMPANY_FIRST + c, HasBit(companies, c));
574 }
575 }
576 CargoTypes cargoes = this->overlay->GetCargoMask();
577 for (uint c = 0; c < this->num_cargo; c++) {
578 this->SetWidgetLoweredState(WID_LGL_CARGO_FIRST + c, HasBit(cargoes, _sorted_cargo_specs[c]->Index()));
579 }
580}
581
582void LinkGraphLegendWindow::UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize)
583{
584 if (IsInsideMM(widget, WID_LGL_SATURATION_FIRST, WID_LGL_SATURATION_LAST + 1)) {
585 StringID str = STR_NULL;
586 if (widget == WID_LGL_SATURATION_FIRST) {
588 } else if (widget == WID_LGL_SATURATION_LAST) {
590 } else if (widget == (WID_LGL_SATURATION_LAST + WID_LGL_SATURATION_FIRST) / 2) {
592 }
593 if (str != STR_NULL) {
595 dim.width += padding.width;
596 dim.height += padding.height;
597 size = maxdim(size, dim);
598 }
599 }
600 if (IsInsideMM(widget, WID_LGL_CARGO_FIRST, WID_LGL_CARGO_LAST + 1)) {
601 const CargoSpec *cargo = _sorted_cargo_specs[widget - WID_LGL_CARGO_FIRST];
603 dim.width += padding.width;
604 dim.height += padding.height;
605 size = maxdim(size, dim);
606 }
607}
608
610{
612 if (IsInsideMM(widget, WID_LGL_COMPANY_FIRST, WID_LGL_COMPANY_LAST + 1)) {
613 if (this->IsWidgetDisabled(widget)) return;
614 CompanyID cid = (CompanyID)(widget - WID_LGL_COMPANY_FIRST);
615 Dimension sprite_size = GetSpriteSize(SPR_COMPANY_ICON);
616 DrawCompanyIcon(cid, CenterBounds(br.left, br.right, sprite_size.width), CenterBounds(br.top, br.bottom, sprite_size.height));
617 }
618 if (IsInsideMM(widget, WID_LGL_SATURATION_FIRST, WID_LGL_SATURATION_LAST + 1)) {
619 uint8_t colour = LinkGraphOverlay::LINK_COLOURS[_settings_client.gui.linkgraph_colours][widget - WID_LGL_SATURATION_FIRST];
620 GfxFillRect(br, colour);
621 StringID str = STR_NULL;
622 if (widget == WID_LGL_SATURATION_FIRST) {
624 } else if (widget == WID_LGL_SATURATION_LAST) {
626 } else if (widget == (WID_LGL_SATURATION_LAST + WID_LGL_SATURATION_FIRST) / 2) {
628 }
629 if (str != STR_NULL) {
630 DrawString(br.left, br.right, CenterBounds(br.top, br.bottom, GetCharacterHeight(FS_SMALL)), str, GetContrastColour(colour) | TC_FORCED, SA_HOR_CENTER, false, FS_SMALL);
631 }
632 }
633 if (IsInsideMM(widget, WID_LGL_CARGO_FIRST, WID_LGL_CARGO_LAST + 1)) {
634 const CargoSpec *cargo = _sorted_cargo_specs[widget - WID_LGL_CARGO_FIRST];
635 GfxFillRect(br, cargo->legend_colour);
636 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);
637 }
638}
639
640bool LinkGraphLegendWindow::OnTooltip([[maybe_unused]] Point, WidgetID widget, TooltipCloseCondition close_cond)
641{
642 if (IsInsideMM(widget, WID_LGL_COMPANY_FIRST, WID_LGL_COMPANY_LAST + 1)) {
643 if (this->IsWidgetDisabled(widget)) {
645 } else {
647 SetDParam(1, (CompanyID)(widget - WID_LGL_COMPANY_FIRST));
649 }
650 return true;
651 }
652 if (IsInsideMM(widget, WID_LGL_CARGO_FIRST, WID_LGL_CARGO_LAST + 1)) {
653 const CargoSpec *cargo = _sorted_cargo_specs[widget - WID_LGL_CARGO_FIRST];
654 GuiShowTooltips(this, cargo->name, close_cond);
655 return true;
656 }
657 return false;
658}
659
664{
665 uint32_t mask = 0;
666 for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) {
667 if (this->IsWidgetDisabled(WID_LGL_COMPANY_FIRST + c)) continue;
668 if (!this->IsWidgetLowered(WID_LGL_COMPANY_FIRST + c)) continue;
669 SetBit(mask, c);
670 }
671 this->overlay->SetCompanyMask(mask);
672}
673
678{
679 CargoTypes mask = 0;
680 for (uint c = 0; c < num_cargo; c++) {
681 if (!this->IsWidgetLowered(WID_LGL_CARGO_FIRST + c)) continue;
682 SetBit(mask, _sorted_cargo_specs[c]->Index());
683 }
684 this->overlay->SetCargoMask(mask);
685}
686
687void LinkGraphLegendWindow::OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count)
688{
689 /* Check which button is clicked */
690 if (IsInsideMM(widget, WID_LGL_COMPANY_FIRST, WID_LGL_COMPANY_LAST + 1)) {
691 if (!this->IsWidgetDisabled(widget)) {
692 this->ToggleWidgetLoweredState(widget);
694 }
695 } else if (widget == WID_LGL_COMPANIES_ALL || widget == WID_LGL_COMPANIES_NONE) {
696 for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) {
697 if (this->IsWidgetDisabled(WID_LGL_COMPANY_FIRST + c)) continue;
698 this->SetWidgetLoweredState(WID_LGL_COMPANY_FIRST + c, widget == WID_LGL_COMPANIES_ALL);
699 }
701 this->SetDirty();
702 } else if (IsInsideMM(widget, WID_LGL_CARGO_FIRST, WID_LGL_CARGO_LAST + 1)) {
703 this->ToggleWidgetLoweredState(widget);
704 this->UpdateOverlayCargoes();
705 } else if (widget == WID_LGL_CARGOES_ALL || widget == WID_LGL_CARGOES_NONE) {
706 for (uint c = 0; c < this->num_cargo; c++) {
707 this->SetWidgetLoweredState(WID_LGL_CARGO_FIRST + c, widget == WID_LGL_CARGOES_ALL);
708 }
709 this->UpdateOverlayCargoes();
710 }
711 this->SetDirty();
712}
713
719void LinkGraphLegendWindow::OnInvalidateData([[maybe_unused]] int data, [[maybe_unused]] bool gui_scope)
720{
721 if (this->num_cargo != _sorted_cargo_specs.size()) {
722 this->Close();
723 return;
724 }
725
726 /* Disable the companies who are not active */
727 for (CompanyID i = COMPANY_FIRST; i < MAX_COMPANIES; i++) {
728 this->SetWidgetDisabledState(WID_LGL_COMPANY_FIRST + i, !Company::IsValidID(i));
729 }
730}
debug_inline constexpr bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
constexpr T SetBit(T &x, const uint8_t y)
Set a bit in a variable.
uint8_t CargoID
Cargo slots to indicate a cargo type within a game.
Definition cargo_type.h:22
std::vector< const CargoSpec * > _sorted_cargo_specs
Cargo specifications sorted alphabetically by name.
uint GetFlowVia(StationID via) const
Get the sum of flows via a specific station from this FlowStatMap.
bool IsPointVisible(Point pt, const DrawPixelInfo *dpi, int padding=0) const
Determine if a certain point is inside the given DPI, with some lee way.
void SetCargoMask(CargoTypes cargo_mask)
Set a new cargo mask and rebuild the cache.
CargoTypes cargo_mask
Bitmask of cargos to be displayed.
static void DrawVertex(int x, int y, int size, int colour, int border_colour)
Draw a square symbolizing a producer of cargo.
Window * window
Window to be drawn into.
void DrawLinks(const DrawPixelInfo *dpi) const
Draw the cached links or part of them into the given area.
void SetCompanyMask(CompanyMask company_mask)
Set a new company mask and rebuild the cache.
static const uint8_t LINK_COLOURS[][12]
Colours for the various "load" states of links.
StationSupplyList cached_stations
Cache for stations to be drawn.
LinkMap cached_links
Cache for links to reduce recalculation.
bool IsLinkVisible(Point pta, Point ptb, const DrawPixelInfo *dpi, int padding=0) const
Determine if a certain link crosses through the area given by the dpi with some lee way.
Point GetStationMiddle(const Station *st) const
Determine the middle of a station in the current window.
void Draw(const DrawPixelInfo *dpi)
Draw the linkgraph overlay or some part of it, in the area given.
bool dirty
Set if overlay should be rebuilt.
const WidgetID widget_id
ID of Widget in Window to be drawn to.
void DrawContent(Point pta, Point ptb, const LinkProperties &cargo) const
Draw one specific link.
CompanyMask company_mask
Bitmask of companies to be displayed.
uint scale
Width of link lines.
void GetWidgetDpi(DrawPixelInfo *dpi) const
Get a DPI for the widget we will be drawing to.
static void AddStats(CargoID new_cargo, uint new_cap, uint new_usg, uint new_flow, uint32_t time, bool new_shared, LinkProperties &cargo)
Add information from a given pair of link stat and flow stat to the given link properties.
void AddLinks(const Station *sta, const Station *stb)
Add all "interesting" links between the given stations to the cache.
void DrawStationDots(const DrawPixelInfo *dpi) const
Draw dots for stations into the smallmap.
void SetDirty()
Mark the linkgraph dirty to be rebuilt next time Draw() is called.
void RebuildCache()
Rebuild the cache and recalculate which links and stations to be shown.
A connected component of a link graph.
Definition linkgraph.h:37
uint Monthly(uint base) const
Scale a value to its monthly equivalent, based on last compression.
Definition linkgraph.h:249
Baseclass for nested widgets.
uint current_x
Current horizontal size (after resizing).
uint current_y
Current vertical size (after resizing).
static constexpr TimerGameTick::Ticks DAY_TICKS
1 day is 74 ticks; TimerGameCalendar::date_fract used to be uint16_t and incremented by 885.
static bool UsingWallclockUnits(bool newgame=false)
Check if we are using wallclock units.
static WidgetDimensions scaled
Widget dimensions scaled for current zoom level.
Definition window_gui.h:28
static const WidgetDimensions unscaled
Unscaled widget dimensions.
Definition window_gui.h:96
void DrawCompanyIcon(CompanyID c, int x, int y)
Draw the icon of a company.
Owner
Enum for all companies/owners.
@ COMPANY_FIRST
First company, same as owner.
@ OWNER_NONE
The tile has no ownership.
@ MAX_COMPANIES
Maximum number of companies.
int GetCharacterHeight(FontSize size)
Get height of a character for a given font size.
Definition fontcache.cpp:77
Dimension maxdim(const Dimension &d1, const Dimension &d2)
Compute bounding box of both dimensions.
Dimension GetSpriteSize(SpriteID sprid, Point *offset, ZoomLevel zoom)
Get the size of a sprite.
Definition gfx.cpp:922
Dimension GetStringBoundingBox(std::string_view str, FontSize start_fontsize)
Return the string dimension in pixels.
Definition gfx.cpp:851
int DrawString(int left, int right, int top, std::string_view str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
Draw string, possibly truncated to make it fit in its allocated space.
Definition gfx.cpp:657
void GfxFillRect(int left, int top, int right, int bottom, int colour, FillRectMode mode)
Applies a certain FillRectMode-operation to a rectangle [left, right] x [top, bottom] on the screen.
Definition gfx.cpp:114
int CenterBounds(int min, int max, int size)
Determine where to draw a centred object inside a widget.
Definition gfx_func.h:166
@ SA_HOR_CENTER
Horizontally center the text.
Definition gfx_type.h:344
@ FS_SMALL
Index of the small font in the font tables.
Definition gfx_type.h:210
@ TC_FORCED
Ignore colour changes from strings.
Definition gfx_type.h:285
constexpr NWidgetPart NWidgetFunction(NWidgetFunctionType *func_ptr)
Obtain a nested widget (sub)tree from an external source.
constexpr NWidgetPart SetPIP(uint8_t pre, uint8_t inter, uint8_t post)
Widget part function for setting a pre/inter/post spaces.
constexpr NWidgetPart SetPadding(uint8_t top, uint8_t right, uint8_t bottom, uint8_t left)
Widget part function for setting additional space around a widget.
constexpr NWidgetPart SetDataTip(uint32_t data, StringID tip)
Widget part function for setting the data and tooltip.
constexpr NWidgetPart NWidget(WidgetType tp, Colours col, WidgetID idx=-1)
Widget part function for starting a new 'real' widget.
constexpr NWidgetPart EndContainer()
Widget part function for denoting the end of a container (horizontal, vertical, WWT_FRAME,...
void SetDirty() const
Mark entire window as dirty (in need of re-paint)
Definition window.cpp:940
std::unique_ptr< NWidgetBase > MakeCompanyButtonRowsLinkGraphGUI()
Make a number of rows with buttons for each company for the linkgraph legend window.
void ShowLinkGraphLegend()
Open a link graph legend window.
Declaration of linkgraph overlay GUI.
constexpr T abs(const T a)
Returns the absolute value of (scalar) variable.
Definition math_func.hpp:23
constexpr uint Ceil(uint a, uint b)
Computes ceil(a / b) * b for non-negative a and b.
constexpr bool IsInsideMM(const T x, const size_t min, const size_t max) noexcept
Checks if a value is in an interval.
void GuiShowTooltips(Window *parent, StringID str, TooltipCloseCondition close_tooltip, uint paramcount)
Shows a tooltip.
Definition misc_gui.cpp:740
uint8_t GetColourGradient(Colours colour, ColourShade shade)
Get colour gradient palette index.
Definition palette.cpp:314
TextColour GetContrastColour(uint8_t background, uint8_t threshold)
Determine a contrasty text colour for a coloured background.
Definition palette.cpp:287
GameSettings _settings_game
Game settings of a running game or the scenario editor.
Definition settings.cpp:57
ClientSettings _settings_client
The current settings for this game.
Definition settings.cpp:56
Point GetSmallMapStationMiddle(const Window *w, const Station *st)
Determine the middle of a station in the smallmap window.
#define lengthof(array)
Return the length of an fixed size array.
Definition stdafx.h:280
void AppendStringInPlace(std::string &result, StringID string)
Resolve the given StringID and append in place into an existing std::string with all the associated D...
Definition strings.cpp:345
void SetDParam(size_t n, uint64_t v)
Set a string parameter v at index n in the global string parameter array.
Definition strings.cpp:104
std::string GetString(StringID string)
Resolve the given StringID into a std::string with all the associated DParam lookups and formatting.
Definition strings.cpp:333
void SetDParamStr(size_t n, const char *str)
This function is used to "bind" a C string to a OpenTTD dparam slot.
Definition strings.cpp:371
uint32_t StringID
Numeric value that represents a string, independent of the selected language.
Owner owner
The owner of this station.
StationRect rect
NOSAVE: Station spread out rectangle maintained by StationRect::xxx() functions.
Specification of a cargo type.
Definition cargotype.h:76
static CargoSpec * Get(size_t index)
Retrieve cargo details for the given cargo ID.
Definition cargotype.h:139
StringID abbrev
Two letter abbreviation for this cargo type.
Definition cargotype.h:97
StringID name
Name of this type of cargo.
Definition cargotype.h:93
bool IsValid() const
Tests for validity of this cargospec.
Definition cargotype.h:120
GUISettings gui
settings related to the GUI
Dimensions (a width and height) of a rectangle in 2D.
Data about how and where to blit pixels.
Definition gfx_type.h:157
uint8_t linkgraph_colours
linkgraph overlay colours
VehicleSettings vehicle
options for vehicles
Stores station stats for a single cargo.
FlowStatMap flows
Planned flows through this station.
NodeID node
ID of node in link graph referring to this goods entry.
LinkGraphID link_graph
Link graph this station belongs to.
void OnInvalidateData(int data=0, bool gui_scope=true) override
Invalidate the data of this window if the cargoes or companies have changed.
void SetOverlay(std::shared_ptr< LinkGraphOverlay > overlay)
Set the overlay belonging to this menu and import its company/cargo settings.
void UpdateOverlayCompanies()
Update the overlay with the new company selection.
void OnClick(Point pt, WidgetID widget, int click_count) override
A click with the left mouse button has been made on the window.
bool OnTooltip(Point pt, WidgetID widget, TooltipCloseCondition close_cond) override
Event to display a custom tooltip.
void UpdateWidgetSize(WidgetID widget, Dimension &size, const Dimension &padding, Dimension &fill, Dimension &resize) override
Update size and resize step of a widget in the window.
void UpdateOverlayCargoes()
Update the overlay with the new cargo selection.
void DrawWidget(const Rect &r, WidgetID widget) const override
Draw the contents of a nested widget.
An edge in the link graph.
Definition linkgraph.h:42
uint32_t TravelTime() const
Get edge's average travel time.
Definition linkgraph.h:56
uint usage
Usage of the link.
Definition linkgraph.h:44
uint capacity
Capacity of the link.
Definition linkgraph.h:43
Node of the link graph.
Definition linkgraph.h:90
std::vector< BaseEdge > edges
Sorted list of outgoing edges from this node.
Definition linkgraph.h:97
uint supply
Supply at the station.
Definition linkgraph.h:91
Monthly statistics for a link between two stations.
uint usage
Actual usage of the link.
bool shared
If this is a shared link to be drawn dashed.
CargoID cargo
Cargo type of the link.
uint planned
Planned usage of the link.
uint Usage() const
Return the usage of the link to display.
uint capacity
Capacity of the link.
uint32_t time
Travel time of the link.
Partial widget specification to allow NWidgets to be written nested.
Coordinates of a point in 2D.
Tindex index
Index of this pool item.
static bool IsValidID(size_t index)
Tests whether given index can be used to get valid (non-nullptr) Titem.
static Titem * Get(size_t index)
Returns Titem with given index.
Specification of a rectangle with absolute coordinates of all edges.
Rect Shrink(int s) const
Copy and shrink Rect by s pixels.
Iterable ensemble of each set bit in a value.
static Pool::IterateWrapper< Station > Iterate(size_t from=0)
Returns an iterable ensemble of all valid stations of type T.
static Station * Get(size_t index)
Gets station with given index.
static Station * GetIfValid(size_t index)
Returns station if the index is a valid index for this station type.
static bool IsValidID(size_t index)
Tests whether given index is a valid index for station of this type.
Station data structure.
GoodsEntry goods[NUM_CARGO]
Goods at this station.
uint8_t road_side
the side of the road vehicles drive on
High level window description.
Definition window_gui.h:159
Data structure for an opened window.
Definition window_gui.h:273
virtual void Close(int data=0)
Hide the window and all its child windows, and mark them for a later deletion.
Definition window.cpp:1047
ViewportData * viewport
Pointer to viewport data, if present.
Definition window_gui.h:318
bool IsWidgetLowered(WidgetID widget_index) const
Gets the lowered state of a widget.
Definition window_gui.h:497
bool IsWidgetDisabled(WidgetID widget_index) const
Gets the enabled/disabled status of a widget.
Definition window_gui.h:416
void SetWidgetLoweredState(WidgetID widget_index, bool lowered_stat)
Sets the lowered/raised status of a widget.
Definition window_gui.h:447
const NWID * GetWidget(WidgetID widnum) const
Get the nested widget with number widnum from the nested widget tree.
Definition window_gui.h:977
void SetWidgetDisabledState(WidgetID widget_index, bool disab_stat)
Sets the enabled/disabled status of a widget.
Definition window_gui.h:387
void ToggleWidgetLoweredState(WidgetID widget_index)
Invert the lowered/raised status of a widget.
Definition window_gui.h:456
static RectPadding ScaleGUITrad(const RectPadding &r)
Scale a RectPadding to GUI zoom level.
Definition widget.cpp:35
std::unique_ptr< NWidgetBase > MakeCompanyButtonRows(WidgetID widget_first, WidgetID widget_last, Colours button_colour, int max_length, StringID button_tooltip, bool resizable)
Make a number of rows with button-like graphics, for enabling/disabling each company.
Definition widget.cpp:3358
@ NC_EQUALSIZE
Value of the NCB_EQUALSIZE flag.
@ WWT_PUSHTXTBTN
Normal push-button (no toggle button) with text caption.
@ NWID_HORIZONTAL
Horizontal container.
Definition widget_type.h:75
@ WWT_PANEL
Simple depressed panel.
Definition widget_type.h:50
@ WWT_STICKYBOX
Sticky box (at top-right of a window, after WWT_DEFSIZEBOX)
Definition widget_type.h:66
@ WWT_SHADEBOX
Shade box (at top-right of a window, between WWT_DEBUGBOX and WWT_DEFSIZEBOX)
Definition widget_type.h:64
@ WWT_CAPTION
Window caption (window title between closebox and stickybox)
Definition widget_type.h:61
@ NWID_VERTICAL
Vertical container.
Definition widget_type.h:77
@ WWT_CLOSEBOX
Close box (at top-left of a window)
Definition widget_type.h:69
Window * GetMainWindow()
Get the main window, i.e.
Definition window.cpp:1127
@ WDP_AUTO
Find a place automatically.
Definition window_gui.h:147
int WidgetID
Widget ID.
Definition window_type.h:18
@ WC_LINKGRAPH_LEGEND
Linkgraph legend; Window numbers:
@ WC_NONE
No window, redirects to WC_MAIN_WINDOW.
Definition window_type.h:45