// This file is part of crownsegmentr, an R package for identifying tree crowns // within 3D point clouds. // // Copyright (C) 2025 Leon Steinmeier, Nikolai Knapp, UFZ Leipzig // Contact: timon.miesner@thuenen.de // // crownsegmentr is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // crownsegmentr is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with crownsegmentr in a file called "COPYING". If not, // see . #include "spatial_util.h" #include // for std::back_inserter #include // for std::accumulate namespace spatial { /** \note When modifying this function keep in mind that there might be a * significant impact on performance since the function is called very * often while calculating modes. */ std::vector< point_3d_t > get_points_intersecting_vertical_cylinder ( const index_for_3d_points_t &point_cloud, const point_2d_t &xy_center, distance_t radius, distance_t bottom_height, distance_t top_height ) { // Construct a box around the cylinder. point_3d_t min_corner_point { _geom::get<0>( xy_center ) - radius, _geom::get<1>( xy_center ) - radius, bottom_height }; point_3d_t max_corner_point { _geom::get<0>( xy_center ) + radius, _geom::get<1>( xy_center ) + radius, top_height }; box_t cylinder_box{ min_corner_point, max_corner_point }; // Set up a vector for storing the points inside the cylinder. std::vector< point_3d_t > intersecting_points{}; // This is a "magic number" guessed from experience // TODO make more meaningful estimates based on the point cloud density // Keep in mind that the default allocation strategy of std::vectors is // likely quadratic. I.e. if you reserve a capacity of e.g. 1000 at the // beginning, the vector will allocate twice as much (2000) when it is // asked to insert the 1001th element. When it is asked to insert the // 2001th element it will allocate capacity for 4000 elements and so on. intersecting_points.reserve( 1000 ); // Set up a unary predicate object. _within_xy_distance_functor within_xy_distance { xy_center, radius }; // Query the point cloud. point_cloud.query ( _geom::index::intersects( cylinder_box ) && _geom::index::satisfies( within_xy_distance ), std::back_inserter( intersecting_points ) ); return intersecting_points; } point_3d_t weighted_mean_of ( const std::vector< point_3d_t > &points, const std::vector< double > &weights ) { // Set up a mean point point_3d_t mean_point{ 0, 0 ,0 }; // Weigh all points and add them together for(std::size_t i{ 0 }; i < points.size(); i++) { _geom::add_point ( mean_point, point_3d_t { _geom::get<0>( points[i] ) * weights[i], _geom::get<1>( points[i] ) * weights[i], _geom::get<2>( points[i] ) * weights[i] } ); } // Divide the sum of all weighted points by the sum of the weights _geom::divide_value( mean_point, std::accumulate( weights.begin(), weights.end(), double{ 0 } ) ); return mean_point; } }