diff --git a/sdk/map/__provinces.bmp b/sdk/map/__provinces.bmp new file mode 100644 index 0000000..9289c27 Binary files /dev/null and b/sdk/map/__provinces.bmp differ diff --git a/sdk/map/_provinces.bmp b/sdk/map/_provinces.bmp deleted file mode 100644 index 05012ce..0000000 Binary files a/sdk/map/_provinces.bmp and /dev/null differ diff --git a/sdk/map/convert_map.cpp b/sdk/map/convert_map.cpp index c6bddf3..59d2af4 100644 --- a/sdk/map/convert_map.cpp +++ b/sdk/map/convert_map.cpp @@ -5,7 +5,7 @@ #include #include #include -#include +#include #include #define bytes(value) reinterpret_cast(value) @@ -171,10 +171,213 @@ void FindContour(){ } } -/* ================= STUBS ================= */ +static double PerpendicularDistance(size_t p, size_t a, size_t b, size_t width) +{ + double px = p % width; + double py = p / width; -void Aproximate(size_t){} -void Triangulate(){} + double ax = a % width; + double ay = a / width; + + double bx = b % width; + double by = b / width; + + double dx = bx - ax; + double dy = by - ay; + + if(dx == 0 && dy == 0) + return std::hypot(px - ax, py - ay); + + double num = std::abs(dy * px - dx * py + bx * ay - by * ax); + double den = std::sqrt(dx * dx + dy * dy); + + return num / den; +} + +// Douglas-Peucker iterative +void Aproximate(size_t epsilon) +{ + for(auto& [state, provs] : pixelsPerProvincesPerState) + { + for(std::vector& prov : provs) + { + if(prov.size() < 3) + continue; + + const size_t n = prov.size(); + + std::vector keep_point(n, true); + std::stack> st; + + // в стек кладем первый и последний индекс + st.push({0, n - 1}); + + while(!st.empty()) + { + auto [startIndex, endIndex] = st.top(); + st.pop(); + + double dMax = 0.0; + size_t index = startIndex; + + for(size_t i = startIndex + 1; i < endIndex; ++i) + { + if(keep_point[i]) + { + double d = PerpendicularDistance( + prov[i], + prov[startIndex], + prov[endIndex], + width + ); + + if(d > dMax) + { + dMax = d; + index = i; + } + } + } + + if(dMax >= epsilon) + { + st.push({startIndex, index}); + st.push({index, endIndex}); + } + else + { + for(size_t j = startIndex + 1; j < endIndex; ++j) + keep_point[j] = false; + } + } + + // формируем итоговый список + std::vector result; + result.reserve(n); + + for(size_t i = 0; i < n; ++i) + { + if(keep_point[i]) + result.push_back(prov[i]); + } + + prov.swap(result); + } + } +} + +struct Pt { + int x, y; + size_t id; +}; + +inline Pt make_pt(size_t i, size_t width) { + return { int(i % width), int(i / width), i }; +} + +inline long long cross(const Pt& a, const Pt& b, const Pt& c) { + return 1LL * (b.x - a.x) * (c.y - a.y) + - 1LL * (b.y - a.y) * (c.x - a.x); +} + +long long signed_area2(const std::vector& p) { + long long a = 0; + for (size_t i = 0; i < p.size(); ++i) { + const auto& p0 = p[i]; + const auto& p1 = p[(i + 1) % p.size()]; + a += 1LL * p0.x * p1.y - 1LL * p1.x * p0.y; + } + return a; +} + +bool point_in_triangle(const Pt& p, + const Pt& a, + const Pt& b, + const Pt& c) +{ + long long c1 = cross(a, b, p); + long long c2 = cross(b, c, p); + long long c3 = cross(c, a, p); + return (c1 >= 0 && c2 >= 0 && c3 >= 0); +} + +std::vector triangulate_prov(const std::vector& prov, + size_t width) +{ + std::vector poly; + poly.reserve(prov.size()); + for (size_t i : prov) + poly.push_back(make_pt(i, width)); + + // CCW + if (signed_area2(poly) < 0) + std::reverse(poly.begin(), poly.end()); + + std::vector out; // a,b,c,a,b,c,... + + while (poly.size() > 3) { + bool ear_found = false; + + for (size_t i = 0; i < poly.size(); ++i) { + size_t i0 = (i + poly.size() - 1) % poly.size(); + size_t i1 = i; + size_t i2 = (i + 1) % poly.size(); + + const Pt& a = poly[i0]; + const Pt& b = poly[i1]; + const Pt& c = poly[i2]; + + if (cross(a, b, c) <= 0) + continue; // не выпуклая + + bool inside = false; + for (size_t j = 0; j < poly.size(); ++j) { + if (j == i0 || j == i1 || j == i2) continue; + if (point_in_triangle(poly[j], a, b, c)) { + inside = true; + break; + } + } + + if (!inside) { + out.push_back(a.id); + out.push_back(b.id); + out.push_back(c.id); + poly.erase(poly.begin() + i1); + ear_found = true; + break; + } + } + + if (!ear_found) { + // плохой контур (самопересечение / коллинеарность) + break; + } + } + + if (poly.size() == 3) { + out.push_back(poly[0].id); + out.push_back(poly[1].id); + out.push_back(poly[2].id); + } + + return out; +} + + +void Triangulate() +{ + for(auto& [state, provs] : pixelsPerProvincesPerState) + { + for(std::vector& prov : provs) + { + if (prov.size() < 3) + continue; + + prov = triangulate_prov(prov, width); + } + } +} /* ================= OUTPUT ================= */ @@ -224,7 +427,7 @@ int main(int argc,char**argv){ FloodFill(); FindContour(); - Aproximate(0); + Aproximate(1); Triangulate(); GenVertices(fout); } diff --git a/sdk/map/provinces.bmp b/sdk/map/provinces.bmp index 9289c27..2f210e3 100644 Binary files a/sdk/map/provinces.bmp and b/sdk/map/provinces.bmp differ diff --git a/sdk/map/provinces.map b/sdk/map/provinces.map index a55a684..c098e62 100644 Binary files a/sdk/map/provinces.map and b/sdk/map/provinces.map differ