Freeciv21
Develop your civilization from humble roots to a global empire
view_research_reqtree.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
4 / \\..// \ redistribute it and/or modify it under the terms of the GNU
5  ( oo ) General Public License as published by the Free Software
6  \__/ Foundation, either version 3 of the License, or (at your
7  option) any later version. You should have received
8  a copy of the GNU General Public License along with Freeciv21. If not,
9  see https://www.gnu.org/licenses/.
10  */
11 
12 /*
13  * This file contains functions to generate the GUI for the research
14  * requirements tree inside of the research view (formally known as the
15  * reqtree).
16  */
17 
18 // utility
19 #include "log.h"
20 
21 // common
22 #include "government.h"
23 #include "improvement.h"
24 #include "research.h"
25 #include "tech.h"
26 
27 // client
28 #include "client_main.h"
29 #include "options.h"
30 #include "tileset/tilespec.h"
32 
33 #include "colors_g.h"
34 #include "sprite_g.h"
35 
36 // Qt
37 #include <QPainter>
38 #include <QPainterPath>
39 #include <QPixmap>
40 #include <QRect>
41 
42 /*
43  * Hierarchical directed draph drawing for Freeciv21's technology tree
44  *
45  *
46  * \ Layer 0 / \ Layer 1 / \ Layer 2 /
47  * vvvvvvvvvvvvvvvv vvvvvvvvvvvvvvv vvvvvvvvvvv
48  *
49  * +-----------------+ +-------------+ +----------+
50  * | Alphabeth |----------| Code of Laws|----| Monarchy |
51  * +-----------------+ +-------------+ /+----------+
52  * /
53  * +-----------------+ Dummy node /
54  * |Ceremonial Burial|-----------=============/
55  * +-----------------+
56  *
57  * ^ node_y
58  * |
59  * |
60  * | node_x
61  * +-------->
62  */
63 
68  REQTREE_EDGE = 0, // Normal, "unvisited"
70  REQTREE_KNOWN_EDGE, // Both nodes known, "visited"
72  REQTREE_GOAL_EDGE // Dest node is part of goal "future visited"
73 };
74 
78 static void add_requirement(struct tree_node *node, struct tree_node *req)
79 {
80  fc_assert_ret(node != nullptr);
81  fc_assert_ret(req != nullptr);
82 
83  node->require = static_cast<tree_node **>(fc_realloc(
84  node->require, sizeof(*node->require) * (node->nrequire + 1)));
85  node->require[node->nrequire] = req;
86  node->nrequire++;
87 
88  req->provide = static_cast<tree_node **>(
89  fc_realloc(req->provide, sizeof(*req->provide) * (req->nprovide + 1)));
90  req->provide[req->nprovide] = node;
91  req->nprovide++;
92 }
93 
97 static struct tree_node *new_tree_node()
98 {
99  struct tree_node *node = new tree_node();
100 
101  node->nrequire = 0;
102  node->nprovide = 0;
103  node->require = nullptr;
104  node->provide = nullptr;
105  node->order = -1;
106  node->layer = -1;
107  return node;
108 }
109 
114 static void node_rectangle_minimum_size(struct tree_node *node, int *width,
115  int *height)
116 {
117  int max_icon_height; // maximal height of icons below the text
118  int icons_width_sum; // sum of icons width plus space between them
119  const QPixmap *sprite;
120 
121  if (node->is_dummy) {
122  // Dummy node is a straight line, tech tree lines are 2px wide
123  *width = *height = 2;
124  } else {
125  auto font = get_font(FONT_REQTREE_TEXT);
126  auto rect =
127  QFontMetrics(font).boundingRect(research_advance_name_translation(
128  research_get(client_player()), node->tech));
129  *width = rect.width() + 8;
130  *height = rect.height() + 8;
131 
132  max_icon_height = 0;
133  icons_width_sum = 5;
134 
136  // units
138  {
139  if (advance_number(unit->require_advance) != node->tech) {
140  continue;
141  }
142  sprite = get_unittype_sprite(tileset, unit, direction8_invalid());
143  max_icon_height = std::max(max_icon_height, sprite->height());
144  icons_width_sum += sprite->width() + 2;
145  }
147 
148  // buildings
149  improvement_iterate(pimprove)
150  {
151  requirement_vector_iterate(&(pimprove->reqs), preq)
152  {
153  if (VUT_ADVANCE == preq->source.kind
154  && advance_number(preq->source.value.advance) == node->tech) {
155  sprite = get_building_sprite(tileset, pimprove);
156  // Improvement icons are not guaranteed to exist
157  if (sprite) {
158  max_icon_height = MAX(max_icon_height, sprite->height());
159  icons_width_sum += sprite->width() + 2;
160  }
161  }
162  }
164  }
166 
167  // governments
168  for (const auto &gov : governments) {
169  requirement_vector_iterate(&gov.reqs, preq)
170  {
171  if (VUT_ADVANCE == preq->source.kind
172  && advance_number(preq->source.value.advance) == node->tech) {
173  sprite = get_government_sprite(tileset, &gov);
174  max_icon_height = MAX(max_icon_height, sprite->height());
175  icons_width_sum += sprite->width() + 2;
176  }
177  }
179  }
180  }
181 
182  *height += max_icon_height;
183  if (*width < icons_width_sum) {
184  *width = icons_width_sum;
185  }
186  }
187 }
188 
193 static void symmetrize(struct reqtree *tree)
194 {
195  int layer;
196  int i, j;
197 
198  for (layer = 0; layer < tree->num_layers; layer++) {
199  for (i = 0; i < tree->layer_size[layer]; i++) {
200  struct tree_node *node = tree->layers[layer][i];
201  int v, node_y, node_height;
202 
203  if (node->nrequire == 0) {
204  continue;
205  }
206  v = 0;
207  for (j = 0; j < node->nrequire; j++) {
208  struct tree_node *node_require = node->require[j];
209 
210  v += node_require->node_y + node_require->node_height / 2;
211  }
212  v /= node->nrequire;
213  node_y = node->node_y;
214  node_height = node->node_height;
215  if (v < node_y + node_height / 2) {
216  if (node_y <= 0) {
217  continue;
218  }
219  if (i > 0) {
220  struct tree_node *node_above = tree->layers[layer][i - 1];
221 
222  if (node_above->node_y + node_above->node_height >= node_y - 11) {
223  continue;
224  }
225  }
226  node_y--;
227  } else if (v > node_y + node_height / 2) {
228  if (node_y + node_height >= tree->diagram_height - 1) {
229  continue;
230  }
231  if (i < tree->layer_size[layer] - 1) {
232  struct tree_node *node_below = tree->layers[layer][i + 1];
233 
234  if (node_y + node_height >= node_below->node_y - 11) {
235  continue;
236  }
237  }
238  node_y++;
239  }
240  node->node_y = node_y;
241  }
242  }
243 }
244 
249 static void calculate_diagram_layout(struct reqtree *tree)
250 {
251  int i, layer, layer_offs;
252 
253  // calculate minimum size of rectangle for each node
254  for (i = 0; i < tree->num_nodes; i++) {
255  struct tree_node *node = tree->nodes[i];
256 
258  &node->node_height);
259  node->number = i;
260  }
261 
262  /* calculate height of the diagram. There should be at least 10 pixels
263  * beetween any two nodes */
264  tree->diagram_height = 0;
265  for (layer = 0; layer < tree->num_layers; layer++) {
266  int h_sum = 0;
267 
268  for (i = 0; i < tree->layer_size[layer]; i++) {
269  struct tree_node *node = tree->layers[layer][i];
270 
271  h_sum += node->node_height;
272  if (i < tree->layer_size[layer] - 1) {
273  h_sum += 10;
274  }
275  }
276  tree->diagram_height = MAX(tree->diagram_height, h_sum);
277  }
278 
279  /* calculate maximum width of node for each layer and enlarge other nodes
280  * to match maximum width
281  * calculate x offsets
282  */
283  layer_offs = 0;
284  for (layer = 0; layer < tree->num_layers; layer++) {
285  int max_width = 0;
286 
287  for (i = 0; i < tree->layer_size[layer]; i++) {
288  struct tree_node *node = tree->layers[layer][i];
289 
290  max_width = MAX(max_width, node->node_width);
291  }
292 
293  for (i = 0; i < tree->layer_size[layer]; i++) {
294  struct tree_node *node = tree->layers[layer][i];
295 
296  node->node_width = max_width;
297  node->node_x = layer_offs;
298  }
299 
300  // space between layers should be proportional to their size
301  if (layer != tree->num_layers - 1) {
302  layer_offs += max_width * 5 / 4 + 80;
303  } else {
304  layer_offs += max_width + 10;
305  }
306  }
307  tree->diagram_width = layer_offs;
308 
309  /* Once we have x positions calculated we can
310  * calculate y-position of nodes on the diagram
311  * Distribute nodes steadily.
312  */
313  for (layer = 0; layer < tree->num_layers; layer++) {
314  int y = 0;
315  int h_sum = 0;
316 
317  for (i = 0; i < tree->layer_size[layer]; i++) {
318  struct tree_node *node = tree->layers[layer][i];
319 
320  h_sum += node->node_height;
321  }
322  for (i = 0; i < tree->layer_size[layer]; i++) {
323  struct tree_node *node = tree->layers[layer][i];
324 
325  node->node_y = y;
326  y += node->node_height;
327  if (tree->layer_size[layer] > 1) {
328  y += (tree->diagram_height - h_sum) / (tree->layer_size[layer] - 1)
329  - 1;
330  }
331  }
332  }
333 
334  // The symetrize() function moves node by one pixel per call
335  for (i = 0; i < tree->diagram_height; i++) {
336  symmetrize(tree);
337  }
338 }
339 
347 static struct reqtree *create_dummy_reqtree(struct player *pplayer,
348  bool show_all)
349 {
350  const struct research *presearch = research_get(pplayer);
351  struct reqtree *tree = new reqtree();
352  int j;
353  std::vector<struct tree_node *> nodes;
354  nodes.resize(advance_count());
355 
356  nodes[A_NONE] = nullptr;
358  {
359  if (!valid_advance_by_number(tech)) {
360  nodes[tech] = nullptr;
361  continue;
362  }
363  if (pplayer && !show_all
364  && !research_invention_reachable(presearch, tech)) {
365  /* Reqtree requested for particular player and this tech is
366  * unreachable to him/her. */
367  nodes[tech] = nullptr;
368  continue;
369  }
370  nodes[tech] = new_tree_node();
371  nodes[tech]->is_dummy = false;
372  nodes[tech]->tech = tech;
373  }
375 
377  {
378  struct advance *padvance = valid_advance_by_number(tech);
379  Tech_type_id tech_one, tech_two;
380 
381  if (!padvance) {
382  continue;
383  }
384  if (nodes[tech] == nullptr) {
385  continue;
386  }
387 
388  tech_one = advance_required(tech, AR_ONE);
389  tech_two = advance_required(tech, AR_TWO);
390 
391  if (!show_all && A_NONE != tech_one && A_LAST != tech_two
392  && A_NONE != tech_two
393  && (nodes[tech_one] == nullptr || nodes[tech_two] == nullptr)) {
394  // Print only reachable techs.
395  continue;
396  }
397 
398  /* Formerly, we used to remove the redundant requirement nodes (the
399  * technologies already included in the requirements of the other
400  * requirement). However, it doesn't look like a good idea, because
401  * a player can steal any technology independently of the technology
402  * tree. */
403  if (A_NONE != tech_one && A_LAST != tech_two) {
404  add_requirement(nodes[tech], nodes[tech_one]);
405  if (A_NONE != tech_two) {
406  add_requirement(nodes[tech], nodes[tech_two]);
407  }
408  }
409  }
411 
412  /* Copy nodes from local array to dynamically allocated one.
413  * Skip non-existing entries */
414  tree->nodes = new tree_node *[advance_count()]();
415  j = 0;
417  {
418  if (nodes[tech]) {
419  fc_assert_action(valid_advance_by_number(nodes[tech]->tech), continue);
420  tree->nodes[j++] = nodes[tech];
421  }
422  }
424  tree->num_nodes = j;
425  tree->layers = nullptr;
426 
427  return tree;
428 }
429 
433 void destroy_reqtree(struct reqtree *tree)
434 {
435  int i;
436 
437  for (i = 0; i < tree->num_nodes; i++) {
438  free(tree->nodes[i]->require);
439  free(tree->nodes[i]->provide);
440  delete tree->nodes[i];
441  }
442  delete[] tree->nodes;
443  if (tree->layers) {
444  for (i = 0; i < tree->num_layers; i++) {
445  delete[] tree->layers[i];
446  }
447  if (tree->layer_size) {
448  delete[] tree->layer_size;
449  }
450  }
451  delete tree;
452 }
453 
458 static int longest_path(struct tree_node *node)
459 {
460  int max, i;
461 
462  if (node->layer != -1) {
463  return node->layer;
464  }
465  max = -1;
466  for (i = 0; i < node->nrequire; i++) {
467  max = MAX(max, longest_path(node->require[i]));
468  }
469  node->layer = max + 1;
470  return node->layer;
471 }
472 
476 static void longest_path_layering(struct reqtree *tree)
477 {
478  int i;
479 
480  for (i = 0; i < tree->num_nodes; i++) {
481  if (tree->nodes[i]) {
482  longest_path(tree->nodes[i]);
483  }
484  }
485 }
486 
490 static int max_provide_layer(struct tree_node *node)
491 {
492  int i;
493  int max = node->layer;
494 
495  for (i = 0; i < node->nprovide; i++) {
496  if (node->provide[i]->layer > max) {
497  max = node->provide[i]->layer;
498  }
499  }
500  return max;
501 }
502 
507 static struct reqtree *add_dummy_nodes(struct reqtree *tree)
508 {
509  struct reqtree *new_tree;
510  int num_dummy_nodes = 0;
511  int k, i, j;
512 
513  // Count dummy nodes to be added
514  for (i = 0; i < tree->num_nodes; i++) {
515  int mpl;
516 
517  if (tree->nodes[i] == nullptr) {
518  continue;
519  }
520  mpl = max_provide_layer(tree->nodes[i]);
521  if (mpl > tree->nodes[i]->layer + 1) {
522  num_dummy_nodes += mpl - tree->nodes[i]->layer - 1;
523  }
524  }
525 
526  // create new tree
527  new_tree = new reqtree();
528  new_tree->nodes = new tree_node *[tree->num_nodes + num_dummy_nodes];
529  new_tree->num_nodes = tree->num_nodes + num_dummy_nodes;
530 
531  // copy normal nodes
532  for (i = 0; i < tree->num_nodes; i++) {
533  new_tree->nodes[i] = new_tree_node();
534  new_tree->nodes[i]->is_dummy = false;
535  new_tree->nodes[i]->tech = tree->nodes[i]->tech;
536  new_tree->nodes[i]->layer = tree->nodes[i]->layer;
537  tree->nodes[i]->number = i;
538  }
539 
540  // allocate dummy nodes
541  for (i = 0; i < num_dummy_nodes; i++) {
542  new_tree->nodes[i + tree->num_nodes] = new_tree_node();
543  new_tree->nodes[i + tree->num_nodes]->is_dummy = true;
544  }
545  // k points to the first unused dummy node
546  k = tree->num_nodes;
547 
548  for (i = 0; i < tree->num_nodes; i++) {
549  struct tree_node *node = tree->nodes[i];
550  int mpl;
551 
552  fc_assert_action(!node->is_dummy, continue);
553 
554  mpl = max_provide_layer(node);
555 
556  // if this node will have dummy as ancestors, connect them in a chain
557  if (mpl > node->layer + 1) {
558  add_requirement(new_tree->nodes[k], new_tree->nodes[i]);
559  for (j = node->layer + 2; j < mpl; j++) {
560  add_requirement(new_tree->nodes[k + j - node->layer - 1],
561  new_tree->nodes[k + j - node->layer - 2]);
562  }
563  for (j = node->layer + 1; j < mpl; j++) {
564  new_tree->nodes[k + j - node->layer - 1]->layer = j;
565  }
566  }
567 
568  // copy all edges and create edges with dummy nodes
569  for (j = 0; j < node->nprovide; j++) {
570  int provide_y = node->provide[j]->layer;
571 
572  if (provide_y == node->layer + 1) {
573  // direct connection
574  add_requirement(new_tree->nodes[node->provide[j]->number],
575  new_tree->nodes[i]);
576  } else {
577  // connection through dummy node
578  add_requirement(new_tree->nodes[node->provide[j]->number],
579  new_tree->nodes[k + provide_y - node->layer - 2]);
580  }
581  }
582 
583  if (mpl > node->layer + 1) {
584  k += mpl - node->layer - 1;
585  fc_assert(k <= new_tree->num_nodes);
586  }
587  }
588  new_tree->layers = nullptr;
589 
590  return new_tree;
591 }
592 
598 static void set_layers(struct reqtree *tree)
599 {
600  int i;
601  int num_layers = 0;
602 
603  // count total number of layers
604  for (i = 0; i < tree->num_nodes; i++) {
605  num_layers = MAX(num_layers, tree->nodes[i]->layer);
606  }
607  num_layers++;
608  tree->num_layers = num_layers;
609 
610  {
611  // Counters for order - order number for the next node in the layer
612  std::vector<int> T;
613  T.resize(num_layers);
614 
615  tree->layers = new tree_node **[num_layers];
616  tree->layer_size = new int[num_layers];
617  for (i = 0; i < num_layers; i++) {
618  T[i] = 0;
619  tree->layer_size[i] = 0;
620  }
621  for (i = 0; i < tree->num_nodes; i++) {
622  tree->layer_size[tree->nodes[i]->layer]++;
623  }
624 
625  for (i = 0; i < num_layers; i++) {
626  tree->layers[i] = new tree_node *[tree->layer_size[i]];
627  }
628  for (i = 0; i < tree->num_nodes; i++) {
629  struct tree_node *node = tree->nodes[i];
630 
631  tree->layers[node->layer][T[node->layer]] = node;
632  node->order = T[node->layer];
633  T[node->layer]++;
634  }
635  }
636 }
637 
639  struct tree_node *node;
640  float value;
641 };
642 
646 static int cmp_func(const node_and_float &a, const node_and_float &b)
647 {
648  return a.value < b.value;
649 }
650 
655 static void barycentric_sort(struct reqtree *tree, int layer)
656 {
657  std::vector<struct node_and_float> T;
658  T.resize(tree->layer_size[layer]);
659 
660  int i, j;
661  float v;
662 
663  for (i = 0; i < tree->layer_size[layer]; i++) {
664  struct tree_node *node = tree->layers[layer][i];
665 
666  T[i].node = node;
667  v = 0.0;
668  for (j = 0; j < node->nrequire; j++) {
669  v += node->require[j]->order;
670  }
671  if (node->nrequire > 0) {
672  v /= static_cast<float>(node->nrequire);
673  }
674  T[i].value = v;
675  }
676  std::sort(T.begin(), T.end(), cmp_func);
677 
678  for (i = 0; i < tree->layer_size[layer]; i++) {
679  tree->layers[layer][i] = T[i].node;
680  T[i].node->order = i;
681  }
682 }
683 
687 static int count_crossings(struct reqtree *tree, int layer)
688 {
689  int layer1_size = tree->layer_size[layer];
690  int layer2_size = tree->layer_size[layer + 1];
691  std::vector<int> X;
692  X.resize(layer2_size);
693  int i, j, k;
694  int sum = 0;
695 
696  for (i = 0; i < layer2_size; i++) {
697  X[i] = 0;
698  }
699 
700  for (i = 0; i < layer1_size; i++) {
701  struct tree_node *node = tree->layers[layer][i];
702 
703  for (j = 0; j < node->nprovide; j++) {
704  sum += X[node->provide[j]->order];
705  }
706  for (j = 0; j < node->nprovide; j++) {
707  for (k = 0; k < node->provide[j]->order; k++) {
708  X[k]++;
709  }
710  }
711  }
712 
713  return sum;
714 }
715 
719 static void swap(struct reqtree *tree, int layer, int order1, int order2)
720 {
721  struct tree_node *node1 = tree->layers[layer][order1];
722  struct tree_node *node2 = tree->layers[layer][order2];
723 
724  tree->layers[layer][order1] = node2;
725  tree->layers[layer][order2] = node1;
726  node1->order = order2;
727  node2->order = order1;
728 }
729 
734 static void improve(struct reqtree *tree)
735 {
736  std::vector<int> crossings;
737  crossings.resize(tree->num_layers - 1);
738  int i, x1, x2, layer;
739 
740  for (i = 0; i < tree->num_layers - 1; i++) {
741  crossings[i] = count_crossings(tree, i);
742  }
743 
744  for (layer = 0; layer < tree->num_layers; layer++) {
745  int layer_size = tree->layer_size[layer];
746  int layer_sum = 0;
747 
748  if (layer > 0) {
749  layer_sum += crossings[layer - 1];
750  }
751  if (layer < tree->num_layers - 1) {
752  layer_sum += crossings[layer];
753  }
754 
755  for (x1 = 0; x1 < layer_size; x1++) {
756  for (x2 = x1 + 1; x2 < layer_size; x2++) {
757  int new_crossings = 0;
758  int new_crossings_before = 0;
759 
760  swap(tree, layer, x1, x2);
761  if (layer > 0) {
762  new_crossings_before += count_crossings(tree, layer - 1);
763  }
764  if (layer < tree->num_layers - 1) {
765  new_crossings += count_crossings(tree, layer);
766  }
767  if (new_crossings + new_crossings_before > layer_sum) {
768  swap(tree, layer, x1, x2);
769  } else {
770  layer_sum = new_crossings + new_crossings_before;
771  if (layer > 0) {
772  crossings[layer - 1] = new_crossings_before;
773  }
774  if (layer < tree->num_layers - 1) {
775  crossings[layer] = new_crossings;
776  }
777  }
778  }
779  }
780  }
781 }
782 
789 struct reqtree *create_reqtree(struct player *pplayer, bool show_all)
790 {
791  struct reqtree *tree1, *tree2;
792  int i, j;
793 
794  tree1 = create_dummy_reqtree(pplayer, show_all);
795  longest_path_layering(tree1);
796  tree2 = add_dummy_nodes(tree1);
797  destroy_reqtree(tree1);
798  set_layers(tree2);
799 
800  // It's good heuristics for beginning
801  for (j = 0; j < 20; j++) {
802  for (i = 0; i < tree2->num_layers; i++) {
803  barycentric_sort(tree2, i);
804  }
805  }
806 
807  // Now burn some CPU
808  for (j = 0; j < 20; j++) {
809  improve(tree2);
810  }
811 
813 
814  return tree2;
815 }
816 
820 void get_reqtree_dimensions(struct reqtree *reqtree, int *width, int *height)
821 {
822  if (width) {
823  *width = reqtree->diagram_width;
824  }
825  if (height) {
826  *height = reqtree->diagram_height;
827  }
828 }
829 
833 static QColor node_color(struct tree_node *node)
834 {
835  if (!node->is_dummy) {
837 
838  if (!research) {
839  return get_diag_color(COLOR_REQTREE_KNOWN);
840  }
841 
843  return get_diag_color(COLOR_REQTREE_UNREACHABLE);
844  }
845 
846  if (!research_invention_gettable(research, node->tech, true)) {
848  || node->tech == research->tech_goal) {
849  return get_diag_color(COLOR_REQTREE_GOAL_NOT_GETTABLE);
850  } else {
851  return get_diag_color(COLOR_REQTREE_NOT_GETTABLE);
852  }
853  }
854 
855  if (research->researching == node->tech) {
856  return get_diag_color(COLOR_REQTREE_RESEARCHING);
857  }
858 
859  if (TECH_KNOWN == research_invention_state(research, node->tech)) {
860  return get_diag_color(COLOR_REQTREE_KNOWN);
861  }
862 
864  || node->tech == research->tech_goal) {
865  if (TECH_PREREQS_KNOWN
867  return get_diag_color(COLOR_REQTREE_GOAL_PREREQS_KNOWN);
868  } else {
869  return get_diag_color(COLOR_REQTREE_GOAL_UNKNOWN);
870  }
871  }
872 
873  if (TECH_PREREQS_KNOWN
875  return get_diag_color(COLOR_REQTREE_PREREQS_KNOWN);
876  }
877 
878  return get_diag_color(COLOR_REQTREE_UNKNOWN);
879  } else {
880  return Qt::transparent;
881  }
882 }
883 
888 static enum reqtree_edge_type get_edge_type(struct tree_node *node,
889  struct tree_node *dest_node)
890 {
892 
893  if (dest_node == nullptr) {
894  // assume node is a dummy
895  dest_node = node;
896  }
897 
898  // find the required tech
899  while (node->is_dummy) {
900  fc_assert(node->nrequire == 1);
901  node = node->require[0];
902  }
903 
904  /* find destination advance by recursing in dest_node->provide[]
905  * watch out: recursion */
906  if (dest_node->is_dummy) {
907  enum reqtree_edge_type sum_type = REQTREE_EDGE;
908  int i;
909 
910  fc_assert(dest_node->nprovide > 0);
911  for (i = 0; i < dest_node->nprovide; ++i) {
912  enum reqtree_edge_type type =
913  get_edge_type(node, dest_node->provide[i]);
914  switch (type) {
915  case REQTREE_ACTIVE_EDGE:
916  case REQTREE_GOAL_EDGE:
917  return type;
918  case REQTREE_KNOWN_EDGE:
919  case REQTREE_READY_EDGE:
920  sum_type = type;
921  break;
922  default:
923  // no change
924  break;
925  };
926  }
927  return sum_type;
928  }
929 
930  if (!research) {
931  // Global observer case
932  return REQTREE_KNOWN_EDGE;
933  }
934 
935  if (research->researching == dest_node->tech) {
936  return REQTREE_ACTIVE_EDGE;
937  }
938 
940  || dest_node->tech == research->tech_goal) {
941  return REQTREE_GOAL_EDGE;
942  }
943 
944  if (TECH_KNOWN == research_invention_state(research, node->tech)) {
945  if (TECH_KNOWN == research_invention_state(research, dest_node->tech)) {
946  return REQTREE_KNOWN_EDGE;
947  } else {
948  return REQTREE_READY_EDGE;
949  }
950  }
951 
952  return REQTREE_EDGE;
953 }
954 
959 static QColor edge_color(struct tree_node *node, struct tree_node *dest_node)
960 {
961  enum reqtree_edge_type type = get_edge_type(node, dest_node);
962 
963  switch (type) {
964  case REQTREE_ACTIVE_EDGE:
965  return get_diag_color(COLOR_REQTREE_RESEARCHING);
966  case REQTREE_GOAL_EDGE:
967  return get_diag_color(COLOR_REQTREE_GOAL_UNKNOWN);
968  case REQTREE_KNOWN_EDGE:
969  /* using "text" black instead of "known" white/ground/green */
970  return get_diag_color(COLOR_REQTREE_TEXT);
971  case REQTREE_READY_EDGE:
972  return get_diag_color(COLOR_REQTREE_PREREQS_KNOWN);
973  default:
974  return get_diag_color(COLOR_REQTREE_EDGE);
975  };
976 }
977 
984 QList<req_tooltip_help *> *draw_reqtree(struct reqtree *tree,
985  QPixmap *pcanvas, int canvas_x,
986  int canvas_y, int tt_x, int tt_y,
987  int w, int h)
988 {
989  Q_UNUSED(h)
990  Q_UNUSED(w)
991  Q_UNUSED(tt_x)
992  Q_UNUSED(tt_y)
993  Q_UNUSED(canvas_x)
994  Q_UNUSED(canvas_y)
995  int i, j, k;
996  const QPixmap *sprite;
997  req_tooltip_help *rttp;
998 
999  QList<req_tooltip_help *> *tt_help = new QList<req_tooltip_help *>;
1000  QPainter p(pcanvas);
1001  p.setFont(get_font(FONT_REQTREE_TEXT));
1002  p.setRenderHint(QPainter::Antialiasing);
1003 
1004  auto fm = p.fontMetrics();
1005 
1006  // draw the diagram
1007  for (i = 0; i < tree->num_layers; i++) {
1008  for (j = 0; j < tree->layer_size[i]; j++) {
1009  struct tree_node *node = tree->layers[i][j];
1010  int startx, starty, endx, endy, width, height;
1011 
1012  startx = node->node_x;
1013  starty = node->node_y;
1014  width = node->node_width;
1015  height = node->node_height;
1016 
1017  if (node->is_dummy) {
1018  // Use the same layout as lines for dummy nodes
1019  p.setPen(QPen(edge_color(node, nullptr), 2));
1020  p.drawLine(startx, starty, startx + width, starty);
1021  } else {
1022  const QString text = research_advance_name_translation(
1023  research_get(client_player()), node->tech);
1024  int text_w, text_h;
1025  int icon_startx;
1026 
1027  p.setBrush(node_color(node));
1028  p.setPen(QPen(get_diag_color(COLOR_REQTREE_BACKGROUND), 1));
1029  p.drawRect(startx, starty, width - 2, height - 2);
1030  p.setBrush(Qt::NoBrush);
1031 
1032  /* The following code is similar to the one in
1033  * node_rectangle_minimum_size(). If you change something here,
1034  * change also node_rectangle_minimum_size().
1035  */
1036  auto rect = fm.boundingRect(text);
1037  text_w = rect.width();
1038  text_h = rect.height();
1039 
1040  rttp = new req_tooltip_help();
1041  rttp->rect =
1042  QRect(startx + (width - text_w) / 2, starty + 4, text_w, text_h);
1043  rttp->tech_id = node->tech;
1044  tt_help->append(rttp);
1045 
1046  p.setPen(get_color(tileset, COLOR_REQTREE_TEXT));
1047  p.drawText(startx + (width - text_w) / 2 - rect.left(),
1048  starty + 4 + fm.ascent(), text);
1049 
1050  icon_startx = startx + 5;
1051 
1054  {
1055  if (advance_number(unit->require_advance) != node->tech) {
1056  continue;
1057  }
1058  sprite =
1059  get_unittype_sprite(tileset, unit, direction8_invalid());
1060  rttp = new req_tooltip_help();
1061  rttp->rect =
1062  QRect(icon_startx,
1063  starty + text_h + 4
1064  + (height - text_h - 4 - sprite->height()) / 2,
1065  sprite->width(), sprite->height());
1066  rttp->tunit = unit;
1067  tt_help->append(rttp);
1068  p.drawPixmap(icon_startx,
1069  starty + text_h + 4
1070  + (height - text_h - 4 - sprite->height()) / 2,
1071  *sprite);
1072  icon_startx += sprite->width() + 2;
1073  }
1075 
1076  improvement_iterate(pimprove)
1077  {
1078  requirement_vector_iterate(&(pimprove->reqs), preq)
1079  {
1080  if (VUT_ADVANCE == preq->source.kind
1081  && advance_number(preq->source.value.advance)
1082  == node->tech) {
1083  sprite = get_building_sprite(tileset, pimprove);
1084  // Improvement icons are not guaranteed to exist
1085  if (sprite) {
1086  rttp = new req_tooltip_help();
1087  rttp->rect = QRect(
1088  icon_startx,
1089  starty + text_h + 4
1090  + (height - text_h - 4 - sprite->height()) / 2,
1091  sprite->width(), sprite->height());
1092  rttp->timpr = pimprove;
1093  tt_help->append(rttp);
1094  p.drawPixmap(icon_startx,
1095  starty + text_h + 4
1096  + (height - text_h - 4 - sprite->height())
1097  / 2,
1098  *sprite);
1099  icon_startx += sprite->width() + 2;
1100  }
1101  }
1102  }
1104  }
1106 
1107  for (auto &gov : governments) {
1108  requirement_vector_iterate(&gov.reqs, preq)
1109  {
1110  if (VUT_ADVANCE == preq->source.kind
1111  && advance_number(preq->source.value.advance)
1112  == node->tech) {
1113  sprite = get_government_sprite(tileset, &gov);
1114  rttp = new req_tooltip_help();
1115  rttp->rect =
1116  QRect(icon_startx,
1117  starty + text_h + 4
1118  + (height - text_h - 4 - sprite->height()) / 2,
1119  sprite->width(), sprite->height());
1120  rttp->tgov = &gov;
1121  tt_help->append(rttp);
1122  p.drawPixmap(icon_startx,
1123  starty + text_h + 4
1124  + (height - text_h - 4 - sprite->height())
1125  / 2,
1126  *sprite);
1127  icon_startx += sprite->width() + 2;
1128  }
1129  }
1131  } // government iterate - gov
1132  }
1133  }
1134 
1135  // Draw all outgoing edges
1136  startx = node->node_x + node->node_width;
1137  // -1 for pen half-width
1138  starty = node->node_y + node->node_height / 2 - 1;
1139  for (k = 0; k < node->nprovide; k++) {
1140  struct tree_node *dest_node = node->provide[k];
1141  p.setPen(QPen(edge_color(node, dest_node), 2));
1142 
1143  endx = dest_node->node_x;
1144  // -1 for pen half-width
1145  endy = dest_node->node_y + dest_node->node_height / 2 - 1;
1146 
1148  QPainterPath path;
1149  path.moveTo(startx, starty);
1150  path.cubicTo((startx + endx) / 2., starty, startx,
1151  (starty + endy) / 2., endx, endy);
1152  p.drawPath(path);
1153  } else {
1154  p.drawLine(startx, starty, endx, endy);
1155  }
1156  }
1157  }
1158  }
1159 
1160  p.end();
1161 
1162  return tt_help;
1163 }
1164 
1168 Tech_type_id get_tech_on_reqtree(struct reqtree *tree, int x, int y)
1169 {
1170  int i;
1171 
1172  for (i = 0; i < tree->num_nodes; i++) {
1173  struct tree_node *node = tree->nodes[i];
1174 
1175  if (node->is_dummy) {
1176  continue;
1177  }
1178  if (node->node_x <= x && node->node_y <= y
1179  && node->node_x + node->node_width > x
1180  && node->node_y + node->node_height > y) {
1181  return node->tech;
1182  }
1183  }
1184  return A_NONE;
1185 }
1186 
1194  int *y)
1195 {
1196  for (int i = 0; i < tree->num_nodes; i++) {
1197  struct tree_node *node = tree->nodes[i];
1198 
1199  if (tech == node->tech) {
1200  *x = node->node_x + node->node_width / 2;
1201  *y = node->node_y + node->node_height / 2;
1202  return true;
1203  }
1204  }
1205 
1206  return false;
1207 }
QFont get_font(client_font font)
Returns given font.
Definition: canvas.cpp:57
@ FONT_REQTREE_TEXT
Definition: canvas.h:22
struct government * tgov
struct unit_type * tunit
struct impr_type * timpr
struct player * client_player()
Either controlling or observing.
QColor get_color(const struct tileset *t, enum color_std stdcolor)
Return a pointer to the given "standard" color.
QColor get_diag_color(color_std color)
Gets a diagram color.
Definition: themes.cpp:255
int Tech_type_id
Definition: fc_types.h:294
std::vector< government > governments
Definition: government.cpp:28
#define improvement_iterate_end
Definition: improvement.h:199
#define improvement_iterate(_p)
Definition: improvement.h:193
#define fc_assert_ret(condition)
Definition: log.h:112
#define fc_assert(condition)
Definition: log.h:89
#define fc_assert_action(condition, action)
Definition: log.h:104
client_options * gui_options
Definition: options.cpp:74
#define requirement_vector_iterate_end
Definition: requirements.h:80
#define requirement_vector_iterate(req_vec, preq)
Definition: requirements.h:78
bool research_invention_reachable(const struct research *presearch, const Tech_type_id tech)
Returns TRUE iff the given tech is ever reachable via research by the players sharing the research by...
Definition: research.cpp:659
bool research_goal_tech_req(const struct research *presearch, Tech_type_id goal, Tech_type_id tech)
Returns if the given tech has to be researched to reach the goal.
Definition: research.cpp:794
struct research * research_get(const struct player *pplayer)
Returns the research structure associated with the player.
Definition: research.cpp:110
QString research_advance_name_translation(const struct research *presearch, Tech_type_id tech)
Store the translated name of the given tech (including A_FUTURE) in 'buf'.
Definition: research.cpp:257
enum tech_state research_invention_state(const struct research *presearch, Tech_type_id tech)
Returns state of the tech for current research.
Definition: research.cpp:609
bool research_invention_gettable(const struct research *presearch, const Tech_type_id tech, bool allow_holes)
Returns TRUE iff the given tech can be given to the players sharing the research immediately.
Definition: research.cpp:686
#define MAX(x, y)
Definition: shared.h:48
Definition: tech.h:113
bool reqtree_show_icons
Definition: options.h:146
bool reqtree_curved_lines
Definition: options.h:147
struct tree_node * node
Definition: player.h:231
struct tree_node ** nodes
struct tree_node *** layers
Tech_type_id researching
Definition: research.h:45
Tech_type_id tech_goal
Definition: research.h:78
struct tree_node ** provide
Tech_type_id tech
struct tree_node ** require
Definition: unit.h:134
#define fc_realloc(ptr, sz)
Definition: support.h:59
Tech_type_id advance_required(const Tech_type_id tech, enum tech_req require)
Accessor for requirements.
Definition: tech.cpp:108
struct advance * valid_advance_by_number(const Tech_type_id id)
Returns pointer when the advance "exists" in this game, returns nullptr otherwise.
Definition: tech.cpp:154
Tech_type_id advance_count()
Return the number of advances/technologies.
Definition: tech.cpp:68
Tech_type_id advance_number(const struct advance *padvance)
Return the advance index.
Definition: tech.cpp:85
#define advance_index_iterate_end
Definition: tech.h:226
@ AR_TWO
Definition: tech.h:104
@ AR_ONE
Definition: tech.h:104
#define A_FIRST
Definition: tech.h:37
#define A_NONE
Definition: tech.h:36
#define A_LAST
Definition: tech.h:38
#define advance_index_iterate(_start, _index)
Definition: tech.h:221
const QPixmap * get_unittype_sprite(const struct tileset *t, const struct unit_type *punittype, enum direction8 facing, const QColor &replace)
Return the sprite for the unit type (the base "unit" sprite).
Definition: tilespec.cpp:3416
const QPixmap * get_government_sprite(const struct tileset *t, const struct government *gov)
Return the sprite for the government.
Definition: tilespec.cpp:3404
const QPixmap * get_building_sprite(const struct tileset *t, const struct impr_type *pimprove)
Return the sprite for the building/improvement.
Definition: tilespec.cpp:3394
#define unit_type_iterate(_p)
Definition: unittype.h:785
#define unit_type_iterate_end
Definition: unittype.h:791
static QColor node_color(struct tree_node *node)
Return a background color of node's rectangle.
static struct reqtree * add_dummy_nodes(struct reqtree *tree)
Create new tree which has dummy nodes added.
static int max_provide_layer(struct tree_node *node)
Find the largest value of layer amongst children of the given node.
static int longest_path(struct tree_node *node)
Compute the longest path from this tree_node to the node with no requirements.
static void barycentric_sort(struct reqtree *tree, int layer)
Simple heuristic: Sort nodes on the given layer by the average x-value of its' parents.
void get_reqtree_dimensions(struct reqtree *reqtree, int *width, int *height)
Give the dimensions of the reqtree.
static void node_rectangle_minimum_size(struct tree_node *node, int *width, int *height)
Return minimum size of the rectangle in pixels on the diagram which corresponds to the given node.
static void add_requirement(struct tree_node *node, struct tree_node *req)
Add requirement edge to node and provide edge to req.
static struct reqtree * create_dummy_reqtree(struct player *pplayer, bool show_all)
Create a "dummy" tech tree from current ruleset.
static int count_crossings(struct reqtree *tree, int layer)
Calculate number of edge crossings beetwen layer and layer+1.
struct reqtree * create_reqtree(struct player *pplayer, bool show_all)
Generate optimized tech_tree from current ruleset.
static void improve(struct reqtree *tree)
Try to reduce the number of crossings by swapping two nodes and checking if it improves the situation...
static QColor edge_color(struct tree_node *node, struct tree_node *dest_node)
Return a stroke color for an edge between two nodes if node is a dummy, dest_node can be nullptr.
static void symmetrize(struct reqtree *tree)
Move nodes up and down without changing order but making it more symetrical.
static void set_layers(struct reqtree *tree)
Calculate layers[] and layer_size[] fields of tree.
static void longest_path_layering(struct reqtree *tree)
Compute longest_path for all nodes, thus prepare longest path layering.
static void calculate_diagram_layout(struct reqtree *tree)
Calculate rectangles position and size from the tree.
QList< req_tooltip_help * > * draw_reqtree(struct reqtree *tree, QPixmap *pcanvas, int canvas_x, int canvas_y, int tt_x, int tt_y, int w, int h)
Draw the reqtree diagram!
Tech_type_id get_tech_on_reqtree(struct reqtree *tree, int x, int y)
Return the tech ID at the given position of the reqtree (or A_NONE).
static void swap(struct reqtree *tree, int layer, int order1, int order2)
Swap positions of two nodes on the same layer.
static enum reqtree_edge_type get_edge_type(struct tree_node *node, struct tree_node *dest_node)
Return the type for an edge between two nodes if node is a dummy, dest_node can be nullptr.
void destroy_reqtree(struct reqtree *tree)
Free all memory used by tech_tree struct.
static int cmp_func(const node_and_float &a, const node_and_float &b)
Comparison function used by barycentric_sort.
static struct tree_node * new_tree_node()
Allocate and initialize new tree node.
bool get_position_on_reqtree(struct reqtree *tree, Tech_type_id tech, int *x, int *y)
Find the center of a node, identified by tech id in a given reqtree and return true if the node was f...
reqtree_edge_type
Edge types for coloring the edges by type in the tree.
@ REQTREE_READY_EDGE
@ REQTREE_ACTIVE_EDGE
@ REQTREE_KNOWN_EDGE
@ REQTREE_GOAL_EDGE