mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-10-31 12:41:20 -06:00 
			
		
		
		
	Merge branch 'master' of https://github.com/prusa3d/PrusaSlicer into et_gcode_viewer
This commit is contained in:
		
						commit
						80c2f107c1
					
				
					 28 changed files with 1208 additions and 52 deletions
				
			
		|  | @ -2,3 +2,4 @@ | |||
| #add_subdirectory(openvdb) | ||||
| add_subdirectory(meshboolean) | ||||
| add_subdirectory(opencsg) | ||||
| #add_subdirectory(aabb-evaluation) | ||||
							
								
								
									
										2
									
								
								sandboxes/aabb-evaluation/CMakeLists.txt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								sandboxes/aabb-evaluation/CMakeLists.txt
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,2 @@ | |||
| add_executable(aabb-evaluation aabb-evaluation.cpp) | ||||
| target_link_libraries(aabb-evaluation libslic3r ${Boost_LIBRARIES} ${TBB_LIBRARIES} ${Boost_LIBRARIES} ${CMAKE_DL_LIBS}) | ||||
							
								
								
									
										224
									
								
								sandboxes/aabb-evaluation/aabb-evaluation.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										224
									
								
								sandboxes/aabb-evaluation/aabb-evaluation.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,224 @@ | |||
| #include <iostream> | ||||
| #include <fstream> | ||||
| #include <string> | ||||
| 
 | ||||
| #include <libslic3r/TriangleMesh.hpp> | ||||
| #include <libslic3r/AABBTreeIndirect.hpp> | ||||
| #include <libslic3r/SLA/EigenMesh3D.hpp> | ||||
| 
 | ||||
| #include <Shiny/Shiny.h> | ||||
| 
 | ||||
| #ifdef _MSC_VER | ||||
| #pragma warning(push) | ||||
| #pragma warning(disable: 4244) | ||||
| #pragma warning(disable: 4267) | ||||
| #endif | ||||
| #include <igl/ray_mesh_intersect.h> | ||||
| #include <igl/point_mesh_squared_distance.h> | ||||
| #include <igl/remove_duplicate_vertices.h> | ||||
| #include <igl/signed_distance.h> | ||||
| #include <igl/random_dir.h> | ||||
| #ifdef _MSC_VER | ||||
| #pragma warning(pop) | ||||
| #endif | ||||
| 
 | ||||
| const std::string USAGE_STR = { | ||||
|     "Usage: aabb-evaluation stlfilename.stl" | ||||
| }; | ||||
| 
 | ||||
| using namespace Slic3r; | ||||
| 
 | ||||
