81 const std::vector<std::array<double, spacedim>>& points,
82 const std::vector<double>& densities,
87 if (points.size() <= k) {
89 std::vector<int> assignments(points.size());
90 std::iota(assignments.begin(), assignments.end(), 0);
91 return {points, densities, assignments};
99 auto start = std::chrono::high_resolution_clock::now();
102 std::vector<std::array<double, spacedim>> centers;
103 std::vector<uint32_t> cluster_assignments;
107 auto stop = std::chrono::high_resolution_clock::now();
108 auto duration = std::chrono::duration_cast<std::chrono::seconds>(stop - start);
110 std::cout <<
" K-means clustering took " << duration.count() <<
" seconds" << std::endl;
113 std::vector<int> assignments(cluster_assignments.begin(), cluster_assignments.end());
116 std::vector<double> cluster_densities(k, 0.0);
117 for (
size_t i = 0; i < points.size(); ++i) {
118 int cluster = assignments[i];
119 double point_density = densities.empty() ? 1.0 : densities[i];
120 cluster_densities[cluster] += point_density;
123 return {centers, cluster_densities, assignments};
128 const std::vector<Point<spacedim>>& input_points,
129 const std::vector<double>& input_densities,
130 const std::string& output_dir) {
133 if (input_points.empty()) {
134 throw std::runtime_error(
"Input point cloud is empty");
138 auto start_total = std::chrono::high_resolution_clock::now();
141 std::vector<double> densities;
142 if (input_densities.empty()) {
143 densities.resize(input_points.size(), 1.0 / input_points.size());
145 if (input_densities.size() != input_points.size()) {
146 throw std::runtime_error(
"density vector size doesn't match point cloud size");
148 densities = input_densities;
155 const int total_points = input_points.size();
156 const int level1_points = std::min(total_points,
max_points_);
161 std::cout <<
"Initial point cloud has " << total_points <<
" points" << std::endl;
162 std::cout <<
"Level 1 will have maximum of " <<
max_points_ <<
" points" << std::endl;
163 std::cout <<
"Creating " <<
num_levels_ <<
" levels of point clouds" << std::endl;
175 std::vector<std::array<double, spacedim>> points_array(input_points.size());
176 for (
size_t i = 0; i < input_points.size(); ++i) {
177 for (
int d = 0; d < spacedim; ++d) {
178 points_array[i][d] = input_points[i][d];
183 std::vector<std::vector<std::array<double, spacedim>>> level_points_vec(
num_levels_);
184 std::vector<std::vector<double>> level_densities_vec(
num_levels_);
187 level_points_vec[0] = points_array;
188 level_densities_vec[0] = densities;
191 std::cout <<
"Level 0: using original point cloud with " << level_points_vec[0].size() <<
" points" << std::endl;
194 for (
int level = 1; level <
num_levels_; ++level) {
196 std::cout <<
"Level " << level <<
": targeting " << points_for_level <<
" points" << std::endl;
199 std::vector<std::array<double, spacedim>> coarse_points;
200 std::vector<double> coarse_densities;
201 std::vector<int> assignments;
203 std::tie(coarse_points, coarse_densities, assignments) = kmeansClustering<spacedim>(
204 level_points_vec[level-1], level_densities_vec[level-1], points_for_level);
206 level_points_vec[level] = coarse_points;
207 level_densities_vec[level] = coarse_densities;
210 std::cout <<
" Generated " << coarse_points.size() <<
" points after clustering" << std::endl;
213 const size_t n_fine_points = level_points_vec[level-1].size();
214 const size_t n_coarse_points = coarse_points.size();
223 for (
size_t i = 0; i < n_fine_points; ++i) {
224 int coarse_point_idx = assignments[i];
225 if (coarse_point_idx >= 0 && coarse_point_idx <
static_cast<int>(n_coarse_points)) {
236 for (
int level = 0; level <
num_levels_; ++level) {
237 std::string points_file = output_dir +
"/level_" + std::to_string(level) +
"_points.txt";
238 std::string densities_file = output_dir +
"/level_" + std::to_string(level) +
"_density.txt";
240 std::ofstream points_out(points_file);
241 std::ofstream densities_out(densities_file);
243 if (!points_out || !densities_out) {
244 throw std::runtime_error(
"Failed to open output files for level " + std::to_string(level));
248 for (
size_t i = 0; i < level_points_vec[level].size(); ++i) {
249 for (
int d = 0; d < spacedim; ++d) {
250 points_out << level_points_vec[level][i][d] << (d < spacedim - 1 ?
" " :
"");
252 points_out << std::endl;
254 densities_out << level_densities_vec[level][i] << std::endl;
260 std::string parents_file = output_dir +
"/level_" + std::to_string(level) +
"_parents.txt";
261 std::ofstream parents_out(parents_file);
264 throw std::runtime_error(
"Failed to open parents file for level " + std::to_string(level));
271 parents_out <<
" " << parent_idx;
273 parents_out << std::endl;
279 std::string children_file = output_dir +
"/level_" + std::to_string(level) +
"_children.txt";
280 std::ofstream children_out(children_file);
283 throw std::runtime_error(
"Failed to open children file for level " + std::to_string(level));
290 children_out <<
" " << child_idx;
292 children_out << std::endl;
296 std::cout <<
"Saved level " << level <<
" point cloud and relationships to " << output_dir << std::endl;
300 auto stop_total = std::chrono::high_resolution_clock::now();
301 auto duration_total = std::chrono::duration_cast<std::chrono::seconds>(stop_total - start_total);
303 std::cout <<
"Total hierarchy generation took " << duration_total.count() <<
" seconds" << std::endl;