diff --git a/src/location.cpp b/src/location.cpp index 1972ddf5..569ffaea 100644 --- a/src/location.cpp +++ b/src/location.cpp @@ -11,6 +11,7 @@ #include #include #include +#include eDirection& operator++ (eDirection& me, int) { if(me == DIR_HERE) return me = DIR_N; @@ -31,6 +32,10 @@ short dist(location p1,location p2){ return hypot(p1.x - p2.x, p1.y - p2.y); } +float fdist(location p1,location p2){ + return hypot(p1.x - p2.x, p1.y - p2.y); +} + short vdist(location p1,location p2) { short i,j; i = abs((long double)p1.x - p2.x); @@ -306,3 +311,109 @@ std::ostream& operator<< (std::ostream& out, info_rect_t r) { out << " -- \"" << r.descr << '"'; return out; } + +bool is_on_screen(location loc, location view_center, int radius) { + return loc.x >= view_center.x - radius + && loc.x <= view_center.x + radius + && loc.y >= view_center.y - radius + && loc.y <= view_center.y + radius; +} + +location between_anchor_points(location anchor1, location anchor2, int padding){ + // First check the point directly between anchor1 and anchor2 (rounding towards anchor1) + float center_x_f = (anchor1.x + anchor2.x) / 2.0; + float center_y_f = (anchor1.y + anchor2.y) / 2.0; + location point_between; + if(anchor1.x < anchor2.x){ + point_between.x = floor(center_x_f); + }else{ + point_between.x = ceil(center_x_f); + } + if(anchor1.y < anchor2.y){ + point_between.y = floor(center_y_f); + }else{ + point_between.y = ceil(center_y_f); + } + + // Then if necessary, move back towards anchor1 until it is visible with the desired padding + int padded_radius = SCREEN_RADIUS - padding; + while(!is_on_screen(anchor1, point_between, padded_radius)){ + if(anchor1.x < point_between.x - padded_radius){ + --point_between.x; + }else if(anchor1.x > point_between.x + padded_radius){ + ++point_between.x; + } + if(anchor1.y < point_between.y - padded_radius){ + --point_between.y; + }else if(anchor1.y > point_between.y + padded_radius){ + ++point_between.y; + } + } + return point_between; +} + +std::vector points_containing_most(std::vector points, int padding) { + // Find the bounding box of the given points: + int min_x = std::numeric_limits::max(); + int min_y = min_x; + int max_x = std::numeric_limits::min(); + int max_y = max_x; + + for(location p : points){ + if(p.x < min_x) min_x = p.x; + if(p.y < min_y) min_y = p.y; + if(p.x > max_x) max_x = p.x; + if(p.y > max_y) max_y = p.y; + } + + // Expand the bounds by the view radius: + int padded_radius = SCREEN_RADIUS - padding; + min_x -= padded_radius; min_y -= padded_radius; + max_x += padded_radius; max_y += padded_radius; + // This function's output may include out-of-bounds points, but those will be eliminated + // by calling closest_point() with an anchor point that is in-bounds. + + // Calculate how many of the points can be seen from each point in the bounding box: + std::vector> points_seen_from; + location checking_point; + for(checking_point.x = min_x; checking_point.x <= max_x; ++checking_point.x){ + for(checking_point.y = min_y; checking_point.y <= max_y; ++checking_point.y){ + int can_see = 0; + for(location p : points){ + if(is_on_screen(p, checking_point, padded_radius)) ++can_see; + } + points_seen_from.push_back(std::make_pair(checking_point, can_see)); + } + } + + // Sort candidates by how many of the points they see + std::sort(points_seen_from.begin(), points_seen_from.end(), [](std::pair pair1, std::pair pair2) -> bool { + return pair1.second > pair2.second; + }); + int max_seen = points_seen_from[0].second; + + // Return all of them that see the max number + std::vector return_points; + for(auto pair : points_seen_from){ + if(pair.second == max_seen) return_points.push_back(pair.first); + } + + return return_points; +} + +int closest_point_idx(std::vector points, location anchor) { + float min_distance = std::numeric_limits::max(); + int min_idx = -1; + for(int idx = 0; idx < points.size(); ++idx){ + float distance = fdist(points[idx], anchor); + if(distance < min_distance){ + min_distance = distance; + min_idx = idx; + } + } + return min_idx; +} + +location closest_point(std::vector points, location anchor) { + return points[closest_point_idx(points, anchor)]; +} \ No newline at end of file diff --git a/src/location.hpp b/src/location.hpp index 7080794f..dfcf92ca 100644 --- a/src/location.hpp +++ b/src/location.hpp @@ -154,6 +154,7 @@ bool operator != (rectangle r1, rectangle r2); rectangle operator&(rectangle one, rectangle two); rectangle rectunion(rectangle one, rectangle two); short dist(location p1,location p2); +float fdist(location p1,location p2); short vdist(location p1,location p2); location loc(int a, int b); @@ -172,4 +173,30 @@ std::ostream& operator<< (std::ostream& out, rectangle r); std::istream& operator>> (std::istream& in, rectangle& r); std::ostream& operator<< (std::ostream& out, info_rect_t r); +const int SCREEN_RADIUS=4; + +// CAMERA UTILITY FUNCTIONS + +// Whether a tile is within a radius of a given view center. +// 4 is the actual tile radius of the terrain screen. +bool is_on_screen(location loc, location view_center, int radius=SCREEN_RADIUS); + +// Default view center: the actual current view center +extern location center; +inline bool is_on_screen(location loc, int radius=SCREEN_RADIUS) { + return is_on_screen(loc, center, radius); +} + +// Calculate the closest point to the center of two anchors that is +// guaranteed to contain the first anchor with the given amount of padding +location between_anchor_points(location anchor1, location anchor2, int padding=1); + +// Get all view center points which contain the greatest possible number of the given points +// with the given amount of padding +std::vector points_containing_most(std::vector points, int padding=1); + +// Find which of the given points is closest to the given anchor point. +int closest_point_idx(std::vector points, location anchor); +location closest_point(std::vector points, location anchor); + #endif diff --git a/src/pcedit/pc.main.cpp b/src/pcedit/pc.main.cpp index 51d8c0a0..eca7bdc7 100644 --- a/src/pcedit/pc.main.cpp +++ b/src/pcedit/pc.main.cpp @@ -80,6 +80,9 @@ extern bool cur_scen_is_mac; short specials_res_id; char start_name[256]; +// This is just to make location.hpp compile, and represents nothing: +location center; + static void process_args(int argc, char* argv[]) { preprocess_args(argc, argv); clara::Args args(argc, argv);