Project Alice
Loading...
Searching...
No Matches
map_state.cpp
Go to the documentation of this file.
1#include "map_state.hpp"
2
3#include <glm/gtx/intersect.hpp>
4#include <glm/gtx/polar_coordinates.hpp>
5#include <glm/gtc/constants.hpp>
6#include <glm/gtx/transform.hpp>
7
8#include "system_state.hpp"
9#include "military.hpp"
11#include "gui_graphics.hpp"
12#include "gui_element_base.hpp"
13
14#include <set>
15
16namespace map {
17
19 return selected_province;
20}
21
22// Called to load the map. Will load the texture and shaders from disk
24 map_data.load_map(state);
25}
26
29 if(state.user_settings.map_is_globe == sys::projection_mode::flat) {
31 } else if(state.user_settings.map_is_globe == sys::projection_mode::globe_perpect) {
33 }
34 return current_view;
35}
36
37void map_state::set_selected_province(dcon::province_id prov_id) {
39 selected_province = prov_id;
40}
41
42void map_state::render(sys::state& state, uint32_t screen_x, uint32_t screen_y) {
43 update(state);
44 glm::vec2 offset = glm::vec2(glm::mod(pos.x, 1.f) - 0.5f, pos.y - 0.5f);
45 map_data.render(state, glm::vec2(screen_x, screen_y), offset, zoom,
47}
48
49glm::vec2 get_port_location(sys::state& state, dcon::province_id p) {
50 auto pt = state.world.province_get_port_to(p);
51 if(!pt)
52 return glm::vec2{};
53
54 auto adj = state.world.get_province_adjacency_by_province_pair(p, pt);
55 assert(adj);
56 auto id = adj.index();
57 auto& map_data = state.map_state.map_data;
58 auto& border = map_data.borders[id];
59 auto& vertex = map_data.border_vertices[border.start_index + border.count / 2];
60 glm::vec2 map_size = glm::vec2(map_data.size_x, map_data.size_y);
61
62 return vertex.position * map_size;
63}
64
65bool is_sea_province(sys::state& state, dcon::province_id prov_id) {
66 return prov_id.index() >= state.province_definitions.first_sea_province.index();
67}
68
69glm::vec2 get_navy_location(sys::state& state, dcon::province_id prov_id) {
70 if(is_sea_province(state, prov_id))
71 return state.world.province_get_mid_point(prov_id);
72 else
73 return get_port_location(state, prov_id);
74}
75
76glm::vec2 get_army_location(sys::state& state, dcon::province_id prov_id) {
77 return state.world.province_get_mid_point(prov_id);
78}
79
81 map_data.unit_arrow_vertices.clear();
82 map_data.unit_arrow_counts.clear();
83 map_data.unit_arrow_starts.clear();
84
85 map_data.attack_unit_arrow_vertices.clear();
86 map_data.attack_unit_arrow_counts.clear();
87 map_data.attack_unit_arrow_starts.clear();
88
89 map_data.retreat_unit_arrow_vertices.clear();
90 map_data.retreat_unit_arrow_counts.clear();
91 map_data.retreat_unit_arrow_starts.clear();
92
93 map_data.strategy_unit_arrow_vertices.clear();
94 map_data.strategy_unit_arrow_counts.clear();
95 map_data.strategy_unit_arrow_starts.clear();
96
97 map_data.objective_unit_arrow_vertices.clear();
98 map_data.objective_unit_arrow_counts.clear();
99 map_data.objective_unit_arrow_starts.clear();
100
102 map_data.other_objective_unit_arrow_counts.clear();
103 map_data.other_objective_unit_arrow_starts.clear();
104
105 for(auto selected_army : state.selected_armies) {
106 if(auto ps = state.world.army_get_path(selected_army); ps.size() > 0) {
107 auto dest_controller = state.world.province_get_nation_from_province_control(ps[0]);
108 if(state.world.army_get_black_flag(selected_army) || state.world.army_get_is_retreating(selected_army)) {
109 auto old_size = map_data.retreat_unit_arrow_vertices.size();
110 map_data.retreat_unit_arrow_starts.push_back(GLint(old_size));
111 map::make_army_path(state, map_data.retreat_unit_arrow_vertices, selected_army, float(map_data.size_x), float(map_data.size_y));
112 map_data.retreat_unit_arrow_counts.push_back(GLsizei(map_data.retreat_unit_arrow_vertices.size() - old_size));
113 } else if(state.world.army_get_is_ai_controlled(selected_army)) {
114 if(state.local_player_nation && dest_controller && military::are_at_war(state, dest_controller, state.local_player_nation)) {
115 auto old_size = map_data.other_objective_unit_arrow_vertices.size();
116 map_data.other_objective_unit_arrow_starts.push_back(GLint(old_size));
117 map::make_army_path(state, map_data.other_objective_unit_arrow_vertices, selected_army, float(map_data.size_x), float(map_data.size_y));
118 map_data.other_objective_unit_arrow_counts.push_back(GLsizei(map_data.other_objective_unit_arrow_vertices.size() - old_size));
119 } else {
120 auto old_size = map_data.objective_unit_arrow_vertices.size();
121 map_data.objective_unit_arrow_starts.push_back(GLint(old_size));
122 map::make_army_path(state, map_data.objective_unit_arrow_vertices, selected_army, float(map_data.size_x), float(map_data.size_y));
123 map_data.objective_unit_arrow_counts.push_back(GLsizei(map_data.objective_unit_arrow_vertices.size() - old_size));
124 }
125 } else if(state.local_player_nation && dest_controller && military::are_at_war(state, dest_controller, state.local_player_nation)) {
126 auto old_size = map_data.attack_unit_arrow_vertices.size();
127 map_data.attack_unit_arrow_starts.push_back(GLint(old_size));
128 map::make_army_path(state, map_data.attack_unit_arrow_vertices, selected_army, float(map_data.size_x), float(map_data.size_y));
129 map_data.attack_unit_arrow_counts.push_back(GLsizei(map_data.attack_unit_arrow_vertices.size() - old_size));
130 } else {
131 auto old_size = map_data.unit_arrow_vertices.size();
132 map_data.unit_arrow_starts.push_back(GLint(old_size));
133 map::make_army_path(state, map_data.unit_arrow_vertices, selected_army, float(map_data.size_x), float(map_data.size_y));
134 map_data.unit_arrow_counts.push_back(GLsizei(map_data.unit_arrow_vertices.size() - old_size));
135 }
136 }
137 }
138 for(auto selected_navy : state.selected_navies) {
139 if(state.world.navy_get_is_retreating(selected_navy)) {
140 auto old_size = map_data.retreat_unit_arrow_vertices.size();
141 map_data.retreat_unit_arrow_starts.push_back(GLint(old_size));
142 map::make_navy_path(state, map_data.retreat_unit_arrow_vertices, selected_navy, float(map_data.size_x), float(map_data.size_y));
143 map_data.retreat_unit_arrow_counts.push_back(GLsizei(map_data.retreat_unit_arrow_vertices.size() - old_size));
144 } else {
145 auto old_size = map_data.unit_arrow_vertices.size();
146 map_data.unit_arrow_starts.push_back(GLint(old_size));
147 map::make_navy_path(state, map_data.unit_arrow_vertices, selected_navy, float(map_data.size_x), float(map_data.size_y));
148 map_data.unit_arrow_counts.push_back(GLsizei(map_data.unit_arrow_vertices.size() - old_size));
149 }
150 }
151
152 if(!map_data.unit_arrow_vertices.empty()) {
153 glBindBuffer(GL_ARRAY_BUFFER, map_data.vbo_array[map_data.vo_unit_arrow]);
154 glBufferData(GL_ARRAY_BUFFER, sizeof(curved_line_vertex) * map_data.unit_arrow_vertices.size(), map_data.unit_arrow_vertices.data(), GL_STATIC_DRAW);
155 }
156 if(!map_data.attack_unit_arrow_vertices.empty()) {
157 glBindBuffer(GL_ARRAY_BUFFER, map_data.vbo_array[map_data.vo_attack_unit_arrow]);
158 glBufferData(GL_ARRAY_BUFFER, sizeof(curved_line_vertex) * map_data.attack_unit_arrow_vertices.size(), map_data.attack_unit_arrow_vertices.data(), GL_STATIC_DRAW);
159 }
160 if(!map_data.retreat_unit_arrow_vertices.empty()) {
161 glBindBuffer(GL_ARRAY_BUFFER, map_data.vbo_array[map_data.vo_retreat_unit_arrow]);
162 glBufferData(GL_ARRAY_BUFFER, sizeof(curved_line_vertex) * map_data.retreat_unit_arrow_vertices.size(), map_data.retreat_unit_arrow_vertices.data(), GL_STATIC_DRAW);
163 }
164 if(!map_data.strategy_unit_arrow_vertices.empty()) {
165 glBindBuffer(GL_ARRAY_BUFFER, map_data.vbo_array[map_data.vo_strategy_unit_arrow]);
166 glBufferData(GL_ARRAY_BUFFER, sizeof(curved_line_vertex) * map_data.strategy_unit_arrow_vertices.size(), map_data.strategy_unit_arrow_vertices.data(), GL_STATIC_DRAW);
167 }
168 if(!map_data.objective_unit_arrow_vertices.empty()) {
169 glBindBuffer(GL_ARRAY_BUFFER, map_data.vbo_array[map_data.vo_objective_unit_arrow]);
170 glBufferData(GL_ARRAY_BUFFER, sizeof(curved_line_vertex) * map_data.objective_unit_arrow_vertices.size(), map_data.objective_unit_arrow_vertices.data(), GL_STATIC_DRAW);
171 }
172 if(!map_data.other_objective_unit_arrow_vertices.empty()) {
173 glBindBuffer(GL_ARRAY_BUFFER, map_data.vbo_array[map_data.vo_other_objective_unit_arrow]);
174 glBufferData(GL_ARRAY_BUFFER, sizeof(curved_line_vertex) * map_data.other_objective_unit_arrow_vertices.size(), map_data.other_objective_unit_arrow_vertices.data(), GL_STATIC_DRAW);
175 }
176 glBindBuffer(GL_ARRAY_BUFFER, 0);
177}
178
179void update_bbox(std::array<glm::vec2, 5>& bbox, glm::vec2 p) {
180 if(p.x <= bbox[1].x) {
181 bbox[1] = p;
182 } if(p.y <= bbox[2].y) {
183 bbox[2] = p;
184 } if(p.x >= bbox[3].x) {
185 bbox[3] = p;
186 } if(p.y >= bbox[4].y) {
187 bbox[4] = p;
188 }
189}
190
191bool is_inside_bbox(std::array<glm::vec2, 5>& bbox, glm::vec2 p) {
192 if(p.x <= bbox[1].x) {
193 return false;
194 } if(p.y <= bbox[2].y) {
195 return false;
196 } if(p.x >= bbox[3].x) {
197 return false;
198 } if(p.y >= bbox[4].y) {
199 return false;
200 }
201
202 return true;
203}
204
205void update_bbox_negative(std::array<glm::vec2, 5>& bbox, glm::vec2 p) {
206 if(!is_inside_bbox(bbox, p))
207 return;
208
209 //auto mp = p.get_mid_point();
210 //if(mp.x <= bbox[0].x)
211 // bbox[1].x = mp.x * 0.1f + bbox[1].x * 0.9f;
212 //if(mp.x >= bbox[0].x)
213 // bbox[3].x = mp.x * 0.1f + bbox[3].x * 0.9f;
214}
215
216dcon::nation_id get_top_overlord(sys::state& state, dcon::nation_id n) {
217 auto olr = state.world.nation_get_overlord_as_subject(n);
218 auto ol = state.world.overlord_get_ruler(olr);
219 auto ol_temp = n;
220
221 while(ol && state.world.nation_get_owned_province_count(ol) > 0) {
222 olr = state.world.nation_get_overlord_as_subject(ol);
223 ol_temp = ol;
224 ol = state.world.overlord_get_ruler(olr);
225 }
226
227 return ol_temp;
228}
229
231 auto& f = state.font_collection.get_font(state, text::font_selection::map_font);
232
233 // retroscipt
234 std::vector<text_line_generator_data> text_data;
235 std::vector<bool> visited(65536, false);
236 std::vector<bool> visited_sea_provinces(65536, false);
237 std::vector<bool> friendly_sea_provinces(65536, false);
238 std::vector<uint16_t> group_of_regions;
239
240 std::unordered_map<uint16_t, std::set<uint16_t>> regions_graph;
241
242 int samples_N = 200;
243 int samples_M = 100;
244 float step_x = float(map_data.size_x) / float(samples_N);
245 float step_y = float(map_data.size_y) / float(samples_M);
246
247 // generate additional points
248 /*std::vector<uint16_t> samples_regions;
249 for(int i = 0; i < samples_N; i++)
250 for(int j = 0; j < samples_M; j++) {
251 float x = float(i) * step_x;
252 float y = float(map_data.size_y) - float(j) * step_y;
253 auto idx = int32_t(y) * int32_t(map_data.size_x) + int32_t(x);
254
255 if(0 <= idx && size_t(idx) < map_data.province_id_map.size()) {
256 auto fat_id = dcon::fatten(state.world, province::from_map_id(map_data.province_id_map[idx]));
257 samples_regions.push_back(fat_id.get_connected_region_id());
258 } else {
259 samples_regions.push_back(0);
260 }
261 }*/
262
263 // generate graph of regions:
264 for(auto candidate : state.world.in_province) {
265 auto rid = candidate.get_connected_region_id();
266
267 auto nation = get_top_overlord(state, state.world.province_get_nation_from_province_ownership(candidate));
268
269 for(auto adj : candidate.get_province_adjacency()) {
270 auto indx = adj.get_connected_provinces(0) != candidate.id ? 0 : 1;
271 auto neighbor = adj.get_connected_provinces(indx);
272
273 // if sea, try to jump to the next province
274 if(neighbor.id.index() >= state.province_definitions.first_sea_province.index()) {
275 for(auto adj_of_neighbor : neighbor.get_province_adjacency()) {
276 auto indx2 = adj_of_neighbor.get_connected_provinces(0) != neighbor.id ? 0 : 1;
277 auto neighbor_of_neighbor = adj_of_neighbor.get_connected_provinces(indx2);
278
279 // check for a "cut line"
280 glm::vec2 point_candidate = candidate.get_mid_point();
281 glm::vec2 point_potential_friend = neighbor_of_neighbor.get_mid_point();
282
283 if(glm::distance(point_candidate, point_potential_friend) > map_data.size_x * 0.5f) {
284 // do nothing
285 } else if(neighbor_of_neighbor.id.index() < state.province_definitions.first_sea_province.index()) {
286 auto nation_2 = get_top_overlord(state, state.world.province_get_nation_from_province_ownership(neighbor_of_neighbor));
287 if(nation == nation_2)
288 regions_graph[rid].insert(neighbor_of_neighbor.get_connected_region_id());
289 }
290 }
291 } else {
292 auto nation_2 = get_top_overlord(state, state.world.province_get_nation_from_province_ownership(neighbor));
293 if(nation == nation_2)
294 regions_graph[rid].insert(neighbor.get_connected_region_id());
295 }
296 }
297 }
298 for(auto p : state.world.in_province) {
299 if(p.id.index() >= state.province_definitions.first_sea_province.index())
300 break;
301 auto rid = p.get_connected_region_id();
302 if(visited[uint16_t(rid)])
303 continue;
304 visited[uint16_t(rid)] = true;
305
306 auto n = p.get_nation_from_province_ownership();
307 n = get_top_overlord(state, n.id);
308
309 //flood fill regions
310 group_of_regions.clear();
311 group_of_regions.push_back(rid);
312 int first_index = 0;
313 int vacant_index = 1;
314 while(first_index < vacant_index) {
315 auto current_region = group_of_regions[first_index];
316 first_index++;
317 for(auto neighbour_region : regions_graph[current_region]) {
318 if(!visited[neighbour_region]) {
319 group_of_regions.push_back(neighbour_region);
320 visited[neighbour_region] = true;
321 vacant_index++;
322 }
323 }
324 }
325 if(!n || n.get_owned_province_count() == 0)
326 continue;
327
328 auto nation_name = text::produce_simple_string(state, text::get_name(state, n));
329 auto prefix_remove = text::produce_simple_string(state, "map_remove_prefix");
330 if(nation_name.starts_with(prefix_remove)) {
331 nation_name.erase(0, prefix_remove.size());
332 }
333 auto acronym_expand = text::produce_simple_string(state, "map_expand_acronym");
334 if(acronym_expand.size() > 0 && nation_name.starts_with(acronym_expand)) {
335 nation_name.erase(0, acronym_expand.size());
336 auto acronym_expand_to = text::produce_simple_string(state, "map_expand_acronym_to");
337 nation_name.insert(0, acronym_expand_to.data(), acronym_expand_to.size());
338 }
339
340 std::string name = nation_name;
341 bool connected_to_capital = false;
342 for(auto visited_region : group_of_regions) {
343 if(n.get_capital().get_connected_region_id() == visited_region) {
344 connected_to_capital = true;
345 }
346 }
349 text::add_to_substitution_map(sub, text::variable_type::country, std::string_view(nation_name));
351 text::add_to_substitution_map(sub, text::variable_type::state, p.get_state_membership());
352 text::add_to_substitution_map(sub, text::variable_type::continentname, p.get_continent().get_name());
353 if(!connected_to_capital) {
354 // Adjective + " " + Continent
355 name = text::resolve_string_substitution(state, "map_label_adj_continent", sub);
356 // 66% of the provinces correspond to a single national identity
357 // then it gets named after that identity
358 ankerl::unordered_dense::map<int32_t, uint32_t> map;
359 uint32_t total_provinces = 0;
360 dcon::province_id last_province;
361 bool in_same_state = true;
362 for(auto visited_region : group_of_regions) {
363 for(auto candidate : state.world.in_province) {
364 if(candidate.get_connected_region_id() == visited_region) {
365 if(candidate.get_state_membership() != p.get_state_membership())
366 in_same_state = false;
367 ++total_provinces;
368 for(const auto core : candidate.get_core_as_province()) {
369 uint32_t v = 1;
370 if(auto const it = map.find(core.get_identity().id.index()); it != map.end()) {
371 v += it->second;
372 }
373 map.insert_or_assign(core.get_identity().id.index(), v);
374 }
375 }
376 }
377 }
378 if(in_same_state == true) {
379 name = text::resolve_string_substitution(state, "map_label_adj_state", sub);
380 }
381 if(total_provinces <= 2) {
382 // Adjective + Province name
383 name = text::resolve_string_substitution(state, "map_label_adj_province", sub);
384 } else {
385 for(const auto& e : map) {
386 if(float(e.second) / float(total_provinces) >= 0.75f) {
387 // Adjective + " " + National identity
388 auto const nid = dcon::national_identity_id(dcon::national_identity_id::value_base_t(e.first));
389 if(auto k = state.world.national_identity_get_name(nid); state.key_is_localized(k)) {
390 if(nid == n.get_primary_culture().get_group_from_culture_group_membership().get_identity_from_cultural_union_of()
391 || nid == n.get_identity_from_identity_holder()) {
392 if(n.get_capital().get_continent() == p.get_continent()) {
393 //cultural union tag -> use our name
394 name = text::produce_simple_string(state, text::get_name(state, n));
395 //Get cardinality
396 auto p1 = n.get_capital().get_mid_point();
397 auto p2 = p.get_mid_point();
398 auto radians = glm::atan(p1.y - p2.y, p2.x - p1.x);
399 auto degrees = std::fmod(glm::degrees(radians) + 45.f, 360.f);
400 if(degrees < 0.f) {
401 degrees = 360.f + degrees;
402 }
403 assert(degrees >= 0.f && degrees <= 360.f);
404 //fallback just in the very unlikely case
405 name = text::resolve_string_substitution(state, "map_label_adj_state", sub);
406 if(degrees >= 0.f && degrees < 90.f) {
407 name = text::resolve_string_substitution(state, "map_label_east_country", sub);
408 } else if(degrees >= 90.f && degrees < 180.f) {
409 name = text::resolve_string_substitution(state, "map_label_south_country", sub);
410 } else if(degrees >= 180.f && degrees < 270.f) {
411 name = text::resolve_string_substitution(state, "map_label_west_country", sub);
412 } else if(degrees >= 270.f && degrees < 360.f) {
413 name = text::resolve_string_substitution(state, "map_label_north_country", sub);
414 }
415 break;
416 }
417 } else {
418 text::add_to_substitution_map(sub, text::variable_type::tag, state.world.national_identity_get_name(nid));
419 name = text::resolve_string_substitution(state, "map_label_adj_tag", sub);
420 }
421 }
422 }
423 }
424 }
425 }
426 //name = "جُمْهُورِيَّة ٱلْعِرَاق";
427 //name = "在标准状况下";
428 if(name.empty())
429 continue;
430
431 float rough_box_left = std::numeric_limits<float>::max();
432 float rough_box_right = 0;
433 float rough_box_bottom = std::numeric_limits<float>::max();
434 float rough_box_top = 0;
435
436 for(auto visited_region : group_of_regions) {
437 for(auto candidate : state.world.in_province) {
438 if(candidate.get_connected_region_id() == visited_region) {
439 glm::vec2 mid_point = candidate.get_mid_point();
440
441 if(mid_point.x < rough_box_left) {
442 rough_box_left = mid_point.x;
443 }
444 if(mid_point.x > rough_box_right) {
445 rough_box_right = mid_point.x;
446 }
447 if(mid_point.y < rough_box_bottom) {
448 rough_box_bottom = mid_point.y;
449 }
450 if(mid_point.y > rough_box_top) {
451 rough_box_top = mid_point.y;
452 }
453 }
454 }
455 }
456
457 if(rough_box_right - rough_box_left > map_data.size_x * 0.9f) {
458 continue;
459 }
460
461
462 std::vector<glm::vec2> points;
463 std::vector<glm::vec2> bad_points;
464
465 rough_box_bottom = std::max(0.f, rough_box_bottom - step_y);
466 rough_box_top = std::min(float(map_data.size_y), rough_box_top + step_y);
467 rough_box_left = std::max(0.f, rough_box_left - step_x);
468 rough_box_right = std::min(float(map_data.size_x), rough_box_right + step_x);
469
470 float rough_box_width = rough_box_right - rough_box_left;
471 float rough_box_height = rough_box_top - rough_box_bottom;
472
473 float rough_box_ratio = rough_box_width / rough_box_height;
474 float height_steps = 15.f;
475 float width_steps = std::max(10.f, height_steps * rough_box_ratio);
476
477 glm::vec2 local_step = glm::vec2(rough_box_width, rough_box_height) / glm::vec2(width_steps, height_steps);
478
479 float best_y = 0.f;
480 //float best_y_length = 0.f;
481 float counter_from_the_bottom = 0.f;
482 float best_y_length_real = 0.f;
483 float best_y_left_x = 0.f;
484
485 // prepare points for a local grid
486 for(int j = 0; j < height_steps; j++) {
487 float y = rough_box_bottom + j * local_step.y;
488
489 for(int i = 0; i < width_steps; i++) {
490 float x = rough_box_left + float(i) * local_step.x;
491 glm::vec2 candidate = { x, y };
492 auto idx = int32_t(y) * int32_t(map_data.size_x) + int32_t(x);
493 if(0 <= idx && size_t(idx) < map_data.province_id_map.size()) {
494 auto fat_id = dcon::fatten(state.world, province::from_map_id(map_data.province_id_map[idx]));
495 for(auto visited_region : group_of_regions) {
496 if(fat_id.get_connected_region_id() == visited_region) {
497 points.push_back(candidate);
498 }
499 }
500 }
501 }
502 }
503
504 float points_above = 0.f;
505
506 for(int j = 0; j < height_steps; j++) {
507 float y = rough_box_bottom + j * local_step.y;
508
509 float current_length = 0.f;
510 float left_x = (float)(map_data.size_x);
511
512 for(int i = 0; i < width_steps; i++) {
513 float x = rough_box_left + float(i) * local_step.x;
514
515 glm::vec2 candidate = { x, y };
516
517 auto idx = int32_t(y) * int32_t(map_data.size_x) + int32_t(x);
518 if(0 <= idx && size_t(idx) < map_data.province_id_map.size()) {
519 auto fat_id = dcon::fatten(state.world, province::from_map_id(map_data.province_id_map[idx]));
520 for(auto visited_region : group_of_regions)
521 if(fat_id.get_connected_region_id() == visited_region) {
522 points_above++;
523 current_length += local_step.x;
524 if(x < left_x) {
525 left_x = x;
526 }
527 }
528 }
529 }
530
531 if(points_above * 2.f > points.size()) {
532 //best_y_length = current_length_adjusted;
533 best_y_length_real = current_length;
534 best_y = y;
535 best_y_left_x = left_x;
536 break;
537 }
538 }
539
540 if(points.size() < 2) {
541 continue;
542 }
543
544 // clustering points into num_of_clusters parts
545 size_t min_amount = 2;
546 if(state.user_settings.map_label == sys::map_label_mode::cubic) {
547 min_amount = 4;
548 }
549 if(state.user_settings.map_label == sys::map_label_mode::quadratic) {
550 min_amount = 3;
551 }
552 size_t num_of_clusters = std::max(min_amount, (size_t)(points.size() / 40));
553 size_t neighbours_requirement = std::clamp(int(std::log(num_of_clusters + 1)), 1, 3);
554
555 if(points.size() < num_of_clusters) {
556 num_of_clusters = points.size();
557 }
558
559 std::vector<glm::vec2> centroids;
560
561 for(size_t i = 0; i < num_of_clusters; i++) {
562 centroids.push_back(points[i]);
563 }
564
565 for(int step = 0; step < 100; step++) {
566 std::vector<glm::vec2> new_centroids;
567 std::vector<int> counters;
568 for(size_t i = 0; i < num_of_clusters; i++) {
569 new_centroids.push_back(glm::vec2(0, 0));
570 counters.push_back(0);
571 }
572
573
574 for(size_t i = 0; i < points.size(); i++) {
575 size_t closest = 0;
576 float best_dist = std::numeric_limits<float>::max();
577
578 //finding the closest centroid
579 for(size_t cluster = 0; cluster < num_of_clusters; cluster++) {
580 if(best_dist > glm::distance(centroids[cluster], points[i])) {
581 closest = cluster;
582 best_dist = glm::distance(centroids[cluster], points[i]);
583 }
584 }
585
586 new_centroids[closest] += points[i];
587 counters[closest] += 1;
588 }
589
590 for(size_t i = 0; i < num_of_clusters; i++) {
591 new_centroids[i] /= counters[i];
592 }
593
594 centroids = new_centroids;
595 }
596
597 std::vector<size_t> good_centroids;
598 float min_cross = 1;
599
600 std::vector<glm::vec2> final_points;
601
602 for(size_t i = 0; i < num_of_clusters; i++) {
603 float locally_good_distance = std::numeric_limits<float>::max();
604 for(size_t j = 0; j < num_of_clusters; j++) {
605 if(i == j) continue;
606 if(locally_good_distance > glm::distance(centroids[i], centroids[j]))
607 locally_good_distance = glm::distance(centroids[i], centroids[j]);
608 }
609
610 size_t counter_of_neighbors = 0;
611 for(size_t j = 0; j < num_of_clusters; j++) {
612 if(i == j) {
613 continue;
614 }
615 if(glm::distance(centroids[i], centroids[j]) < locally_good_distance * 1.2f) {
616 counter_of_neighbors++;
617 }
618 }
619 if(counter_of_neighbors >= neighbours_requirement) {
620 good_centroids.push_back(i);
621 final_points.push_back(centroids[i]);
622 }
623 }
624
625
626 if(good_centroids.size() <= 1) {
627 good_centroids.clear();
628 final_points.clear();
629 for(size_t i = 0; i < num_of_clusters; i++) {
630 good_centroids.push_back(i);
631 final_points.push_back(centroids[i]);
632 }
633 }
634
635 //throwing away bad cluster
636
637 std::vector<glm::vec2> good_points;
638
639 glm::vec2 sum_points = { 0.f, 0.f };
640
641 //OutputDebugStringA("\n\n");
642
643 for(auto point : points) {
644 size_t closest = 0;
645 float best_dist = std::numeric_limits<float>::max();
646
647 //finding the closest centroid
648 for(size_t cluster = 0; cluster < num_of_clusters; cluster++) {
649 if(best_dist > glm::distance(centroids[cluster], point)) {
650 closest = cluster;
651 best_dist = glm::distance(centroids[cluster], point);
652 }
653 }
654
655
656 bool is_good = false;
657 for(size_t i = 0; i < good_centroids.size(); i++) {
658 if(closest == good_centroids[i])
659 is_good = true;
660 }
661
662 if (is_good) {
663 good_points.push_back(point);
664 sum_points += point;
665 }
666 }
667
668 points = good_points;
669
670
671 //initial center:
672 glm::vec2 center = sum_points / (float)(points.size());
673
674 //calculate deviation
675 float total_sum = 0;
676
677 for(auto point : points) {
678 auto dif_v = point - center;
679 total_sum += dif_v.x * dif_v.x;
680 }
681
682 float mse = total_sum / points.size();
683 //ignore points beyond 3 std
684 float limit = mse * 3;
685
686 //calculate radius
687 //OutputDebugStringA("\n");
688 //OutputDebugStringA("\n");
689 float right = 0.f;
690 float left = 0.f;
691 float top = 0.f;
692 float bottom = 0.f;
693 for(auto point: points) {
694 //OutputDebugStringA((std::to_string(point.x) + ", " + std::to_string(point.y) + ", \n").c_str());
695 glm::vec2 current = point - center;
696 if((current.x > right) && (current.x * current.x < limit)) {
697 right = current.x;
698 }
699 if(current.y > top) {
700 top = current.y;
701 }
702 if((current.x < left) && (current.x * current.x < limit)) {
703 left = current.x;
704 }
705 if(current.y < bottom) {
706 bottom = current.y;
707 }
708 }
709
710 std::vector<glm::vec2> key_points;
711
712 key_points.push_back(center + glm::vec2(left, 0));
713 key_points.push_back(center + glm::vec2(0, bottom + local_step.y));
714 key_points.push_back(center + glm::vec2(right, 0));
715 key_points.push_back(center + glm::vec2(0, top - local_step.y));
716
717 std::array<glm::vec2, 5> key_provs{
718 center, //capital
719 center, //min x
720 center, //min y
721 center, //max x
722 center //max y
723 };
724
725 for(auto key_point : key_points) {
726 //if (glm::length(key_point - center) < 100.f * glm::length(eigenvector_1))
727 update_bbox(key_provs, key_point);
728 }
729
730
731 glm::vec2 map_size{ float(state.map_state.map_data.size_x), float(state.map_state.map_data.size_y) };
732 glm::vec2 basis{ key_provs[1].x, key_provs[2].y };
733 glm::vec2 ratio{ key_provs[3].x - key_provs[1].x, key_provs[4].y - key_provs[2].y };
734
735 if(ratio.x < 0.001f || ratio.y < 0.001f)
736 continue;
737
738 points = final_points;
739
740 //regularisation parameters
741 float lambda = 0.00001f;
742
743 float l_0 = 1.f;
744 float l_1 = 1.f;
745 float l_2 = 1 / 4.f;
746 float l_3 = 1 / 8.f;
747
748 // Populate common dataset points
749 std::vector<float> out_y;
750 std::vector<float> out_x;
751 std::vector<float> w;
752 std::vector<std::array<float, 4>> in_x;
753 std::vector<std::array<float, 4>> in_y;
754
755 for (auto point : points) {
756 auto e = point;
757
758 if(e.x < basis.x) {
759 continue;
760 }
761 if(e.x > basis.x + ratio.x) {
762 continue;
763 }
764
765 w.push_back(1);
766
767 e -= basis;
768 e /= ratio;
769 out_y.push_back(e.y);
770 out_x.push_back(e.x);
771 //w.push_back(10 * float(map_data.province_area[province::to_map_id(p2)]));
772
773 in_x.push_back(std::array<float, 4>{ l_0 * 1.f, l_1* e.x, l_1* e.x* e.x, l_3* e.x* e.x* e.x});
774 in_y.push_back(std::array<float, 4>{ l_0 * 1.f, l_1* e.y, l_1* e.y* e.y, l_3* e.y* e.y* e.y});
775 }
776
777 auto prepared_name = text::stored_glyphs(state, text::font_selection::map_font, name);
778 float name_extent = f.text_extent(state, prepared_name, 0, uint32_t(prepared_name.glyph_info.size()), 1);
779
780 bool use_quadratic = false;
781 // We will try cubic regression first, if that results in very
782 // weird lines, for example, lines that go to the infinite
783 // we will "fallback" to using a quadratic instead
784 if(state.user_settings.map_label == sys::map_label_mode::cubic) {
785 // Columns -> n
786 // Rows -> fixed size of 4
787 // [ x0^0 x0^1 x0^2 x0^3 ]
788 // [ x1^0 x1^1 x1^2 x1^3 ]
789 // [ ... ... ... ... ]
790 // [ xn^0 xn^1 xn^2 xn^3 ]
791 // [AB]i,j = sum(n, r=1, a_(i,r) * b(r,j))
792 // [ x0^0 x0^1 x0^2 x0^3 ] * [ x0^0 x1^0 ... xn^0 ] = [ a0 a1 a2 ... an ]
793 // [ x1^0 x1^1 x1^2 x1^3 ] * [ x0^1 x1^1 ... xn^1 ] = [ b0 b1 b2 ... bn ]
794 // [ ... ... ... ... ] * [ x0^2 x1^2 ... xn^2 ] = [ c0 c1 c2 ... cn ]
795 // [ xn^0 xn^1 xn^2 xn^3 ] * [ x0^3 x1^3 ... xn^3 ] = [ d0 d1 d2 ... dn ]
796 glm::mat4x4 m0(0.f);
797 for(glm::length_t i = 0; i < m0.length(); i++)
798 for(glm::length_t j = 0; j < m0.length(); j++)
799 for(glm::length_t r = 0; r < glm::length_t(in_x.size()); r++)
800 m0[i][j] += in_x[r][j] * w[r] * in_x[r][i] / in_x.size();
801 for(glm::length_t i = 0; i < m0.length(); i++)
802 m0[i][i] += lambda;
803 m0 = glm::inverse(m0); // m0 = (T(X)*X/n + I*lambda)^-1
804 glm::vec4 m1(0.f); // m1 = T(X)*Y / n
805 for(glm::length_t i = 0; i < m1.length(); i++)
806 for(glm::length_t r = 0; r < glm::length_t(in_x.size()); r++)
807 m1[i] += in_x[r][i] * w[r] * out_y[r] / in_x.size();
808 glm::vec4 mo(0.f); // mo = m1 * m0
809 for(glm::length_t i = 0; i < mo.length(); i++)
810 for(glm::length_t j = 0; j < mo.length(); j++)
811 mo[i] += m0[i][j] * m1[j];
812 // y = a + bx + cx^2 + dx^3
813 // y = mo[0] + mo[1] * x + mo[2] * x * x + mo[3] * x * x * x
814 auto poly_fn = [&](float x) {
815 return mo[0] * l_0 + mo[1] * x * l_1 + mo[2] * x * x * l_2 + mo[3] * x * x * x * l_3;
816 };
817 auto dx_fn = [&](float x) {
818 return mo[1] * l_1 + 2.f * mo[2] * x * l_2 + 3.f * mo[3] * x * x * l_3;
819 };
820 auto error_grad = [&](float x, float y) {
821 float error_linear = poly_fn(x) - y;
822 return glm::vec4(error_linear * error_linear * error_linear * error_linear * error_linear * mo);
823 };
824
825 auto regularisation_grad = [&]() {
826 return glm::vec4(0, 0, mo[2] / 4.f, mo[3] / 6.f);
827 };
828
829 float xstep = (1.f / float(name_extent * 2.f));
830 for(float x = 0.f; x <= 1.f; x += xstep) {
831 float y = poly_fn(x);
832 if(y < 0.f || y > 1.f) {
833 use_quadratic = true;
834 break;
835 }
836 // Steep change in curve => use cuadratic
837 float dx = glm::abs(dx_fn(x) - dx_fn(x - xstep));
838 if(dx / xstep >= 0.45f) {
839 use_quadratic = true;
840 break;
841 }
842 }
843
844 if(!use_quadratic)
845 text_data.emplace_back(std::move(prepared_name), mo, basis, ratio);
846 }
847
848 bool use_linear = false;
849 if(state.user_settings.map_label == sys::map_label_mode::quadratic || use_quadratic) {
850 // Now lets try quadratic
851 glm::mat3x3 m0(0.f);
852 for(glm::length_t i = 0; i < m0.length(); i++)
853 for(glm::length_t j = 0; j < m0.length(); j++)
854 for(glm::length_t r = 0; r < glm::length_t(in_x.size()); r++)
855 m0[i][j] += in_x[r][j] * w[r] * in_x[r][i] / in_x.size();
856 for(glm::length_t i = 0; i < m0.length(); i++)
857 m0[i][i] += lambda;
858 m0 = glm::inverse(m0); // m0 = (T(X)*X)^-1
859 glm::vec3 m1(0.f); // m1 = T(X)*Y
860 for(glm::length_t i = 0; i < m1.length(); i++)
861 for(glm::length_t r = 0; r < glm::length_t(in_x.size()); r++)
862 m1[i] += in_x[r][i] * w[r] * out_y[r] / in_x.size();
863 glm::vec3 mo(0.f); // mo = m1 * m0
864 for(glm::length_t i = 0; i < mo.length(); i++)
865 for(glm::length_t j = 0; j < mo.length(); j++)
866 mo[i] += m0[i][j] * m1[j];
867 // y = a + bx + cx^2
868 // y = mo[0] + mo[1] * x + mo[2] * x * x
869 auto poly_fn = [&](float x) {
870 return mo[0] * l_0 + mo[1] * x * l_1 + mo[2] * x * x * l_2;
871 };
872 auto dx_fn = [&](float x) {
873 return mo[1] * l_1 + 2.f * mo[2] * x * l_2;
874 };
875 float xstep = (1.f / float(name_extent * 2.f));
876 for(float x = 0.f; x <= 1.f; x += xstep) {
877 float y = poly_fn(x);
878 if(y < 0.f || y > 1.f) {
879 use_linear = true;
880 break;
881 }
882 // Steep change in curve => use cuadratic
883 float dx = glm::abs(dx_fn(x) - dx_fn(x - xstep));
884 if(dx / xstep >= 0.45f) {
885 use_linear = true;
886 break;
887 }
888 }
889 if(!use_linear)
890 text_data.emplace_back(std::move(prepared_name), glm::vec4(mo, 0.f), basis, ratio);
891 }
892
893 if(state.user_settings.map_label == sys::map_label_mode::linear || use_linear) {
894 // Now lets try linear
895 glm::mat2x2 m0(0.f);
896 for(glm::length_t i = 0; i < m0.length(); i++)
897 for(glm::length_t j = 0; j < m0.length(); j++)
898 for(glm::length_t r = 0; r < glm::length_t(in_x.size()); r++)
899 m0[i][j] += in_x[r][j] * w[r] * in_x[r][i];
900 for(glm::length_t i = 0; i < m0.length(); i++)
901 m0[i][i] += lambda;
902 m0 = glm::inverse(m0); // m0 = (T(X)*X)^-1
903 glm::vec2 m1(0.f); // m1 = T(X)*Y
904 for(glm::length_t i = 0; i < m1.length(); i++)
905 for(glm::length_t r = 0; r < glm::length_t(in_x.size()); r++)
906 m1[i] += in_x[r][i] * w[r] * out_y[r];
907 glm::vec2 mo(0.f); // mo = m1 * m0
908 for(glm::length_t i = 0; i < mo.length(); i++)
909 for(glm::length_t j = 0; j < mo.length(); j++)
910 mo[i] += m0[i][j] * m1[j];
911
912 // y = a + bx
913 // y = mo[0] + mo[1] * x
914 auto poly_fn = [&](float x) {
915 return mo[0] * l_0 + mo[1] * x * l_1;
916 };
917
918
919 // check if this is really better than taking the longest horizontal
920
921 // firstly check if we are already horizontal
922 if(abs(mo[1]) > 0.05) {
923 // calculate where our line will start and end:
924 float left_side = 0.f;
925 float right_side = 1.f;
926
927 if(mo[1] > 0.01f) {
928 left_side = -mo[0] / mo[1];
929 right_side = (1.f - mo[0]) / mo[1];
930 } else if(mo[1] < -0.01f) {
931 left_side = (1.f - mo[0]) / mo[1];
932 right_side = -mo[0] / mo[1];
933 }
934
935 left_side = std::clamp(left_side, 0.f, 1.f);
936 right_side = std::clamp(right_side, 0.f, 1.f);
937
938 float length_in_box_units = glm::length(ratio * glm::vec2(poly_fn(left_side), poly_fn(right_side)));
939
940 if(best_y_length_real * 1.05f >= length_in_box_units) {
941 basis.x = best_y_left_x;
942 ratio.x = best_y_length_real;
943 mo[0] = (best_y - basis.y) / ratio.y;
944 mo[1] = 0;
945 }
946 }
947
948
949 if(ratio.x <= map_size.x * 0.75f && ratio.y <= map_size.y * 0.75f)
950 text_data.emplace_back(std::move(prepared_name), glm::vec4(mo, 0.f, 0.f), basis, ratio);
951 }
952 }
953 map_data.set_text_lines(state, text_data);
954
955 if(state.cheat_data.province_names) {
956 std::vector<text_line_generator_data> p_text_data;
957 for(auto p : state.world.in_province) {
958 if(p.get_name()) {
959 std::string name = text::produce_simple_string(state, p.get_name());
960 p_text_data.emplace_back(text::stored_glyphs(state, text::font_selection::map_font, name), glm::vec4(0.f, 0.f, 0.f, 0.f), p.get_mid_point() - glm::vec2(5.f, 0.f), glm::vec2(10.f, 10.f));
961 }
962 }
963 map_data.set_province_text_lines(state, p_text_data);
964 }
965}
966
968 std::chrono::time_point<std::chrono::steady_clock> now = std::chrono::steady_clock::now();
969 // Set the last_update_time if it hasn't been set yet
970 if(last_update_time == std::chrono::time_point<std::chrono::steady_clock>{})
971 last_update_time = now;
972
974
975 // Update railroads, only if railroads are being built and we have 'em enabled
976 if(state.user_settings.railroads_enabled && state.railroad_built.load(std::memory_order::acquire)) {
977 state.map_state.map_data.update_railroad_paths(state);
978 state.railroad_built.store(false, std::memory_order::release);
979 }
980
981 auto microseconds_since_last_update = std::chrono::duration_cast<std::chrono::microseconds>(now - last_update_time);
982 float seconds_since_last_update = (float)(microseconds_since_last_update.count() / 1e6);
983 last_update_time = now;
984
985 time_counter += seconds_since_last_update;
986 time_counter = (float)std::fmod(time_counter, 600.f); // Reset it after every 10 minutes
987
989 glm::vec2 arrow_key_velocity_vector{};
991 arrow_key_velocity_vector.x -= 1.f;
992 }
994 arrow_key_velocity_vector.x += 1.f;
995 }
996 if (up_arrow_key_down) {
997 arrow_key_velocity_vector.y -= 1.f;
998 }
1000 arrow_key_velocity_vector.y += 1.f;
1001 }
1002 arrow_key_velocity_vector = glm::normalize(arrow_key_velocity_vector);
1003 arrow_key_velocity_vector *= 0.175f;
1004 if(shift_key_down)
1005 arrow_key_velocity_vector *= glm::e<float>();
1006 pos_velocity += arrow_key_velocity_vector;
1007 }
1008
1009 if(state.user_settings.mouse_edge_scrolling) {
1010 glm::vec2 mouse_pos_percent{ state.mouse_x_position / float(state.x_size), state.mouse_y_position / float(state.y_size) };
1011 glm::vec2 cursor_velocity_vector{ 0.0f, 0.0f };
1012
1013 //check if mouse is at edge of screen, in order to move the map
1014 if(mouse_pos_percent.x < 0.02f) {
1015 cursor_velocity_vector.x -= 1.f;
1016 } else if(mouse_pos_percent.x > 0.98f) {
1017 cursor_velocity_vector.x += 1.f;
1018 }
1019 if(mouse_pos_percent.y < 0.02f) {
1020 cursor_velocity_vector.y -= 1.f;
1021 } else if(mouse_pos_percent.y > 0.98f) {
1022 cursor_velocity_vector.y += 1.f;
1023 }
1024
1025 // check if the vector length is not zero before normalizing
1026 if(glm::length(cursor_velocity_vector) != 0.0f) {
1027 cursor_velocity_vector = glm::normalize(cursor_velocity_vector);
1028 cursor_velocity_vector *= 0.175f;
1029 pos_velocity += cursor_velocity_vector;
1030 }
1031 }
1032
1033 pos_velocity /= 1.125;
1034
1035 glm::vec2 velocity = pos_velocity * (seconds_since_last_update / zoom);
1036 velocity.x *= float(map_data.size_y) / float(map_data.size_x);
1037 pos += velocity;
1038
1039 pos.x = glm::mod(pos.x, 1.f);
1040 pos.y = glm::clamp(pos.y, 0.f, 1.f);
1041
1042 glm::vec2 mouse_pos{ state.mouse_x_position, state.mouse_y_position };
1043 glm::vec2 screen_size{ state.x_size, state.y_size };
1044 glm::vec2 screen_center = screen_size / 2.f;
1045 auto view_mode = current_view(state);
1046 glm::vec2 pos_before_zoom;
1047 bool valid_pos = screen_to_map(mouse_pos, screen_size, view_mode, pos_before_zoom);
1048
1049 auto zoom_diff = (zoom_change * seconds_since_last_update) / (1 / zoom);
1050 zoom += zoom_diff;
1051 zoom_change *= std::exp(-seconds_since_last_update * state.user_settings.zoom_speed);
1052 zoom = glm::clamp(zoom, min_zoom, max_zoom);
1053
1054 glm::vec2 pos_after_zoom;
1055 if(valid_pos && screen_to_map(mouse_pos, screen_size, view_mode, pos_after_zoom)) {
1056 switch(state.user_settings.zoom_mode) {
1058 pos += pos_before_zoom - pos_after_zoom;
1059 break;
1061 pos -= pos_before_zoom - pos_after_zoom;
1062 break;
1064 if(zoom_change < 0.f) {
1065 pos -= pos_before_zoom - pos_after_zoom;
1066 } else {
1067 pos += pos_before_zoom - pos_after_zoom;
1068 }
1069 break;
1071 if(zoom_change < 0.f) {
1072 pos += pos_before_zoom - pos_after_zoom;
1073 } else {
1074 pos -= pos_before_zoom - pos_after_zoom;
1075 }
1076 break;
1078 //no pos change
1079 break;
1080 default:
1081 break;
1082 }
1083 }
1084
1085 static float keyboard_zoom_change = 0.f;
1086 if(pgup_key_down) {
1087 keyboard_zoom_change += 0.1f;
1088 }
1089 if(pgdn_key_down) {
1090 keyboard_zoom_change -= 0.1f;
1091 }
1092
1093 glm::vec2 pos_before_keyboard_zoom;
1094 valid_pos = screen_to_map(screen_center, screen_size, view_mode, pos_before_keyboard_zoom);
1095
1096 auto keyboard_zoom_diff = (keyboard_zoom_change * seconds_since_last_update) / (1 / zoom);
1097 zoom += keyboard_zoom_diff;
1098 keyboard_zoom_change *= std::exp(-seconds_since_last_update * state.user_settings.zoom_speed);
1099 zoom = glm::clamp(zoom, min_zoom, max_zoom);
1100
1101 glm::vec2 pos_after_keyboard_zoom;
1102 if(valid_pos && screen_to_map(screen_center, screen_size, view_mode, pos_after_keyboard_zoom)) {
1103 pos += pos_before_keyboard_zoom - pos_after_keyboard_zoom;
1104 }
1105
1106
1107 globe_rotation = glm::rotate(glm::mat4(1.f), (0.25f - pos.x) * 2 * glm::pi<float>(), glm::vec3(0, 0, 1));
1108 // Rotation axis
1109 glm::vec3 axis = glm::vec3(globe_rotation * glm::vec4(1, 0, 0, 0));
1110 axis.z = 0;
1111 axis = glm::normalize(axis);
1112 axis.y *= -1;
1113 globe_rotation = glm::rotate(globe_rotation, (-pos.y + 0.5f) * glm::pi<float>(), axis);
1114
1115
1120 }
1121}
1122
1123void map_state::set_province_color(std::vector<uint32_t> const& prov_color, map_mode::mode new_map_mode) {
1124 active_map_mode = new_map_mode;
1125 map_data.set_province_color(prov_color);
1126}
1127
1130}
1131
1133 switch (keycode) {
1135 left_arrow_key_down = true;
1136 break;
1138 right_arrow_key_down = true;
1139 break;
1141 up_arrow_key_down = true;
1142 break;
1144 down_arrow_key_down = true;
1145 break;
1147 pgup_key_down = true;
1148 break;
1150 pgdn_key_down = true;
1151 break;
1153 shift_key_down = true;
1154 break;
1155 default:
1156 break;
1157 }
1158}
1159
1161 switch(keycode) {
1163 left_arrow_key_down = false;
1164 break;
1166 right_arrow_key_down = false;
1167 break;
1169 up_arrow_key_down = false;
1170 break;
1172 down_arrow_key_down = false;
1173 break;
1175 pgup_key_down = false;
1176 break;
1178 pgdn_key_down = false;
1179 break;
1181 shift_key_down = false;
1182 break;
1183 default:
1184 break;
1185 }
1186}
1187
1188void map_state::set_pos(glm::vec2 new_pos) {
1189 pos.x = glm::mod(new_pos.x, 1.f);
1190 pos.y = glm::clamp(new_pos.y, 0.f, 1.0f);
1191}
1192
1193void map_state::center_map_on_province(sys::state& state, dcon::province_id p) {
1194 if(!p)
1195 return;
1196
1197 auto map_pos = state.world.province_get_mid_point(p);
1198 map_pos.x /= float(map_data.size_x);
1199 map_pos.y /= float(map_data.size_y);
1200 map_pos.y = 1.0f - map_pos.y;
1201 set_pos(map_pos);
1202}
1203
1204void map_state::on_mouse_wheel(int32_t x, int32_t y, int32_t screen_size_x, int32_t screen_size_y, sys::key_modifiers mod,
1205 float amount) {
1206 constexpr auto zoom_speed_factor = 15.f;
1207
1208 zoom_change = (amount / 5.f) * zoom_speed_factor;
1209}
1210
1211void map_state::on_mouse_move(int32_t x, int32_t y, int32_t screen_size_x, int32_t screen_size_y, sys::key_modifiers mod) {
1212 auto mouse_pos = glm::vec2(x, y);
1213 auto screen_size = glm::vec2(screen_size_x, screen_size_y);
1214 if(is_dragging) { // Drag the map with middlemouse
1215 glm::vec2 map_pos;
1216 screen_to_map(mouse_pos, screen_size, map_view::flat, map_pos);
1217
1218 set_pos(pos + last_camera_drag_pos - glm::vec2(map_pos));
1219 }
1220 glm::vec2 mouse_diff = glm::abs(last_unit_box_drag_pos - mouse_pos);
1221 if((mouse_diff.x > std::ceil(screen_size_x * 0.0025f) || mouse_diff.y > std::ceil(screen_size_y * 0.0025f))
1222 && left_mouse_down)
1223 {
1224 auto pos1 = last_unit_box_drag_pos / screen_size;
1225 auto pos2 = mouse_pos / screen_size;
1226 auto pixel_size = glm::vec2(1) / screen_size;
1227 map_data.set_drag_box(true, pos1, pos2, pixel_size);
1228 } else {
1229 map_data.set_drag_box(false, {}, {}, {});
1230 }
1231}
1232
1233bool map_state::screen_to_map(glm::vec2 screen_pos, glm::vec2 screen_size, map_view view_mode, glm::vec2& map_pos) {
1234 if(view_mode == map_view::globe) {
1235 screen_pos -= screen_size * 0.5f;
1236 screen_pos /= screen_size;
1237 screen_pos.x *= screen_size.x / screen_size.y;
1238
1239 float cursor_radius = glm::length(screen_pos);
1240 glm::vec3 cursor_pos = glm::vec3(screen_pos.x, -10 * zoom, -screen_pos.y);
1241 glm::vec3 cursor_direction = glm::vec3(0, 1, 0);
1242 glm::vec3 sphere_center = glm::vec3(0, 0, 0);
1243 float sphere_radius = zoom / glm::pi<float>();
1244
1245 glm::vec3 intersection_pos;
1246 glm::vec3 intersection_normal;
1247
1248 if(glm::intersectRaySphere(cursor_pos, cursor_direction, sphere_center, sphere_radius, intersection_pos,
1249 intersection_normal)) {
1250 intersection_pos = glm::mat3(glm::inverse(globe_rotation)) * intersection_pos;
1251 float theta = std::acos(std::clamp(intersection_pos.z / glm::length(intersection_pos), -1.f, 1.f));
1252 float phi = std::atan2(intersection_pos.y, intersection_pos.x);
1253 float pi = glm::pi<float>();
1254 map_pos = glm::vec2((phi / (2 * pi)) + 0.5f, theta / pi);
1255 return true;
1256 }
1257 return false;
1258 } else if (view_mode == map_view::globe_perspect) {
1259 float aspect_ratio = screen_size.x / screen_size.y;
1260 float pi = glm::pi<float>();
1261
1262 //normalize screen
1263 screen_pos -= screen_size * 0.5f;
1264 screen_pos /= -screen_size;
1265
1266 //perspective values
1267 float near_plane = 0.1f;
1268 float far_plane = 1.2f;
1269 float right = near_plane * tan(pi / 6.f) / zoom * aspect_ratio * 2.f;
1270 float top = near_plane * tan(pi / 6.f) / zoom * 2.f;
1271
1272 //transform screen plane to near plane
1273 screen_pos.x *= right;
1274 screen_pos.y *= top;
1275
1276 //set up data for glm::intersectRaySphere
1277 float cursor_radius = glm::length(screen_pos);
1278 glm::vec3 camera = glm::vec3(0.f, 0.f, 0.f);
1279 glm::vec3 cursor_pos = glm::vec3(screen_pos.x, screen_pos.y, -near_plane);
1280 glm::vec3 cursor_direction = glm::normalize(cursor_pos);
1281 glm::vec3 sphere_center = glm::vec3(0.f, 0.f, -1.2f);
1282 float sphere_radius = 1.f / pi;
1283
1284
1285 glm::vec3 intersection_pos;
1286 glm::vec3 intersection_normal;
1287
1288 if(glm::intersectRaySphere(camera, cursor_direction, sphere_center, sphere_radius, intersection_pos,
1289 intersection_normal)) {
1290 intersection_pos -= sphere_center;
1291
1292 intersection_pos = glm::vec3(-intersection_pos.x, -intersection_pos.z, intersection_pos.y);
1293
1294 intersection_pos = glm::mat3(glm::inverse(globe_rotation)) * intersection_pos;
1295 float theta = std::acos(std::clamp(intersection_pos.z / glm::length(intersection_pos), -1.f, 1.f));
1296 float phi = std::atan2(intersection_pos.y, intersection_pos.x);
1297 map_pos = glm::vec2((phi / (2.f * pi)) + 0.5f, theta / pi);
1298 return true;
1299 }
1300 return false;
1301 } else {
1302 screen_pos -= screen_size * 0.5f;
1303 screen_pos /= screen_size;
1304 screen_pos.x *= screen_size.x / screen_size.y;
1305 screen_pos.x *= float(map_data.size_y) / float(map_data.size_x);
1306
1307 screen_pos /= zoom;
1308 screen_pos += pos;
1309 map_pos = screen_pos;
1310 return (map_pos.x >= 0 && map_pos.y >= 0 && map_pos.x <= map_data.size_x && map_pos.y <= map_data.size_y);
1311 }
1312}
1313
1314void map_state::on_mbuttom_down(int32_t x, int32_t y, int32_t screen_size_x, int32_t screen_size_y, sys::key_modifiers mod) {
1315 auto mouse_pos = glm::vec2(x, y);
1316 auto screen_size = glm::vec2(screen_size_x, screen_size_y);
1317
1318 glm::vec2 map_pos;
1319 screen_to_map(mouse_pos, screen_size, map_view::flat, map_pos);
1320
1321 last_camera_drag_pos = map_pos;
1322 is_dragging = true;
1323 pos_velocity = glm::vec2(0);
1324}
1325
1326void map_state::on_mbuttom_up(int32_t x, int32_t y, sys::key_modifiers mod) {
1327 is_dragging = false;
1328}
1329
1330void map_state::on_lbutton_down(sys::state& state, int32_t x, int32_t y, int32_t screen_size_x, int32_t screen_size_y,
1331 sys::key_modifiers mod) {
1332 left_mouse_down = true;
1333 map_data.set_drag_box(false, {}, {}, {});
1334 last_unit_box_drag_pos = glm::vec2(x, y);
1335}
1336
1337void map_state::on_lbutton_up(sys::state& state, int32_t x, int32_t y, int32_t screen_size_x, int32_t screen_size_y,
1338 sys::key_modifiers mod) {
1339 left_mouse_down = false;
1340 map_data.set_drag_box(false, {}, {}, {});
1341 auto mouse_pos = glm::vec2(x, y);
1342 glm::vec2 mouse_diff = glm::abs(last_unit_box_drag_pos - mouse_pos);
1343 if(mouse_diff.x <= std::ceil(screen_size_x * 0.0025f) && mouse_diff.y <= std::ceil(screen_size_y * 0.0025f)) {
1344 auto screen_size = glm::vec2(screen_size_x, screen_size_y);
1345 glm::vec2 map_pos;
1346 if(!screen_to_map(mouse_pos, screen_size, current_view(state), map_pos)) {
1347 return;
1348 }
1349 map_pos *= glm::vec2(float(map_data.size_x), float(map_data.size_y));
1350 auto idx = int32_t(map_data.size_y - map_pos.y) * int32_t(map_data.size_x) + int32_t(map_pos.x);
1351 if(0 <= idx && size_t(idx) < map_data.province_id_map.size()) {
1353 state.user_settings.interface_volume * state.user_settings.master_volume);
1354 auto fat_id = dcon::fatten(state.world, province::from_map_id(map_data.province_id_map[idx]));
1355 if(map_data.province_id_map[idx] < province::to_map_id(state.province_definitions.first_sea_province)) {
1357 //state.current_scene.
1358 } else {
1359 set_selected_province(dcon::province_id{});
1360 }
1361 } else {
1362 set_selected_province(dcon::province_id{});
1363 }
1364 }
1365}
1366
1367
1368void map_state::on_rbutton_down(sys::state& state, int32_t x, int32_t y, int32_t screen_size_x, int32_t screen_size_y,
1369 sys::key_modifiers mod) {
1370 auto mouse_pos = glm::vec2(x, y);
1371 auto screen_size = glm::vec2(screen_size_x, screen_size_y);
1372 glm::vec2 map_pos;
1373 if(!screen_to_map(mouse_pos, screen_size, current_view(state), map_pos)) {
1374 return;
1375 }
1376 map_pos *= glm::vec2(float(map_data.size_x), float(map_data.size_y));
1377 auto idx = int32_t(map_data.size_y - map_pos.y) * int32_t(map_data.size_x) + int32_t(map_pos.x);
1378 if(0 <= idx && size_t(idx) < map_data.province_id_map.size()) {
1379
1380 } else {
1381 set_selected_province(dcon::province_id{});
1382 }
1383}
1384
1385dcon::province_id map_state::get_province_under_mouse(sys::state& state, int32_t x, int32_t y, int32_t screen_size_x, int32_t screen_size_y) {
1386 auto mouse_pos = glm::vec2(x, y);
1387 auto screen_size = glm::vec2(screen_size_x, screen_size_y);
1388 glm::vec2 map_pos;
1389 if(!map_state::screen_to_map(mouse_pos, screen_size, current_view(state), map_pos)) {
1390 return dcon::province_id{};
1391 }
1392 map_pos *= glm::vec2(float(map_data.size_x), float(map_data.size_y));
1393 auto idx = int32_t(map_data.size_y - map_pos.y) * int32_t(map_data.size_x) + int32_t(map_pos.x);
1394 if(0 <= idx && size_t(idx) < map_data.province_id_map.size()) {
1395 auto fat_id = dcon::fatten(state.world, province::from_map_id(map_data.province_id_map[idx]));
1396 //if(map_data.province_id_map[idx] < province::to_map_id(state.province_definitions.first_sea_province)) {
1398 /*} else {
1399 return dcon::province_id{};
1400 }*/
1401 } else {
1402 return dcon::province_id{};
1403 }
1404}
1405
1406bool map_state::map_to_screen(sys::state& state, glm::vec2 map_pos, glm::vec2 screen_size, glm::vec2& screen_pos) {
1407 switch(state.user_settings.map_is_globe) {
1409 {
1410 glm::vec3 cartesian_coords;
1411 float section = 200;
1412 float pi = glm::pi<float>();
1413 float angle_x1 = 2 * pi * std::floor(map_pos.x * section) / section;
1414 float angle_x2 = 2 * pi * std::floor(map_pos.x * section + 1) / section;
1415 if(!std::isfinite(angle_x1)) {
1416 assert(false);
1417 angle_x1 = 0.0f;
1418 }
1419 if(!std::isfinite(angle_x2)) {
1420 assert(false);
1421 angle_x2 = 0.0f;
1422 }
1423 if(!std::isfinite(map_pos.x)) {
1424 assert(false);
1425 map_pos.x = 0.0f;
1426 }
1427 if(!std::isfinite(map_pos.y)) {
1428 assert(false);
1429 map_pos.y = 0.0f;
1430 }
1431 cartesian_coords.x = std::lerp(std::cos(angle_x1), std::cos(angle_x2), std::fmod(map_pos.x * section, 1.f));
1432 cartesian_coords.y = std::lerp(std::sin(angle_x1), std::sin(angle_x2), std::fmod(map_pos.x * section, 1.f));
1433
1434 float angle_y = (1.f - map_pos.y) * pi;
1435 cartesian_coords.x *= std::sin(angle_y);
1436 cartesian_coords.y *= std::sin(angle_y);
1437 cartesian_coords.z = std::cos(angle_y);
1438 cartesian_coords = glm::mat3(globe_rotation) * cartesian_coords;
1439 cartesian_coords /= glm::pi<float>();
1440 cartesian_coords.x *= -1;
1441 cartesian_coords.y *= -1;
1442 if(cartesian_coords.y > 0) {
1443 return false;
1444 }
1445 cartesian_coords += glm::vec3(0.5f);
1446
1447 screen_pos = glm::vec2(cartesian_coords.x, cartesian_coords.z);
1448 screen_pos = (2.f * screen_pos - glm::vec2(1.f));
1449 screen_pos *= zoom;
1450 screen_pos.x *= screen_size.y / screen_size.x;
1451 screen_pos = ((screen_pos + glm::vec2(1.f)) * 0.5f);
1452 screen_pos *= screen_size;
1453 return true;
1454 }
1456 {
1457 float aspect_ratio = screen_size.x / screen_size.y;
1458
1459 glm::vec3 cartesian_coords;
1460 float section = 200;
1461 float angle_x1 = 2.f * glm::pi<float>() * std::floor(map_pos.x * section) / section;
1462 float angle_x2 = 2.f * glm::pi<float>() * std::floor(map_pos.x * section + 1) / section;
1463 if(!std::isfinite(angle_x1)) {
1464 assert(false);
1465 angle_x1 = 0.0f;
1466 }
1467 if(!std::isfinite(angle_x2)) {
1468 assert(false);
1469 angle_x2 = 0.0f;
1470 }
1471 if(!std::isfinite(map_pos.x)) {
1472 assert(false);
1473 map_pos.x = 0.0f;
1474 }
1475 if(!std::isfinite(map_pos.y)) {
1476 assert(false);
1477 map_pos.y = 0.0f;
1478 }
1479 cartesian_coords.x = std::lerp(std::cos(angle_x1), std::cos(angle_x2), std::fmod(map_pos.x * section, 1.f));
1480 cartesian_coords.y = std::lerp(std::sin(angle_x1), std::sin(angle_x2), std::fmod(map_pos.x * section, 1.f));
1481
1482 float angle_y = (map_pos.y) * glm::pi<float>();
1483 cartesian_coords.x *= std::sin(angle_y);
1484 cartesian_coords.y *= std::sin(angle_y);
1485 cartesian_coords.z = std::cos(angle_y);
1486
1487 glm::vec3 temp_vector = cartesian_coords;
1488
1489 // Apply rotation
1490 cartesian_coords.z *= -1;
1491 cartesian_coords = glm::mat3(globe_rotation) * cartesian_coords;
1492 cartesian_coords.z *= -1;
1493
1494 cartesian_coords /= glm::pi<float>(); // Will make the zoom be the same for the globe and flat map
1495 cartesian_coords.x *= -1;
1496 cartesian_coords.z *= -1;
1497
1498 float temp = cartesian_coords.z;
1499 cartesian_coords.z = cartesian_coords.y;
1500 cartesian_coords.y = temp;
1501
1502 // shift the globe away from camera
1503 cartesian_coords.z -= 1.2f;
1504 float near_plane = 0.1f;
1505
1506 // optimal far plane for culling out invisible part of a planet
1507 constexpr float tangent_length_square = 1.2f * 1.2f - 1 / glm::pi<float>() / glm::pi<float>();
1508 float far_plane = tangent_length_square / 1.2f;
1509
1510 float right = near_plane * tan(glm::pi<float>() / 6.f) / zoom;
1511 float top = near_plane * tan(glm::pi<float>() / 6.f) / zoom;
1512
1513 cartesian_coords.x *= near_plane / right;
1514 cartesian_coords.y *= near_plane / top;
1515
1516 // depth calculations just for reference
1517 float w = -cartesian_coords.z;
1518 cartesian_coords.z = -(far_plane + near_plane) / (far_plane - near_plane) * cartesian_coords.z - 2 * far_plane * near_plane / (far_plane - near_plane);
1519
1520 if(cartesian_coords.z > far_plane) {
1521 return false;
1522 }
1523
1524 screen_pos = glm::vec2(cartesian_coords.x, cartesian_coords.y) / w;
1525 //screen_pos = (2.f * screen_pos - glm::vec2(1.f));
1526 //screen_pos *= zoom;
1527 screen_pos.x *= screen_size.y / screen_size.x;
1528 screen_pos = ((screen_pos + glm::vec2(1.f)) * 0.5f);
1529 screen_pos *= screen_size;
1530 return true;
1531 }
1533 {
1534 map_pos -= pos;
1535
1536 if(map_pos.x >= 0.5f)
1537 map_pos.x -= 1.0f;
1538 if(map_pos.x < -0.5f)
1539 map_pos.x += 1.0f;
1540
1541 map_pos *= zoom;
1542
1543 map_pos.x *= float(map_data.size_x) / float(map_data.size_y);
1544 map_pos.x *= screen_size.y / screen_size.x;
1545 map_pos *= screen_size;
1546 map_pos += screen_size * 0.5f;
1547 screen_pos = map_pos;
1548 if(screen_pos.x >= float(std::numeric_limits<int16_t>::max() / 2))
1549 return false;
1550 if(screen_pos.x <= float(std::numeric_limits<int16_t>::min() / 2))
1551 return false;
1552 if(screen_pos.y >= float(std::numeric_limits<int16_t>::max() / 2))
1553 return false;
1554 if(screen_pos.y <= float(std::numeric_limits<int16_t>::min() / 2))
1555 return false;
1556 return true;
1557 }
1559 return false;
1560 default:
1561 return false;
1562 }
1563}
1564
1565glm::vec2 map_state::normalize_map_coord(glm::vec2 p) {
1566 auto new_pos = p / glm::vec2{ float(map_data.size_x), float(map_data.size_y) };
1567 new_pos.y = 1.f - new_pos.y;
1568 return new_pos;
1569}
1570
1571} // namespace map
std::vector< curved_line_vertex > retreat_unit_arrow_vertices
Definition: map.hpp:136
void load_map(sys::state &state)
Definition: map.cpp:2406
void set_selected_province(sys::state &state, dcon::province_id province_id)
Definition: map.cpp:1253
std::vector< GLsizei > strategy_unit_arrow_counts
Definition: map.hpp:142
static constexpr uint32_t vo_objective_unit_arrow
Definition: map.hpp:186
std::vector< curved_line_vertex > attack_unit_arrow_vertices
Definition: map.hpp:132
static constexpr uint32_t vo_unit_arrow
Definition: map.hpp:176
std::vector< GLint > objective_unit_arrow_starts
Definition: map.hpp:145
void set_province_text_lines(sys::state &state, std::vector< text_line_generator_data > const &data)
Definition: map.cpp:2006
std::vector< uint16_t > province_id_map
Definition: map.hpp:162
std::vector< curved_line_vertex > other_objective_unit_arrow_vertices
Definition: map.hpp:148
std::vector< GLsizei > other_objective_unit_arrow_counts
Definition: map.hpp:150
std::vector< curved_line_vertex > unit_arrow_vertices
Definition: map.hpp:128
std::vector< GLsizei > retreat_unit_arrow_counts
Definition: map.hpp:138
std::vector< curved_line_vertex > strategy_unit_arrow_vertices
Definition: map.hpp:140
static constexpr uint32_t vo_attack_unit_arrow
Definition: map.hpp:183
static constexpr uint32_t vo_other_objective_unit_arrow
Definition: map.hpp:187
std::vector< GLint > retreat_unit_arrow_starts
Definition: map.hpp:137
std::vector< GLsizei > objective_unit_arrow_counts
Definition: map.hpp:146
std::vector< GLint > attack_unit_arrow_starts
Definition: map.hpp:133
std::vector< GLsizei > unit_arrow_counts
Definition: map.hpp:130
void render(sys::state &state, glm::vec2 screen_size, glm::vec2 offset, float zoom, map_view map_view_mode, map_mode::mode active_map_mode, glm::mat3 globe_rotation, float time_counter)
Definition: map.cpp:449
static constexpr uint32_t vo_retreat_unit_arrow
Definition: map.hpp:184
void set_text_lines(sys::state &state, std::vector< text_line_generator_data > const &data)
Definition: map.cpp:1817
uint32_t size_y
Definition: map.hpp:169
static constexpr uint32_t vo_strategy_unit_arrow
Definition: map.hpp:185
GLuint vbo_array[vo_count]
Definition: map.hpp:190
uint32_t size_x
Definition: map.hpp:168
std::vector< GLsizei > attack_unit_arrow_counts
Definition: map.hpp:134
std::vector< GLint > strategy_unit_arrow_starts
Definition: map.hpp:141
std::vector< GLint > unit_arrow_starts
Definition: map.hpp:129
std::vector< GLint > other_objective_unit_arrow_starts
Definition: map.hpp:149
void set_drag_box(bool draw_box, glm::vec2 pos1, glm::vec2 pos2, glm::vec2 pixel_size)
Definition: map.cpp:1282
std::vector< curved_line_vertex > objective_unit_arrow_vertices
Definition: map.hpp:144
void set_province_color(std::vector< uint32_t > const &prov_color)
Definition: map.cpp:1259
float time_counter
Definition: map_state.hpp:67
bool map_to_screen(sys::state &state, glm::vec2 map_pos, glm::vec2 screen_size, glm::vec2 &screen_pos)
Definition: map_state.cpp:1406
bool up_arrow_key_down
Definition: map_state.hpp:86
glm::vec2 last_camera_drag_pos
Definition: map_state.hpp:75
void on_mouse_wheel(int32_t x, int32_t y, int32_t screen_size_x, int32_t screen_size_y, sys::key_modifiers mod, float amount)
Definition: map_state.cpp:1204
bool down_arrow_key_down
Definition: map_state.hpp:87
void on_lbutton_up(sys::state &state, int32_t x, int32_t y, int32_t screen_size_x, int32_t screen_size_y, sys::key_modifiers mod)
Definition: map_state.cpp:1337
void on_mbuttom_up(int32_t x, int32_t y, sys::key_modifiers mod)
Definition: map_state.cpp:1326
void on_mbuttom_down(int32_t x, int32_t y, int32_t screen_size_x, int32_t screen_size_y, sys::key_modifiers mod)
Definition: map_state.cpp:1314
bool screen_to_map(glm::vec2 screen_pos, glm::vec2 screen_size, map_view view_mode, glm::vec2 &map_pos)
Definition: map_state.cpp:1233
bool pgdn_key_down
Definition: map_state.hpp:83
void set_selected_province(dcon::province_id prov_id)
Definition: map_state.cpp:37
bool unhandled_province_selection
Definition: map_state.hpp:70
map_view current_view(sys::state &state)
Definition: map_state.cpp:27
bool pgup_key_down
Definition: map_state.hpp:82
bool shift_key_down
Definition: map_state.hpp:88
void update(sys::state &state)
Definition: map_state.cpp:967
glm::mat4 globe_rotation
Definition: map_state.hpp:76
std::chrono::time_point< std::chrono::steady_clock > last_update_time
Definition: map_state.hpp:64
dcon::province_id get_selected_province()
Definition: map_state.cpp:18
void on_key_up(sys::virtual_key keycode, sys::key_modifiers mod)
Definition: map_state.cpp:1160
glm::vec2 pos_velocity
Definition: map_state.hpp:74
void set_terrain_map_mode()
Definition: map_state.cpp:1128
void load_map(sys::state &state)
Definition: map_state.cpp:23
dcon::province_id selected_province
Definition: map_state.hpp:58
void on_rbutton_down(sys::state &state, int32_t x, int32_t y, int32_t screen_size_x, int32_t screen_size_y, sys::key_modifiers mod)
Definition: map_state.cpp:1368
float zoom_change
Definition: map_state.hpp:80
void on_key_down(sys::virtual_key keycode, sys::key_modifiers mod)
Definition: map_state.cpp:1132
void set_province_color(std::vector< uint32_t > const &prov_color, map_mode::mode map_mode)
Definition: map_state.cpp:1123
bool left_mouse_down
Definition: map_state.hpp:89
glm::vec2 last_unit_box_drag_pos
Definition: map_state.hpp:77
void on_lbutton_down(sys::state &state, int32_t x, int32_t y, int32_t screen_size_x, int32_t screen_size_y, sys::key_modifiers mod)
Definition: map_state.cpp:1330
glm::vec2 pos
Definition: map_state.hpp:73
glm::vec2 normalize_map_coord(glm::vec2 pos)
Definition: map_state.cpp:1565
map_mode::mode active_map_mode
Definition: map_state.hpp:57
void on_mouse_move(int32_t x, int32_t y, int32_t screen_size_x, int32_t screen_size_y, sys::key_modifiers mod)
Definition: map_state.cpp:1211
void center_map_on_province(sys::state &state, dcon::province_id)
Definition: map_state.cpp:1193
bool left_arrow_key_down
Definition: map_state.hpp:84
void render(sys::state &state, uint32_t screen_x, uint32_t screen_y)
Definition: map_state.cpp:42
dcon::province_id get_province_under_mouse(sys::state &state, int32_t x, int32_t y, int32_t screen_size_x, int32_t screen_size_y)
Definition: map_state.cpp:1385
bool right_arrow_key_down
Definition: map_state.hpp:85
display_data map_data
Definition: map_state.hpp:60
void set_pos(glm::vec2 pos)
Definition: map_state.cpp:1188
#define assert(condition)
Definition: debug.h:74
pop_satisfaction_wrapper_fat fatten(data_container const &c, pop_satisfaction_wrapper_id id) noexcept
void update_map_mode(sys::state &state)
Definition: map_modes.cpp:1021
void update_unit_arrows(sys::state &state, display_data &map_data)
Definition: map_state.cpp:80
glm::vec2 get_army_location(sys::state &state, dcon::province_id prov_id)
Definition: map_state.cpp:76
void make_army_path(sys::state &state, std::vector< map::curved_line_vertex > &buffer, dcon::army_id selected_army, float size_x, float size_y)
Definition: map.cpp:1616
glm::vec2 get_navy_location(sys::state &state, dcon::province_id prov_id)
Definition: map_state.cpp:69
glm::vec2 get_port_location(sys::state &state, dcon::province_id p)
Definition: map_state.cpp:49
bool is_inside_bbox(std::array< glm::vec2, 5 > &bbox, glm::vec2 p)
Definition: map_state.cpp:191
dcon::nation_id get_top_overlord(sys::state &state, dcon::nation_id n)
Definition: map_state.cpp:216
map_view
Definition: map_state.hpp:17
constexpr float max_zoom
Definition: constants.hpp:604
void update_bbox_negative(std::array< glm::vec2, 5 > &bbox, glm::vec2 p)
Definition: map_state.cpp:205
constexpr float min_zoom
Definition: constants.hpp:603
void make_navy_path(sys::state &state, std::vector< map::curved_line_vertex > &buffer, dcon::navy_id selected_navy, float size_x, float size_y)
Definition: map.cpp:1572
bool is_sea_province(sys::state &state, dcon::province_id prov_id)
Definition: map_state.cpp:65
void update_bbox(std::array< glm::vec2, 5 > &bbox, glm::vec2 p)
Definition: map_state.cpp:179
void update_text_lines(sys::state &state, display_data &map_data)
Definition: map_state.cpp:230
bool are_at_war(sys::state const &state, dcon::nation_id a, dcon::nation_id b)
Definition: military.cpp:475
constexpr dcon::province_id from_map_id(uint16_t id)
Definition: province.hpp:13
constexpr uint16_t to_map_id(dcon::province_id id)
Definition: province.hpp:10
audio_instance & get_random_province_select_sound(sys::state &state)
Definition: sound_nix.cpp:403
void play_interface_sound(sys::state &state, audio_instance &s, float volume)
Definition: sound_nix.cpp:203
virtual_key
Definition: constants.hpp:5
key_modifiers
Definition: constants.hpp:156
std::string resolve_string_substitution(sys::state &state, dcon::text_key source_text, substitution_map const &mp)
Definition: text.cpp:2113
void add_to_substitution_map(substitution_map &mp, variable_type key, substitution value)
Definition: text.cpp:1068
dcon::text_key get_adjective(sys::state &state, dcon::nation_id id)
Definition: text.cpp:890
ankerl::unordered_dense::map< uint32_t, substitution > substitution_map
Definition: text.hpp:794
std::string produce_simple_string(sys::state const &state, dcon::text_key id)
Definition: text.cpp:617
dcon::text_key get_name(sys::state &state, dcon::nation_id id)
Definition: text.cpp:880
uint uint32_t
int start_index
Definition: map.hpp:86
int count
Definition: map.hpp:87