mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-10-31 04:31:15 -06:00 
			
		
		
		
	
		
			
				
	
	
		
			82 lines
		
	
	
	
		
			2.9 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			82 lines
		
	
	
	
		
			2.9 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #ifndef SLA_CLUSTERING_HPP
 | |
| #define SLA_CLUSTERING_HPP
 | |
| 
 | |
| #include <vector>
 | |
| 
 | |
| #include <libslic3r/Point.hpp>
 | |
| #include <libslic3r/SLA/SpatIndex.hpp>
 | |
| 
 | |
| namespace Slic3r { namespace sla {
 | |
| 
 | |
| using ClusterEl = std::vector<unsigned>;
 | |
| using ClusteredPoints = std::vector<ClusterEl>;
 | |
| 
 | |
| // Clustering a set of points by the given distance.
 | |
| ClusteredPoints cluster(const std::vector<unsigned>& indices,
 | |
|                         std::function<Vec3d(unsigned)> pointfn,
 | |
|                         double dist,
 | |
|                         unsigned max_points);
 | |
| 
 | |
| ClusteredPoints cluster(const Eigen::MatrixXd& points,
 | |
|                         double dist,
 | |
|                         unsigned max_points);
 | |
| 
 | |
| ClusteredPoints cluster(
 | |
|     const std::vector<unsigned>& indices,
 | |
|     std::function<Vec3d(unsigned)> pointfn,
 | |
|     std::function<bool(const PointIndexEl&, const PointIndexEl&)> predicate,
 | |
|     unsigned max_points);
 | |
| 
 | |
| // This function returns the position of the centroid in the input 'clust'
 | |
| // vector of point indices.
 | |
| template<class DistFn, class PointFn>
 | |
| long cluster_centroid(const ClusterEl &clust, PointFn pointfn, DistFn df)
 | |
| {
 | |
|     switch(clust.size()) {
 | |
|     case 0: /* empty cluster */ return -1;
 | |
|     case 1: /* only one element */ return 0;
 | |
|     case 2: /* if two elements, there is no center */ return 0;
 | |
|     default: ;
 | |
|     }
 | |
| 
 | |
|     // The function works by calculating for each point the average distance
 | |
|     // from all the other points in the cluster. We create a selector bitmask of
 | |
|     // the same size as the cluster. The bitmask will have two true bits and
 | |
|     // false bits for the rest of items and we will loop through all the
 | |
|     // permutations of the bitmask (combinations of two points). Get the
 | |
|     // distance for the two points and add the distance to the averages.
 | |
|     // The point with the smallest average than wins.
 | |
| 
 | |
|     // The complexity should be O(n^2) but we will mostly apply this function
 | |
|     // for small clusters only (cca 3 elements)
 | |
| 
 | |
|     std::vector<bool> sel(clust.size(), false);   // create full zero bitmask
 | |
|     std::fill(sel.end() - 2, sel.end(), true);    // insert the two ones
 | |
|     std::vector<double> avgs(clust.size(), 0.0);  // store the average distances
 | |
| 
 | |
|     do {
 | |
|         std::array<size_t, 2> idx;
 | |
|         for(size_t i = 0, j = 0; i < clust.size(); i++)
 | |
|             if(sel[i]) idx[j++] = i;
 | |
| 
 | |
|         double d = df(pointfn(clust[idx[0]]),
 | |
|                       pointfn(clust[idx[1]]));
 | |
| 
 | |
|         // add the distance to the sums for both associated points
 | |
|         for(auto i : idx) avgs[i] += d;
 | |
| 
 | |
|         // now continue with the next permutation of the bitmask with two 1s
 | |
|     } while(std::next_permutation(sel.begin(), sel.end()));
 | |
| 
 | |
|     // Divide by point size in the cluster to get the average (may be redundant)
 | |
|     for(auto& a : avgs) a /= clust.size();
 | |
| 
 | |
|     // get the lowest average distance and return the index
 | |
|     auto minit = std::min_element(avgs.begin(), avgs.end());
 | |
|     return long(minit - avgs.begin());
 | |
| }
 | |
| 
 | |
| 
 | |
| }} // namespace Slic3r::sla
 | |
| 
 | |
| #endif // CLUSTERING_HPP
 | 
