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>
44 glm::vec2 offset = glm::vec2(glm::mod(
pos.x, 1.f) - 0.5f,
pos.y - 0.5f);
50 auto pt = state.world.province_get_port_to(p);
54 auto adj = state.world.get_province_adjacency_by_province_pair(p, pt);
56 auto id = adj.index();
57 auto& map_data = state.map_state.map_data;
58 auto&
border = map_data.borders[id];
60 glm::vec2 map_size = glm::vec2(map_data.size_x, map_data.size_y);
62 return vertex.position * map_size;
66 return prov_id.index() >= state.province_definitions.first_sea_province.index();
71 return state.world.province_get_mid_point(prov_id);
77 return state.world.province_get_mid_point(prov_id);
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)) {
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)) {
125 }
else if(state.local_player_nation && dest_controller &&
military::are_at_war(state, dest_controller, state.local_player_nation)) {
138 for(
auto selected_navy : state.selected_navies) {
139 if(state.world.navy_get_is_retreating(selected_navy)) {
176 glBindBuffer(GL_ARRAY_BUFFER, 0);
180 if(p.x <= bbox[1].x) {
182 }
if(p.y <= bbox[2].y) {
184 }
if(p.x >= bbox[3].x) {
186 }
if(p.y >= bbox[4].y) {
192 if(p.x <= bbox[1].x) {
194 }
if(p.y <= bbox[2].y) {
196 }
if(p.x >= bbox[3].x) {
198 }
if(p.y >= bbox[4].y) {
217 auto olr = state.world.nation_get_overlord_as_subject(n);
218 auto ol = state.world.overlord_get_ruler(olr);
221 while(ol && state.world.nation_get_owned_province_count(ol) > 0) {
222 olr = state.world.nation_get_overlord_as_subject(ol);
224 ol = state.world.overlord_get_ruler(olr);
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;
240 std::unordered_map<uint16_t, std::set<uint16_t>> regions_graph;
244 float step_x = float(map_data.
size_x) / float(samples_N);
245 float step_y = float(map_data.
size_y) / float(samples_M);
264 for(
auto candidate : state.world.in_province) {
265 auto rid = candidate.get_connected_region_id();
267 auto nation =
get_top_overlord(state, state.world.province_get_nation_from_province_ownership(candidate));
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);
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);
280 glm::vec2 point_candidate = candidate.get_mid_point();
281 glm::vec2 point_potential_friend = neighbor_of_neighbor.get_mid_point();
283 if(glm::distance(point_candidate, point_potential_friend) > map_data.
size_x * 0.5f) {
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());
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());
298 for(
auto p : state.world.in_province) {
299 if(p.id.index() >= state.province_definitions.first_sea_province.index())
301 auto rid = p.get_connected_region_id();
302 if(visited[uint16_t(rid)])
304 visited[uint16_t(rid)] =
true;
306 auto n = p.get_nation_from_province_ownership();
310 group_of_regions.clear();
311 group_of_regions.push_back(rid);
313 int vacant_index = 1;
314 while(first_index < vacant_index) {
315 auto current_region = group_of_regions[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;
325 if(!n || n.get_owned_province_count() == 0)
330 if(nation_name.starts_with(prefix_remove)) {
331 nation_name.erase(0, prefix_remove.size());
334 if(acronym_expand.size() > 0 && nation_name.starts_with(acronym_expand)) {
335 nation_name.erase(0, acronym_expand.size());
337 nation_name.insert(0, acronym_expand_to.data(), acronym_expand_to.size());
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;
353 if(!connected_to_capital) {
358 ankerl::unordered_dense::map<int32_t, uint32_t>
map;
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;
368 for(
const auto core : candidate.get_core_as_province()) {
370 if(
auto const it =
map.find(core.get_identity().id.index()); it !=
map.end()) {
373 map.insert_or_assign(core.get_identity().id.index(), v);
378 if(in_same_state ==
true) {
381 if(total_provinces <= 2) {
385 for(
const auto& e :
map) {
386 if(
float(e.second) /
float(total_provinces) >= 0.75f) {
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()) {
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);
401 degrees = 360.f + degrees;
403 assert(degrees >= 0.f && degrees <= 360.f);
406 if(degrees >= 0.f && degrees < 90.f) {
408 }
else if(degrees >= 90.f && degrees < 180.f) {
410 }
else if(degrees >= 180.f && degrees < 270.f) {
412 }
else if(degrees >= 270.f && degrees < 360.f) {
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;
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();
441 if(mid_point.x < rough_box_left) {
442 rough_box_left = mid_point.x;
444 if(mid_point.x > rough_box_right) {
445 rough_box_right = mid_point.x;
447 if(mid_point.y < rough_box_bottom) {
448 rough_box_bottom = mid_point.y;
450 if(mid_point.y > rough_box_top) {
451 rough_box_top = mid_point.y;
457 if(rough_box_right - rough_box_left > map_data.
size_x * 0.9f) {
462 std::vector<glm::vec2> points;
463 std::vector<glm::vec2> bad_points;
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);
470 float rough_box_width = rough_box_right - rough_box_left;
471 float rough_box_height = rough_box_top - rough_box_bottom;
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);
477 glm::vec2 local_step = glm::vec2(rough_box_width, rough_box_height) / glm::vec2(width_steps, height_steps);
481 float counter_from_the_bottom = 0.f;
482 float best_y_length_real = 0.f;
483 float best_y_left_x = 0.f;
486 for(
int j = 0; j < height_steps; j++) {
487 float y = rough_box_bottom + j * local_step.y;
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);
495 for(
auto visited_region : group_of_regions) {
496 if(fat_id.get_connected_region_id() == visited_region) {
497 points.push_back(candidate);
504 float points_above = 0.f;
506 for(
int j = 0; j < height_steps; j++) {
507 float y = rough_box_bottom + j * local_step.y;
509 float current_length = 0.f;
510 float left_x = (float)(map_data.
size_x);
512 for(
int i = 0; i < width_steps; i++) {
513 float x = rough_box_left + float(i) * local_step.x;
515 glm::vec2 candidate = { x, y };
517 auto idx = int32_t(y) * int32_t(map_data.
size_x) + int32_t(x);
520 for(
auto visited_region : group_of_regions)
521 if(fat_id.get_connected_region_id() == visited_region) {
523 current_length += local_step.x;
531 if(points_above * 2.f > points.size()) {
533 best_y_length_real = current_length;
535 best_y_left_x = left_x;
540 if(points.size() < 2) {
545 size_t min_amount = 2;
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);
555 if(points.size() < num_of_clusters) {
556 num_of_clusters = points.size();
559 std::vector<glm::vec2> centroids;
561 for(
size_t i = 0; i < num_of_clusters; i++) {
562 centroids.push_back(points[i]);
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);
574 for(
size_t i = 0; i < points.size(); i++) {
576 float best_dist = std::numeric_limits<float>::max();
579 for(
size_t cluster = 0; cluster < num_of_clusters; cluster++) {
580 if(best_dist > glm::distance(centroids[cluster], points[i])) {
582 best_dist = glm::distance(centroids[cluster], points[i]);
586 new_centroids[closest] += points[i];
587 counters[closest] += 1;
590 for(
size_t i = 0; i < num_of_clusters; i++) {
591 new_centroids[i] /= counters[i];
594 centroids = new_centroids;
597 std::vector<size_t> good_centroids;
600 std::vector<glm::vec2> final_points;
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++) {
606 if(locally_good_distance > glm::distance(centroids[i], centroids[j]))
607 locally_good_distance = glm::distance(centroids[i], centroids[j]);
610 size_t counter_of_neighbors = 0;
611 for(
size_t j = 0; j < num_of_clusters; j++) {
615 if(glm::distance(centroids[i], centroids[j]) < locally_good_distance * 1.2f) {
616 counter_of_neighbors++;
619 if(counter_of_neighbors >= neighbours_requirement) {
620 good_centroids.push_back(i);
621 final_points.push_back(centroids[i]);
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]);
637 std::vector<glm::vec2> good_points;
639 glm::vec2 sum_points = { 0.f, 0.f };
643 for(
auto point : points) {
645 float best_dist = std::numeric_limits<float>::max();
648 for(
size_t cluster = 0; cluster < num_of_clusters; cluster++) {
649 if(best_dist > glm::distance(centroids[cluster], point)) {
651 best_dist = glm::distance(centroids[cluster], point);
656 bool is_good =
false;
657 for(
size_t i = 0; i < good_centroids.size(); i++) {
658 if(closest == good_centroids[i])
663 good_points.push_back(point);
668 points = good_points;
672 glm::vec2 center = sum_points / (float)(points.size());
677 for(
auto point : points) {
678 auto dif_v = point - center;
679 total_sum += dif_v.x * dif_v.x;
682 float mse = total_sum / points.size();
684 float limit = mse * 3;
693 for(
auto point: points) {
695 glm::vec2 current = point - center;
696 if((current.x > right) && (current.x * current.x < limit)) {
699 if(current.y > top) {
702 if((current.x < left) && (current.x * current.x < limit)) {
705 if(current.y < bottom) {
710 std::vector<glm::vec2> key_points;
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));
717 std::array<glm::vec2, 5> key_provs{
725 for(
auto key_point : key_points) {
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 };
735 if(ratio.x < 0.001f || ratio.y < 0.001f)
738 points = final_points;
741 float lambda = 0.00001f;
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;
755 for (
auto point : points) {
761 if(e.x > basis.x + ratio.x) {
769 out_y.push_back(e.y);
770 out_x.push_back(e.x);
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});
778 float name_extent = f.text_extent(state, prepared_name, 0,
uint32_t(prepared_name.glyph_info.size()), 1);
780 bool use_quadratic =
false;
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++)
803 m0 = glm::inverse(m0);
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();
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];
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;
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;
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);
825 auto regularisation_grad = [&]() {
826 return glm::vec4(0, 0, mo[2] / 4.f, mo[3] / 6.f);
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;
837 float dx = glm::abs(dx_fn(x) - dx_fn(x - xstep));
838 if(dx / xstep >= 0.45f) {
839 use_quadratic =
true;
845 text_data.emplace_back(std::move(prepared_name), mo, basis, ratio);
848 bool use_linear =
false;
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++)
858 m0 = glm::inverse(m0);
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();
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];
869 auto poly_fn = [&](
float x) {
870 return mo[0] * l_0 + mo[1] * x * l_1 + mo[2] * x * x * l_2;
872 auto dx_fn = [&](
float x) {
873 return mo[1] * l_1 + 2.f * mo[2] * x * l_2;
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) {
883 float dx = glm::abs(dx_fn(x) - dx_fn(x - xstep));
884 if(dx / xstep >= 0.45f) {
890 text_data.emplace_back(std::move(prepared_name), glm::vec4(mo, 0.f), basis, ratio);
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++)
902 m0 = glm::inverse(m0);
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];
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];
914 auto poly_fn = [&](
float x) {
915 return mo[0] * l_0 + mo[1] * x * l_1;
922 if(abs(mo[1]) > 0.05) {
924 float left_side = 0.f;
925 float right_side = 1.f;
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];
935 left_side = std::clamp(left_side, 0.f, 1.f);
936 right_side = std::clamp(right_side, 0.f, 1.f);
938 float length_in_box_units = glm::length(ratio * glm::vec2(poly_fn(left_side), poly_fn(right_side)));
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;
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);
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) {
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));
968 std::chrono::time_point<std::chrono::steady_clock> now = std::chrono::steady_clock::now();
970 if(
last_update_time == std::chrono::time_point<std::chrono::steady_clock>{})
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);
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);
989 glm::vec2 arrow_key_velocity_vector{};
991 arrow_key_velocity_vector.x -= 1.f;
994 arrow_key_velocity_vector.x += 1.f;
997 arrow_key_velocity_vector.y -= 1.f;
1000 arrow_key_velocity_vector.y += 1.f;
1002 arrow_key_velocity_vector = glm::normalize(arrow_key_velocity_vector);
1003 arrow_key_velocity_vector *= 0.175f;
1005 arrow_key_velocity_vector *= glm::e<float>();
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 };
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;
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;
1026 if(glm::length(cursor_velocity_vector) != 0.0f) {
1027 cursor_velocity_vector = glm::normalize(cursor_velocity_vector);
1028 cursor_velocity_vector *= 0.175f;
1039 pos.x = glm::mod(
pos.x, 1.f);
1040 pos.y = glm::clamp(
pos.y, 0.f, 1.f);
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;
1046 glm::vec2 pos_before_zoom;
1047 bool valid_pos =
screen_to_map(mouse_pos, screen_size, view_mode, pos_before_zoom);
1049 auto zoom_diff = (
zoom_change * seconds_since_last_update) / (1 /
zoom);
1051 zoom_change *= std::exp(-seconds_since_last_update * state.user_settings.zoom_speed);
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;
1061 pos -= pos_before_zoom - pos_after_zoom;
1065 pos -= pos_before_zoom - pos_after_zoom;
1067 pos += pos_before_zoom - pos_after_zoom;
1072 pos += pos_before_zoom - pos_after_zoom;
1074 pos -= pos_before_zoom - pos_after_zoom;
1085 static float keyboard_zoom_change = 0.f;
1087 keyboard_zoom_change += 0.1f;
1090 keyboard_zoom_change -= 0.1f;
1093 glm::vec2 pos_before_keyboard_zoom;
1094 valid_pos =
screen_to_map(screen_center, screen_size, view_mode, pos_before_keyboard_zoom);
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);
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;
1107 globe_rotation = glm::rotate(glm::mat4(1.f), (0.25f -
pos.x) * 2 * glm::pi<float>(), glm::vec3(0, 0, 1));
1109 glm::vec3 axis = glm::vec3(
globe_rotation * glm::vec4(1, 0, 0, 0));
1111 axis = glm::normalize(axis);
1189 pos.x = glm::mod(new_pos.x, 1.f);
1190 pos.y = glm::clamp(new_pos.y, 0.f, 1.0f);
1197 auto map_pos = state.world.province_get_mid_point(p);
1200 map_pos.y = 1.0f - map_pos.y;
1206 constexpr auto zoom_speed_factor = 15.f;
1212 auto mouse_pos = glm::vec2(x, y);
1213 auto screen_size = glm::vec2(screen_size_x, screen_size_y);
1221 if((mouse_diff.x > std::ceil(screen_size_x * 0.0025f) || mouse_diff.y > std::ceil(screen_size_y * 0.0025f))
1225 auto pos2 = mouse_pos / screen_size;
1226 auto pixel_size = glm::vec2(1) / screen_size;
1235 screen_pos -= screen_size * 0.5f;
1236 screen_pos /= screen_size;
1237 screen_pos.x *= screen_size.x / screen_size.y;
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>();
1245 glm::vec3 intersection_pos;
1246 glm::vec3 intersection_normal;
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);
1259 float aspect_ratio = screen_size.x / screen_size.y;
1260 float pi = glm::pi<float>();
1263 screen_pos -= screen_size * 0.5f;
1264 screen_pos /= -screen_size;
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;
1273 screen_pos.x *= right;
1274 screen_pos.y *= top;
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;
1285 glm::vec3 intersection_pos;
1286 glm::vec3 intersection_normal;
1288 if(glm::intersectRaySphere(camera, cursor_direction, sphere_center, sphere_radius, intersection_pos,
1289 intersection_normal)) {
1290 intersection_pos -= sphere_center;
1292 intersection_pos = glm::vec3(-intersection_pos.x, -intersection_pos.z, intersection_pos.y);
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);
1302 screen_pos -= screen_size * 0.5f;
1303 screen_pos /= screen_size;
1304 screen_pos.x *= screen_size.x / screen_size.y;
1309 map_pos = screen_pos;
1315 auto mouse_pos = glm::vec2(x, y);
1316 auto screen_size = glm::vec2(screen_size_x, screen_size_y);
1341 auto mouse_pos = glm::vec2(x, y);
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);
1353 state.user_settings.interface_volume * state.user_settings.master_volume);
1370 auto mouse_pos = glm::vec2(x, y);
1371 auto screen_size = glm::vec2(screen_size_x, screen_size_y);
1386 auto mouse_pos = glm::vec2(x, y);
1387 auto screen_size = glm::vec2(screen_size_x, screen_size_y);
1390 return dcon::province_id{};
1402 return dcon::province_id{};
1407 switch(state.user_settings.map_is_globe) {
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)) {
1419 if(!std::isfinite(angle_x2)) {
1423 if(!std::isfinite(map_pos.x)) {
1427 if(!std::isfinite(map_pos.y)) {
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));
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);
1439 cartesian_coords /= glm::pi<float>();
1440 cartesian_coords.x *= -1;
1441 cartesian_coords.y *= -1;
1442 if(cartesian_coords.y > 0) {
1445 cartesian_coords += glm::vec3(0.5f);
1447 screen_pos = glm::vec2(cartesian_coords.x, cartesian_coords.z);
1448 screen_pos = (2.f * screen_pos - glm::vec2(1.f));
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;
1457 float aspect_ratio = screen_size.x / screen_size.y;
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)) {
1467 if(!std::isfinite(angle_x2)) {
1471 if(!std::isfinite(map_pos.x)) {
1475 if(!std::isfinite(map_pos.y)) {
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));
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);
1487 glm::vec3 temp_vector = cartesian_coords;
1490 cartesian_coords.z *= -1;
1492 cartesian_coords.z *= -1;
1494 cartesian_coords /= glm::pi<float>();
1495 cartesian_coords.x *= -1;
1496 cartesian_coords.z *= -1;
1498 float temp = cartesian_coords.z;
1499 cartesian_coords.z = cartesian_coords.y;
1500 cartesian_coords.y = temp;
1503 cartesian_coords.z -= 1.2f;
1504 float near_plane = 0.1f;
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;
1510 float right = near_plane * tan(glm::pi<float>() / 6.f) /
zoom;
1511 float top = near_plane * tan(glm::pi<float>() / 6.f) /
zoom;
1513 cartesian_coords.x *= near_plane / right;
1514 cartesian_coords.y *= near_plane / top;
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);
1520 if(cartesian_coords.z > far_plane) {
1524 screen_pos = glm::vec2(cartesian_coords.x, cartesian_coords.y) / w;
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;
1536 if(map_pos.x >= 0.5f)
1538 if(map_pos.x < -0.5f)
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))
1550 if(screen_pos.x <=
float(std::numeric_limits<int16_t>::min() / 2))
1552 if(screen_pos.y >=
float(std::numeric_limits<int16_t>::max() / 2))
1554 if(screen_pos.y <=
float(std::numeric_limits<int16_t>::min() / 2))
1567 new_pos.y = 1.f - new_pos.y;
std::vector< curved_line_vertex > retreat_unit_arrow_vertices
void load_map(sys::state &state)
void set_selected_province(sys::state &state, dcon::province_id province_id)
std::vector< GLsizei > strategy_unit_arrow_counts
static constexpr uint32_t vo_objective_unit_arrow
std::vector< curved_line_vertex > attack_unit_arrow_vertices
static constexpr uint32_t vo_unit_arrow
std::vector< GLint > objective_unit_arrow_starts
void set_province_text_lines(sys::state &state, std::vector< text_line_generator_data > const &data)
std::vector< uint16_t > province_id_map
std::vector< curved_line_vertex > other_objective_unit_arrow_vertices
std::vector< GLsizei > other_objective_unit_arrow_counts
std::vector< curved_line_vertex > unit_arrow_vertices
std::vector< GLsizei > retreat_unit_arrow_counts
std::vector< curved_line_vertex > strategy_unit_arrow_vertices
static constexpr uint32_t vo_attack_unit_arrow
static constexpr uint32_t vo_other_objective_unit_arrow
std::vector< GLint > retreat_unit_arrow_starts
std::vector< GLsizei > objective_unit_arrow_counts
std::vector< GLint > attack_unit_arrow_starts
std::vector< GLsizei > unit_arrow_counts
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)
static constexpr uint32_t vo_retreat_unit_arrow
void set_text_lines(sys::state &state, std::vector< text_line_generator_data > const &data)
static constexpr uint32_t vo_strategy_unit_arrow
GLuint vbo_array[vo_count]
std::vector< GLsizei > attack_unit_arrow_counts
std::vector< GLint > strategy_unit_arrow_starts
std::vector< GLint > unit_arrow_starts
std::vector< GLint > other_objective_unit_arrow_starts
void set_drag_box(bool draw_box, glm::vec2 pos1, glm::vec2 pos2, glm::vec2 pixel_size)
std::vector< curved_line_vertex > objective_unit_arrow_vertices
void set_province_color(std::vector< uint32_t > const &prov_color)
bool map_to_screen(sys::state &state, glm::vec2 map_pos, glm::vec2 screen_size, glm::vec2 &screen_pos)
glm::vec2 last_camera_drag_pos
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)
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)
void on_mbuttom_up(int32_t x, int32_t y, sys::key_modifiers mod)
void on_mbuttom_down(int32_t x, int32_t y, int32_t screen_size_x, int32_t screen_size_y, sys::key_modifiers mod)
bool screen_to_map(glm::vec2 screen_pos, glm::vec2 screen_size, map_view view_mode, glm::vec2 &map_pos)
void set_selected_province(dcon::province_id prov_id)
bool unhandled_province_selection
map_view current_view(sys::state &state)
void update(sys::state &state)
std::chrono::time_point< std::chrono::steady_clock > last_update_time
dcon::province_id get_selected_province()
void on_key_up(sys::virtual_key keycode, sys::key_modifiers mod)
void set_terrain_map_mode()
void load_map(sys::state &state)
dcon::province_id selected_province
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)
void on_key_down(sys::virtual_key keycode, sys::key_modifiers mod)
void set_province_color(std::vector< uint32_t > const &prov_color, map_mode::mode map_mode)
glm::vec2 last_unit_box_drag_pos
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)
glm::vec2 normalize_map_coord(glm::vec2 pos)
map_mode::mode active_map_mode
void on_mouse_move(int32_t x, int32_t y, int32_t screen_size_x, int32_t screen_size_y, sys::key_modifiers mod)
void center_map_on_province(sys::state &state, dcon::province_id)
void render(sys::state &state, uint32_t screen_x, uint32_t screen_y)
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)
bool right_arrow_key_down
void set_pos(glm::vec2 pos)
#define assert(condition)
pop_satisfaction_wrapper_fat fatten(data_container const &c, pop_satisfaction_wrapper_id id) noexcept
void update_map_mode(sys::state &state)
void update_unit_arrows(sys::state &state, display_data &map_data)
glm::vec2 get_army_location(sys::state &state, dcon::province_id prov_id)
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)
glm::vec2 get_navy_location(sys::state &state, dcon::province_id prov_id)
glm::vec2 get_port_location(sys::state &state, dcon::province_id p)
bool is_inside_bbox(std::array< glm::vec2, 5 > &bbox, glm::vec2 p)
dcon::nation_id get_top_overlord(sys::state &state, dcon::nation_id n)
void update_bbox_negative(std::array< glm::vec2, 5 > &bbox, glm::vec2 p)
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)
bool is_sea_province(sys::state &state, dcon::province_id prov_id)
void update_bbox(std::array< glm::vec2, 5 > &bbox, glm::vec2 p)
void update_text_lines(sys::state &state, display_data &map_data)
bool are_at_war(sys::state const &state, dcon::nation_id a, dcon::nation_id b)
constexpr dcon::province_id from_map_id(uint16_t id)
constexpr uint16_t to_map_id(dcon::province_id id)
audio_instance & get_random_province_select_sound(sys::state &state)
void play_interface_sound(sys::state &state, audio_instance &s, float volume)
std::string resolve_string_substitution(sys::state &state, dcon::text_key source_text, substitution_map const &mp)
void add_to_substitution_map(substitution_map &mp, variable_type key, substitution value)
dcon::text_key get_adjective(sys::state &state, dcon::nation_id id)
ankerl::unordered_dense::map< uint32_t, substitution > substitution_map
std::string produce_simple_string(sys::state const &state, dcon::text_key id)
dcon::text_key get_name(sys::state &state, dcon::nation_id id)