| void profile(const TriangleMesh &mesh) | ||||
| { | ||||
|     Eigen::MatrixXd V; | ||||
|     Eigen::MatrixXi F; | ||||
|     Eigen::MatrixXd vertex_normals; | ||||
|     sla::to_eigen_mesh(mesh, V, F); | ||||
|     igl::per_vertex_normals(V, F, vertex_normals); | ||||
| 
 | ||||
|     static constexpr int num_samples = 100; | ||||
|     const int num_vertices = std::min(10000, int(mesh.its.vertices.size())); | ||||
|     const Eigen::MatrixXd dirs = igl::random_dir_stratified(num_samples).cast<double>(); | ||||
| 
 | ||||
|     Eigen::MatrixXd occlusion_output0; | ||||
|     { | ||||
|         AABBTreeIndirect::Tree3f tree; | ||||
|         { | ||||
|             PROFILE_BLOCK(AABBIndirect_Init); | ||||
|             tree = AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set(mesh.its.vertices, mesh.its.indices); | ||||
|         } | ||||
|         { | ||||
|             PROFILE_BLOCK(EigenMesh3D_AABBIndirectF_AmbientOcclusion); | ||||
|             occlusion_output0.resize(num_vertices, 1); | ||||
|             for (int ivertex = 0; ivertex < num_vertices; ++ ivertex) { | ||||
|                 const Eigen::Vector3d origin = mesh.its.vertices[ivertex].template cast<double>(); | ||||
|                 const Eigen::Vector3d normal = vertex_normals.row(ivertex).template cast<double>(); | ||||
|                 int num_hits = 0; | ||||
|                 for (int s = 0; s < num_samples; s++) { | ||||
|                     Eigen::Vector3d d = dirs.row(s); | ||||
|                     if(d.dot(normal) < 0) { | ||||
|                         // reverse ray
 | ||||
|                         d *= -1; | ||||
|                     } | ||||
|                     igl::Hit hit; | ||||
|                     if (AABBTreeIndirect::intersect_ray_first_hit(mesh.its.vertices, mesh.its.indices, tree, (origin + 1e-4 * d).eval(), d, hit)) | ||||
|                         ++ num_hits; | ||||
|                 } | ||||
|                 occlusion_output0(ivertex) = (double)num_hits/(double)num_samples; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         { | ||||
|             PROFILE_BLOCK(EigenMesh3D_AABBIndirectFF_AmbientOcclusion); | ||||
|             occlusion_output0.resize(num_vertices, 1); | ||||
|             for (int ivertex = 0; ivertex < num_vertices; ++ ivertex) { | ||||
|                 const Eigen::Vector3d origin = mesh.its.vertices[ivertex].template cast<double>(); | ||||
|                 const Eigen::Vector3d normal = vertex_normals.row(ivertex).template cast<double>(); | ||||
|                 int num_hits = 0; | ||||
|                 for (int s = 0; s < num_samples; s++) { | ||||
|                     Eigen::Vector3d d = dirs.row(s); | ||||
|                     if(d.dot(normal) < 0) { | ||||
|                         // reverse ray
 | ||||
|                         d *= -1; | ||||
|                     } | ||||
|                     igl::Hit hit; | ||||
|                     if (AABBTreeIndirect::intersect_ray_first_hit(mesh.its.vertices, mesh.its.indices, tree,  | ||||
|                             Eigen::Vector3f((origin + 1e-4 * d).template cast<float>()), | ||||
|                             Eigen::Vector3f(d.template cast<float>()), hit)) | ||||
|                         ++ num_hits; | ||||
|                 } | ||||
|                 occlusion_output0(ivertex) = (double)num_hits/(double)num_samples; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     Eigen::MatrixXd occlusion_output1; | ||||
|     { | ||||
|         std::vector<Vec3d> vertices; | ||||
|         std::vector<Vec3i> triangles; | ||||
|         for (int i = 0; i < V.rows(); ++ i) | ||||
|             vertices.emplace_back(V.row(i).transpose()); | ||||
|         for (int i = 0; i < F.rows(); ++ i) | ||||
|             triangles.emplace_back(F.row(i).transpose()); | ||||
|         AABBTreeIndirect::Tree3d tree; | ||||
|         { | ||||
|             PROFILE_BLOCK(AABBIndirectD_Init); | ||||
|             tree = AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set(vertices, triangles); | ||||
|         } | ||||
| 
 | ||||
|         { | ||||
|             PROFILE_BLOCK(EigenMesh3D_AABBIndirectD_AmbientOcclusion); | ||||
|             occlusion_output1.resize(num_vertices, 1); | ||||
|             for (int ivertex = 0; ivertex < num_vertices; ++ ivertex) { | ||||
|                 const Eigen::Vector3d origin = V.row(ivertex).template cast<double>(); | ||||
|                 const Eigen::Vector3d normal = vertex_normals.row(ivertex).template cast<double>(); | ||||
|                 int num_hits = 0; | ||||
|                 for (int s = 0; s < num_samples; s++) { | ||||
|                     Eigen::Vector3d d = dirs.row(s); | ||||
|                     if(d.dot(normal) < 0) { | ||||
|                         // reverse ray
 | ||||
|                         d *= -1; | ||||
|                     } | ||||
|                     igl::Hit hit; | ||||
|                     if (AABBTreeIndirect::intersect_ray_first_hit(vertices, triangles, tree, Eigen::Vector3d(origin + 1e-4 * d), d, hit)) | ||||
|                         ++ num_hits; | ||||
|                 } | ||||
|                 occlusion_output1(ivertex) = (double)num_hits/(double)num_samples; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // Build the AABB accelaration tree
 | ||||
| 
 | ||||
|     Eigen::MatrixXd occlusion_output2; | ||||
|     { | ||||
|         igl::AABB<Eigen::MatrixXd, 3> AABB; | ||||
|         { | ||||
|             PROFILE_BLOCK(EigenMesh3D_AABB_Init); | ||||
|             AABB.init(V, F); | ||||
|         } | ||||
|         { | ||||
|             PROFILE_BLOCK(EigenMesh3D_AABB_AmbientOcclusion); | ||||
|             occlusion_output2.resize(num_vertices, 1); | ||||
|             for (int ivertex = 0; ivertex < num_vertices; ++ ivertex) { | ||||
|                 const Eigen::Vector3d origin = V.row(ivertex).template cast<double>(); | ||||
|                 const Eigen::Vector3d normal = vertex_normals.row(ivertex).template cast<double>(); | ||||
|                 int num_hits = 0; | ||||
|                 for (int s = 0; s < num_samples; s++) { | ||||
|                     Eigen::Vector3d d = dirs.row(s); | ||||
|                     if(d.dot(normal) < 0) { | ||||
|                         // reverse ray
 | ||||
|                         d *= -1; | ||||
|                     } | ||||
|                     igl::Hit hit; | ||||
|                     if (AABB.intersect_ray(V, F, origin + 1e-4 * d, d, hit)) | ||||
|                         ++ num_hits; | ||||
|                 } | ||||
|                 occlusion_output2(ivertex) = (double)num_hits/(double)num_samples; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     Eigen::MatrixXd occlusion_output3; | ||||
|     { | ||||
|         typedef Eigen::Map<const Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor | Eigen::DontAlign>> MapMatrixXfUnaligned; | ||||
|         typedef Eigen::Map<const Eigen::Matrix<int, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor | Eigen::DontAlign>> MapMatrixXiUnaligned; | ||||
|         igl::AABB<MapMatrixXfUnaligned, 3> AABB; | ||||
|         auto vertices = MapMatrixXfUnaligned(mesh.its.vertices.front().data(), mesh.its.vertices.size(), 3); | ||||
|         auto faces = MapMatrixXiUnaligned(mesh.its.indices.front().data(), mesh.its.indices.size(), 3); | ||||
|         { | ||||
|             PROFILE_BLOCK(EigenMesh3D_AABBf_Init); | ||||
|             AABB.init( | ||||
|                 vertices, | ||||
|                 faces); | ||||
|         } | ||||
| 
 | ||||
|         { | ||||
|             PROFILE_BLOCK(EigenMesh3D_AABBf_AmbientOcclusion); | ||||
|             occlusion_output3.resize(num_vertices, 1); | ||||
|             for (int ivertex = 0; ivertex < num_vertices; ++ ivertex) { | ||||
|                 const Eigen::Vector3d origin = mesh.its.vertices[ivertex].template cast<double>(); | ||||
|                 const Eigen::Vector3d normal = vertex_normals.row(ivertex).template cast<double>(); | ||||
|                 int num_hits = 0; | ||||
|                 for (int s = 0; s < num_samples; s++) { | ||||
|                     Eigen::Vector3d d = dirs.row(s); | ||||
|                     if(d.dot(normal) < 0) { | ||||
|                         // reverse ray
 | ||||
|                         d *= -1; | ||||
|                     } | ||||
|                     igl::Hit hit; | ||||
|                     if (AABB.intersect_ray(vertices, faces, (origin + 1e-4 * d).eval().template cast<float>(), d.template cast<float>(), hit)) | ||||
|                         ++ num_hits; | ||||
|                 } | ||||
|                 occlusion_output3(ivertex) = (double)num_hits/(double)num_samples; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     PROFILE_UPDATE(); | ||||
|     PROFILE_OUTPUT(nullptr); | ||||
| } | ||||
| 
 | ||||
| int main(const int argc, const char *argv[]) | ||||
| { | ||||
|     if(argc < 2) { | ||||
|         std::cout << USAGE_STR << std::endl; | ||||
|         return EXIT_SUCCESS; | ||||
|     } | ||||
| 
 | ||||
|     TriangleMesh mesh; | ||||
|     if (! mesh.ReadSTLFile(argv[1])) { | ||||
|         std::cerr << "Error loading " << argv[1] << std::endl; | ||||
|         return -1; | ||||
|     } | ||||
| 
 | ||||
|     mesh.repair(); | ||||
|     if (mesh.facets_count() == 0) { | ||||
|         std::cerr << "Error loading " << argv[1] << " . It is empty." << std::endl; | ||||
|         return -1; | ||||
|     } | ||||
| 
 | ||||
|     profile(mesh);     | ||||
| 
 | ||||
|     return EXIT_SUCCESS; | ||||
| } | ||||
|  | @ -40,8 +40,8 @@ THE SOFTWARE. | |||
| /*---------------------------------------------------------------------------*/ | ||||
| 
 | ||||
| #define OUTPUT_WIDTH_CALL	6 | ||||
| #define OUTPUT_WIDTH_TIME	6 | ||||
| #define OUTPUT_WIDTH_PERC	4 | ||||
| #define OUTPUT_WIDTH_TIME	(6+3) | ||||
| #define OUTPUT_WIDTH_PERC	(4+3) | ||||
| #define OUTPUT_WIDTH_SUM	120  | ||||
| 
 | ||||
| #define OUTPUT_WIDTH_DATA	(1+OUTPUT_WIDTH_CALL + 1 + 2*(OUTPUT_WIDTH_TIME+4+OUTPUT_WIDTH_PERC+1) + 1) | ||||
|  | @ -70,7 +70,7 @@ SHINY_INLINE char* printData(char *output, const ShinyData *a_data, float a_tope | |||
| 	const ShinyTimeUnit *totalUnit = ShinyGetTimeUnit(totalTicksAvg); | ||||
| 
 | ||||
| 	snprintf(output, OUTPUT_WIDTH_DATA + TRAILING, | ||||
| 		" %*.1f %*.0f %-2s %*.0f%% %*.0f %-2s %*.0f%%", | ||||
| 		" %*.1f %*.2f %-2s %*.2f%% %*.2f %-2s %*.2f%%", | ||||
| 		OUTPUT_WIDTH_CALL, a_data->entryCount.avg, | ||||
| 		OUTPUT_WIDTH_TIME, a_data->selfTicks.avg * selfUnit->invTickFreq, selfUnit->suffix, | ||||
| 		OUTPUT_WIDTH_PERC, a_data->selfTicks.avg * a_topercent, | ||||
|  |  | |||
|  | @ -7,3 +7,7 @@ add_library(clipper STATIC | |||
|     clipper_z.cpp | ||||
|     clipper_z.hpp | ||||
| ) | ||||
| 
 | ||||
| if(SLIC3R_PROFILE) | ||||
|     target_link_libraries(clipper Shiny) | ||||
| endif() | ||||
|  |  | |||
							
								
								
									
										698
									
								
								src/libslic3r/AABBTreeIndirect.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										698
									
								
								src/libslic3r/AABBTreeIndirect.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,698 @@ | |||
| // AABB tree built upon external data set, referencing the external data by integer indices.
 | ||||
| // The AABB tree balancing and traversal (ray casting, closest triangle of an indexed triangle mesh)
 | ||||
| // were adapted from libigl AABB.{cpp,hpp} Copyright (C) 2015 Alec Jacobson <alecjacobson@gmail.com>
 | ||||
| // while the implicit balanced tree representation and memory optimizations are Vojtech's.
 | ||||
| 
 | ||||
| #ifndef slic3r_AABBTreeIndirect_hpp_ | ||||
| #define slic3r_AABBTreeIndirect_hpp_ | ||||
| 
 | ||||
| #include <algorithm> | ||||
| #include <limits> | ||||
| #include <type_traits> | ||||
| #include <vector> | ||||
| 
 | ||||
| #include "Utils.hpp" // for next_highest_power_of_2()
 | ||||
| 
 | ||||
| extern "C" | ||||
| { | ||||
| // Ray-Triangle Intersection Test Routines by Tomas Moller, May 2000
 | ||||
| #include <igl/raytri.c> | ||||
| } | ||||
| // Definition of the ray intersection hit structure.
 | ||||
| #include <igl/Hit.h> | ||||
| 
 | ||||
| namespace Slic3r { | ||||
| namespace AABBTreeIndirect { | ||||
| 
 | ||||
| // Static balanced AABB tree for raycasting and closest triangle search.
 | ||||
| // The balanced tree is built over a single large std::vector of nodes, where the children of nodes
 | ||||
| // are addressed implicitely using a power of two indexing rule.
 | ||||
| // Memory for a full balanced tree is allocated, but not all nodes at the last level are used.
 | ||||
| // This may seem like a waste of memory, but one saves memory for the node links and there is zero
 | ||||
| // overhead of a memory allocator management (usually the memory allocator adds at least one pointer 
 | ||||
| // before the memory returned). However, allocating memory in a single vector is very fast even
 | ||||
| // in multi-threaded environment and it is cache friendly.
 | ||||
| //
 | ||||
| // A balanced tree is built upon a vector of bounding boxes and their centroids, storing the reference
 | ||||
| // to the source entity (a 3D triangle, a 2D segment etc, a 3D or 2D point etc).
 | ||||
| // The source bounding boxes may have an epsilon applied to fight numeric rounding errors when 
 | ||||
| // traversing the AABB tree.
 | ||||
| template<int ANumDimensions, typename ACoordType> | ||||
| class Tree | ||||
| { | ||||
| public: | ||||
|     static constexpr int    NumDimensions = ANumDimensions; | ||||
| 	using					CoordType     = ACoordType; | ||||
|     using 					VectorType 	  = Eigen::Matrix<CoordType, NumDimensions, 1, Eigen::DontAlign>; | ||||
|     using  					BoundingBox   = Eigen::AlignedBox<CoordType, NumDimensions>; | ||||
|     // Following could be static constexpr size_t, but that would not link in C++11
 | ||||
|     enum : size_t { | ||||
|     	// Node is not used.
 | ||||
|         npos  = size_t(-1), | ||||
|         // Inner node (not leaf).
 | ||||
|         inner = size_t(-2) | ||||
|     }; | ||||
| 
 | ||||
|     // Single node of the implicit balanced AABB tree. There are no links to the children nodes,
 | ||||
|     // as these links are calculated implicitely using a power of two rule.
 | ||||
|     struct Node { | ||||
|     	// Index of the external source entity, for which this AABB tree was built, npos for internal nodes.
 | ||||
|         size_t 			idx = npos; | ||||
|     	// Bounding box around this entity, possibly with epsilons applied to fight numeric rounding errors
 | ||||
|     	// when traversing the AABB tree.
 | ||||
|         BoundingBox 	bbox; | ||||
| 
 | ||||
|         bool 	is_valid() const { return this->idx != npos; } | ||||
|         bool 	is_inner() const { return this->idx == inner; } | ||||
|         bool 	is_leaf()  const { return ! this->is_inner(); } | ||||
| 
 | ||||
|     	template<typename SourceNode> | ||||
|     	void set(const SourceNode &rhs) { | ||||
|             this->idx  = rhs.idx(); | ||||
|             this->bbox = rhs.bbox(); | ||||
| 		} | ||||
|     }; | ||||
| 
 | ||||
| 	void clear() { m_nodes.clear(); } | ||||
| 
 | ||||
| 	// SourceNode shall implement
 | ||||
| 	// size_t SourceNode::idx() const
 | ||||
| 	// 		- Index to the outside entity (triangle, edge, point etc).
 | ||||
| 	// const VectorType& SourceNode::centroid() const
 | ||||
| 	// 		- Centroid of this node. The centroid is used for balancing the tree.
 | ||||
| 	// const BoundingBox& SourceNode::bbox() const
 | ||||
| 	// 		- Bounding box of this node, likely expanded with epsilon to account for numeric rounding during tree traversal.
 | ||||
| 	//        Union of bounding boxes at a single level of the AABB tree is used for deciding the longest axis aligned dimension
 | ||||
| 	//        to split around.
 | ||||
| 	template<typename SourceNode> | ||||
| 	void build(std::vector<SourceNode> &&input) | ||||
| 	{ | ||||
|         if (input.empty()) | ||||
| 			clear(); | ||||
| 		else { | ||||
| 			// Allocate enough memory for a full binary tree.
 | ||||
|             m_nodes.assign(next_highest_power_of_2(input.size()) * 2 - 1, Node()); | ||||
|             build_recursive(input, 0, 0, input.size() - 1); | ||||
| 		} | ||||
|         input.clear(); | ||||
| 	} | ||||
| 
 | ||||
| 	const std::vector<Node>& 	nodes() const { return m_nodes; } | ||||
| 	const Node& 				node(size_t idx) const { return m_nodes[idx]; } | ||||
| 	bool 						empty() const { return m_nodes.empty(); } | ||||
| 
 | ||||
| 	// Addressing the child nodes using the power of two rule.
 | ||||
|     static size_t				left_child_idx(size_t idx) { return idx * 2 + 1; } | ||||
|     static size_t				right_child_idx(size_t idx) { return left_child_idx(idx) + 1; } | ||||
| 	const Node&					left_child(size_t idx) const { return m_nodes[left_child_idx(idx)]; } | ||||
| 	const Node&					right_child(size_t idx) const { return m_nodes[right_child_idx(idx)]; } | ||||
| 
 | ||||
| 	template<typename SourceNode> | ||||
|     void build(const std::vector<SourceNode> &input) | ||||
| 	{ | ||||
|         std::vector<SourceNode> copy(input); | ||||
|         this->build(std::move(copy)); | ||||
| 	} | ||||
| 
 | ||||
| private: | ||||
| 	// Build a balanced tree by splitting the input sequence by an axis aligned plane at a dimension.
 | ||||
| 	template<typename SourceNode> | ||||
| 	void build_recursive(std::vector<SourceNode> &input, size_t node, const size_t left, const size_t right) | ||||
| 	{ | ||||
|         assert(node < m_nodes.size()); | ||||
|         assert(left <= right); | ||||
| 
 | ||||
| 		if (left == right) { | ||||
| 			// Insert a node into the balanced tree.
 | ||||
| 			m_nodes[node].set(input[left]); | ||||
| 			return; | ||||
| 		} | ||||
| 
 | ||||
| 		// Calculate bounding box of the input.
 | ||||
|         BoundingBox bbox(input[left].bbox()); | ||||
|         for (size_t i = left + 1; i <= right; ++ i) | ||||
|             bbox.extend(input[i].bbox()); | ||||
|         int dimension = -1; | ||||
|         bbox.diagonal().maxCoeff(&dimension); | ||||
| 
 | ||||
| 		// Partition the input to left / right pieces of the same length to produce a balanced tree.
 | ||||
| 		size_t center = (left + right) / 2; | ||||
| 		partition_input(input, size_t(dimension), left, right, center); | ||||
| 		// Insert an inner node into the tree. Inner node does not reference any input entity (triangle, line segment etc).
 | ||||
| 		m_nodes[node].idx  = inner; | ||||
| 		m_nodes[node].bbox = bbox; | ||||
|         build_recursive(input, node * 2 + 1, left, center); | ||||
| 		build_recursive(input, node * 2 + 2, center + 1, right); | ||||
| 	} | ||||
| 
 | ||||
| 	// Partition the input m_nodes <left, right> at "k" and "dimension" using the QuickSelect method:
 | ||||
| 	// https://en.wikipedia.org/wiki/Quickselect
 | ||||
| 	// Items left of the k'th item are lower than the k'th item in the "dimension", 
 | ||||
| 	// items right of the k'th item are higher than the k'th item in the "dimension", 
 | ||||
| 	template<typename SourceNode> | ||||
| 	void partition_input(std::vector<SourceNode> &input, const size_t dimension, size_t left, size_t right, const size_t k) const | ||||
| 	{ | ||||
| 		while (left < right) { | ||||
| 			size_t center = (left + right) / 2; | ||||
| 			CoordType pivot; | ||||
| 			{ | ||||
| 				// Bubble sort the input[left], input[center], input[right], so that a median of the three values
 | ||||
| 				// will end up in input[center].
 | ||||
| 				CoordType left_value   = input[left  ].centroid()(dimension); | ||||
| 				CoordType center_value = input[center].centroid()(dimension); | ||||
| 				CoordType right_value  = input[right ].centroid()(dimension); | ||||
| 				if (left_value > center_value) { | ||||
| 					std::swap(input[left], input[center]); | ||||
| 					std::swap(left_value,  center_value); | ||||
| 				} | ||||
| 				if (left_value > right_value) { | ||||
| 					std::swap(input[left], input[right]); | ||||
| 					right_value = left_value; | ||||
| 				} | ||||
| 				if (center_value > right_value) { | ||||
| 					std::swap(input[center], input[right]); | ||||
| 					center_value = right_value; | ||||
| 				} | ||||
| 				pivot = center_value; | ||||
| 			} | ||||
| 			if (right <= left + 2) | ||||
| 				// The <left, right> interval is already sorted.
 | ||||
| 				break; | ||||
| 			size_t i = left; | ||||
| 			size_t j = right - 1; | ||||
| 			std::swap(input[center], input[j]); | ||||
| 			// Partition the set based on the pivot.
 | ||||
| 			for (;;) { | ||||
| 				// Skip left points that are already at correct positions.
 | ||||
| 				// Search will certainly stop at position (right - 1), which stores the pivot.
 | ||||
| 				while (input[++ i].centroid()(dimension) < pivot) ; | ||||
| 				// Skip right points that are already at correct positions.
 | ||||
| 				while (input[-- j].centroid()(dimension) > pivot && i < j) ; | ||||
| 				if (i >= j) | ||||
| 					break; | ||||
| 				std::swap(input[i], input[j]); | ||||
| 			} | ||||
| 			// Restore pivot to the center of the sequence.
 | ||||
| 			std::swap(input[i], input[right - 1]); | ||||
| 			// Which side the kth element is in?
 | ||||
| 			if (k < i) | ||||
| 				right = i - 1; | ||||
| 			else if (k == i) | ||||
| 				// Sequence is partitioned, kth element is at its place.
 | ||||
| 				break; | ||||
| 			else | ||||
| 				left = i + 1; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// The balanced tree storage.
 | ||||
| 	std::vector<Node> m_nodes; | ||||
| }; | ||||
| 
 | ||||
| using Tree2f = Tree<2, float>; | ||||
| using Tree3f = Tree<3, float>; | ||||
| using Tree2d = Tree<2, double>; | ||||
| using Tree3d = Tree<3, double>; | ||||
| 
 | ||||
| namespace detail { | ||||
| 	template<typename AVertexType, typename AIndexedFaceType, typename ATreeType, typename AVectorType> | ||||
| 	struct RayIntersector { | ||||
| 		using VertexType 		= AVertexType; | ||||
| 		using IndexedFaceType 	= AIndexedFaceType; | ||||
| 		using TreeType			= ATreeType; | ||||
| 		using VectorType 		= AVectorType; | ||||
| 
 | ||||
| 		const std::vector<VertexType> 		&vertices; | ||||
| 		const std::vector<IndexedFaceType> 	&faces; | ||||
| 		const TreeType 						&tree; | ||||
| 
 | ||||
| 		const VectorType					 origin; | ||||
| 		const VectorType 					 dir; | ||||
| 		const VectorType					 invdir; | ||||
| 	}; | ||||
| 
 | ||||
|     template<typename VertexType, typename IndexedFaceType, typename TreeType, typename VectorType> | ||||
|     struct RayIntersectorHits : RayIntersector<VertexType, IndexedFaceType, TreeType, VectorType> { | ||||
| 		std::vector<igl::Hit>				 hits; | ||||
| 	}; | ||||
| 
 | ||||
| 	//FIXME implement SSE for float AABB trees with float ray queries.
 | ||||
| 	// SSE/SSE2 is supported by any Intel/AMD x64 processor.
 | ||||
| 	// SSE support requires 16 byte alignment of the AABB nodes, representing the bounding boxes with 4+4 floats,
 | ||||
| 	// storing the node index as the 4th element of the bounding box min value etc.
 | ||||
| 	// https://www.flipcode.com/archives/SSE_RayBox_Intersection_Test.shtml
 | ||||
| 	template <typename Derivedsource, typename Deriveddir, typename Scalar> | ||||
| 	inline bool ray_box_intersect_invdir( | ||||
|   		const Eigen::MatrixBase<Derivedsource> 	&origin, | ||||
|   		const Eigen::MatrixBase<Deriveddir> 	&inv_dir, | ||||
|   		Eigen::AlignedBox<Scalar,3> 			 box, | ||||
|   		const Scalar 							&t0, | ||||
|   		const Scalar 							&t1) { | ||||
| 		// http://people.csail.mit.edu/amy/papers/box-jgt.pdf
 | ||||
| 		// "An Efficient and Robust Ray–Box Intersection Algorithm"
 | ||||
| 		if (inv_dir.x() < 0) | ||||
| 			std::swap(box.min().x(), box.max().x()); | ||||
| 		if (inv_dir.y() < 0) | ||||
| 			std::swap(box.min().y(), box.max().y()); | ||||
|         Scalar tmin = (box.min().x() - origin.x()) * inv_dir.x(); | ||||
| 		Scalar tymax = (box.max().y() - origin.y()) * inv_dir.y(); | ||||
| 		if (tmin > tymax) | ||||
| 			return false; | ||||
|         Scalar tmax = (box.max().x() - origin.x()) * inv_dir.x(); | ||||
| 		Scalar tymin = (box.min().y()  - origin.y()) * inv_dir.y(); | ||||
| 		if (tymin > tmax) | ||||
| 			return false; | ||||
| 		if (tymin > tmin) | ||||
| 			tmin = tymin; | ||||
| 		if (tymax < tmax) | ||||
| 			tmax = tymax; | ||||
| 		if (inv_dir.z() < 0) | ||||
| 			std::swap(box.min().z(), box.max().z()); | ||||
| 		Scalar tzmin = (box.min().z()  - origin.z()) * inv_dir.z(); | ||||
| 		if (tzmin > tmax) | ||||
| 			return false; | ||||
| 		Scalar tzmax = (box.max().z() - origin.z()) * inv_dir.z(); | ||||
| 		if (tmin > tzmax) | ||||
| 			return false; | ||||
| 		if (tzmin > tmin) | ||||
| 			tmin = tzmin; | ||||
| 		if (tzmax < tmax) | ||||
| 			tmax = tzmax; | ||||
|         return tmin < t1 && tmax > t0; | ||||
| 	} | ||||
| 
 | ||||
| 	template<typename V, typename W> | ||||
|     std::enable_if_t<std::is_same<typename V::Scalar, double>::value && std::is_same<typename W::Scalar, double>::value, bool> | ||||
| 	intersect_triangle(const V &origin, const V &dir, const W &v0, const W &v1, const W v2, double &t, double &u, double &v) { | ||||
|         return intersect_triangle1(const_cast<double*>(origin.data()), const_cast<double*>(dir.data()), | ||||
|                                    const_cast<double*>(v0.data()), const_cast<double*>(v1.data()), const_cast<double*>(v2.data()), | ||||
|                                    &t, &u, &v); | ||||
| 	} | ||||
| 
 | ||||
| 	template<typename V, typename W> | ||||
|     std::enable_if_t<std::is_same<typename V::Scalar, double>::value && !std::is_same<typename W::Scalar, double>::value, bool> | ||||
| 	intersect_triangle(const V &origin, const V &dir, const W &v0, const W &v1, const W v2, double &t, double &u, double &v) { | ||||
|         using Vector = Eigen::Matrix<double, 3, 1>; | ||||
|         Vector w0 = v0.template cast<double>(); | ||||
|         Vector w1 = v1.template cast<double>(); | ||||
|         Vector w2 = v2.template cast<double>(); | ||||
|         return intersect_triangle1(const_cast<double*>(origin.data()), const_cast<double*>(dir.data()), | ||||
|                                    w0.data(), w1.data(), w2.data(), &t, &u, &v); | ||||
| 	} | ||||
| 
 | ||||
| 	template<typename V, typename W> | ||||
|     std::enable_if_t<! std::is_same<typename V::Scalar, double>::value && std::is_same<typename W::Scalar, double>::value, bool> | ||||
| 	intersect_triangle(const V &origin, const V &dir, const W &v0, const W &v1, const W v2, double &t, double &u, double &v) { | ||||
|         using Vector = Eigen::Matrix<double, 3, 1>; | ||||
|         Vector o  = origin.template cast<double>(); | ||||
|         Vector d  = dir.template cast<double>(); | ||||
|         return intersect_triangle1(o.data(), d.data(), const_cast<double*>(v0.data()), const_cast<double*>(v1.data()), const_cast<double*>(v2.data()), &t, &u, &v); | ||||
| 	} | ||||
| 
 | ||||
| 	template<typename V, typename W> | ||||
|     std::enable_if_t<! std::is_same<typename V::Scalar, double>::value && ! std::is_same<typename W::Scalar, double>::value, bool> | ||||
| 	intersect_triangle(const V &origin, const V &dir, const W &v0, const W &v1, const W v2, double &t, double &u, double &v) { | ||||
|         using Vector = Eigen::Matrix<double, 3, 1>; | ||||
|         Vector o  = origin.template cast<double>(); | ||||
|         Vector d  = dir.template cast<double>(); | ||||
|         Vector w0 = v0.template cast<double>(); | ||||
|         Vector w1 = v1.template cast<double>(); | ||||
|         Vector w2 = v2.template cast<double>(); | ||||
| 	    return intersect_triangle1(o.data(), d.data(), w0.data(), w1.data(), w2.data(), &t, &u, &v); | ||||
| 	} | ||||
| 
 | ||||
|     template<typename RayIntersectorType, typename Scalar> | ||||
| 	static inline bool intersect_ray_recursive_first_hit( | ||||
|         RayIntersectorType 	   &ray_intersector, | ||||
|         size_t 				    node_idx, | ||||
|         Scalar                  min_t, | ||||
|         igl::Hit 			   &hit) | ||||
| 	{ | ||||
|         const auto &node = ray_intersector.tree.node(node_idx); | ||||
|         assert(node.is_valid()); | ||||
| 		 | ||||
|         if (! ray_box_intersect_invdir(ray_intersector.origin, ray_intersector.invdir, node.bbox.template cast<Scalar>(), Scalar(0), min_t)) | ||||
| 			return false; | ||||
| 
 | ||||
| 	  	if (node.is_leaf()) { | ||||
| 		    // shoot ray, record hit
 | ||||
|             auto   face = ray_intersector.faces[node.idx]; | ||||
| 		    double t, u, v; | ||||
| 		    if (intersect_triangle( | ||||
| 		    		ray_intersector.origin, ray_intersector.dir,  | ||||
| 		    		ray_intersector.vertices[face(0)], ray_intersector.vertices[face(1)], ray_intersector.vertices[face(2)],  | ||||
|                     t, u, v) | ||||
| 		    	&& t > 0.) { | ||||
|                 hit = igl::Hit { int(node.idx), -1, float(u), float(v), float(t) }; | ||||
| 				return true; | ||||
| 		    } else | ||||
| 		    	return false; | ||||
| 	  	} else { | ||||
| 			// Left / right child node index.
 | ||||
| 			size_t left  = node_idx * 2 + 1; | ||||
| 			size_t right = left + 1; | ||||
| 			igl::Hit left_hit; | ||||
| 			igl::Hit right_hit; | ||||
| 	        bool left_ret = intersect_ray_recursive_first_hit(ray_intersector, left,  min_t, left_hit); | ||||
| 			if (left_ret && left_hit.t < min_t) { | ||||
| 	    		min_t = left_hit.t; | ||||
| 	    		hit   = left_hit; | ||||
| 	  		} else | ||||
| 			    left_ret = false; | ||||
| 	        bool right_ret = intersect_ray_recursive_first_hit(ray_intersector, right, min_t, right_hit); | ||||
| 			if (right_ret && right_hit.t < min_t) | ||||
| 				hit = right_hit; | ||||
| 			else | ||||
| 				right_ret = false; | ||||
| 			return left_ret || right_ret; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
|     template<typename RayIntersectorType> | ||||
| 	static inline void intersect_ray_recursive_all_hits(RayIntersectorType &ray_intersector, size_t node_idx) | ||||
| 	{ | ||||
|         using Scalar = typename RayIntersectorType::VectorType::Scalar; | ||||
| 
 | ||||
| 		const auto &node = ray_intersector.tree.node(node_idx); | ||||
| 		assert(node.is_valid()); | ||||
| 
 | ||||
|         if (! ray_box_intersect_invdir(ray_intersector.origin, ray_intersector.invdir, node.bbox.template cast<Scalar>(), | ||||
|     			Scalar(0), std::numeric_limits<Scalar>::infinity())) | ||||
| 			return; | ||||
| 
 | ||||
| 	  	if (node.is_leaf()) { | ||||
|             auto   face = ray_intersector.faces[node.idx]; | ||||
| 		    double t, u, v; | ||||
| 		    if (intersect_triangle( | ||||
| 		    		ray_intersector.origin, ray_intersector.dir,  | ||||
| 		    		ray_intersector.vertices[face(0)], ray_intersector.vertices[face(1)], ray_intersector.vertices[face(2)],  | ||||
|                     t, u, v) | ||||
| 		    	&& t > 0.) { | ||||
|                 ray_intersector.hits.emplace_back(igl::Hit{ int(node.idx), -1, float(u), float(v), float(t) }); | ||||
| 			} | ||||
| 	  	} else { | ||||
| 			// Left / right child node index.
 | ||||
| 			size_t left  = node_idx * 2 + 1; | ||||
| 			size_t right = left + 1; | ||||
| 		  	intersect_ray_recursive_all_hits(ray_intersector, left); | ||||
| 		  	intersect_ray_recursive_all_hits(ray_intersector, right); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// Nothing to do with COVID-19 social distancing.
 | ||||
| 	template<typename AVertexType, typename AIndexedFaceType, typename ATreeType, typename AVectorType> | ||||
| 	struct IndexedTriangleSetDistancer { | ||||
| 		using VertexType 		= AVertexType; | ||||
| 		using IndexedFaceType 	= AIndexedFaceType; | ||||
| 		using TreeType			= ATreeType; | ||||
| 		using VectorType 		= AVectorType; | ||||
| 
 | ||||
| 		const std::vector<VertexType> 		&vertices; | ||||
| 		const std::vector<IndexedFaceType> 	&faces; | ||||
| 		const TreeType 						&tree; | ||||
| 
 | ||||
| 		const VectorType					 origin; | ||||
| 	}; | ||||
| 
 | ||||
| 	// Real-time collision detection, Ericson, Chapter 5
 | ||||
| 	template<typename Vector> | ||||
| 	static inline Vector closest_point_to_triangle(const Vector &p, const Vector &a, const Vector &b, const Vector &c) | ||||
| 	{ | ||||
| 		using Scalar = typename Vector::Scalar; | ||||
| 		// Check if P in vertex region outside A
 | ||||
| 		Vector ab = b - a; | ||||
| 		Vector ac = c - a; | ||||
| 		Vector ap = p - a; | ||||
| 		Scalar d1 = ab.dot(ap); | ||||
| 		Scalar d2 = ac.dot(ap); | ||||
| 		if (d1 <= 0 && d2 <= 0) | ||||
| 		  return a; | ||||
| 		// Check if P in vertex region outside B
 | ||||
| 		Vector bp = p - b; | ||||
| 		Scalar d3 = ab.dot(bp); | ||||
| 		Scalar d4 = ac.dot(bp); | ||||
| 		if (d3 >= 0 && d4 <= d3) | ||||
| 		  return b; | ||||
| 		// Check if P in edge region of AB, if so return projection of P onto AB
 | ||||
| 		Scalar vc = d1*d4 - d3*d2; | ||||
| 		if (a != b && vc <= 0 && d1 >= 0 && d3 <= 0) { | ||||
| 		    Scalar v = d1 / (d1 - d3); | ||||
| 		    return a + v * ab; | ||||
| 		} | ||||
| 		// Check if P in vertex region outside C
 | ||||
| 		Vector cp = p - c; | ||||
| 		Scalar d5 = ab.dot(cp); | ||||
| 		Scalar d6 = ac.dot(cp); | ||||
| 		if (d6 >= 0 && d5 <= d6) | ||||
| 		  return c; | ||||
| 		// Check if P in edge region of AC, if so return projection of P onto AC
 | ||||
| 		Scalar vb = d5*d2 - d1*d6; | ||||
| 		if (vb <= 0 && d2 >= 0 && d6 <= 0) { | ||||
| 		  Scalar w = d2 / (d2 - d6); | ||||
| 		  return a + w * ac; | ||||
| 		} | ||||
| 		// Check if P in edge region of BC, if so return projection of P onto BC
 | ||||
| 		Scalar va = d3*d6 - d5*d4; | ||||
| 		if (va <= 0 && (d4 - d3) >= 0 && (d5 - d6) >= 0) { | ||||
| 		  Scalar w = (d4 - d3) / ((d4 - d3) + (d5 - d6)); | ||||
| 		  return b + w * (c - b); | ||||
| 		} | ||||
| 		// P inside face region. Compute Q through its barycentric coordinates (u,v,w)
 | ||||
| 		Scalar denom = Scalar(1.0) / (va + vb + vc); | ||||
| 		Scalar v = vb * denom; | ||||
| 		Scalar w = vc * denom; | ||||
| 		return a + ab * v + ac * w; // = u*a + v*b + w*c, u = va * denom = 1.0-v-w
 | ||||
| 	}; | ||||
| 
 | ||||
| 	template<typename IndexedTriangleSetDistancerType, typename Scalar> | ||||
|     static inline Scalar squared_distance_to_indexed_triangle_set_recursive( | ||||
|         IndexedTriangleSetDistancerType	&distancer, | ||||
| 		size_t 							 node_idx, | ||||
| 		Scalar 							 low_sqr_d, | ||||
|   		Scalar 							 up_sqr_d, | ||||
| 		size_t 							&i, | ||||
|   		Eigen::PlainObjectBase<typename IndexedTriangleSetDistancerType::VectorType> &c) | ||||
| 	{ | ||||
| 		using Vector = typename IndexedTriangleSetDistancerType::VectorType; | ||||
| 
 | ||||
|   		if (low_sqr_d > up_sqr_d) | ||||
| 			return low_sqr_d; | ||||
| 	   | ||||
| 	  	// Save the best achieved hit.
 | ||||
|         auto set_min = [&i, &c, &up_sqr_d](const Scalar sqr_d_candidate, const size_t i_candidate, const Vector &c_candidate) { | ||||
| 			if (sqr_d_candidate < up_sqr_d) { | ||||
| 				i     	 = i_candidate; | ||||
| 				c     	 = c_candidate; | ||||
| 				up_sqr_d = sqr_d_candidate; | ||||
| 			} | ||||
|         }; | ||||
| 
 | ||||
| 		const auto &node = distancer.tree.node(node_idx); | ||||
| 		assert(node.is_valid()); | ||||
|   		if (node.is_leaf())  | ||||
|   		{ | ||||
|             const auto &triangle = distancer.faces[node.idx]; | ||||
|             Vector c_candidate = closest_point_to_triangle<Vector>( | ||||
| 				distancer.origin,  | ||||
|                 distancer.vertices[triangle(0)].template cast<Scalar>(), | ||||
|                 distancer.vertices[triangle(1)].template cast<Scalar>(), | ||||
|                 distancer.vertices[triangle(2)].template cast<Scalar>()); | ||||
|             set_min((c_candidate - distancer.origin).squaredNorm(), node.idx, c_candidate); | ||||
|   		}  | ||||
|   		else | ||||
|   		{ | ||||
| 			size_t left_node_idx  = node_idx * 2 + 1; | ||||
|             size_t right_node_idx = left_node_idx + 1; | ||||
| 			const auto &node_left  = distancer.tree.node(left_node_idx); | ||||
| 			const auto &node_right = distancer.tree.node(right_node_idx); | ||||
| 			assert(node_left.is_valid()); | ||||
| 			assert(node_right.is_valid()); | ||||
| 
 | ||||
| 			bool   looked_left    = false; | ||||
| 			bool   looked_right   = false; | ||||
| 			const auto &look_left = [&]() | ||||
| 			{ | ||||
|                 size_t	i_left; | ||||
|                 Vector 	c_left = c; | ||||
|                 Scalar	sqr_d_left = squared_distance_to_indexed_triangle_set_recursive(distancer, left_node_idx, low_sqr_d, up_sqr_d, i_left, c_left); | ||||
| 				set_min(sqr_d_left, i_left, c_left); | ||||
| 				looked_left = true; | ||||
| 			}; | ||||
| 			const auto &look_right = [&]() | ||||
| 			{ | ||||
|                 size_t	i_right; | ||||
|                 Vector	c_right = c; | ||||
|                 Scalar	sqr_d_right = squared_distance_to_indexed_triangle_set_recursive(distancer, right_node_idx, low_sqr_d, up_sqr_d, i_right, c_right); | ||||
| 				set_min(sqr_d_right, i_right, c_right); | ||||
| 				looked_right = true; | ||||
| 			}; | ||||
| 
 | ||||
| 			// must look left or right if in box
 | ||||
|             using BBoxScalar = typename IndexedTriangleSetDistancerType::TreeType::BoundingBox::Scalar; | ||||
|             if (node_left.bbox.contains(distancer.origin.template cast<BBoxScalar>())) | ||||
| 			  	look_left(); | ||||
|             if (node_right.bbox.contains(distancer.origin.template cast<BBoxScalar>())) | ||||
| 			  	look_right(); | ||||
| 			// if haven't looked left and could be less than current min, then look
 | ||||
| 			Scalar left_up_sqr_d  = node_left.bbox.squaredExteriorDistance(distancer.origin); | ||||
| 			Scalar right_up_sqr_d = node_right.bbox.squaredExteriorDistance(distancer.origin); | ||||
| 			if (left_up_sqr_d < right_up_sqr_d) { | ||||
| 			  	if (! looked_left && left_up_sqr_d < up_sqr_d) | ||||
| 			    	look_left(); | ||||
| 			  	if (! looked_right && right_up_sqr_d < up_sqr_d) | ||||
| 			    	look_right(); | ||||
| 			} else { | ||||
| 			  	if (! looked_right && right_up_sqr_d < up_sqr_d) | ||||
| 			    	look_right(); | ||||
| 			  	if (! looked_left && left_up_sqr_d < up_sqr_d) | ||||
| 			    	look_left(); | ||||
| 			} | ||||
| 		} | ||||
| 		return up_sqr_d; | ||||
| 	} | ||||
| 
 | ||||
| } // namespace detail
 | ||||
| 
 | ||||
| // Build a balanced AABB Tree over an indexed triangles set, balancing the tree
 | ||||
| // on centroids of the triangles.
 | ||||
| // Epsilon is applied to the bounding boxes of the AABB Tree to cope with numeric inaccuracies
 | ||||
| // during tree traversal.
 | ||||
| template<typename VertexType, typename IndexedFaceType> | ||||
| inline Tree<3, typename VertexType::Scalar> build_aabb_tree_over_indexed_triangle_set( | ||||
| 	// Indexed triangle set - 3D vertices.
 | ||||
| 	const std::vector<VertexType> 		&vertices,  | ||||
| 	// Indexed triangle set - triangular faces, references to vertices.
 | ||||
|     const std::vector<IndexedFaceType> 	&faces, | ||||
| 	//FIXME do we want to apply an epsilon?
 | ||||
|     const typename VertexType::Scalar 	 eps = 0) | ||||
| { | ||||
|     using 				 TreeType 		= Tree<3, typename VertexType::Scalar>; | ||||
| //    using				 CoordType      = typename TreeType::CoordType;
 | ||||
|     using 				 VectorType	    = typename TreeType::VectorType; | ||||
|     using 				 BoundingBox 	= typename TreeType::BoundingBox; | ||||
| 
 | ||||
| 	struct InputType { | ||||
|         size_t 				idx()       const { return m_idx; } | ||||
|         const BoundingBox& 	bbox()      const { return m_bbox; } | ||||
|         const VectorType& 	centroid()  const { return m_centroid; } | ||||
| 
 | ||||
| 		size_t 		m_idx; | ||||
| 		BoundingBox m_bbox; | ||||
|         VectorType 	m_centroid; | ||||
| 	}; | ||||
| 
 | ||||
| 	std::vector<InputType> input; | ||||
| 	input.reserve(faces.size()); | ||||
|     const VectorType veps(eps, eps, eps); | ||||
| 	for (size_t i = 0; i < faces.size(); ++ i) { | ||||
|         const IndexedFaceType &face = faces[i]; | ||||
| 		const VertexType &v1 = vertices[face(0)]; | ||||
| 		const VertexType &v2 = vertices[face(1)]; | ||||
| 		const VertexType &v3 = vertices[face(2)]; | ||||
| 		InputType n; | ||||
|         n.m_idx      = i; | ||||
|         n.m_centroid = (1./3.) * (v1 + v2 + v3); | ||||
|         n.m_bbox = BoundingBox(v1, v1); | ||||
|         n.m_bbox.extend(v2); | ||||
|         n.m_bbox.extend(v3); | ||||
|         n.m_bbox.min() -= veps; | ||||
|         n.m_bbox.max() += veps; | ||||
|         input.emplace_back(n); | ||||
| 	} | ||||
| 
 | ||||
| 	TreeType out; | ||||
| 	out.build(std::move(input)); | ||||
| 	return out; | ||||
| } | ||||
| 
 | ||||
| // Find a first intersection of a ray with indexed triangle set.
 | ||||
| // Intersection test is calculated with the accuracy of VectorType::Scalar
 | ||||
| // even if the triangle mesh and the AABB Tree are built with floats.
 | ||||
| template<typename VertexType, typename IndexedFaceType, typename TreeType, typename VectorType> | ||||
| inline bool intersect_ray_first_hit( | ||||
| 	// Indexed triangle set - 3D vertices.
 | ||||
| 	const std::vector<VertexType> 		&vertices, | ||||
| 	// Indexed triangle set - triangular faces, references to vertices.
 | ||||
| 	const std::vector<IndexedFaceType> 	&faces, | ||||
| 	// AABBTreeIndirect::Tree over vertices & faces, bounding boxes built with the accuracy of vertices.
 | ||||
| 	const TreeType 						&tree, | ||||
| 	// Origin of the ray.
 | ||||
| 	const VectorType					&origin, | ||||
| 	// Direction of the ray.
 | ||||
| 	const VectorType 					&dir, | ||||
| 	// First intersection of the ray with the indexed triangle set.
 | ||||
| 	igl::Hit 							&hit) | ||||
| { | ||||
|     using Scalar = typename VectorType::Scalar; | ||||
|     auto ray_intersector = detail::RayIntersector<VertexType, IndexedFaceType, TreeType, VectorType> { | ||||
| 		vertices, faces, tree, | ||||
|         origin, dir, VectorType(dir.cwiseInverse()) | ||||
| 	}; | ||||
| 	return ! tree.empty() && detail::intersect_ray_recursive_first_hit( | ||||
|         ray_intersector, size_t(0), std::numeric_limits<Scalar>::infinity(), hit); | ||||
| } | ||||
| 
 | ||||
| // Find all intersections of a ray with indexed triangle set.
 | ||||
| // Intersection test is calculated with the accuracy of VectorType::Scalar
 | ||||
| // even if the triangle mesh and the AABB Tree are built with floats.
 | ||||
| // The output hits are sorted by the ray parameter.
 | ||||
| // If the ray intersects a shared edge of two triangles, hits for both triangles are returned.
 | ||||
| template<typename VertexType, typename IndexedFaceType, typename TreeType, typename VectorType> | ||||
| inline bool intersect_ray_all_hits( | ||||
| 	// Indexed triangle set - 3D vertices.
 | ||||
| 	const std::vector<VertexType> 		&vertices, | ||||
| 	// Indexed triangle set - triangular faces, references to vertices.
 | ||||
| 	const std::vector<IndexedFaceType> 	&faces, | ||||
| 	// AABBTreeIndirect::Tree over vertices & faces, bounding boxes built with the accuracy of vertices.
 | ||||
| 	const TreeType 						&tree, | ||||
| 	// Origin of the ray.
 | ||||
| 	const VectorType					&origin, | ||||
| 	// Direction of the ray.
 | ||||
| 	const VectorType 					&dir, | ||||
| 	// All intersections of the ray with the indexed triangle set, sorted by parameter t.
 | ||||
| 	std::vector<igl::Hit> 				&hits) | ||||
| { | ||||
|     auto ray_intersector = detail::RayIntersectorHits<VertexType, IndexedFaceType, TreeType, VectorType> { | ||||
| 		vertices, faces, tree, | ||||
|         origin, dir, VectorType(dir.cwiseInverse()) | ||||
| 	}; | ||||
| 	if (! tree.empty()) { | ||||
|         ray_intersector.hits.reserve(8); | ||||
| 		detail::intersect_ray_recursive_all_hits(ray_intersector, 0); | ||||
| 		std::swap(hits, ray_intersector.hits); | ||||
| 	    std::sort(hits.begin(), hits.end(), [](const auto &l, const auto &r) { return l.t < r.t; }); | ||||
| 	} | ||||
| 	return ! hits.empty(); | ||||
| } | ||||
| 
 | ||||
| // Finding a closest triangle, its closest point and squared distance to the closest point
 | ||||
| // on a 3D indexed triangle set using a pre-built AABBTreeIndirect::Tree.
 | ||||
| // Closest point to triangle test will be performed with the accuracy of VectorType::Scalar
 | ||||
| // even if the triangle mesh and the AABB Tree are built with floats.
 | ||||
| // Returns squared distance to the closest point or -1 if the input is empty.
 | ||||
| template<typename VertexType, typename IndexedFaceType, typename TreeType, typename VectorType> | ||||
| inline typename VectorType::Scalar squared_distance_to_indexed_triangle_set( | ||||
| 	// Indexed triangle set - 3D vertices.
 | ||||
| 	const std::vector<VertexType> 		&vertices, | ||||
| 	// Indexed triangle set - triangular faces, references to vertices.
 | ||||
| 	const std::vector<IndexedFaceType> 	&faces, | ||||
| 	// AABBTreeIndirect::Tree over vertices & faces, bounding boxes built with the accuracy of vertices.
 | ||||
| 	const TreeType 						&tree, | ||||
| 	// Point to which the closest point on the indexed triangle set is searched for.
 | ||||
| 	const VectorType					&point, | ||||
| 	// Index of the closest triangle in faces.
 | ||||
| 	size_t 								&hit_idx_out, | ||||
| 	// Position of the closest point on the indexed triangle set.
 | ||||
| 	Eigen::PlainObjectBase<VectorType>	&hit_point_out) | ||||
| { | ||||
|     using Scalar = typename VectorType::Scalar; | ||||
|     auto distancer = detail::IndexedTriangleSetDistancer<VertexType, IndexedFaceType, TreeType, VectorType> | ||||
|         { vertices, faces, tree, point }; | ||||
|     return tree.empty() ? Scalar(-1) :  | ||||
|     	detail::squared_distance_to_indexed_triangle_set_recursive(distancer, size_t(0), Scalar(0), std::numeric_limits<Scalar>::infinity(), hit_idx_out, hit_point_out); | ||||
| } | ||||
| 
 | ||||
| } // namespace AABBTreeIndirect
 | ||||
| } // namespace Slic3r
 | ||||
| 
 | ||||
| #endif /* slic3r_AABBTreeIndirect_hpp_ */ | ||||
|  | @ -51,8 +51,8 @@ template<class S> struct NfpImpl<S, NfpLevel::CONVEX_ONLY> | |||
| namespace Slic3r { | ||||
| 
 | ||||
| template<class Tout = double, class = FloatingOnly<Tout>, int...EigenArgs> | ||||
| inline SLIC3R_CONSTEXPR Eigen::Matrix<Tout, 2, EigenArgs...> unscaled( | ||||
|     const ClipperLib::IntPoint &v) SLIC3R_NOEXCEPT | ||||
| inline constexpr Eigen::Matrix<Tout, 2, EigenArgs...> unscaled( | ||||
|     const ClipperLib::IntPoint &v) noexcept | ||||
| { | ||||
|     return Eigen::Matrix<Tout, 2, EigenArgs...>{unscaled<Tout>(v.X), | ||||
|                                                 unscaled<Tout>(v.Y)}; | ||||
|  |  | |||
|  | @ -306,7 +306,7 @@ if(WIN32) | |||
| endif() | ||||
| 
 | ||||
| if(SLIC3R_PROFILE) | ||||
|     target_link_libraries(slic3r Shiny) | ||||
|     target_link_libraries(libslic3r Shiny) | ||||
| endif() | ||||
| 
 | ||||
| if (SLIC3R_PCH AND NOT SLIC3R_SYNTAXONLY) | ||||
|  |  | |||
|  | @ -369,7 +369,7 @@ namespace boost { namespace polygon { | |||
|         typedef coord_t coordinate_type; | ||||
|      | ||||
|         static inline coordinate_type get(const Slic3r::Point& point, orientation_2d orient) { | ||||
|             return (orient == HORIZONTAL) ? (coordinate_type)point(0) : (coordinate_type)point(1); | ||||
|             return (coordinate_type)point((orient == HORIZONTAL) ? 0 : 1); | ||||
|         } | ||||
|     }; | ||||
|      | ||||
|  | @ -377,16 +377,10 @@ namespace boost { namespace polygon { | |||
|     struct point_mutable_traits<Slic3r::Point> { | ||||
|         typedef coord_t coordinate_type; | ||||
|         static inline void set(Slic3r::Point& point, orientation_2d orient, coord_t value) { | ||||
|             if (orient == HORIZONTAL) | ||||
|                 point(0) = value; | ||||
|             else | ||||
|                 point(1) = value; | ||||
|             point((orient == HORIZONTAL) ? 0 : 1) = value; | ||||
|         } | ||||
|         static inline Slic3r::Point construct(coord_t x_value, coord_t y_value) { | ||||
|             Slic3r::Point retval; | ||||
|             retval(0) = x_value; | ||||
|             retval(1) = y_value;  | ||||
|             return retval; | ||||
|             return Slic3r::Point(x_value, y_value); | ||||
|         } | ||||
|     }; | ||||
| } } | ||||
|  |  | |||
|  | @ -24,37 +24,37 @@ | |||
| 
 | ||||
| #if 1 | ||||
| // Saves around 32% RAM after slicing step, 6.7% after G-code export (tested on PrusaSlicer 2.2.0 final).
 | ||||
| typedef int32_t coord_t; | ||||
| using coord_t = int32_t; | ||||
| #else | ||||
| //FIXME At least FillRectilinear2 requires coord_t to be 32bit.
 | ||||
| typedef int64_t coord_t; | ||||
| #endif | ||||
| 
 | ||||
| typedef double  coordf_t; | ||||
| using coordf_t = double; | ||||
| 
 | ||||
| //FIXME This epsilon value is used for many non-related purposes:
 | ||||
| // For a threshold of a squared Euclidean distance,
 | ||||
| // for a trheshold in a difference of radians,
 | ||||
| // for a threshold of a cross product of two non-normalized vectors etc.
 | ||||
| #define EPSILON 1e-4 | ||||
| static constexpr double EPSILON = 1e-4; | ||||
| // Scaling factor for a conversion from coord_t to coordf_t: 10e-6
 | ||||
| // This scaling generates a following fixed point representation with for a 32bit integer:
 | ||||
| // 0..4294mm with 1nm resolution
 | ||||
| // int32_t fits an interval of (-2147.48mm, +2147.48mm)
 | ||||
| // with int64_t we don't have to worry anymore about the size of the int.
 | ||||
| #define SCALING_FACTOR 0.000001 | ||||
| static constexpr double SCALING_FACTOR = 0.000001; | ||||
| // RESOLUTION, SCALED_RESOLUTION: Used as an error threshold for a Douglas-Peucker polyline simplification algorithm.
 | ||||
| #define RESOLUTION 0.0125 | ||||
| #define SCALED_RESOLUTION (RESOLUTION / SCALING_FACTOR) | ||||
| #define PI 3.141592653589793238 | ||||
| static constexpr double RESOLUTION = 0.0125; | ||||
| #define                 SCALED_RESOLUTION (RESOLUTION / SCALING_FACTOR) | ||||
| static constexpr double PI = 3.141592653589793238; | ||||
| // When extruding a closed loop, the loop is interrupted and shortened a bit to reduce the seam.
 | ||||
| #define LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER 0.15 | ||||
| static constexpr double LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER = 0.15; | ||||
| // Maximum perimeter length for the loop to apply the small perimeter speed. 
 | ||||
| #define SMALL_PERIMETER_LENGTH (6.5 / SCALING_FACTOR) * 2 * PI | ||||
| #define INSET_OVERLAP_TOLERANCE 0.4 | ||||
| #define                 SMALL_PERIMETER_LENGTH  ((6.5 / SCALING_FACTOR) * 2 * PI) | ||||
| static constexpr double INSET_OVERLAP_TOLERANCE = 0.4; | ||||
| // 3mm ring around the top / bottom / bridging areas.
 | ||||
| //FIXME This is quite a lot.
 | ||||
| #define EXTERNAL_INFILL_MARGIN 3. | ||||
| static constexpr double EXTERNAL_INFILL_MARGIN = 3.; | ||||
| //FIXME Better to use an inline function with an explicit return type.
 | ||||
| //inline coord_t scale_(coordf_t v) { return coord_t(floor(v / SCALING_FACTOR + 0.5f)); }
 | ||||
| #define scale_(val) ((val) / SCALING_FACTOR) | ||||
|  | @ -63,14 +63,6 @@ typedef double  coordf_t; | |||
| 
 | ||||
| #define SLIC3R_DEBUG_OUT_PATH_PREFIX "out/" | ||||
| 
 | ||||
| #if defined(_MSC_VER) &&  _MSC_VER < 1900 | ||||
| # define SLIC3R_CONSTEXPR | ||||
| # define SLIC3R_NOEXCEPT | ||||
| #else | ||||
| #define SLIC3R_CONSTEXPR constexpr | ||||
| #define SLIC3R_NOEXCEPT  noexcept | ||||
| #endif | ||||
| 
 | ||||
| inline std::string debug_out_path(const char *name, ...) | ||||
| { | ||||
| 	char buffer[2048]; | ||||
|  | @ -92,11 +84,6 @@ inline std::string debug_out_path(const char *name, ...) | |||
| #define UNUSED(x) (void)(x) | ||||
| #endif /* UNUSED */ | ||||
| 
 | ||||
| // Detect whether the compiler supports C++11 noexcept exception specifications.
 | ||||
| #if defined(_MSC_VER) && _MSC_VER < 1900 | ||||
|     #define noexcept throw() | ||||
| #endif | ||||
| 
 | ||||
| // Write slices as SVG images into out directory during the 2D processing of the slices.
 | ||||
| // #define SLIC3R_DEBUG_SLICE_PROCESSING
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -310,7 +310,8 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config) | |||
|     toggle_field("standby_temperature_delta", have_ooze_prevention); | ||||
| 
 | ||||
|     bool have_wipe_tower = config->opt_bool("wipe_tower"); | ||||
|     for (auto el : { "wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_bridging" }) | ||||
|     for (auto el : { "wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_rotation_angle", | ||||
|                      "wipe_tower_bridging", "wipe_tower_no_sparse_layers", "single_extruder_multi_material_priming" }) | ||||
|         toggle_field(el, have_wipe_tower); | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -4029,6 +4029,26 @@ void ObjectList::msw_rescale() | |||
|     Layout(); | ||||
| } | ||||
| 
 | ||||
| void ObjectList::sys_color_changed() | ||||
| { | ||||
|     // msw_rescale_icons() updates icons, so use it
 | ||||
|     msw_rescale_icons(); | ||||
| 
 | ||||
|     // update existing items with bitmaps
 | ||||
|     m_objects_model->Rescale(); | ||||
| 
 | ||||
|     // msw_rescale_menu updates just icons, so use it
 | ||||
|     for (MenuWithSeparators* menu : { &m_menu_object,  | ||||
|                                       &m_menu_part,  | ||||
|                                       &m_menu_sla_object,  | ||||
|                                       &m_menu_instance,  | ||||
|                                       &m_menu_layer, | ||||
|                                       &m_menu_default}) | ||||
|         msw_rescale_menu(menu); | ||||
| 
 | ||||
|     Layout(); | ||||
| } | ||||
| 
 | ||||
| void ObjectList::ItemValueChanged(wxDataViewEvent &event) | ||||
| { | ||||
|     if (event.GetColumn() == colName) | ||||
|  |  | |||
|  | @ -386,6 +386,7 @@ public: | |||
|     void paste_objects_into_list(const std::vector<size_t>& object_idxs); | ||||
| 
 | ||||
|     void msw_rescale(); | ||||
|     void sys_color_changed(); | ||||
| 
 | ||||
|     void update_after_undo_redo(); | ||||
|     //update printable state for item from objects model
 | ||||
|  |  | |||
|  | @ -981,6 +981,23 @@ void ObjectManipulation::msw_rescale() | |||
|     get_og()->msw_rescale(); | ||||
| } | ||||
| 
 | ||||
| void ObjectManipulation::sys_color_changed() | ||||
| { | ||||
|     // btn...->msw_rescale() updates icon on button, so use it
 | ||||
|     m_mirror_bitmap_on.msw_rescale(); | ||||
|     m_mirror_bitmap_off.msw_rescale(); | ||||
|     m_mirror_bitmap_hidden.msw_rescale(); | ||||
|     m_reset_scale_button->msw_rescale(); | ||||
|     m_reset_rotation_button->msw_rescale(); | ||||
|     m_drop_to_bed_button->msw_rescale(); | ||||
|     m_lock_bnt->msw_rescale(); | ||||
| 
 | ||||
|     for (int id = 0; id < 3; ++id) | ||||
|         m_mirror_buttons[id].first->msw_rescale(); | ||||
| 
 | ||||
|     get_og()->msw_rescale(); | ||||
| } | ||||
| 
 | ||||
| static const char axes[] = { 'x', 'y', 'z' }; | ||||
| ManipulationEditor::ManipulationEditor(ObjectManipulation* parent, | ||||
|                                        const std::string& opt_key, | ||||
|  |  | |||
|  | @ -173,6 +173,7 @@ public: | |||
|     void update_item_name(const wxString &item_name); | ||||
|     void update_warning_icon_state(const wxString& tooltip); | ||||
|     void msw_rescale(); | ||||
|     void sys_color_changed(); | ||||
|     void on_change(const std::string& opt_key, int axis, double new_value); | ||||
|     void set_focused_editor(ManipulationEditor* focused_editor) { | ||||
| #ifndef __APPLE__ | ||||
|  |  | |||
|  | @ -124,6 +124,12 @@ public: | |||
|             // set value to _true_ in purpose of possibility of a display dpi changing from System Settings
 | ||||
|                 m_can_rescale = true; | ||||
|         }); | ||||
| 
 | ||||
|         this->Bind(wxEVT_SYS_COLOUR_CHANGED, [this](wxSysColourChangedEvent& event) | ||||
|         { | ||||
|             event.Skip(); | ||||
|             on_sys_color_changed(); | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     virtual ~DPIAware() {} | ||||
|  | @ -137,6 +143,7 @@ public: | |||
| 
 | ||||
| protected: | ||||
|     virtual void on_dpi_changed(const wxRect &suggested_rect) = 0; | ||||
|     virtual void on_sys_color_changed() {}; | ||||
| 
 | ||||
| private: | ||||
|     float m_scale_factor; | ||||
|  |  | |||
|  | @ -805,14 +805,6 @@ static const ImWchar ranges_keyboard_shortcuts[] = | |||
| 
 | ||||
| std::vector<unsigned char> ImGuiWrapper::load_svg(const std::string& bitmap_name, unsigned target_width, unsigned target_height) | ||||
| { | ||||
| #ifdef __APPLE__ | ||||
|     // Note: win->GetContentScaleFactor() is not used anymore here because it tends to
 | ||||
|     // return bogus results quite often (such as 1.0 on Retina or even 0.0).
 | ||||
|     // We're using the max scaling factor across all screens because it's very likely to be good enough.
 | ||||
|     double	scale = mac_max_scaling_factor(); | ||||
| #else | ||||
|     double	scale = 1.0; | ||||
| #endif | ||||
|     std::vector<unsigned char> empty_vector; | ||||
| 
 | ||||
| #ifdef __WXMSW__ | ||||
|  | @ -827,8 +819,6 @@ std::vector<unsigned char> ImGuiWrapper::load_svg(const std::string& bitmap_name | |||
|     if (image == nullptr) | ||||
|         return empty_vector; | ||||
| 
 | ||||
|     target_height != 0 ? target_height *= scale : target_width *= scale; | ||||
| 
 | ||||
|     float svg_scale = target_height != 0 ? | ||||
|         (float)target_height / image->height : target_width != 0 ? | ||||
|         (float)target_width / image->width : 1; | ||||
|  |  | |||
|  | @ -190,6 +190,29 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S | |||
|         event.Skip(); | ||||
|     }); | ||||
| 
 | ||||
|     /*
 | ||||
|     Bind(wxEVT_SYS_COLOUR_CHANGED, [this](wxSysColourChangedEvent& event) | ||||
|     { | ||||
|         bool recreate_gui = false; | ||||
|         { | ||||
|             // the dialog needs to be destroyed before the call to recreate_gui()
 | ||||
|             // or sometimes the application crashes into wxDialogBase() destructor
 | ||||
|             // so we put it into an inner scope
 | ||||
|             wxMessageDialog dialog(nullptr, | ||||
|                                    _L("System color mode was changed. " | ||||
|                                       "It is possible to update the Slicer in respect to the system mode.") + "\n" + | ||||
|                                    _L("You will lose content of the plater.") + "\n\n" + | ||||
|                                    _L("Do you want to proceed?"), | ||||
|                                    wxString(SLIC3R_APP_NAME) + " - " + _L("Switching system color mode"), | ||||
|                                    wxICON_QUESTION | wxOK | wxCANCEL); | ||||
|             recreate_gui = dialog.ShowModal() == wxID_OK; | ||||
|         } | ||||
|         if (recreate_gui) | ||||
|             wxGetApp().recreate_GUI(_L("Changing of an application in respect to the system mode") + dots); | ||||
|         event.Skip(); | ||||
|     }); | ||||
|     */ | ||||
| 
 | ||||
|     wxGetApp().persist_window_geometry(this, true); | ||||
| 
 | ||||
|     update_ui_from_settings();    // FIXME (?)
 | ||||
|  | @ -554,6 +577,28 @@ void MainFrame::on_dpi_changed(const wxRect &suggested_rect) | |||
|     this->Maximize(is_maximized); | ||||
| } | ||||
| 
 | ||||
| void MainFrame::on_sys_color_changed() | ||||
| { | ||||
|     wxBusyCursor wait; | ||||
| 
 | ||||
|     // update label colors in respect to the system mode
 | ||||
|     wxGetApp().init_label_colours(); | ||||
| 
 | ||||
|     wxGetApp().preset_bundle->load_default_preset_bitmaps(); | ||||
| 
 | ||||
|     // update Plater
 | ||||
|     wxGetApp().plater()->sys_color_changed(); | ||||
| 
 | ||||
|     // update Tabs
 | ||||
|     for (auto tab : wxGetApp().tabs_list) | ||||
|         tab->sys_color_changed(); | ||||
| 
 | ||||
|     // msw_rescale_menu updates just icons, so use it
 | ||||
|     wxMenuBar* menu_bar = this->GetMenuBar(); | ||||
|     for (size_t id = 0; id < menu_bar->GetMenuCount(); id++) | ||||
|         msw_rescale_menu(menu_bar->GetMenu(id)); | ||||
| } | ||||
| 
 | ||||
| void MainFrame::init_menubar() | ||||
| { | ||||
| #ifdef __APPLE__ | ||||
|  |  | |||
|  | @ -121,6 +121,7 @@ class MainFrame : public DPIFrame | |||
| 
 | ||||
| protected: | ||||
|     virtual void on_dpi_changed(const wxRect &suggested_rect); | ||||
|     virtual void on_sys_color_changed() override; | ||||
| 
 | ||||
| public: | ||||
|     MainFrame(); | ||||
|  |  | |||
|  | @ -1092,6 +1092,34 @@ void Sidebar::msw_rescale() | |||
|     p->scrolled->Layout(); | ||||
| } | ||||
| 
 | ||||
| void Sidebar::sys_color_changed() | ||||
| { | ||||
|     // Update preset comboboxes in respect to the system color ...
 | ||||
|     // combo->msw_rescale() updates icon on button, so use it
 | ||||
|     for (PresetComboBox* combo : std::vector<PresetComboBox*>{  p->combo_print, | ||||
|                                                                 p->combo_sla_print, | ||||
|                                                                 p->combo_sla_material, | ||||
|                                                                 p->combo_printer }) | ||||
|         combo->msw_rescale(); | ||||
|     for (PresetComboBox* combo : p->combos_filament) | ||||
|         combo->msw_rescale(); | ||||
| 
 | ||||
|     // ... then refill them and set min size to correct layout of the sidebar
 | ||||
|     update_all_preset_comboboxes(); | ||||
| 
 | ||||
|     p->object_list->sys_color_changed(); | ||||
|     p->object_manipulation->sys_color_changed(); | ||||
| //    p->object_settings->msw_rescale();
 | ||||
| //    p->object_layers->msw_rescale();
 | ||||
| 
 | ||||
|     // btn...->msw_rescale() updates icon on button, so use it
 | ||||
|     p->btn_send_gcode->msw_rescale(); | ||||
|     p->btn_remove_device->msw_rescale(); | ||||
|     p->btn_export_gcode_removable->msw_rescale(); | ||||
| 
 | ||||
|     p->scrolled->Layout(); | ||||
| } | ||||
| 
 | ||||
| void Sidebar::search() | ||||
| { | ||||
|     p->searcher.search(); | ||||
|  | @ -5514,6 +5542,17 @@ void Plater::msw_rescale() | |||
|     GetParent()->Layout(); | ||||
| } | ||||
| 
 | ||||
| void Plater::sys_color_changed() | ||||
| { | ||||
|     p->sidebar->sys_color_changed(); | ||||
| 
 | ||||
|     // msw_rescale_menu updates just icons, so use it
 | ||||
|     p->msw_rescale_object_menu(); | ||||
| 
 | ||||
|     Layout(); | ||||
|     GetParent()->Layout(); | ||||
| } | ||||
| 
 | ||||
| bool Plater::init_view_toolbar() | ||||
| { | ||||
|     return p->init_view_toolbar(); | ||||
|  |  | |||
|  | @ -105,6 +105,7 @@ public: | |||
|     void update_mode_sizer() const; | ||||
|     void update_reslice_btn_tooltip() const; | ||||
|     void msw_rescale(); | ||||
|     void sys_color_changed(); | ||||
|     void search(); | ||||
|     void jump_to_option(size_t selected); | ||||
| 
 | ||||
|  | @ -308,6 +309,7 @@ public: | |||
|     bool can_reload_from_disk() const; | ||||
| 
 | ||||
|     void msw_rescale(); | ||||
|     void sys_color_changed(); | ||||
| 
 | ||||
|     bool init_view_toolbar(); | ||||
| 
 | ||||
|  |  | |||
|  | @ -668,6 +668,14 @@ void SearchDialog::on_dpi_changed(const wxRect& suggested_rect) | |||
|     Refresh(); | ||||
| } | ||||
| 
 | ||||
| void SearchDialog::on_sys_color_changed() | ||||
| { | ||||
|     // msw_rescale updates just icons, so use it
 | ||||
|     search_list_model->msw_rescale(); | ||||
| 
 | ||||
|     Refresh(); | ||||
| } | ||||
| 
 | ||||
| // ----------------------------------------------------------------------------
 | ||||
| // SearchListModel
 | ||||
| // ----------------------------------------------------------------------------
 | ||||
|  |  | |||
|  | @ -198,6 +198,7 @@ public: | |||
| 
 | ||||
| protected: | ||||
|     void on_dpi_changed(const wxRect& suggested_rect) override; | ||||
|     virtual void on_sys_color_changed() override; | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -6,6 +6,8 @@ | |||
| 
 | ||||
| #include <string> | ||||
| 
 | ||||
| #include <Eigen/Core> | ||||
| 
 | ||||
| #include <wx/clipbrd.h> | ||||
| #include <wx/platinfo.h> | ||||
| #include "GUI_App.hpp" | ||||
|  | @ -145,11 +147,11 @@ SysInfoDialog::SysInfoDialog() | |||
|             "</font>" | ||||
|             "</body>" | ||||
|             "</html>", bgr_clr_str, text_clr_str, text_clr_str, | ||||
|             get_mem_info(true) + "<br>" + wxGetApp().get_gl_info(true, true)); | ||||
|             get_mem_info(true) + "<br>" + wxGetApp().get_gl_info(true, true) + "<br>Eigen vectorization supported: " + Eigen::SimdInstructionSetsInUse()); | ||||
|         m_opengl_info_html->SetPage(text); | ||||
|         main_sizer->Add(m_opengl_info_html, 1, wxEXPAND | wxBOTTOM, 15); | ||||
|     } | ||||
|      | ||||
| 
 | ||||
|     wxStdDialogButtonSizer* buttons = this->CreateStdDialogButtonSizer(wxOK); | ||||
|     m_btn_copy_to_clipboard = new wxButton(this, wxID_ANY, _(L("Copy to Clipboard")), wxDefaultPosition, wxDefaultSize); | ||||
| 
 | ||||
|  |  | |||
|  | @ -590,6 +590,18 @@ void TabPrinter::msw_rescale() | |||
|     Layout(); | ||||
| } | ||||
| 
 | ||||
| void TabPrinter::sys_color_changed()  | ||||
| { | ||||
|     Tab::sys_color_changed(); | ||||
| 
 | ||||
|     // update missed options_groups
 | ||||
|     const std::vector<PageShp>& pages = m_printer_technology == ptFFF ? m_pages_sla : m_pages_fff; | ||||
|     for (auto page : pages) | ||||
|         page->msw_rescale(); | ||||
| 
 | ||||
|     Layout(); | ||||
| } | ||||
| 
 | ||||
| void TabSLAMaterial::init_options_list() | ||||
| { | ||||
|     if (!m_options_list.empty()) | ||||
|  | @ -869,6 +881,41 @@ void Tab::msw_rescale() | |||
|     Layout(); | ||||
| } | ||||
| 
 | ||||
| void Tab::sys_color_changed() | ||||
| { | ||||
|     update_tab_ui(); | ||||
| 
 | ||||
|     // update buttons and cached bitmaps
 | ||||
|     for (const auto btn : m_scaled_buttons) | ||||
|         btn->msw_rescale(); | ||||
|     for (const auto bmp : m_scaled_bitmaps) | ||||
|         bmp->msw_rescale(); | ||||
|     for (ScalableBitmap& bmp : m_mode_bitmap_cache) | ||||
|         bmp.msw_rescale(); | ||||
| 
 | ||||
|     // update icons for tree_ctrl
 | ||||
|     for (ScalableBitmap& bmp : m_scaled_icons_list) | ||||
|         bmp.msw_rescale(); | ||||
|     // recreate and set new ImageList for tree_ctrl
 | ||||
|     m_icons->RemoveAll(); | ||||
|     m_icons = new wxImageList(m_scaled_icons_list.front().bmp().GetWidth(), m_scaled_icons_list.front().bmp().GetHeight()); | ||||
|     for (ScalableBitmap& bmp : m_scaled_icons_list) | ||||
|         m_icons->Add(bmp.bmp()); | ||||
|     m_treectrl->AssignImageList(m_icons); | ||||
| 
 | ||||
| 
 | ||||
|     // Colors for ui "decoration"
 | ||||
|     m_sys_label_clr = wxGetApp().get_label_clr_sys(); | ||||
|     m_modified_label_clr = wxGetApp().get_label_clr_modified(); | ||||
|     update_labels_colour(); | ||||
| 
 | ||||
|     // update options_groups
 | ||||
|     for (auto page : m_pages) | ||||
|         page->msw_rescale(); | ||||
| 
 | ||||
|     Layout(); | ||||
| } | ||||
| 
 | ||||
| Field* Tab::get_field(const t_config_option_key& opt_key, int opt_index/* = -1*/) const | ||||
| { | ||||
|     Field* field = nullptr; | ||||
|  |  | |||
|  | @ -318,6 +318,7 @@ public: | |||
|     void            update_mode(); | ||||
|     void            update_visibility(); | ||||
|     virtual void    msw_rescale(); | ||||
|     virtual void	sys_color_changed(); | ||||
| 	Field*			get_field(const t_config_option_key& opt_key, int opt_index = -1) const; | ||||
|     Field*          get_field(const t_config_option_key &opt_key, Page** selected_page, int opt_index = -1); | ||||
| 	bool			set_value(const t_config_option_key& opt_key, const boost::any& value); | ||||
|  | @ -436,6 +437,7 @@ public: | |||
| 	void		on_preset_loaded() override; | ||||
| 	void		init_options_list() override; | ||||
| 	void		msw_rescale() override; | ||||
| 	void		sys_color_changed() override; | ||||
|     bool 		supports_printer_technology(const PrinterTechnology /* tech */) override { return true; } | ||||
| 
 | ||||
| 	wxSizer*	create_bed_shape_widget(wxWindow* parent); | ||||
|  |  | |||
|  | @ -3,6 +3,7 @@ get_filename_component(_TEST_NAME ${CMAKE_CURRENT_LIST_DIR} NAME) | |||
| add_executable(${_TEST_NAME}_tests  | ||||
| 	${_TEST_NAME}_tests.cpp | ||||
| 	test_3mf.cpp | ||||
| 	test_aabbindirect.cpp | ||||
| 	test_clipper_offset.cpp | ||||
| 	test_clipper_utils.cpp | ||||
| 	test_config.cpp | ||||
|  |  | |||
							
								
								
									
										61
									
								
								tests/libslic3r/test_aabbindirect.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								tests/libslic3r/test_aabbindirect.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,61 @@ | |||
| #include <catch2/catch.hpp> | ||||
| #include <test_utils.hpp> | ||||
| 
 | ||||
| #include <libslic3r/TriangleMesh.hpp> | ||||
| #include <libslic3r/AABBTreeIndirect.hpp> | ||||
| 
 | ||||
| using namespace Slic3r; | ||||
| 
 | ||||
| TEST_CASE("Building a tree over a box, ray caster and closest query", "[AABBIndirect]") | ||||
| { | ||||
|     TriangleMesh tmesh = make_cube(1., 1., 1.); | ||||
|     tmesh.repair(); | ||||
| 
 | ||||
|     auto tree = AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set(tmesh.its.vertices, tmesh.its.indices); | ||||
|     REQUIRE(! tree.empty()); | ||||
| 
 | ||||
|     igl::Hit hit; | ||||
| 	bool intersected = AABBTreeIndirect::intersect_ray_first_hit( | ||||
| 		tmesh.its.vertices, tmesh.its.indices, | ||||
| 		tree, | ||||
| 		Vec3d(0.5, 0.5, -5.), | ||||
| 		Vec3d(0., 0., 1.), | ||||
| 		hit); | ||||
| 
 | ||||
|     REQUIRE(intersected); | ||||
|     REQUIRE(hit.t == Approx(5.)); | ||||
| 
 | ||||
|     std::vector<igl::Hit> hits; | ||||
| 	bool intersected2 = AABBTreeIndirect::intersect_ray_all_hits( | ||||
| 		tmesh.its.vertices, tmesh.its.indices, | ||||
| 		tree, | ||||
|         Vec3d(0.3, 0.5, -5.), | ||||
| 		Vec3d(0., 0., 1.), | ||||
| 		hits); | ||||
|     REQUIRE(intersected2); | ||||
|     REQUIRE(hits.size() == 2); | ||||
|     REQUIRE(hits.front().t == Approx(5.)); | ||||
|     REQUIRE(hits.back().t == Approx(6.)); | ||||
| 
 | ||||
|     size_t hit_idx; | ||||
|     Vec3d  closest_point; | ||||
|     double squared_distance = AABBTreeIndirect::squared_distance_to_indexed_triangle_set( | ||||
| 		tmesh.its.vertices, tmesh.its.indices, | ||||
| 		tree, | ||||
|         Vec3d(0.3, 0.5, -5.), | ||||
| 		hit_idx, closest_point); | ||||
|     REQUIRE(squared_distance == Approx(5. * 5.)); | ||||
|     REQUIRE(closest_point.x() == Approx(0.3)); | ||||
|     REQUIRE(closest_point.y() == Approx(0.5)); | ||||
|     REQUIRE(closest_point.z() == Approx(0.)); | ||||
| 
 | ||||
|     squared_distance = AABBTreeIndirect::squared_distance_to_indexed_triangle_set( | ||||
| 		tmesh.its.vertices, tmesh.its.indices, | ||||
| 		tree, | ||||
|         Vec3d(0.3, 0.5, 5.), | ||||
| 		hit_idx, closest_point); | ||||
|     REQUIRE(squared_distance == Approx(4. * 4.)); | ||||
|     REQUIRE(closest_point.x() == Approx(0.3)); | ||||
|     REQUIRE(closest_point.y() == Approx(0.5)); | ||||
|     REQUIRE(closest_point.z() == Approx(1.)); | ||||
| } | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 enricoturri1966
						enricoturri1966