61 lines
1.7 KiB
C++
61 lines
1.7 KiB
C++
#pragma once
|
|
|
|
#include <boost/container/flat_set.hpp>
|
|
#include <cassert>
|
|
#include <utility>
|
|
|
|
// Using boost flat set was faster than std::set.
|
|
|
|
template <typename Field, typename Cluster>
|
|
auto make_empty(Field field, Cluster const & c){
|
|
for(auto&& p : c){
|
|
field.get(p) = 0;
|
|
}
|
|
field.collapse();
|
|
return field;
|
|
}
|
|
|
|
namespace detail {
|
|
template <typename Field, typename Rules>
|
|
void cluster_expand(Field const & field, Rules const & rules, typename Field::Position const & p, boost::container::flat_set<typename Field::Position>& ret){
|
|
for(auto&& q : field.neighbors(p)){
|
|
if(!rules.same_cluster(field.get(p), field.get(q))) continue;
|
|
|
|
if(ret.insert(q).second){
|
|
cluster_expand(field, rules, q, ret);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
template <typename Field, typename Rules>
|
|
auto cluster_at(Field const & field, Rules const & rules, typename Field::Position const & p){
|
|
boost::container::flat_set<typename Field::Position> ret;
|
|
ret.reserve(10); // speed improvement of 20%
|
|
ret.insert(p);
|
|
detail::cluster_expand(field, rules, p, ret);
|
|
return ret;
|
|
}
|
|
|
|
template <typename Grid, typename Rules>
|
|
auto all_clusters(Grid const & grid, Rules const & rules){
|
|
std::vector<decltype(cluster_at(grid, rules, std::declval<typename Grid::Position>()))> ret;
|
|
ret.reserve(10);
|
|
|
|
auto partition = make_empty(grid, grid.all_positions());
|
|
auto current_p = 1;
|
|
for(auto&& p : partition.all_positions()){
|
|
// ignore if it already belongs to a cluster or is empty
|
|
if(!partition.empty(p) || grid.empty(p)) continue;
|
|
|
|
auto cluster = cluster_at(grid, rules, p);
|
|
for(auto&& q : cluster){
|
|
partition.get(q) = current_p;
|
|
}
|
|
|
|
current_p++;
|
|
if(cluster.size() >= rules.min_cluster_size()) ret.push_back(std::move(cluster));
|
|
}
|
|
|
|
return ret;
|
|
}
|