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