mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-11-02 20:51:23 -07:00 
			
		
		
		
	Merge branch 'et_gcode_viewer' of https://github.com/prusa3d/PrusaSlicer into et_gcode_viewer
This commit is contained in:
		
						commit
						e3cb969474
					
				
					 36 changed files with 1316 additions and 80 deletions
				
			
		| 
						 | 
					@ -2,3 +2,4 @@
 | 
				
			||||||
#add_subdirectory(openvdb)
 | 
					#add_subdirectory(openvdb)
 | 
				
			||||||
add_subdirectory(meshboolean)
 | 
					add_subdirectory(meshboolean)
 | 
				
			||||||
add_subdirectory(opencsg)
 | 
					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_CALL	6
 | 
				
			||||||
#define OUTPUT_WIDTH_TIME	6
 | 
					#define OUTPUT_WIDTH_TIME	(6+3)
 | 
				
			||||||
#define OUTPUT_WIDTH_PERC	4
 | 
					#define OUTPUT_WIDTH_PERC	(4+3)
 | 
				
			||||||
#define OUTPUT_WIDTH_SUM	120 
 | 
					#define OUTPUT_WIDTH_SUM	120 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define OUTPUT_WIDTH_DATA	(1+OUTPUT_WIDTH_CALL + 1 + 2*(OUTPUT_WIDTH_TIME+4+OUTPUT_WIDTH_PERC+1) + 1)
 | 
					#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);
 | 
						const ShinyTimeUnit *totalUnit = ShinyGetTimeUnit(totalTicksAvg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	snprintf(output, OUTPUT_WIDTH_DATA + TRAILING,
 | 
						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_CALL, a_data->entryCount.avg,
 | 
				
			||||||
		OUTPUT_WIDTH_TIME, a_data->selfTicks.avg * selfUnit->invTickFreq, selfUnit->suffix,
 | 
							OUTPUT_WIDTH_TIME, a_data->selfTicks.avg * selfUnit->invTickFreq, selfUnit->suffix,
 | 
				
			||||||
		OUTPUT_WIDTH_PERC, a_data->selfTicks.avg * a_topercent,
 | 
							OUTPUT_WIDTH_PERC, a_data->selfTicks.avg * a_topercent,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,3 +7,7 @@ add_library(clipper STATIC
 | 
				
			||||||
    clipper_z.cpp
 | 
					    clipper_z.cpp
 | 
				
			||||||
    clipper_z.hpp
 | 
					    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 {
 | 
					namespace Slic3r {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
template<class Tout = double, class = FloatingOnly<Tout>, int...EigenArgs>
 | 
					template<class Tout = double, class = FloatingOnly<Tout>, int...EigenArgs>
 | 
				
			||||||
inline SLIC3R_CONSTEXPR Eigen::Matrix<Tout, 2, EigenArgs...> unscaled(
 | 
					inline constexpr Eigen::Matrix<Tout, 2, EigenArgs...> unscaled(
 | 
				
			||||||
    const ClipperLib::IntPoint &v) SLIC3R_NOEXCEPT
 | 
					    const ClipperLib::IntPoint &v) noexcept
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    return Eigen::Matrix<Tout, 2, EigenArgs...>{unscaled<Tout>(v.X),
 | 
					    return Eigen::Matrix<Tout, 2, EigenArgs...>{unscaled<Tout>(v.X),
 | 
				
			||||||
                                                unscaled<Tout>(v.Y)};
 | 
					                                                unscaled<Tout>(v.Y)};
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -306,7 +306,7 @@ if(WIN32)
 | 
				
			||||||
endif()
 | 
					endif()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if(SLIC3R_PROFILE)
 | 
					if(SLIC3R_PROFILE)
 | 
				
			||||||
    target_link_libraries(slic3r Shiny)
 | 
					    target_link_libraries(libslic3r Shiny)
 | 
				
			||||||
endif()
 | 
					endif()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if (SLIC3R_PCH AND NOT SLIC3R_SYNTAXONLY)
 | 
					if (SLIC3R_PCH AND NOT SLIC3R_SYNTAXONLY)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -369,7 +369,7 @@ namespace boost { namespace polygon {
 | 
				
			||||||
        typedef coord_t coordinate_type;
 | 
					        typedef coord_t coordinate_type;
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
        static inline coordinate_type get(const Slic3r::Point& point, orientation_2d orient) {
 | 
					        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> {
 | 
					    struct point_mutable_traits<Slic3r::Point> {
 | 
				
			||||||
        typedef coord_t coordinate_type;
 | 
					        typedef coord_t coordinate_type;
 | 
				
			||||||
        static inline void set(Slic3r::Point& point, orientation_2d orient, coord_t value) {
 | 
					        static inline void set(Slic3r::Point& point, orientation_2d orient, coord_t value) {
 | 
				
			||||||
            if (orient == HORIZONTAL)
 | 
					            point((orient == HORIZONTAL) ? 0 : 1) = value;
 | 
				
			||||||
                point(0) = value;
 | 
					 | 
				
			||||||
            else
 | 
					 | 
				
			||||||
                point(1) = value;
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        static inline Slic3r::Point construct(coord_t x_value, coord_t y_value) {
 | 
					        static inline Slic3r::Point construct(coord_t x_value, coord_t y_value) {
 | 
				
			||||||
            Slic3r::Point retval;
 | 
					            return Slic3r::Point(x_value, y_value);
 | 
				
			||||||
            retval(0) = x_value;
 | 
					 | 
				
			||||||
            retval(1) = y_value; 
 | 
					 | 
				
			||||||
            return retval;
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
} }
 | 
					} }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -24,37 +24,37 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#if 1
 | 
					#if 1
 | 
				
			||||||
// Saves around 32% RAM after slicing step, 6.7% after G-code export (tested on PrusaSlicer 2.2.0 final).
 | 
					// 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
 | 
					#else
 | 
				
			||||||
//FIXME At least FillRectilinear2 requires coord_t to be 32bit.
 | 
					//FIXME At least FillRectilinear2 requires coord_t to be 32bit.
 | 
				
			||||||
typedef int64_t coord_t;
 | 
					typedef int64_t coord_t;
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef double  coordf_t;
 | 
					using coordf_t = double;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//FIXME This epsilon value is used for many non-related purposes:
 | 
					//FIXME This epsilon value is used for many non-related purposes:
 | 
				
			||||||
// For a threshold of a squared Euclidean distance,
 | 
					// For a threshold of a squared Euclidean distance,
 | 
				
			||||||
// for a trheshold in a difference of radians,
 | 
					// for a trheshold in a difference of radians,
 | 
				
			||||||
// for a threshold of a cross product of two non-normalized vectors etc.
 | 
					// 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
 | 
					// 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:
 | 
					// This scaling generates a following fixed point representation with for a 32bit integer:
 | 
				
			||||||
// 0..4294mm with 1nm resolution
 | 
					// 0..4294mm with 1nm resolution
 | 
				
			||||||
// int32_t fits an interval of (-2147.48mm, +2147.48mm)
 | 
					// 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.
 | 
					// 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.
 | 
					// RESOLUTION, SCALED_RESOLUTION: Used as an error threshold for a Douglas-Peucker polyline simplification algorithm.
 | 
				
			||||||
#define RESOLUTION 0.0125
 | 
					static constexpr double RESOLUTION = 0.0125;
 | 
				
			||||||
#define SCALED_RESOLUTION (RESOLUTION / SCALING_FACTOR)
 | 
					#define                 SCALED_RESOLUTION (RESOLUTION / SCALING_FACTOR)
 | 
				
			||||||
#define PI 3.141592653589793238
 | 
					static constexpr double PI = 3.141592653589793238;
 | 
				
			||||||
// When extruding a closed loop, the loop is interrupted and shortened a bit to reduce the seam.
 | 
					// 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. 
 | 
					// Maximum perimeter length for the loop to apply the small perimeter speed. 
 | 
				
			||||||
#define SMALL_PERIMETER_LENGTH (6.5 / SCALING_FACTOR) * 2 * PI
 | 
					#define                 SMALL_PERIMETER_LENGTH  ((6.5 / SCALING_FACTOR) * 2 * PI)
 | 
				
			||||||
#define INSET_OVERLAP_TOLERANCE 0.4
 | 
					static constexpr double INSET_OVERLAP_TOLERANCE = 0.4;
 | 
				
			||||||
// 3mm ring around the top / bottom / bridging areas.
 | 
					// 3mm ring around the top / bottom / bridging areas.
 | 
				
			||||||
//FIXME This is quite a lot.
 | 
					//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.
 | 
					//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)); }
 | 
					//inline coord_t scale_(coordf_t v) { return coord_t(floor(v / SCALING_FACTOR + 0.5f)); }
 | 
				
			||||||
#define scale_(val) ((val) / SCALING_FACTOR)
 | 
					#define scale_(val) ((val) / SCALING_FACTOR)
 | 
				
			||||||
| 
						 | 
					@ -63,14 +63,6 @@ typedef double  coordf_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define SLIC3R_DEBUG_OUT_PATH_PREFIX "out/"
 | 
					#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, ...)
 | 
					inline std::string debug_out_path(const char *name, ...)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	char buffer[2048];
 | 
						char buffer[2048];
 | 
				
			||||||
| 
						 | 
					@ -92,11 +84,6 @@ inline std::string debug_out_path(const char *name, ...)
 | 
				
			||||||
#define UNUSED(x) (void)(x)
 | 
					#define UNUSED(x) (void)(x)
 | 
				
			||||||
#endif /* UNUSED */
 | 
					#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.
 | 
					// Write slices as SVG images into out directory during the 2D processing of the slices.
 | 
				
			||||||
// #define SLIC3R_DEBUG_SLICE_PROCESSING
 | 
					// #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);
 | 
					    toggle_field("standby_temperature_delta", have_ooze_prevention);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    bool have_wipe_tower = config->opt_bool("wipe_tower");
 | 
					    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);
 | 
					        toggle_field(el, have_wipe_tower);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -156,6 +156,12 @@ void GCodeViewer::SequentialView::Marker::init()
 | 
				
			||||||
#endif // !ENABLE_SHADERS_MANAGER
 | 
					#endif // !ENABLE_SHADERS_MANAGER
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void GCodeViewer::SequentialView::Marker::set_world_position(const Vec3f& position)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    m_world_transform = (Geometry::assemble_transform(position.cast<double>()) * Geometry::assemble_transform(m_model.get_bounding_box().size()[2] * Vec3d::UnitZ(), { M_PI, 0.0, 0.0 })).cast<float>();
 | 
				
			||||||
 | 
					    m_world_bounding_box = m_model.get_bounding_box().transformed(m_world_transform.cast<double>());
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void GCodeViewer::SequentialView::Marker::render() const
 | 
					void GCodeViewer::SequentialView::Marker::render() const
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
#if ENABLE_SHADERS_MANAGER
 | 
					#if ENABLE_SHADERS_MANAGER
 | 
				
			||||||
| 
						 | 
					@ -227,6 +233,15 @@ const std::vector<GCodeViewer::Color> GCodeViewer::Extrusion_Role_Colors {{
 | 
				
			||||||
    { 0.00f, 0.00f, 0.00f }    // erMixed
 | 
					    { 0.00f, 0.00f, 0.00f }    // erMixed
 | 
				
			||||||
}};
 | 
					}};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const std::vector<GCodeViewer::Color> GCodeViewer::Options_Colors {{
 | 
				
			||||||
 | 
					    { 1.00f, 0.00f, 1.00f },   // Retractions
 | 
				
			||||||
 | 
					    { 0.00f, 1.00f, 1.00f },   // Unretractions
 | 
				
			||||||
 | 
					    { 1.00f, 1.00f, 1.00f },   // ToolChanges
 | 
				
			||||||
 | 
					    { 1.00f, 0.00f, 0.00f },   // ColorChanges
 | 
				
			||||||
 | 
					    { 0.00f, 1.00f, 0.00f },   // PausePrints
 | 
				
			||||||
 | 
					    { 0.00f, 0.00f, 1.00f }    // CustomGCodes
 | 
				
			||||||
 | 
					}};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const std::vector<GCodeViewer::Color> GCodeViewer::Travel_Colors {{
 | 
					const std::vector<GCodeViewer::Color> GCodeViewer::Travel_Colors {{
 | 
				
			||||||
    { 0.0f, 0.0f, 0.5f }, // Move
 | 
					    { 0.0f, 0.0f, 0.5f }, // Move
 | 
				
			||||||
    { 0.0f, 0.5f, 0.0f }, // Extrude
 | 
					    { 0.0f, 0.5f, 0.0f }, // Extrude
 | 
				
			||||||
| 
						 | 
					@ -346,7 +361,7 @@ void GCodeViewer::render() const
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    glsafe(::glEnable(GL_DEPTH_TEST));
 | 
					    glsafe(::glEnable(GL_DEPTH_TEST));
 | 
				
			||||||
    render_toolpaths();
 | 
					    render_toolpaths();
 | 
				
			||||||
    m_sequential_view.marker.set_world_transform(Geometry::assemble_transform(m_sequential_view.current_position.cast<double>() + (0.5 + 12.0) * Vec3d::UnitZ(), { M_PI, 0.0, 0.0 }).cast<float>());
 | 
					    m_sequential_view.marker.set_world_position(m_sequential_view.current_position + m_sequential_view_marker_z_offset * Vec3f::UnitZ());
 | 
				
			||||||
    m_sequential_view.marker.render();
 | 
					    m_sequential_view.marker.render();
 | 
				
			||||||
    render_shells();
 | 
					    render_shells();
 | 
				
			||||||
    render_legend();
 | 
					    render_legend();
 | 
				
			||||||
| 
						 | 
					@ -851,11 +866,10 @@ void GCodeViewer::render_toolpaths() const
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
            case GCodeProcessor::EMoveType::Tool_change:
 | 
					            case GCodeProcessor::EMoveType::Tool_change:
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                Color color = { 1.0f, 1.0f, 1.0f };
 | 
					 | 
				
			||||||
#if ENABLE_SHADERS_MANAGER
 | 
					#if ENABLE_SHADERS_MANAGER
 | 
				
			||||||
                shader->set_uniform("uniform_color", color);
 | 
					                shader->set_uniform("uniform_color", Options_Colors[static_cast<unsigned int>(EOptionsColors::ToolChanges)]);
 | 
				
			||||||
#else
 | 
					#else
 | 
				
			||||||
                set_color(static_cast<GLint>(buffer.shader.get_shader_program_id()), color);
 | 
					                set_color(static_cast<GLint>(buffer.shader.get_shader_program_id()), Options_Colors[static_cast<unsigned int>(EOptionsColors::ToolChanges)]);
 | 
				
			||||||
#endif // ENABLE_SHADERS_MANAGER
 | 
					#endif // ENABLE_SHADERS_MANAGER
 | 
				
			||||||
                for (const RenderPath& path : buffer.render_paths)
 | 
					                for (const RenderPath& path : buffer.render_paths)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
| 
						 | 
					@ -871,11 +885,10 @@ void GCodeViewer::render_toolpaths() const
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            case GCodeProcessor::EMoveType::Color_change:
 | 
					            case GCodeProcessor::EMoveType::Color_change:
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                Color color = { 1.0f, 0.0f, 0.0f };
 | 
					 | 
				
			||||||
#if ENABLE_SHADERS_MANAGER
 | 
					#if ENABLE_SHADERS_MANAGER
 | 
				
			||||||
                shader->set_uniform("uniform_color", color);
 | 
					                shader->set_uniform("uniform_color", Options_Colors[static_cast<unsigned int>(EOptionsColors::ColorChanges)]);
 | 
				
			||||||
#else
 | 
					#else
 | 
				
			||||||
                set_color(static_cast<GLint>(buffer.shader.get_shader_program_id()), color);
 | 
					                set_color(static_cast<GLint>(buffer.shader.get_shader_program_id()), Options_Colors[static_cast<unsigned int>(EOptionsColors::ColorChanges)]);
 | 
				
			||||||
#endif // ENABLE_SHADERS_MANAGER
 | 
					#endif // ENABLE_SHADERS_MANAGER
 | 
				
			||||||
                for (const RenderPath& path : buffer.render_paths)
 | 
					                for (const RenderPath& path : buffer.render_paths)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
| 
						 | 
					@ -891,11 +904,10 @@ void GCodeViewer::render_toolpaths() const
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            case GCodeProcessor::EMoveType::Pause_Print:
 | 
					            case GCodeProcessor::EMoveType::Pause_Print:
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                Color color = { 0.0f, 1.0f, 0.0f };
 | 
					 | 
				
			||||||
#if ENABLE_SHADERS_MANAGER
 | 
					#if ENABLE_SHADERS_MANAGER
 | 
				
			||||||
                shader->set_uniform("uniform_color", color);
 | 
					                shader->set_uniform("uniform_color", Options_Colors[static_cast<unsigned int>(EOptionsColors::PausePrints)]);
 | 
				
			||||||
#else
 | 
					#else
 | 
				
			||||||
                set_color(static_cast<GLint>(buffer.shader.get_shader_program_id()), color);
 | 
					                set_color(static_cast<GLint>(buffer.shader.get_shader_program_id()), Options_Colors[static_cast<unsigned int>(EOptionsColors::PausePrints)]);
 | 
				
			||||||
#endif // ENABLE_SHADERS_MANAGER
 | 
					#endif // ENABLE_SHADERS_MANAGER
 | 
				
			||||||
                for (const RenderPath& path : buffer.render_paths)
 | 
					                for (const RenderPath& path : buffer.render_paths)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
| 
						 | 
					@ -911,11 +923,10 @@ void GCodeViewer::render_toolpaths() const
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            case GCodeProcessor::EMoveType::Custom_GCode:
 | 
					            case GCodeProcessor::EMoveType::Custom_GCode:
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                Color color = { 0.0f, 0.0f, 1.0f };
 | 
					 | 
				
			||||||
#if ENABLE_SHADERS_MANAGER
 | 
					#if ENABLE_SHADERS_MANAGER
 | 
				
			||||||
                shader->set_uniform("uniform_color", color);
 | 
					                shader->set_uniform("uniform_color", Options_Colors[static_cast<unsigned int>(EOptionsColors::CustomGCodes)]);
 | 
				
			||||||
#else
 | 
					#else
 | 
				
			||||||
                set_color(static_cast<GLint>(buffer.shader.get_shader_program_id()), color);
 | 
					                set_color(static_cast<GLint>(buffer.shader.get_shader_program_id()), Options_Colors[static_cast<unsigned int>(EOptionsColors::CustomGCodes)]);
 | 
				
			||||||
#endif // ENABLE_SHADERS_MANAGER
 | 
					#endif // ENABLE_SHADERS_MANAGER
 | 
				
			||||||
                for (const RenderPath& path : buffer.render_paths)
 | 
					                for (const RenderPath& path : buffer.render_paths)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
| 
						 | 
					@ -931,11 +942,10 @@ void GCodeViewer::render_toolpaths() const
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            case GCodeProcessor::EMoveType::Retract:
 | 
					            case GCodeProcessor::EMoveType::Retract:
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                Color color = { 1.0f, 0.0f, 1.0f };
 | 
					 | 
				
			||||||
#if ENABLE_SHADERS_MANAGER
 | 
					#if ENABLE_SHADERS_MANAGER
 | 
				
			||||||
                shader->set_uniform("uniform_color", color);
 | 
					                shader->set_uniform("uniform_color", Options_Colors[static_cast<unsigned int>(EOptionsColors::Retractions)]);
 | 
				
			||||||
#else
 | 
					#else
 | 
				
			||||||
                set_color(static_cast<GLint>(buffer.shader.get_shader_program_id()), color);
 | 
					                set_color(static_cast<GLint>(buffer.shader.get_shader_program_id()), Options_Colors[static_cast<unsigned int>(EOptionsColors::Retractions)]);
 | 
				
			||||||
#endif // ENABLE_SHADERS_MANAGER
 | 
					#endif // ENABLE_SHADERS_MANAGER
 | 
				
			||||||
                for (const RenderPath& path : buffer.render_paths)
 | 
					                for (const RenderPath& path : buffer.render_paths)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
| 
						 | 
					@ -951,11 +961,10 @@ void GCodeViewer::render_toolpaths() const
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            case GCodeProcessor::EMoveType::Unretract:
 | 
					            case GCodeProcessor::EMoveType::Unretract:
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                Color color = { 0.0f, 1.0f, 1.0f };
 | 
					 | 
				
			||||||
#if ENABLE_SHADERS_MANAGER
 | 
					#if ENABLE_SHADERS_MANAGER
 | 
				
			||||||
                shader->set_uniform("uniform_color", color);
 | 
					                shader->set_uniform("uniform_color", Options_Colors[static_cast<unsigned int>(EOptionsColors::Unretractions)]);
 | 
				
			||||||
#else
 | 
					#else
 | 
				
			||||||
                set_color(static_cast<GLint>(buffer.shader.get_shader_program_id()), color);
 | 
					                set_color(static_cast<GLint>(buffer.shader.get_shader_program_id()), Options_Colors[static_cast<unsigned int>(EOptionsColors::Unretractions)]);
 | 
				
			||||||
#endif // ENABLE_SHADERS_MANAGER
 | 
					#endif // ENABLE_SHADERS_MANAGER
 | 
				
			||||||
                for (const RenderPath& path : buffer.render_paths)
 | 
					                for (const RenderPath& path : buffer.render_paths)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
| 
						 | 
					@ -1273,6 +1282,41 @@ void GCodeViewer::render_legend() const
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    auto any_option_visible = [this]() {
 | 
				
			||||||
 | 
					        return m_buffers[buffer_id(GCodeProcessor::EMoveType::Color_change)].visible ||
 | 
				
			||||||
 | 
					               m_buffers[buffer_id(GCodeProcessor::EMoveType::Custom_GCode)].visible ||
 | 
				
			||||||
 | 
					               m_buffers[buffer_id(GCodeProcessor::EMoveType::Pause_Print)].visible ||
 | 
				
			||||||
 | 
					               m_buffers[buffer_id(GCodeProcessor::EMoveType::Retract)].visible ||
 | 
				
			||||||
 | 
					               m_buffers[buffer_id(GCodeProcessor::EMoveType::Tool_change)].visible ||
 | 
				
			||||||
 | 
					               m_buffers[buffer_id(GCodeProcessor::EMoveType::Unretract)].visible;
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    auto add_option = [this, add_item](GCodeProcessor::EMoveType move_type, EOptionsColors color, const std::string& text) {
 | 
				
			||||||
 | 
					        const IBuffer& buffer = m_buffers[buffer_id(move_type)];
 | 
				
			||||||
 | 
					        if (buffer.visible && buffer.indices_count > 0)
 | 
				
			||||||
 | 
					            add_item(Options_Colors[static_cast<unsigned int>(color)], text);
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // options
 | 
				
			||||||
 | 
					    if (any_option_visible())
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        // title
 | 
				
			||||||
 | 
					        ImGui::Spacing();
 | 
				
			||||||
 | 
					        ImGui::Spacing();
 | 
				
			||||||
 | 
					        ImGui::PushStyleColor(ImGuiCol_Text, ORANGE);
 | 
				
			||||||
 | 
					        imgui.text(_u8L("Options"));
 | 
				
			||||||
 | 
					        ImGui::PopStyleColor();
 | 
				
			||||||
 | 
					        ImGui::Separator();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // items
 | 
				
			||||||
 | 
					        add_option(GCodeProcessor::EMoveType::Retract, EOptionsColors::Retractions, _u8L("Retractions"));
 | 
				
			||||||
 | 
					        add_option(GCodeProcessor::EMoveType::Unretract, EOptionsColors::Unretractions, _u8L("Unretractions"));
 | 
				
			||||||
 | 
					        add_option(GCodeProcessor::EMoveType::Tool_change, EOptionsColors::ToolChanges, _u8L("Tool changes"));
 | 
				
			||||||
 | 
					        add_option(GCodeProcessor::EMoveType::Color_change, EOptionsColors::ColorChanges, _u8L("Color changes"));
 | 
				
			||||||
 | 
					        add_option(GCodeProcessor::EMoveType::Pause_Print, EOptionsColors::PausePrints, _u8L("Pause prints"));
 | 
				
			||||||
 | 
					        add_option(GCodeProcessor::EMoveType::Custom_GCode, EOptionsColors::CustomGCodes, _u8L("Custom GCodes"));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    imgui.end();
 | 
					    imgui.end();
 | 
				
			||||||
    ImGui::PopStyleVar();
 | 
					    ImGui::PopStyleVar();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -22,9 +22,20 @@ class GCodeViewer
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    using Color = std::array<float, 3>;
 | 
					    using Color = std::array<float, 3>;
 | 
				
			||||||
    static const std::vector<Color> Extrusion_Role_Colors;
 | 
					    static const std::vector<Color> Extrusion_Role_Colors;
 | 
				
			||||||
 | 
					    static const std::vector<Color> Options_Colors;
 | 
				
			||||||
    static const std::vector<Color> Travel_Colors;
 | 
					    static const std::vector<Color> Travel_Colors;
 | 
				
			||||||
    static const std::vector<Color> Range_Colors;
 | 
					    static const std::vector<Color> Range_Colors;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    enum class EOptionsColors : unsigned char
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        Retractions,
 | 
				
			||||||
 | 
					        Unretractions,
 | 
				
			||||||
 | 
					        ToolChanges,
 | 
				
			||||||
 | 
					        ColorChanges,
 | 
				
			||||||
 | 
					        PausePrints,
 | 
				
			||||||
 | 
					        CustomGCodes
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // buffer containing vertices data
 | 
					    // buffer containing vertices data
 | 
				
			||||||
    struct VBuffer
 | 
					    struct VBuffer
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
| 
						 | 
					@ -211,6 +222,7 @@ public:
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            GL_Model m_model;
 | 
					            GL_Model m_model;
 | 
				
			||||||
            Transform3f m_world_transform;
 | 
					            Transform3f m_world_transform;
 | 
				
			||||||
 | 
					            BoundingBoxf3 m_world_bounding_box;
 | 
				
			||||||
            std::array<float, 4> m_color{ 1.0f, 1.0f, 1.0f, 1.0f };
 | 
					            std::array<float, 4> m_color{ 1.0f, 1.0f, 1.0f, 1.0f };
 | 
				
			||||||
            bool m_visible{ false };
 | 
					            bool m_visible{ false };
 | 
				
			||||||
#if !ENABLE_SHADERS_MANAGER
 | 
					#if !ENABLE_SHADERS_MANAGER
 | 
				
			||||||
| 
						 | 
					@ -220,9 +232,9 @@ public:
 | 
				
			||||||
        public:
 | 
					        public:
 | 
				
			||||||
            void init();
 | 
					            void init();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            const BoundingBoxf3& get_bounding_box() const { return m_model.get_bounding_box(); }
 | 
					            const BoundingBoxf3& get_bounding_box() const { return m_world_bounding_box; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            void set_world_transform(const Transform3f& transform) { m_world_transform = transform; }
 | 
					            void set_world_position(const Vec3f& position);
 | 
				
			||||||
            void set_color(const std::array<float, 4>& color) { m_color = color; }
 | 
					            void set_color(const std::array<float, 4>& color) { m_color = color; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            bool is_visible() const { return m_visible; }
 | 
					            bool is_visible() const { return m_visible; }
 | 
				
			||||||
| 
						 | 
					@ -273,6 +285,7 @@ private:
 | 
				
			||||||
    std::vector<unsigned char> m_extruder_ids;
 | 
					    std::vector<unsigned char> m_extruder_ids;
 | 
				
			||||||
    mutable Extrusions m_extrusions;
 | 
					    mutable Extrusions m_extrusions;
 | 
				
			||||||
    mutable SequentialView m_sequential_view;
 | 
					    mutable SequentialView m_sequential_view;
 | 
				
			||||||
 | 
					    float m_sequential_view_marker_z_offset{ 0.5f };
 | 
				
			||||||
    Shells m_shells;
 | 
					    Shells m_shells;
 | 
				
			||||||
    EViewType m_view_type{ EViewType::FeatureType };
 | 
					    EViewType m_view_type{ EViewType::FeatureType };
 | 
				
			||||||
    bool m_legend_enabled{ true };
 | 
					    bool m_legend_enabled{ true };
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2182,7 +2182,7 @@ void GLCanvas3D::render()
 | 
				
			||||||
#endif // ENABLE_RENDER_STATISTICS
 | 
					#endif // ENABLE_RENDER_STATISTICS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#if ENABLE_CAMERA_STATISTICS
 | 
					#if ENABLE_CAMERA_STATISTICS
 | 
				
			||||||
    m_camera.debug_render();
 | 
					    camera.debug_render();
 | 
				
			||||||
#endif // ENABLE_CAMERA_STATISTICS
 | 
					#endif // ENABLE_CAMERA_STATISTICS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    std::string tooltip;
 | 
					    std::string tooltip;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -211,6 +211,11 @@ bool GLShaderProgram::set_uniform(const char* name, float value) const
 | 
				
			||||||
    return false;
 | 
					    return false;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool GLShaderProgram::set_uniform(const char* name, double value) const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    return set_uniform(name, static_cast<float>(value));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool GLShaderProgram::set_uniform(const char* name, const std::array<float, 2>& value) const
 | 
					bool GLShaderProgram::set_uniform(const char* name, const std::array<float, 2>& value) const
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    int id = get_uniform_location(name);
 | 
					    int id = get_uniform_location(name);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -43,6 +43,7 @@ public:
 | 
				
			||||||
    bool set_uniform(const char* name, int value) const;
 | 
					    bool set_uniform(const char* name, int value) const;
 | 
				
			||||||
    bool set_uniform(const char* name, bool value) const;
 | 
					    bool set_uniform(const char* name, bool value) const;
 | 
				
			||||||
    bool set_uniform(const char* name, float value) const;
 | 
					    bool set_uniform(const char* name, float value) const;
 | 
				
			||||||
 | 
					    bool set_uniform(const char* name, double value) const;
 | 
				
			||||||
    bool set_uniform(const char* name, const std::array<float, 2>& value) const;
 | 
					    bool set_uniform(const char* name, const std::array<float, 2>& value) const;
 | 
				
			||||||
    bool set_uniform(const char* name, const std::array<float, 3>& value) const;
 | 
					    bool set_uniform(const char* name, const std::array<float, 3>& value) const;
 | 
				
			||||||
    bool set_uniform(const char* name, const std::array<float, 4>& value) const;
 | 
					    bool set_uniform(const char* name, const std::array<float, 4>& value) const;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -222,6 +222,9 @@ public:
 | 
				
			||||||
    GLShaderProgram* get_current_shader() { return m_opengl_mgr.get_current_shader(); }
 | 
					    GLShaderProgram* get_current_shader() { return m_opengl_mgr.get_current_shader(); }
 | 
				
			||||||
#endif // ENABLE_SHADERS_MANAGER
 | 
					#endif // ENABLE_SHADERS_MANAGER
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool is_gl_version_greater_or_equal_to(unsigned int major, unsigned int minor) const { return m_opengl_mgr.get_gl_info().is_version_greater_or_equal_to(major, minor); }
 | 
				
			||||||
 | 
					    bool is_glsl_version_greater_or_equal_to(unsigned int major, unsigned int minor) const { return m_opengl_mgr.get_gl_info().is_glsl_version_greater_or_equal_to(major, minor); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
    bool            on_init_inner();
 | 
					    bool            on_init_inner();
 | 
				
			||||||
	void            init_app_config();
 | 
						void            init_app_config();
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4029,6 +4029,26 @@ void ObjectList::msw_rescale()
 | 
				
			||||||
    Layout();
 | 
					    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)
 | 
					void ObjectList::ItemValueChanged(wxDataViewEvent &event)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    if (event.GetColumn() == colName)
 | 
					    if (event.GetColumn() == colName)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -386,6 +386,7 @@ public:
 | 
				
			||||||
    void paste_objects_into_list(const std::vector<size_t>& object_idxs);
 | 
					    void paste_objects_into_list(const std::vector<size_t>& object_idxs);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void msw_rescale();
 | 
					    void msw_rescale();
 | 
				
			||||||
 | 
					    void sys_color_changed();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void update_after_undo_redo();
 | 
					    void update_after_undo_redo();
 | 
				
			||||||
    //update printable state for item from objects model
 | 
					    //update printable state for item from objects model
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -981,6 +981,23 @@ void ObjectManipulation::msw_rescale()
 | 
				
			||||||
    get_og()->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' };
 | 
					static const char axes[] = { 'x', 'y', 'z' };
 | 
				
			||||||
ManipulationEditor::ManipulationEditor(ObjectManipulation* parent,
 | 
					ManipulationEditor::ManipulationEditor(ObjectManipulation* parent,
 | 
				
			||||||
                                       const std::string& opt_key,
 | 
					                                       const std::string& opt_key,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -173,6 +173,7 @@ public:
 | 
				
			||||||
    void update_item_name(const wxString &item_name);
 | 
					    void update_item_name(const wxString &item_name);
 | 
				
			||||||
    void update_warning_icon_state(const wxString& tooltip);
 | 
					    void update_warning_icon_state(const wxString& tooltip);
 | 
				
			||||||
    void msw_rescale();
 | 
					    void msw_rescale();
 | 
				
			||||||
 | 
					    void sys_color_changed();
 | 
				
			||||||
    void on_change(const std::string& opt_key, int axis, double new_value);
 | 
					    void on_change(const std::string& opt_key, int axis, double new_value);
 | 
				
			||||||
    void set_focused_editor(ManipulationEditor* focused_editor) {
 | 
					    void set_focused_editor(ManipulationEditor* focused_editor) {
 | 
				
			||||||
#ifndef __APPLE__
 | 
					#ifndef __APPLE__
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -124,6 +124,12 @@ public:
 | 
				
			||||||
            // set value to _true_ in purpose of possibility of a display dpi changing from System Settings
 | 
					            // set value to _true_ in purpose of possibility of a display dpi changing from System Settings
 | 
				
			||||||
                m_can_rescale = true;
 | 
					                m_can_rescale = true;
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this->Bind(wxEVT_SYS_COLOUR_CHANGED, [this](wxSysColourChangedEvent& event)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            event.Skip();
 | 
				
			||||||
 | 
					            on_sys_color_changed();
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    virtual ~DPIAware() {}
 | 
					    virtual ~DPIAware() {}
 | 
				
			||||||
| 
						 | 
					@ -137,6 +143,7 @@ public:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
protected:
 | 
					protected:
 | 
				
			||||||
    virtual void on_dpi_changed(const wxRect &suggested_rect) = 0;
 | 
					    virtual void on_dpi_changed(const wxRect &suggested_rect) = 0;
 | 
				
			||||||
 | 
					    virtual void on_sys_color_changed() {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
    float m_scale_factor;
 | 
					    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)
 | 
					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;
 | 
					    std::vector<unsigned char> empty_vector;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef __WXMSW__
 | 
					#ifdef __WXMSW__
 | 
				
			||||||
| 
						 | 
					@ -827,8 +819,6 @@ std::vector<unsigned char> ImGuiWrapper::load_svg(const std::string& bitmap_name
 | 
				
			||||||
    if (image == nullptr)
 | 
					    if (image == nullptr)
 | 
				
			||||||
        return empty_vector;
 | 
					        return empty_vector;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    target_height != 0 ? target_height *= scale : target_width *= scale;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    float svg_scale = target_height != 0 ?
 | 
					    float svg_scale = target_height != 0 ?
 | 
				
			||||||
        (float)target_height / image->height : target_width != 0 ?
 | 
					        (float)target_height / image->height : target_width != 0 ?
 | 
				
			||||||
        (float)target_width / image->width : 1;
 | 
					        (float)target_width / image->width : 1;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -190,6 +190,29 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S
 | 
				
			||||||
        event.Skip();
 | 
					        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);
 | 
					    wxGetApp().persist_window_geometry(this, true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    update_ui_from_settings();    // FIXME (?)
 | 
					    update_ui_from_settings();    // FIXME (?)
 | 
				
			||||||
| 
						 | 
					@ -554,6 +577,28 @@ void MainFrame::on_dpi_changed(const wxRect &suggested_rect)
 | 
				
			||||||
    this->Maximize(is_maximized);
 | 
					    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()
 | 
					void MainFrame::init_menubar()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
#ifdef __APPLE__
 | 
					#ifdef __APPLE__
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -121,6 +121,7 @@ class MainFrame : public DPIFrame
 | 
				
			||||||
 | 
					
 | 
				
			||||||
protected:
 | 
					protected:
 | 
				
			||||||
    virtual void on_dpi_changed(const wxRect &suggested_rect);
 | 
					    virtual void on_dpi_changed(const wxRect &suggested_rect);
 | 
				
			||||||
 | 
					    virtual void on_sys_color_changed() override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public:
 | 
					public:
 | 
				
			||||||
    MainFrame();
 | 
					    MainFrame();
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -130,18 +130,15 @@ void OpenGLManager::GLInfo::detect() const
 | 
				
			||||||
    m_detected = true;
 | 
					    m_detected = true;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool OpenGLManager::GLInfo::is_version_greater_or_equal_to(unsigned int major, unsigned int minor) const
 | 
					static bool version_greater_or_equal_to(const std::string& version, unsigned int major, unsigned int minor)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    if (!m_detected)
 | 
					 | 
				
			||||||
        detect();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#if ENABLE_SHADERS_MANAGER
 | 
					#if ENABLE_SHADERS_MANAGER
 | 
				
			||||||
    if (m_version == "N/A")
 | 
					    if (version == "N/A")
 | 
				
			||||||
        return false;
 | 
					        return false;
 | 
				
			||||||
#endif // ENABLE_SHADERS_MANAGER
 | 
					#endif // ENABLE_SHADERS_MANAGER
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    std::vector<std::string> tokens;
 | 
					    std::vector<std::string> tokens;
 | 
				
			||||||
    boost::split(tokens, m_version, boost::is_any_of(" "), boost::token_compress_on);
 | 
					    boost::split(tokens, version, boost::is_any_of(" "), boost::token_compress_on);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (tokens.empty())
 | 
					    if (tokens.empty())
 | 
				
			||||||
        return false;
 | 
					        return false;
 | 
				
			||||||
| 
						 | 
					@ -166,6 +163,22 @@ bool OpenGLManager::GLInfo::is_version_greater_or_equal_to(unsigned int major, u
 | 
				
			||||||
        return gl_minor >= minor;
 | 
					        return gl_minor >= minor;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool OpenGLManager::GLInfo::is_version_greater_or_equal_to(unsigned int major, unsigned int minor) const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    if (!m_detected)
 | 
				
			||||||
 | 
					        detect();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return version_greater_or_equal_to(m_version, major, minor);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool OpenGLManager::GLInfo::is_glsl_version_greater_or_equal_to(unsigned int major, unsigned int minor) const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    if (!m_detected)
 | 
				
			||||||
 | 
					        detect();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return version_greater_or_equal_to(m_glsl_version, major, minor);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
std::string OpenGLManager::GLInfo::to_string(bool format_as_html, bool extensions) const
 | 
					std::string OpenGLManager::GLInfo::to_string(bool format_as_html, bool extensions) const
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    if (!m_detected)
 | 
					    if (!m_detected)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -45,6 +45,7 @@ public:
 | 
				
			||||||
        float get_max_anisotropy() const;
 | 
					        float get_max_anisotropy() const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        bool is_version_greater_or_equal_to(unsigned int major, unsigned int minor) const;
 | 
					        bool is_version_greater_or_equal_to(unsigned int major, unsigned int minor) const;
 | 
				
			||||||
 | 
					        bool is_glsl_version_greater_or_equal_to(unsigned int major, unsigned int minor) const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        std::string to_string(bool format_as_html, bool extensions) const;
 | 
					        std::string to_string(bool format_as_html, bool extensions) const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1092,6 +1092,34 @@ void Sidebar::msw_rescale()
 | 
				
			||||||
    p->scrolled->Layout();
 | 
					    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()
 | 
					void Sidebar::search()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    p->searcher.search();
 | 
					    p->searcher.search();
 | 
				
			||||||
| 
						 | 
					@ -5514,6 +5542,17 @@ void Plater::msw_rescale()
 | 
				
			||||||
    GetParent()->Layout();
 | 
					    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()
 | 
					bool Plater::init_view_toolbar()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    return p->init_view_toolbar();
 | 
					    return p->init_view_toolbar();
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -105,6 +105,7 @@ public:
 | 
				
			||||||
    void update_mode_sizer() const;
 | 
					    void update_mode_sizer() const;
 | 
				
			||||||
    void update_reslice_btn_tooltip() const;
 | 
					    void update_reslice_btn_tooltip() const;
 | 
				
			||||||
    void msw_rescale();
 | 
					    void msw_rescale();
 | 
				
			||||||
 | 
					    void sys_color_changed();
 | 
				
			||||||
    void search();
 | 
					    void search();
 | 
				
			||||||
    void jump_to_option(size_t selected);
 | 
					    void jump_to_option(size_t selected);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -308,6 +309,7 @@ public:
 | 
				
			||||||
    bool can_reload_from_disk() const;
 | 
					    bool can_reload_from_disk() const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void msw_rescale();
 | 
					    void msw_rescale();
 | 
				
			||||||
 | 
					    void sys_color_changed();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    bool init_view_toolbar();
 | 
					    bool init_view_toolbar();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -668,6 +668,14 @@ void SearchDialog::on_dpi_changed(const wxRect& suggested_rect)
 | 
				
			||||||
    Refresh();
 | 
					    Refresh();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void SearchDialog::on_sys_color_changed()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    // msw_rescale updates just icons, so use it
 | 
				
			||||||
 | 
					    search_list_model->msw_rescale();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Refresh();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ----------------------------------------------------------------------------
 | 
					// ----------------------------------------------------------------------------
 | 
				
			||||||
// SearchListModel
 | 
					// SearchListModel
 | 
				
			||||||
// ----------------------------------------------------------------------------
 | 
					// ----------------------------------------------------------------------------
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -198,6 +198,7 @@ public:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
protected:
 | 
					protected:
 | 
				
			||||||
    void on_dpi_changed(const wxRect& suggested_rect) override;
 | 
					    void on_dpi_changed(const wxRect& suggested_rect) override;
 | 
				
			||||||
 | 
					    virtual void on_sys_color_changed() override;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,6 +6,8 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <string>
 | 
					#include <string>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <Eigen/Core>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <wx/clipbrd.h>
 | 
					#include <wx/clipbrd.h>
 | 
				
			||||||
#include <wx/platinfo.h>
 | 
					#include <wx/platinfo.h>
 | 
				
			||||||
#include "GUI_App.hpp"
 | 
					#include "GUI_App.hpp"
 | 
				
			||||||
| 
						 | 
					@ -145,7 +147,7 @@ SysInfoDialog::SysInfoDialog()
 | 
				
			||||||
            "</font>"
 | 
					            "</font>"
 | 
				
			||||||
            "</body>"
 | 
					            "</body>"
 | 
				
			||||||
            "</html>", bgr_clr_str, text_clr_str, text_clr_str,
 | 
					            "</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);
 | 
					        m_opengl_info_html->SetPage(text);
 | 
				
			||||||
        main_sizer->Add(m_opengl_info_html, 1, wxEXPAND | wxBOTTOM, 15);
 | 
					        main_sizer->Add(m_opengl_info_html, 1, wxEXPAND | wxBOTTOM, 15);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -590,6 +590,18 @@ void TabPrinter::msw_rescale()
 | 
				
			||||||
    Layout();
 | 
					    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()
 | 
					void TabSLAMaterial::init_options_list()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    if (!m_options_list.empty())
 | 
					    if (!m_options_list.empty())
 | 
				
			||||||
| 
						 | 
					@ -869,6 +881,41 @@ void Tab::msw_rescale()
 | 
				
			||||||
    Layout();
 | 
					    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* Tab::get_field(const t_config_option_key& opt_key, int opt_index/* = -1*/) const
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    Field* field = nullptr;
 | 
					    Field* field = nullptr;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -318,6 +318,7 @@ public:
 | 
				
			||||||
    void            update_mode();
 | 
					    void            update_mode();
 | 
				
			||||||
    void            update_visibility();
 | 
					    void            update_visibility();
 | 
				
			||||||
    virtual void    msw_rescale();
 | 
					    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, int opt_index = -1) const;
 | 
				
			||||||
    Field*          get_field(const t_config_option_key &opt_key, Page** selected_page, int opt_index = -1);
 | 
					    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);
 | 
						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		on_preset_loaded() override;
 | 
				
			||||||
	void		init_options_list() override;
 | 
						void		init_options_list() override;
 | 
				
			||||||
	void		msw_rescale() override;
 | 
						void		msw_rescale() override;
 | 
				
			||||||
 | 
						void		sys_color_changed() override;
 | 
				
			||||||
    bool 		supports_printer_technology(const PrinterTechnology /* tech */) override { return true; }
 | 
					    bool 		supports_printer_technology(const PrinterTechnology /* tech */) override { return true; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	wxSizer*	create_bed_shape_widget(wxWindow* parent);
 | 
						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 
 | 
					add_executable(${_TEST_NAME}_tests 
 | 
				
			||||||
	${_TEST_NAME}_tests.cpp
 | 
						${_TEST_NAME}_tests.cpp
 | 
				
			||||||
	test_3mf.cpp
 | 
						test_3mf.cpp
 | 
				
			||||||
 | 
						test_aabbindirect.cpp
 | 
				
			||||||
	test_clipper_offset.cpp
 | 
						test_clipper_offset.cpp
 | 
				
			||||||
	test_clipper_utils.cpp
 | 
						test_clipper_utils.cpp
 | 
				
			||||||
	test_config.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