mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-11-02 20:51:23 -07:00 
			
		
		
		
	Add the full source of BambuStudio
using version 1.0.10
This commit is contained in:
		
							parent
							
								
									30bcadab3e
								
							
						
					
					
						commit
						1555904bef
					
				
					 3771 changed files with 1251328 additions and 0 deletions
				
			
		
							
								
								
									
										6
									
								
								sandboxes/CMakeLists.txt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								sandboxes/CMakeLists.txt
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,6 @@
 | 
			
		|||
#add_subdirectory(slasupporttree)
 | 
			
		||||
#add_subdirectory(openvdb)
 | 
			
		||||
# add_subdirectory(meshboolean)
 | 
			
		||||
add_subdirectory(its_neighbor_index)
 | 
			
		||||
# 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})
 | 
			
		||||
							
								
								
									
										223
									
								
								sandboxes/aabb-evaluation/aabb-evaluation.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										223
									
								
								sandboxes/aabb-evaluation/aabb-evaluation.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,223 @@
 | 
			
		|||
#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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (mesh.empty()) {
 | 
			
		||||
        std::cerr << "Error loading " << argv[1] << " . It is empty." << std::endl;
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    profile(mesh);    
 | 
			
		||||
 | 
			
		||||
    return EXIT_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										7
									
								
								sandboxes/its_neighbor_index/CMakeLists.txt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								sandboxes/its_neighbor_index/CMakeLists.txt
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,7 @@
 | 
			
		|||
add_executable(its_neighbor_index main.cpp ItsNeighborIndex.cpp ItsNeighborIndex.hpp)
 | 
			
		||||
 | 
			
		||||
target_link_libraries(its_neighbor_index libslic3r admesh)
 | 
			
		||||
 | 
			
		||||
if (WIN32)
 | 
			
		||||
    prusaslicer_copy_dlls(its_neighbor_index)
 | 
			
		||||
endif()
 | 
			
		||||
							
								
								
									
										613
									
								
								sandboxes/its_neighbor_index/ItsNeighborIndex.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										613
									
								
								sandboxes/its_neighbor_index/ItsNeighborIndex.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,613 @@
 | 
			
		|||
#include <iostream>
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <unordered_map>
 | 
			
		||||
#include <map>
 | 
			
		||||
 | 
			
		||||
#include "ItsNeighborIndex.hpp"
 | 
			
		||||
#include "libslic3r/Execution/ExecutionTBB.hpp"
 | 
			
		||||
#include "libslic3r/Execution/ExecutionSeq.hpp"
 | 
			
		||||
 | 
			
		||||
#include "tbb/parallel_sort.h"
 | 
			
		||||
 | 
			
		||||
namespace Slic3r {
 | 
			
		||||
 | 
			
		||||
FaceNeighborIndex its_create_neighbors_index_1(const indexed_triangle_set &its)
 | 
			
		||||
{
 | 
			
		||||
    // Just to be clear what type of object are we referencing
 | 
			
		||||
    using FaceID = size_t;
 | 
			
		||||
    using VertexID = uint64_t;
 | 
			
		||||
    using EdgeID = uint64_t;
 | 
			
		||||
 | 
			
		||||
    constexpr auto UNASSIGNED = std::numeric_limits<FaceID>::max();
 | 
			
		||||
 | 
			
		||||
    struct Edge // Will contain IDs of the two facets touching this edge
 | 
			
		||||
    {
 | 
			
		||||
        FaceID first, second;
 | 
			
		||||
        Edge() : first{UNASSIGNED}, second{UNASSIGNED} {}
 | 
			
		||||
        void   assign(FaceID fid)
 | 
			
		||||
        {
 | 
			
		||||
            first == UNASSIGNED ? first = fid : second = fid;
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // All vertex IDs will fit into this number of bits. (Used for hashing)
 | 
			
		||||
    const int max_vertex_id_bits = std::ceil(std::log2(its.vertices.size()));
 | 
			
		||||
    assert(max_vertex_id_bits <= 32);
 | 
			
		||||
 | 
			
		||||
    std::unordered_map< EdgeID, Edge> edge_index;
 | 
			
		||||
 | 
			
		||||
    // Edge id is constructed by concatenating two vertex ids, starting with
 | 
			
		||||
    // the lowest in MSB
 | 
			
		||||
    auto hash = [max_vertex_id_bits] (VertexID a, VertexID b) {
 | 
			
		||||
        if (a > b) std::swap(a, b);
 | 
			
		||||
        return (a << max_vertex_id_bits) + b;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // Go through all edges of all facets and mark the facets touching each edge
 | 
			
		||||
    for (size_t face_id = 0; face_id < its.indices.size(); ++face_id) {
 | 
			
		||||
        const Vec3i &face = its.indices[face_id];
 | 
			
		||||
 | 
			
		||||
        EdgeID e1 = hash(face(0), face(1)), e2 = hash(face(1), face(2)),
 | 
			
		||||
               e3 = hash(face(2), face(0));
 | 
			
		||||
 | 
			
		||||
        edge_index[e1].assign(face_id);
 | 
			
		||||
        edge_index[e2].assign(face_id);
 | 
			
		||||
        edge_index[e3].assign(face_id);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    FaceNeighborIndex index(its.indices.size());
 | 
			
		||||
 | 
			
		||||
    // Now collect the neighbors for each facet into the final index
 | 
			
		||||
    for (size_t face_id = 0; face_id < its.indices.size(); ++face_id) {
 | 
			
		||||
        const Vec3i &face = its.indices[face_id];
 | 
			
		||||
 | 
			
		||||
        EdgeID e1 = hash(face(0), face(1)), e2 = hash(face(1), face(2)),
 | 
			
		||||
               e3 = hash(face(2), face(0));
 | 
			
		||||
 | 
			
		||||
        const Edge &neighs1 = edge_index[e1];
 | 
			
		||||
        const Edge &neighs2 = edge_index[e2];
 | 
			
		||||
        const Edge &neighs3 = edge_index[e3];
 | 
			
		||||
 | 
			
		||||
        std::array<size_t, 3> &neighs = index[face_id];
 | 
			
		||||
        neighs[0] = neighs1.first == face_id ? neighs1.second : neighs1.first;
 | 
			
		||||
        neighs[1] = neighs2.first == face_id ? neighs2.second : neighs2.first;
 | 
			
		||||
        neighs[2] = neighs3.first == face_id ? neighs3.second : neighs3.first;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return index;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::vector<Vec3i> its_create_neighbors_index_2(const indexed_triangle_set &its)
 | 
			
		||||
{
 | 
			
		||||
    std::vector<Vec3i> out(its.indices.size(), Vec3i(-1, -1, -1));
 | 
			
		||||
 | 
			
		||||
    // Create a mapping from triangle edge into face.
 | 
			
		||||
    struct EdgeToFace {
 | 
			
		||||
        // Index of the 1st vertex of the triangle edge. vertex_low <= vertex_high.
 | 
			
		||||
        int  vertex_low;
 | 
			
		||||
        // Index of the 2nd vertex of the triangle edge.
 | 
			
		||||
        int  vertex_high;
 | 
			
		||||
        // Index of a triangular face.
 | 
			
		||||
        int  face;
 | 
			
		||||
        // Index of edge in the face, starting with 1. Negative indices if the edge was stored reverse in (vertex_low, vertex_high).
 | 
			
		||||
        int  face_edge;
 | 
			
		||||
        bool operator==(const EdgeToFace &other) const { return vertex_low == other.vertex_low && vertex_high == other.vertex_high; }
 | 
			
		||||
        bool operator<(const EdgeToFace &other) const { return vertex_low < other.vertex_low || (vertex_low == other.vertex_low && vertex_high < other.vertex_high); }
 | 
			
		||||
    };
 | 
			
		||||
    std::vector<EdgeToFace> edges_map;
 | 
			
		||||
    edges_map.assign(its.indices.size() * 3, EdgeToFace());
 | 
			
		||||
    for (uint32_t facet_idx = 0; facet_idx < its.indices.size(); ++ facet_idx)
 | 
			
		||||
        for (int i = 0; i < 3; ++ i) {
 | 
			
		||||
            EdgeToFace &e2f = edges_map[facet_idx * 3 + i];
 | 
			
		||||
            e2f.vertex_low  = its.indices[facet_idx][i];
 | 
			
		||||
            e2f.vertex_high = its.indices[facet_idx][(i + 1) % 3];
 | 
			
		||||
            e2f.face        = facet_idx;
 | 
			
		||||
            // 1 based indexing, to be always strictly positive.
 | 
			
		||||
            e2f.face_edge   = i + 1;
 | 
			
		||||
            if (e2f.vertex_low > e2f.vertex_high) {
 | 
			
		||||
                // Sort the vertices
 | 
			
		||||
                std::swap(e2f.vertex_low, e2f.vertex_high);
 | 
			
		||||
                // and make the face_edge negative to indicate a flipped edge.
 | 
			
		||||
                e2f.face_edge = - e2f.face_edge;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    std::sort(edges_map.begin(), edges_map.end());
 | 
			
		||||
 | 
			
		||||
    // Assign a unique common edge id to touching triangle edges.
 | 
			
		||||
    int num_edges = 0;
 | 
			
		||||
    for (size_t i = 0; i < edges_map.size(); ++ i) {
 | 
			
		||||
        EdgeToFace &edge_i = edges_map[i];
 | 
			
		||||
        if (edge_i.face == -1)
 | 
			
		||||
            // This edge has been connected to some neighbor already.
 | 
			
		||||
            continue;
 | 
			
		||||
        // Unconnected edge. Find its neighbor with the correct orientation.
 | 
			
		||||
        size_t j;
 | 
			
		||||
        bool found = false;
 | 
			
		||||
        for (j = i + 1; j < edges_map.size() && edge_i == edges_map[j]; ++ j)
 | 
			
		||||
            if (edge_i.face_edge * edges_map[j].face_edge < 0 && edges_map[j].face != -1) {
 | 
			
		||||
                // Faces touching with opposite oriented edges and none of the edges is connected yet.
 | 
			
		||||
                found = true;
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        if (! found) {
 | 
			
		||||
            //FIXME Vojtech: Trying to find an edge with equal orientation. This smells.
 | 
			
		||||
            // admesh can assign the same edge ID to more than two facets (which is
 | 
			
		||||
            // still topologically correct), so we have to search for a duplicate of
 | 
			
		||||
            // this edge too in case it was already seen in this orientation
 | 
			
		||||
            for (j = i + 1; j < edges_map.size() && edge_i == edges_map[j]; ++ j)
 | 
			
		||||
                if (edges_map[j].face != -1) {
 | 
			
		||||
                    // Faces touching with equally oriented edges and none of the edges is connected yet.
 | 
			
		||||
                    found = true;
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
        }
 | 
			
		||||
        // Assign an edge index to the 1st face.
 | 
			
		||||
        //        out[edge_i.face](std::abs(edge_i.face_edge) - 1) = num_edges;
 | 
			
		||||
        if (found) {
 | 
			
		||||
            EdgeToFace &edge_j = edges_map[j];
 | 
			
		||||
            out[edge_i.face](std::abs(edge_i.face_edge) - 1) = edge_j.face;
 | 
			
		||||
            out[edge_j.face](std::abs(edge_j.face_edge) - 1) = edge_i.face;
 | 
			
		||||
            // Mark the edge as connected.
 | 
			
		||||
            edge_j.face = -1;
 | 
			
		||||
        }
 | 
			
		||||
        ++ num_edges;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return out;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::vector<Vec3i> its_create_neighbors_index_3(const indexed_triangle_set &its)
 | 
			
		||||
{
 | 
			
		||||
    std::vector<Vec3i> out(its.indices.size(), Vec3i(-1, -1, -1));
 | 
			
		||||
 | 
			
		||||
    // Create a mapping from triangle edge into face.
 | 
			
		||||
    struct EdgeToFace {
 | 
			
		||||
        // Index of the 1st vertex of the triangle edge. vertex_low <= vertex_high.
 | 
			
		||||
        int  vertex_low;
 | 
			
		||||
        // Index of the 2nd vertex of the triangle edge.
 | 
			
		||||
        int  vertex_high;
 | 
			
		||||
        // Index of a triangular face.
 | 
			
		||||
        int  face;
 | 
			
		||||
        // Index of edge in the face, starting with 1. Negative indices if the edge was stored reverse in (vertex_low, vertex_high).
 | 
			
		||||
        int  face_edge;
 | 
			
		||||
        bool operator==(const EdgeToFace &other) const { return vertex_low == other.vertex_low && vertex_high == other.vertex_high; }
 | 
			
		||||
        bool operator<(const EdgeToFace &other) const { return vertex_low < other.vertex_low || (vertex_low == other.vertex_low && vertex_high < other.vertex_high); }
 | 
			
		||||
    };
 | 
			
		||||
    std::vector<EdgeToFace> edges_map;
 | 
			
		||||
    edges_map.assign(its.indices.size() * 3, EdgeToFace());
 | 
			
		||||
    for (uint32_t facet_idx = 0; facet_idx < its.indices.size(); ++ facet_idx)
 | 
			
		||||
        for (int i = 0; i < 3; ++ i) {
 | 
			
		||||
            EdgeToFace &e2f = edges_map[facet_idx * 3 + i];
 | 
			
		||||
            e2f.vertex_low  = its.indices[facet_idx][i];
 | 
			
		||||
            e2f.vertex_high = its.indices[facet_idx][(i + 1) % 3];
 | 
			
		||||
            e2f.face        = facet_idx;
 | 
			
		||||
            // 1 based indexing, to be always strictly positive.
 | 
			
		||||
            e2f.face_edge   = i + 1;
 | 
			
		||||
            if (e2f.vertex_low > e2f.vertex_high) {
 | 
			
		||||
                // Sort the vertices
 | 
			
		||||
                std::swap(e2f.vertex_low, e2f.vertex_high);
 | 
			
		||||
                // and make the face_edge negative to indicate a flipped edge.
 | 
			
		||||
                e2f.face_edge = - e2f.face_edge;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    tbb::parallel_sort(edges_map.begin(), edges_map.end());
 | 
			
		||||
 | 
			
		||||
    // Assign a unique common edge id to touching triangle edges.
 | 
			
		||||
    int num_edges = 0;
 | 
			
		||||
    for (size_t i = 0; i < edges_map.size(); ++ i) {
 | 
			
		||||
        EdgeToFace &edge_i = edges_map[i];
 | 
			
		||||
        if (edge_i.face == -1)
 | 
			
		||||
            // This edge has been connected to some neighbor already.
 | 
			
		||||
            continue;
 | 
			
		||||
        // Unconnected edge. Find its neighbor with the correct orientation.
 | 
			
		||||
        size_t j;
 | 
			
		||||
        bool found = false;
 | 
			
		||||
        for (j = i + 1; j < edges_map.size() && edge_i == edges_map[j]; ++ j)
 | 
			
		||||
            if (edge_i.face_edge * edges_map[j].face_edge < 0 && edges_map[j].face != -1) {
 | 
			
		||||
                // Faces touching with opposite oriented edges and none of the edges is connected yet.
 | 
			
		||||
                found = true;
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        if (! found) {
 | 
			
		||||
            //FIXME Vojtech: Trying to find an edge with equal orientation. This smells.
 | 
			
		||||
            // admesh can assign the same edge ID to more than two facets (which is
 | 
			
		||||
            // still topologically correct), so we have to search for a duplicate of
 | 
			
		||||
            // this edge too in case it was already seen in this orientation
 | 
			
		||||
            for (j = i + 1; j < edges_map.size() && edge_i == edges_map[j]; ++ j)
 | 
			
		||||
                if (edges_map[j].face != -1) {
 | 
			
		||||
                    // Faces touching with equally oriented edges and none of the edges is connected yet.
 | 
			
		||||
                    found = true;
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
        }
 | 
			
		||||
        // Assign an edge index to the 1st face.
 | 
			
		||||
        //        out[edge_i.face](std::abs(edge_i.face_edge) - 1) = num_edges;
 | 
			
		||||
        if (found) {
 | 
			
		||||
            EdgeToFace &edge_j = edges_map[j];
 | 
			
		||||
            out[edge_i.face](std::abs(edge_i.face_edge) - 1) = edge_j.face;
 | 
			
		||||
            out[edge_j.face](std::abs(edge_j.face_edge) - 1) = edge_i.face;
 | 
			
		||||
            // Mark the edge as connected.
 | 
			
		||||
            edge_j.face = -1;
 | 
			
		||||
        }
 | 
			
		||||
        ++ num_edges;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return out;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
FaceNeighborIndex its_create_neighbors_index_4(const indexed_triangle_set &its)
 | 
			
		||||
{
 | 
			
		||||
    // Just to be clear what type of object are we referencing
 | 
			
		||||
    using FaceID = size_t;
 | 
			
		||||
    using VertexID = uint64_t;
 | 
			
		||||
    using EdgeID = uint64_t;
 | 
			
		||||
 | 
			
		||||
    constexpr auto UNASSIGNED = std::numeric_limits<FaceID>::max();
 | 
			
		||||
 | 
			
		||||
    struct Edge // Will contain IDs of the two facets touching this edge
 | 
			
		||||
    {
 | 
			
		||||
        FaceID first, second;
 | 
			
		||||
        Edge() : first{UNASSIGNED}, second{UNASSIGNED} {}
 | 
			
		||||
        void   assign(FaceID fid)
 | 
			
		||||
        {
 | 
			
		||||
            first == UNASSIGNED ? first = fid : second = fid;
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    Benchmark bm;
 | 
			
		||||
    bm.start();
 | 
			
		||||
 | 
			
		||||
    // All vertex IDs will fit into this number of bits. (Used for hashing)
 | 
			
		||||
    //    const int max_vertex_id_bits = std::ceil(std::log2(its.vertices.size()));
 | 
			
		||||
    //    assert(max_vertex_id_bits <= 32);
 | 
			
		||||
 | 
			
		||||
    const uint64_t Vn  = its.vertices.size();
 | 
			
		||||
    //    const uint64_t Fn  = 3 * its.indices.size();
 | 
			
		||||
    //    double MaxQ = double(Vn) * (Vn + 1) / Fn;
 | 
			
		||||
    //    const uint64_t Nq = MaxQ < 0 ? 0 : std::ceil(std::log2(MaxQ));
 | 
			
		||||
    //    const uint64_t Nr = std::ceil(std::log2(std::min(Vn * (Vn + 1), Fn)));
 | 
			
		||||
    //    const uint64_t Nfn = std::ceil(std::log2(Fn));
 | 
			
		||||
 | 
			
		||||
    ////    const uint64_t max_edge_ids = (uint64_t(1) << (Nq + Nr));
 | 
			
		||||
    //    const uint64_t max_edge_ids = MaxQ * Fn + (std::min(Vn * (Vn + 1), Fn)); //(uint64_t(1) << Nfn);
 | 
			
		||||
    const uint64_t Fn  = 3 * its.indices.size();
 | 
			
		||||
    std::vector< Edge > edge_index(3 * Fn);
 | 
			
		||||
 | 
			
		||||
    // Edge id is constructed by concatenating two vertex ids, starting with
 | 
			
		||||
    // the lowest in MSB
 | 
			
		||||
    auto hash = [Vn, Fn /*, Nr*/] (VertexID a, VertexID b) {
 | 
			
		||||
        if (a > b) std::swap(a, b);
 | 
			
		||||
 | 
			
		||||
        uint64_t C = Vn * a + b;
 | 
			
		||||
        uint64_t Q = C / Fn, R = C % Fn;
 | 
			
		||||
 | 
			
		||||
        return Q * Fn + R;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // Go through all edges of all facets and mark the facets touching each edge
 | 
			
		||||
    for (size_t face_id = 0; face_id < its.indices.size(); ++face_id) {
 | 
			
		||||
        const Vec3i &face = its.indices[face_id];
 | 
			
		||||
 | 
			
		||||
        EdgeID e1 = hash(face(0), face(1)), e2 = hash(face(1), face(2)),
 | 
			
		||||
               e3 = hash(face(2), face(0));
 | 
			
		||||
 | 
			
		||||
        edge_index[e1].assign(face_id);
 | 
			
		||||
        edge_index[e2].assign(face_id);
 | 
			
		||||
        edge_index[e3].assign(face_id);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    FaceNeighborIndex index(its.indices.size());
 | 
			
		||||
 | 
			
		||||
    // Now collect the neighbors for each facet into the final index
 | 
			
		||||
    for (size_t face_id = 0; face_id < its.indices.size(); ++face_id) {
 | 
			
		||||
        const Vec3i &face = its.indices[face_id];
 | 
			
		||||
 | 
			
		||||
        EdgeID e1 = hash(face(0), face(1)), e2 = hash(face(1), face(2)),
 | 
			
		||||
               e3 = hash(face(2), face(0));
 | 
			
		||||
 | 
			
		||||
        const Edge &neighs1 = edge_index[e1];
 | 
			
		||||
        const Edge &neighs2 = edge_index[e2];
 | 
			
		||||
        const Edge &neighs3 = edge_index[e3];
 | 
			
		||||
 | 
			
		||||
        std::array<size_t, 3> &neighs = index[face_id];
 | 
			
		||||
        neighs[0] = neighs1.first == face_id ? neighs1.second : neighs1.first;
 | 
			
		||||
        neighs[1] = neighs2.first == face_id ? neighs2.second : neighs2.first;
 | 
			
		||||
        neighs[2] = neighs3.first == face_id ? neighs3.second : neighs3.first;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bm.stop();
 | 
			
		||||
 | 
			
		||||
    std::cout << "Creating neighbor index took: " << bm.getElapsedSec() << " seconds." << std::endl;
 | 
			
		||||
 | 
			
		||||
    return index;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Create an index of faces belonging to each vertex. The returned vector can
 | 
			
		||||
// be indexed with vertex indices and contains a list of face indices for each
 | 
			
		||||
// vertex.
 | 
			
		||||
std::vector<std::vector<size_t>> create_vertex_faces_index(const indexed_triangle_set &its)
 | 
			
		||||
{
 | 
			
		||||
    std::vector<std::vector<size_t>> index;
 | 
			
		||||
 | 
			
		||||
    if (! its.vertices.empty()) {
 | 
			
		||||
        size_t res = its.indices.size() / its.vertices.size();
 | 
			
		||||
        index.assign(its.vertices.size(), reserve_vector<size_t>(res));
 | 
			
		||||
        for (size_t fi = 0; fi < its.indices.size(); ++fi) {
 | 
			
		||||
            auto &face = its.indices[fi];
 | 
			
		||||
            index[face(0)].emplace_back(fi);
 | 
			
		||||
            index[face(1)].emplace_back(fi);
 | 
			
		||||
            index[face(2)].emplace_back(fi);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return index;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//static int get_vertex_index(size_t vertex_index, const stl_triangle_vertex_indices &triangle_indices) {
 | 
			
		||||
//    if (vertex_index == triangle_indices[0]) return 0;
 | 
			
		||||
//    if (vertex_index == triangle_indices[1]) return 1;
 | 
			
		||||
//    if (vertex_index == triangle_indices[2]) return 2;
 | 
			
		||||
//    return -1;
 | 
			
		||||
//}
 | 
			
		||||
 | 
			
		||||
//static Vec2crd get_edge_indices(int edge_index, const stl_triangle_vertex_indices &triangle_indices)
 | 
			
		||||
//{
 | 
			
		||||
//    int next_edge_index = (edge_index == 2) ? 0 : edge_index + 1;
 | 
			
		||||
//    coord_t vi0             = triangle_indices[edge_index];
 | 
			
		||||
//    coord_t vi1             = triangle_indices[next_edge_index];
 | 
			
		||||
//    return Vec2crd(vi0, vi1);
 | 
			
		||||
//}
 | 
			
		||||
 | 
			
		||||
static std::vector<std::vector<size_t>> create_vertex_faces_index(
 | 
			
		||||
    const std::vector<stl_triangle_vertex_indices>& indices, size_t count_vertices)
 | 
			
		||||
{
 | 
			
		||||
    if (count_vertices == 0) return {};
 | 
			
		||||
    std::vector<std::vector<size_t>> index;
 | 
			
		||||
    size_t res = indices.size() / count_vertices;
 | 
			
		||||
    index.assign(count_vertices, reserve_vector<size_t>(res));
 | 
			
		||||
    for (size_t fi = 0; fi < indices.size(); ++fi) {
 | 
			
		||||
        auto &face = indices[fi];
 | 
			
		||||
        index[face(0)].emplace_back(fi);
 | 
			
		||||
        index[face(1)].emplace_back(fi);
 | 
			
		||||
        index[face(2)].emplace_back(fi);
 | 
			
		||||
    }
 | 
			
		||||
    return index;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::vector<Vec3crd> its_create_neighbors_index_5(const indexed_triangle_set &its)
 | 
			
		||||
{
 | 
			
		||||
    const std::vector<stl_triangle_vertex_indices> &indices = its.indices;
 | 
			
		||||
    size_t vertices_size = its.vertices.size();
 | 
			
		||||
 | 
			
		||||
    if (indices.empty() || vertices_size == 0) return {};
 | 
			
		||||
    std::vector<std::vector<size_t>> vertex_triangles = create_vertex_faces_index(indices, vertices_size);
 | 
			
		||||
    coord_t              no_value = -1;
 | 
			
		||||
    std::vector<Vec3crd> neighbors(indices.size(), Vec3crd(no_value, no_value, no_value));
 | 
			
		||||
    for (const stl_triangle_vertex_indices& triangle_indices : indices) {
 | 
			
		||||
        coord_t index = &triangle_indices - &indices.front();
 | 
			
		||||
        Vec3crd& neighbor = neighbors[index];
 | 
			
		||||
        for (int edge_index = 0; edge_index < 3; ++edge_index) {
 | 
			
		||||
            // check if done
 | 
			
		||||
            coord_t& neighbor_edge = neighbor[edge_index];
 | 
			
		||||
            if (neighbor_edge != no_value) continue;
 | 
			
		||||
            Vec2crd edge_indices = get_edge_indices(edge_index, triangle_indices);
 | 
			
		||||
            // IMPROVE: use same vector for 2 sides of triangle
 | 
			
		||||
            const std::vector<size_t> &faces = vertex_triangles[edge_indices[0]];
 | 
			
		||||
            for (const size_t &face : faces) {
 | 
			
		||||
                if (face <= index) continue;
 | 
			
		||||
                const stl_triangle_vertex_indices &face_indices = indices[face];
 | 
			
		||||
                int vertex_index = get_vertex_index(edge_indices[1], face_indices);
 | 
			
		||||
                // NOT Contain second vertex?
 | 
			
		||||
                if (vertex_index < 0) continue;
 | 
			
		||||
                // Has NOT oposit direction?
 | 
			
		||||
                if (edge_indices[0] != face_indices[(vertex_index + 1) % 3]) continue;
 | 
			
		||||
                neighbor_edge = face;
 | 
			
		||||
                neighbors[face][vertex_index] = index;
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            // must be paired
 | 
			
		||||
            assert(neighbor_edge != no_value);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return neighbors;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::vector<std::array<size_t, 3>> its_create_neighbors_index_6(const indexed_triangle_set &its)
 | 
			
		||||
{
 | 
			
		||||
    constexpr auto UNASSIGNED_EDGE = std::numeric_limits<uint64_t>::max();
 | 
			
		||||
    constexpr auto UNASSIGNED_FACE = std::numeric_limits<size_t>::max();
 | 
			
		||||
    struct Edge
 | 
			
		||||
    {
 | 
			
		||||
        uint64_t id      = UNASSIGNED_EDGE;
 | 
			
		||||
        size_t   face_id = UNASSIGNED_FACE;
 | 
			
		||||
        bool operator < (const Edge &e) const { return id < e.id; }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    const size_t facenum = its.indices.size();
 | 
			
		||||
 | 
			
		||||
    // All vertex IDs will fit into this number of bits. (Used for hashing)
 | 
			
		||||
    const int max_vertex_id_bits = std::ceil(std::log2(its.vertices.size()));
 | 
			
		||||
    assert(max_vertex_id_bits <= 32);
 | 
			
		||||
 | 
			
		||||
    // Edge id is constructed by concatenating two vertex ids, starting with
 | 
			
		||||
    // the lowest in MSB
 | 
			
		||||
    auto hash = [max_vertex_id_bits] (uint64_t a, uint64_t b) {
 | 
			
		||||
        if (a > b) std::swap(a, b);
 | 
			
		||||
        return (a << max_vertex_id_bits) + b;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    std::vector<Edge> edge_map(3 * facenum);
 | 
			
		||||
 | 
			
		||||
    // Go through all edges of all facets and mark the facets touching each edge
 | 
			
		||||
    for (size_t face_id = 0; face_id < facenum; ++face_id) {
 | 
			
		||||
        const Vec3i &face = its.indices[face_id];
 | 
			
		||||
 | 
			
		||||
        edge_map[face_id * 3] = {hash(face(0), face(1)), face_id};
 | 
			
		||||
        edge_map[face_id * 3 + 1] = {hash(face(1), face(2)), face_id};
 | 
			
		||||
        edge_map[face_id * 3 + 2] = {hash(face(2), face(0)), face_id};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::sort(edge_map.begin(), edge_map.end());
 | 
			
		||||
 | 
			
		||||
    std::vector<std::array<size_t, 3>> out(facenum, {UNASSIGNED_FACE, UNASSIGNED_FACE, UNASSIGNED_FACE});
 | 
			
		||||
 | 
			
		||||
    auto add_neighbor = [](std::array<size_t, 3> &slot, size_t face_id) {
 | 
			
		||||
        if (slot[0] == UNASSIGNED_FACE) { slot[0] = face_id; return; }
 | 
			
		||||
        if (slot[1] == UNASSIGNED_FACE) { slot[1] = face_id; return; }
 | 
			
		||||
        if (slot[2] == UNASSIGNED_FACE) { slot[2] = face_id; return; }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    for (auto it = edge_map.begin(); it != edge_map.end();) {
 | 
			
		||||
        size_t face_id = it->face_id;
 | 
			
		||||
        uint64_t edge_id = it->id;
 | 
			
		||||
 | 
			
		||||
        while (++it != edge_map.end() &&  (it->id == edge_id)) {
 | 
			
		||||
            size_t other_face_id = it->face_id;
 | 
			
		||||
            add_neighbor(out[other_face_id], face_id);
 | 
			
		||||
            add_neighbor(out[face_id], other_face_id);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return out;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
std::vector<std::array<size_t, 3>> its_create_neighbors_index_7(const indexed_triangle_set &its)
 | 
			
		||||
{
 | 
			
		||||
    constexpr auto UNASSIGNED_EDGE = std::numeric_limits<uint64_t>::max();
 | 
			
		||||
    constexpr auto UNASSIGNED_FACE = std::numeric_limits<size_t>::max();
 | 
			
		||||
    struct Edge
 | 
			
		||||
    {
 | 
			
		||||
        uint64_t id      = UNASSIGNED_EDGE;
 | 
			
		||||
        size_t   face_id = UNASSIGNED_FACE;
 | 
			
		||||
        bool operator < (const Edge &e) const { return id < e.id; }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    const size_t facenum = its.indices.size();
 | 
			
		||||
 | 
			
		||||
    // All vertex IDs will fit into this number of bits. (Used for hashing)
 | 
			
		||||
    const int max_vertex_id_bits = std::ceil(std::log2(its.vertices.size()));
 | 
			
		||||
    assert(max_vertex_id_bits <= 32);
 | 
			
		||||
 | 
			
		||||
    // Edge id is constructed by concatenating two vertex ids, starting with
 | 
			
		||||
    // the lowest in MSB
 | 
			
		||||
    auto hash = [max_vertex_id_bits] (uint64_t a, uint64_t b) {
 | 
			
		||||
        if (a > b) std::swap(a, b);
 | 
			
		||||
        return (a << max_vertex_id_bits) + b;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    std::vector<Edge> edge_map(3 * facenum);
 | 
			
		||||
 | 
			
		||||
    // Go through all edges of all facets and mark the facets touching each edge
 | 
			
		||||
    for (size_t face_id = 0; face_id < facenum; ++face_id) {
 | 
			
		||||
        const Vec3i &face = its.indices[face_id];
 | 
			
		||||
 | 
			
		||||
        edge_map[face_id * 3] = {hash(face(0), face(1)), face_id};
 | 
			
		||||
        edge_map[face_id * 3 + 1] = {hash(face(1), face(2)), face_id};
 | 
			
		||||
        edge_map[face_id * 3 + 2] = {hash(face(2), face(0)), face_id};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    tbb::parallel_sort(edge_map.begin(), edge_map.end());
 | 
			
		||||
 | 
			
		||||
    std::vector<std::array<size_t, 3>> out(facenum, {UNASSIGNED_FACE, UNASSIGNED_FACE, UNASSIGNED_FACE});
 | 
			
		||||
 | 
			
		||||
    auto add_neighbor = [](std::array<size_t, 3> &slot, size_t face_id) {
 | 
			
		||||
        if (slot[0] == UNASSIGNED_FACE) { slot[0] = face_id; return; }
 | 
			
		||||
        if (slot[1] == UNASSIGNED_FACE) { slot[1] = face_id; return; }
 | 
			
		||||
        if (slot[2] == UNASSIGNED_FACE) { slot[2] = face_id; return; }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    for (auto it = edge_map.begin(); it != edge_map.end();) {
 | 
			
		||||
        size_t face_id = it->face_id;
 | 
			
		||||
        uint64_t edge_id = it->id;
 | 
			
		||||
 | 
			
		||||
        while (++it != edge_map.end() &&  (it->id == edge_id)) {
 | 
			
		||||
            size_t other_face_id = it->face_id;
 | 
			
		||||
            add_neighbor(out[other_face_id], face_id);
 | 
			
		||||
            add_neighbor(out[face_id], other_face_id);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return out;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
FaceNeighborIndex its_create_neighbors_index_8(const indexed_triangle_set &its)
 | 
			
		||||
{
 | 
			
		||||
    // Just to be clear what type of object are we referencing
 | 
			
		||||
    using FaceID = size_t;
 | 
			
		||||
    using VertexID = uint64_t;
 | 
			
		||||
    using EdgeID = uint64_t;
 | 
			
		||||
 | 
			
		||||
    constexpr auto UNASSIGNED = std::numeric_limits<FaceID>::max();
 | 
			
		||||
 | 
			
		||||
    struct Edge // Will contain IDs of the two facets touching this edge
 | 
			
		||||
    {
 | 
			
		||||
        FaceID first, second;
 | 
			
		||||
        Edge() : first{UNASSIGNED}, second{UNASSIGNED} {}
 | 
			
		||||
        void   assign(FaceID fid)
 | 
			
		||||
        {
 | 
			
		||||
            first == UNASSIGNED ? first = fid : second = fid;
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // All vertex IDs will fit into this number of bits. (Used for hashing)
 | 
			
		||||
    const int max_vertex_id_bits = std::ceil(std::log2(its.vertices.size()));
 | 
			
		||||
    assert(max_vertex_id_bits <= 32);
 | 
			
		||||
 | 
			
		||||
    std::map< EdgeID, Edge > edge_index;
 | 
			
		||||
 | 
			
		||||
    // Edge id is constructed by concatenating two vertex ids, starting with
 | 
			
		||||
    // the lowest in MSB
 | 
			
		||||
    auto hash = [max_vertex_id_bits] (VertexID a, VertexID b) {
 | 
			
		||||
        if (a > b) std::swap(a, b);
 | 
			
		||||
        return (a << max_vertex_id_bits) + b;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // Go through all edges of all facets and mark the facets touching each edge
 | 
			
		||||
    for (size_t face_id = 0; face_id < its.indices.size(); ++face_id) {
 | 
			
		||||
        const Vec3i &face = its.indices[face_id];
 | 
			
		||||
 | 
			
		||||
        EdgeID e1 = hash(face(0), face(1)), e2 = hash(face(1), face(2)),
 | 
			
		||||
               e3 = hash(face(2), face(0));
 | 
			
		||||
 | 
			
		||||
        edge_index[e1].assign(face_id);
 | 
			
		||||
        edge_index[e2].assign(face_id);
 | 
			
		||||
        edge_index[e3].assign(face_id);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    FaceNeighborIndex index(its.indices.size());
 | 
			
		||||
 | 
			
		||||
    // Now collect the neighbors for each facet into the final index
 | 
			
		||||
    for (size_t face_id = 0; face_id < its.indices.size(); ++face_id) {
 | 
			
		||||
        const Vec3i &face = its.indices[face_id];
 | 
			
		||||
 | 
			
		||||
        EdgeID e1 = hash(face(0), face(1)), e2 = hash(face(1), face(2)),
 | 
			
		||||
               e3 = hash(face(2), face(0));
 | 
			
		||||
 | 
			
		||||
        const Edge &neighs1 = edge_index[e1];
 | 
			
		||||
        const Edge &neighs2 = edge_index[e2];
 | 
			
		||||
        const Edge &neighs3 = edge_index[e3];
 | 
			
		||||
 | 
			
		||||
        std::array<size_t, 3> &neighs = index[face_id];
 | 
			
		||||
        neighs[0] = neighs1.first == face_id ? neighs1.second : neighs1.first;
 | 
			
		||||
        neighs[1] = neighs2.first == face_id ? neighs2.second : neighs2.first;
 | 
			
		||||
        neighs[2] = neighs3.first == face_id ? neighs3.second : neighs3.first;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return index;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::vector<Vec3crd> its_create_neighbors_index_9(const indexed_triangle_set &its)
 | 
			
		||||
{
 | 
			
		||||
    return create_face_neighbors_index(ex_seq, its);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::vector<Vec3i> its_create_neighbors_index_10(const indexed_triangle_set &its)
 | 
			
		||||
{
 | 
			
		||||
    return create_face_neighbors_index(ex_tbb, its);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace Slic3r
 | 
			
		||||
							
								
								
									
										19
									
								
								sandboxes/its_neighbor_index/ItsNeighborIndex.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								sandboxes/its_neighbor_index/ItsNeighborIndex.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,19 @@
 | 
			
		|||
#include <libslic3r/TriangleMesh.hpp>
 | 
			
		||||
#include "libslic3r/MeshSplitImpl.hpp"
 | 
			
		||||
 | 
			
		||||
namespace Slic3r {
 | 
			
		||||
using FaceNeighborIndex = std::vector<std::array<size_t, 3>>;
 | 
			
		||||
FaceNeighborIndex its_create_neighbors_index_1(const indexed_triangle_set &its);
 | 
			
		||||
std::vector<Vec3i> its_create_neighbors_index_2(const indexed_triangle_set &its);
 | 
			
		||||
std::vector<Vec3i> its_create_neighbors_index_3(const indexed_triangle_set &its);
 | 
			
		||||
FaceNeighborIndex its_create_neighbors_index_4(const indexed_triangle_set &its);
 | 
			
		||||
//FaceNeighborIndex its_create_neighbors_index_4(const indexed_triangle_set &its);
 | 
			
		||||
std::vector<Vec3crd> its_create_neighbors_index_5(const indexed_triangle_set &its);
 | 
			
		||||
std::vector<std::array<size_t, 3>> its_create_neighbors_index_6(const indexed_triangle_set &its);
 | 
			
		||||
std::vector<std::array<size_t, 3>> its_create_neighbors_index_7(const indexed_triangle_set &its);
 | 
			
		||||
FaceNeighborIndex its_create_neighbors_index_8(const indexed_triangle_set &its);
 | 
			
		||||
std::vector<Vec3crd> its_create_neighbors_index_9(const indexed_triangle_set &its);
 | 
			
		||||
std::vector<Vec3i> its_create_neighbors_index_10(const indexed_triangle_set &its);
 | 
			
		||||
 | 
			
		||||
std::vector<std::vector<size_t>> create_vertex_faces_index(const indexed_triangle_set &its);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										273
									
								
								sandboxes/its_neighbor_index/main.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										273
									
								
								sandboxes/its_neighbor_index/main.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,273 @@
 | 
			
		|||
#include <iostream>
 | 
			
		||||
#include <fstream>
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <tuple>
 | 
			
		||||
#include <random>
 | 
			
		||||
 | 
			
		||||
#include "ItsNeighborIndex.hpp"
 | 
			
		||||
 | 
			
		||||
#include "libnest2d/tools/benchmark.h"
 | 
			
		||||
#include "libnest2d/utils/metaloop.hpp"
 | 
			
		||||
 | 
			
		||||
namespace Slic3r {
 | 
			
		||||
 | 
			
		||||
enum { IndexCreation, Split };
 | 
			
		||||
struct MeasureResult
 | 
			
		||||
{
 | 
			
		||||
    static constexpr const char * Names[] = {
 | 
			
		||||
        "Index creation [s]",
 | 
			
		||||
        "Split [s]"
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    double measurements[std::size(Names)] = {0.};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template<class IndexCreatorFn>
 | 
			
		||||
static MeasureResult measure_index(const indexed_triangle_set &its, IndexCreatorFn fn)
 | 
			
		||||
{
 | 
			
		||||
    Benchmark b;
 | 
			
		||||
 | 
			
		||||
    MeasureResult r;
 | 
			
		||||
    for (int i = 0; i < 10; ++i) {
 | 
			
		||||
        b.start();
 | 
			
		||||
        ItsNeighborsWrapper itsn{its, fn(its)};
 | 
			
		||||
        b.stop();
 | 
			
		||||
 | 
			
		||||
        r.measurements[IndexCreation] += b.getElapsedSec();
 | 
			
		||||
 | 
			
		||||
        b.start();
 | 
			
		||||
        auto res = its_split(itsn);
 | 
			
		||||
        b.stop();
 | 
			
		||||
 | 
			
		||||
//        if (res.size() != 2 || res[0].indices.size() != res[1].indices.size() )
 | 
			
		||||
//            std::cerr << "Something is wrong, split result invalid" << std::endl;
 | 
			
		||||
 | 
			
		||||
        r.measurements[Split] += b.getElapsedSec();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    r.measurements[IndexCreation] /= 10;
 | 
			
		||||
    r.measurements[Split] /= 10;
 | 
			
		||||
 | 
			
		||||
    return r;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const auto Seed = 0;// std::random_device{}();
 | 
			
		||||
 | 
			
		||||
static indexed_triangle_set make_sphere_rnd(double radius, double detail)
 | 
			
		||||
{
 | 
			
		||||
    using namespace Slic3r;
 | 
			
		||||
 | 
			
		||||
    auto sphere = its_make_sphere(radius, detail);
 | 
			
		||||
 | 
			
		||||
    auto vfidx = create_vertex_faces_index(sphere);
 | 
			
		||||
 | 
			
		||||
    const size_t vertexnum = sphere.vertices.size();
 | 
			
		||||
    const size_t facenum   = sphere.indices.size();
 | 
			
		||||
 | 
			
		||||
    std::mt19937 rng{Seed};
 | 
			
		||||
    std::uniform_int_distribution<size_t> distv(sphere.vertices.size() / 2, sphere.vertices.size() - 1);
 | 
			
		||||
    std::uniform_int_distribution<size_t> distf(sphere.indices.size() / 2, sphere.indices.size() - 1) ;
 | 
			
		||||
 | 
			
		||||
    std::vector<bool> was(vertexnum / 2, false);
 | 
			
		||||
 | 
			
		||||
    for (size_t i = 0; i < vertexnum / 2; ++i) {
 | 
			
		||||
        size_t image = distv(rng);
 | 
			
		||||
        if (was[image - vertexnum / 2]) continue;
 | 
			
		||||
        was[image - vertexnum / 2] = true;
 | 
			
		||||
 | 
			
		||||
        std::swap(sphere.vertices[i], sphere.vertices[image]);
 | 
			
		||||
        for (size_t face_id : vfidx[i]) {
 | 
			
		||||
            for (int &vi : sphere.indices[face_id])
 | 
			
		||||
                if (vi == int(i)) vi = image;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for (size_t face_id : vfidx[image]) {
 | 
			
		||||
            for (int &vi : sphere.indices[face_id])
 | 
			
		||||
                if (vi == int(image)) vi = i;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        std::swap(vfidx[i], vfidx[image]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (size_t i = 0; i < facenum / 2; ++i) {
 | 
			
		||||
        size_t image = distf(rng);
 | 
			
		||||
        std::swap(sphere.indices[i], sphere.indices[image]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return sphere;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static indexed_triangle_set two_spheres(double detail)
 | 
			
		||||
{
 | 
			
		||||
    auto sphere1 = make_sphere_rnd(10., 2 * PI / detail), sphere2 = sphere1;
 | 
			
		||||
 | 
			
		||||
    its_transform(sphere1, identity3f().translate(Vec3f{-5.f, 0.f, 0.f}));
 | 
			
		||||
    its_transform(sphere2, identity3f().translate(Vec3f{5.f, 0.f, 0.f}));
 | 
			
		||||
 | 
			
		||||
    its_merge(sphere1, sphere2);
 | 
			
		||||
 | 
			
		||||
    return sphere1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static indexed_triangle_set make_spheres(unsigned N, double detail)
 | 
			
		||||
{
 | 
			
		||||
    indexed_triangle_set ret, sphere = make_sphere_rnd(10., 2. * PI / detail);
 | 
			
		||||
 | 
			
		||||
    for (unsigned i = 0u ; i < N; ++i)
 | 
			
		||||
        its_merge(ret, sphere);
 | 
			
		||||
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
constexpr double sq2 = std::sqrt(2.);
 | 
			
		||||
 | 
			
		||||
static const std::pair<const std::string, indexed_triangle_set> ToMeasure[] = {
 | 
			
		||||
//    {"two_spheres_1x", two_spheres(60.)},
 | 
			
		||||
//    {"two_spheres_2x", two_spheres(120.)},
 | 
			
		||||
//    {"two_spheres_4x", two_spheres(240.)},
 | 
			
		||||
//    {"two_spheres_8x", two_spheres(480.)},
 | 
			
		||||
//    {"two_spheres_16x", two_spheres(2 * 480.)},
 | 
			
		||||
//    {"two_spheres_32x", two_spheres(2 * 2 * 480.)},
 | 
			
		||||
 | 
			
		||||
    {"two_spheres_1x", two_spheres(60.)},
 | 
			
		||||
    {"two_spheres_2x", two_spheres(sq2 * 60.)},
 | 
			
		||||
    {"two_spheres_4x", two_spheres(2 * 60.)},
 | 
			
		||||
    {"two_spheres_8x", two_spheres(sq2 * 2. * 60.)},
 | 
			
		||||
    {"two_spheres_16x", two_spheres(4. * 60.)},
 | 
			
		||||
    {"two_spheres_32x", two_spheres(sq2 * 4. * 60.)},
 | 
			
		||||
    {"two_spheres_64x", two_spheres(8. * 60.)},
 | 
			
		||||
    {"two_spheres_128x", two_spheres(sq2 * 8. * 60.)},
 | 
			
		||||
    {"two_spheres_256x", two_spheres(16. * 60.)},
 | 
			
		||||
    {"two_spheres_512x", two_spheres(sq2 * 16. * 60.)},
 | 
			
		||||
 | 
			
		||||
    {"2_spheres", make_spheres(2, 60.)},
 | 
			
		||||
    {"4_spheres", make_spheres(4, 60.)},
 | 
			
		||||
    {"8_spheres", make_spheres(8, 60.)},
 | 
			
		||||
    {"16_spheres", make_spheres(16,  60.)},
 | 
			
		||||
    {"32_spheres", make_spheres(32, 60.)},
 | 
			
		||||
    {"64_spheres", make_spheres(64, 60.)},
 | 
			
		||||
    {"128_spheres", make_spheres(128, 60.)},
 | 
			
		||||
    {"256_spheres", make_spheres(256, 60.)},
 | 
			
		||||
    {"512_spheres", make_spheres(512, 60.)},
 | 
			
		||||
    {"1024_spheres", make_spheres(1024, 60.)}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const auto IndexFunctions = std::make_tuple(
 | 
			
		||||
    std::make_pair("tamas's unordered_map based", [](const auto &its) { return measure_index(its, its_create_neighbors_index_1); }),
 | 
			
		||||
    std::make_pair("vojta std::sort based", [](const auto &its) { return measure_index(its, its_create_neighbors_index_2); }),
 | 
			
		||||
    std::make_pair("vojta tbb::parallel_sort based", [](const auto &its) { return measure_index(its, its_create_neighbors_index_3); }),
 | 
			
		||||
    std::make_pair("filip's vertex->face based", [](const auto &its) { return measure_index(its, its_create_neighbors_index_5); }),
 | 
			
		||||
    std::make_pair("vojta's vertex->face", [](const auto &its) { return measure_index(its, its_create_neighbors_index_9); }),
 | 
			
		||||
    std::make_pair("vojta's vertex->face parallel", [](const auto &its) { return measure_index(its, its_create_neighbors_index_10); }),
 | 
			
		||||
    std::make_pair("tamas's std::sort based", [](const auto &its) { return measure_index(its, its_create_neighbors_index_6); }),
 | 
			
		||||
    std::make_pair("tamas's tbb::parallel_sort based", [](const auto &its) { return measure_index(its, its_create_neighbors_index_7); }),
 | 
			
		||||
    std::make_pair("tamas's map based", [](const auto &its) { return measure_index(its, its_create_neighbors_index_8); }),
 | 
			
		||||
    std::make_pair("TriangleMesh split", [](const auto &its) {
 | 
			
		||||
 | 
			
		||||
        MeasureResult r;
 | 
			
		||||
        for (int i = 0; i < 10; ++i) {
 | 
			
		||||
            TriangleMesh m{its};
 | 
			
		||||
            Benchmark b;
 | 
			
		||||
 | 
			
		||||
            b.start();
 | 
			
		||||
            m.repair(); // FIXME: this does more than just create neighborhood map
 | 
			
		||||
            b.stop();
 | 
			
		||||
            r.measurements[IndexCreation] += b.getElapsedSec();
 | 
			
		||||
 | 
			
		||||
            b.start();
 | 
			
		||||
            auto res = m.split();
 | 
			
		||||
            b.stop();
 | 
			
		||||
            r.measurements[Split] += b.getElapsedSec();
 | 
			
		||||
 | 
			
		||||
//            if (res.size() != 2 || res[0]->size() != res[1]->size())
 | 
			
		||||
//                std::cerr << "Something is wrong, split result invalid" << std::endl;
 | 
			
		||||
        }
 | 
			
		||||
        r.measurements[IndexCreation] /= 10;
 | 
			
		||||
        r.measurements[Split] /= 10;
 | 
			
		||||
 | 
			
		||||
        return r;
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
//    std::make_pair("Vojta's vertex->face index", [](const auto &its){
 | 
			
		||||
//        Benchmark b;
 | 
			
		||||
//        b.start();
 | 
			
		||||
//        auto index = create_vertex_faces_index(its);
 | 
			
		||||
//        b.stop();
 | 
			
		||||
 | 
			
		||||
//        if (index.size() != its.vertices.size())
 | 
			
		||||
//            std::cerr << "Something went wrong!";
 | 
			
		||||
 | 
			
		||||
//        return  MeasureResult{b.getElapsedSec(), 0., 0.};
 | 
			
		||||
//    }),
 | 
			
		||||
//    std::make_pair("Tamas's vertex->face index", [](const auto &its){
 | 
			
		||||
//        Benchmark b;
 | 
			
		||||
//        b.start();
 | 
			
		||||
//        VertexFaceIndex index{its};
 | 
			
		||||
//        b.stop();
 | 
			
		||||
 | 
			
		||||
//        if (index.size() < its.vertices.size())
 | 
			
		||||
//            std::cerr << "Something went wrong!";
 | 
			
		||||
 | 
			
		||||
//        return  MeasureResult{b.getElapsedSec(), 0., 0.};
 | 
			
		||||
//    })
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
static constexpr size_t IndexFuncNum = std::tuple_size_v<decltype (IndexFunctions)>;
 | 
			
		||||
 | 
			
		||||
} // namespace Slic3r
 | 
			
		||||
 | 
			
		||||
int main(const int argc, const char * argv[])
 | 
			
		||||
{
 | 
			
		||||
    using namespace Slic3r;
 | 
			
		||||
 | 
			
		||||
    std::array<MeasureResult, IndexFuncNum> results[std::size(ToMeasure)];
 | 
			
		||||
    std::array<std::string, IndexFuncNum> funcnames;
 | 
			
		||||
 | 
			
		||||
    for (size_t i = 0; i < std::size(ToMeasure); ++i) {
 | 
			
		||||
        auto &m = ToMeasure[i];
 | 
			
		||||
        auto &name = m.first;
 | 
			
		||||
        auto &mesh = m.second;
 | 
			
		||||
 | 
			
		||||
//        its_write_obj(mesh, (std::string(name) + ".obj").c_str());
 | 
			
		||||
 | 
			
		||||
        std::cout << "Mesh " << name << " has " << mesh.indices.size() << " faces and " << mesh.vertices.size() << " vertices." << std::endl;
 | 
			
		||||
        libnest2d::opt::metaloop::apply([&mesh, i, &results, &funcnames](int N, auto &e) {
 | 
			
		||||
            MeasureResult r = e.second(mesh);
 | 
			
		||||
            funcnames[N] = e.first;
 | 
			
		||||
            results[i][N] = r;
 | 
			
		||||
        }, IndexFunctions);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::string outfilename = "out.csv";
 | 
			
		||||
    std::fstream outfile;
 | 
			
		||||
    if (argc > 1) {
 | 
			
		||||
        outfilename = argv[1];
 | 
			
		||||
        outfile.open(outfilename, std::fstream::out);
 | 
			
		||||
        std::cout << outfilename << " will be used" << std::endl;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::ostream &out = outfile.is_open() ? outfile : std::cout;
 | 
			
		||||
 | 
			
		||||
    for (size_t m = 0; m < std::size(MeasureResult::Names); ++m) {
 | 
			
		||||
        out << MeasureResult::Names[m] << "\n";
 | 
			
		||||
        out << std::endl;
 | 
			
		||||
        out << "model;" ;
 | 
			
		||||
        for (const std::string &funcname : funcnames) {
 | 
			
		||||
            out << funcname << ";";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        out << std::endl;
 | 
			
		||||
 | 
			
		||||
        for (size_t i = 0; i < std::size(ToMeasure); ++i) {
 | 
			
		||||
            const auto &result_row = results[i];
 | 
			
		||||
            const std::string &name = ToMeasure[i].first;
 | 
			
		||||
            out << name << ";";
 | 
			
		||||
            for (auto &r : result_row)
 | 
			
		||||
                out << r.measurements[m] << ";";
 | 
			
		||||
 | 
			
		||||
            out << std::endl;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										7
									
								
								sandboxes/meshboolean/CMakeLists.txt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								sandboxes/meshboolean/CMakeLists.txt
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,7 @@
 | 
			
		|||
add_executable(meshboolean MeshBoolean.cpp)
 | 
			
		||||
 | 
			
		||||
target_link_libraries(meshboolean libslic3r)
 | 
			
		||||
 | 
			
		||||
if (WIN32)
 | 
			
		||||
    prusaslicer_copy_dlls(meshboolean)
 | 
			
		||||
endif()
 | 
			
		||||
							
								
								
									
										43
									
								
								sandboxes/meshboolean/MeshBoolean.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								sandboxes/meshboolean/MeshBoolean.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,43 @@
 | 
			
		|||
#include <iostream>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
#include <libslic3r/TriangleMesh.hpp>
 | 
			
		||||
#include <libslic3r/Model.hpp>
 | 
			
		||||
#include <libslic3r/SLAPrint.hpp>
 | 
			
		||||
#include <libslic3r/SLAPrintSteps.hpp>
 | 
			
		||||
#include <libslic3r/MeshBoolean.hpp>
 | 
			
		||||
 | 
			
		||||
#include <libnest2d/tools/benchmark.h>
 | 
			
		||||
 | 
			
		||||
#include <boost/log/trivial.hpp>
 | 
			
		||||
 | 
			
		||||
int main(const int argc, const char * argv[])
 | 
			
		||||
{
 | 
			
		||||
    using namespace Slic3r;
 | 
			
		||||
    
 | 
			
		||||
    if (argc <= 1) {
 | 
			
		||||
        std::cout << "Usage: meshboolean <input_file.3mf>" << std::endl;
 | 
			
		||||
        return EXIT_FAILURE;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    
 | 
			
		||||
    TriangleMesh input;
 | 
			
		||||
    
 | 
			
		||||
    input.ReadSTLFile(argv[1]);
 | 
			
		||||
    
 | 
			
		||||
    Benchmark bench;
 | 
			
		||||
    
 | 
			
		||||
    bench.start();
 | 
			
		||||
    bool fckd = MeshBoolean::cgal::does_self_intersect(input);
 | 
			
		||||
    bench.stop();
 | 
			
		||||
    
 | 
			
		||||
    std::cout << "Self intersect test: " << fckd << " duration: " << bench.getElapsedSec() << std::endl;
 | 
			
		||||
    
 | 
			
		||||
    bench.start();
 | 
			
		||||
    MeshBoolean::self_union(input);
 | 
			
		||||
    bench.stop();
 | 
			
		||||
    
 | 
			
		||||
    std::cout << "Self union duration: " << bench.getElapsedSec() << std::endl;
 | 
			
		||||
    
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										30
									
								
								sandboxes/opencsg/CMakeLists.txt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								sandboxes/opencsg/CMakeLists.txt
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,30 @@
 | 
			
		|||
cmake_minimum_required(VERSION 3.0)
 | 
			
		||||
 | 
			
		||||
project(OpenCSG-example)
 | 
			
		||||
 | 
			
		||||
add_executable(opencsg_example WIN32 
 | 
			
		||||
    main.cpp 
 | 
			
		||||
    Engine.hpp Engine.cpp 
 | 
			
		||||
    ShaderCSGDisplay.hpp ShaderCSGDisplay.cpp
 | 
			
		||||
    ${CMAKE_CURRENT_SOURCE_DIR}/../../src/slic3r/GUI/Jobs/Job.cpp
 | 
			
		||||
    ${CMAKE_CURRENT_SOURCE_DIR}/../../src/slic3r/GUI/ProgressStatusBar.cpp
 | 
			
		||||
    ${CMAKE_CURRENT_SOURCE_DIR}/../../src/slic3r/GUI/I18N.hpp
 | 
			
		||||
    ${CMAKE_CURRENT_SOURCE_DIR}/../../src/slic3r/GUI/I18N.cpp)
 | 
			
		||||
 | 
			
		||||
find_package(wxWidgets 3.1 REQUIRED COMPONENTS core base gl html)
 | 
			
		||||
find_package(OpenGL REQUIRED)
 | 
			
		||||
find_package(GLEW REQUIRED)
 | 
			
		||||
find_package(OpenCSG REQUIRED)
 | 
			
		||||
include(${wxWidgets_USE_FILE})
 | 
			
		||||
 | 
			
		||||
target_link_libraries(opencsg_example libslic3r)
 | 
			
		||||
target_include_directories(opencsg_example PRIVATE ${wxWidgets_INCLUDE_DIRS})
 | 
			
		||||
target_compile_definitions(opencsg_example PRIVATE ${wxWidgets_DEFINITIONS})
 | 
			
		||||
 | 
			
		||||
slic3r_remap_configs(OpenCSG::opencsg RelWithDebInfo Release)
 | 
			
		||||
target_link_libraries(opencsg_example ${wxWidgets_LIBRARIES} 
 | 
			
		||||
    OpenCSG::opencsg 
 | 
			
		||||
    GLEW::GLEW
 | 
			
		||||
    OpenGL::GL 
 | 
			
		||||
    #-lXrandr -lXext -lX11
 | 
			
		||||
    )
 | 
			
		||||
							
								
								
									
										495
									
								
								sandboxes/opencsg/Engine.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										495
									
								
								sandboxes/opencsg/Engine.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,495 @@
 | 
			
		|||
#include "Engine.hpp"
 | 
			
		||||
#include <libslic3r/Utils.hpp>
 | 
			
		||||
#include <libslic3r/SLAPrint.hpp>
 | 
			
		||||
 | 
			
		||||
#include <GL/glew.h>
 | 
			
		||||
 | 
			
		||||
#include <boost/log/trivial.hpp>
 | 
			
		||||
 | 
			
		||||
#ifndef NDEBUG
 | 
			
		||||
#define HAS_GLSAFE
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef HAS_GLSAFE
 | 
			
		||||
extern void glAssertRecentCallImpl(const char *file_name, unsigned int line, const char *function_name);
 | 
			
		||||
inline void glAssertRecentCall() { glAssertRecentCallImpl(__FILE__, __LINE__, __FUNCTION__); }
 | 
			
		||||
#define glsafe(cmd) do { cmd; glAssertRecentCallImpl(__FILE__, __LINE__, __FUNCTION__); } while (false)
 | 
			
		||||
#define glcheck() do { glAssertRecentCallImpl(__FILE__, __LINE__, __FUNCTION__); } while (false)
 | 
			
		||||
 | 
			
		||||
void glAssertRecentCallImpl(const char *file_name, unsigned int line, const char *function_name)
 | 
			
		||||
{
 | 
			
		||||
    GLenum err = glGetError();
 | 
			
		||||
    if (err == GL_NO_ERROR)
 | 
			
		||||
        return;
 | 
			
		||||
    const char *sErr = 0;
 | 
			
		||||
    switch (err) {
 | 
			
		||||
    case GL_INVALID_ENUM:       sErr = "Invalid Enum";      break;
 | 
			
		||||
    case GL_INVALID_VALUE:      sErr = "Invalid Value";     break;
 | 
			
		||||
    // be aware that GL_INVALID_OPERATION is generated if glGetError is executed between the execution of glBegin and the corresponding execution of glEnd 
 | 
			
		||||
    case GL_INVALID_OPERATION:  sErr = "Invalid Operation"; break;
 | 
			
		||||
    case GL_STACK_OVERFLOW:     sErr = "Stack Overflow";    break;
 | 
			
		||||
    case GL_STACK_UNDERFLOW:    sErr = "Stack Underflow";   break;
 | 
			
		||||
    case GL_OUT_OF_MEMORY:      sErr = "Out Of Memory";     break;
 | 
			
		||||
    default:                    sErr = "Unknown";           break;
 | 
			
		||||
    }
 | 
			
		||||
    BOOST_LOG_TRIVIAL(error) << "OpenGL error in " << file_name << ":" << line << ", function " << function_name << "() : " << (int)err << " - " << sErr;
 | 
			
		||||
    assert(false);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#else
 | 
			
		||||
inline void glAssertRecentCall() { }
 | 
			
		||||
#define glsafe(cmd) cmd
 | 
			
		||||
#define glcheck()
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
namespace Slic3r { namespace GL {
 | 
			
		||||
 | 
			
		||||
Scene::Scene() = default;
 | 
			
		||||
Scene::~Scene() = default;
 | 
			
		||||
 | 
			
		||||
void CSGDisplay::render_scene()
 | 
			
		||||
{
 | 
			
		||||
    GLfloat color[] = {1.f, 1.f, 0.f, 0.f};
 | 
			
		||||
    glsafe(::glColor4fv(color));
 | 
			
		||||
    
 | 
			
		||||
    if (m_csgsettings.is_enabled()) {
 | 
			
		||||
        OpenCSG::render(m_scene_cache.primitives_csg);
 | 
			
		||||
        glDepthFunc(GL_EQUAL);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    for (auto& p : m_scene_cache.primitives_csg) p->render();
 | 
			
		||||
    if (m_csgsettings.is_enabled()) glDepthFunc(GL_LESS);
 | 
			
		||||
    
 | 
			
		||||
    for (auto& p : m_scene_cache.primitives_free) p->render();
 | 
			
		||||
    
 | 
			
		||||
    glFlush();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Scene::set_print(std::unique_ptr<SLAPrint> &&print)
 | 
			
		||||
{   
 | 
			
		||||
    m_print = std::move(print);
 | 
			
		||||
        
 | 
			
		||||
    // Notify displays
 | 
			
		||||
    call(&Listener::on_scene_updated, m_listeners, *this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
BoundingBoxf3 Scene::get_bounding_box() const
 | 
			
		||||
{
 | 
			
		||||
    return m_print->model().bounding_box();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void CSGDisplay::SceneCache::clear()
 | 
			
		||||
{
 | 
			
		||||
    primitives_csg.clear();
 | 
			
		||||
    primitives_free.clear();
 | 
			
		||||
    primitives.clear();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::shared_ptr<Primitive> CSGDisplay::SceneCache::add_mesh(const TriangleMesh &mesh)
 | 
			
		||||
{
 | 
			
		||||
    auto p = std::make_shared<Primitive>();
 | 
			
		||||
    p->load_mesh(mesh);
 | 
			
		||||
    primitives.emplace_back(p);
 | 
			
		||||
    primitives_free.emplace_back(p.get());
 | 
			
		||||
    return p;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::shared_ptr<Primitive> CSGDisplay::SceneCache::add_mesh(const TriangleMesh &mesh,
 | 
			
		||||
                                                   OpenCSG::Operation  o,
 | 
			
		||||
                                                   unsigned            c)
 | 
			
		||||
{
 | 
			
		||||
    auto p = std::make_shared<Primitive>(o, c);
 | 
			
		||||
    p->load_mesh(mesh);
 | 
			
		||||
    primitives.emplace_back(p);
 | 
			
		||||
    primitives_csg.emplace_back(p.get());
 | 
			
		||||
    return p;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void IndexedVertexArray::push_geometry(float x, float y, float z, float nx, float ny, float nz)
 | 
			
		||||
{
 | 
			
		||||
    assert(this->vertices_and_normals_interleaved_VBO_id == 0);
 | 
			
		||||
    if (this->vertices_and_normals_interleaved_VBO_id != 0)
 | 
			
		||||
        return;
 | 
			
		||||
    
 | 
			
		||||
    if (this->vertices_and_normals_interleaved.size() + 6 > this->vertices_and_normals_interleaved.capacity())
 | 
			
		||||
        this->vertices_and_normals_interleaved.reserve(next_highest_power_of_2(this->vertices_and_normals_interleaved.size() + 6));
 | 
			
		||||
    this->vertices_and_normals_interleaved.emplace_back(nx);
 | 
			
		||||
    this->vertices_and_normals_interleaved.emplace_back(ny);
 | 
			
		||||
    this->vertices_and_normals_interleaved.emplace_back(nz);
 | 
			
		||||
    this->vertices_and_normals_interleaved.emplace_back(x);
 | 
			
		||||
    this->vertices_and_normals_interleaved.emplace_back(y);
 | 
			
		||||
    this->vertices_and_normals_interleaved.emplace_back(z);
 | 
			
		||||
    
 | 
			
		||||
    this->vertices_and_normals_interleaved_size = this->vertices_and_normals_interleaved.size();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void IndexedVertexArray::push_triangle(int idx1, int idx2, int idx3) {
 | 
			
		||||
    assert(this->vertices_and_normals_interleaved_VBO_id == 0);
 | 
			
		||||
    if (this->vertices_and_normals_interleaved_VBO_id != 0)
 | 
			
		||||
        return;
 | 
			
		||||
    
 | 
			
		||||
    if (this->triangle_indices.size() + 3 > this->vertices_and_normals_interleaved.capacity())
 | 
			
		||||
        this->triangle_indices.reserve(next_highest_power_of_2(this->triangle_indices.size() + 3));
 | 
			
		||||
    this->triangle_indices.emplace_back(idx1);
 | 
			
		||||
    this->triangle_indices.emplace_back(idx2);
 | 
			
		||||
    this->triangle_indices.emplace_back(idx3);
 | 
			
		||||
    this->triangle_indices_size = this->triangle_indices.size();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void IndexedVertexArray::load_mesh(const TriangleMesh &mesh)
 | 
			
		||||
{
 | 
			
		||||
    assert(triangle_indices.empty() && vertices_and_normals_interleaved_size == 0);
 | 
			
		||||
    assert(quad_indices.empty() && triangle_indices_size == 0);
 | 
			
		||||
    assert(vertices_and_normals_interleaved.size() % 6 == 0 && quad_indices_size == vertices_and_normals_interleaved.size());
 | 
			
		||||
    
 | 
			
		||||
    this->vertices_and_normals_interleaved.reserve(this->vertices_and_normals_interleaved.size() + 3 * 3 * 2 * mesh.facets_count());
 | 
			
		||||
    
 | 
			
		||||
    int vertices_count = 0;
 | 
			
		||||
    for (size_t i = 0; i < mesh.facets_count(); ++i) {
 | 
			
		||||
        const stl_facet &facet = mesh.stl.facet_start[i];
 | 
			
		||||
        for (int j = 0; j < 3; ++j)
 | 
			
		||||
            this->push_geometry(facet.vertex[j](0), facet.vertex[j](1), facet.vertex[j](2), facet.normal(0), facet.normal(1), facet.normal(2));
 | 
			
		||||
                
 | 
			
		||||
                this->push_triangle(vertices_count, vertices_count + 1, vertices_count + 2);
 | 
			
		||||
        vertices_count += 3;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void IndexedVertexArray::finalize_geometry()
 | 
			
		||||
{
 | 
			
		||||
    assert(this->vertices_and_normals_interleaved_VBO_id == 0);
 | 
			
		||||
    assert(this->triangle_indices_VBO_id == 0);
 | 
			
		||||
    assert(this->quad_indices_VBO_id == 0);
 | 
			
		||||
 | 
			
		||||
    if (!this->vertices_and_normals_interleaved.empty()) {
 | 
			
		||||
        glsafe(
 | 
			
		||||
            ::glGenBuffers(1, &this->vertices_and_normals_interleaved_VBO_id));
 | 
			
		||||
        glsafe(::glBindBuffer(GL_ARRAY_BUFFER,
 | 
			
		||||
                              this->vertices_and_normals_interleaved_VBO_id));
 | 
			
		||||
        glsafe(
 | 
			
		||||
            ::glBufferData(GL_ARRAY_BUFFER,
 | 
			
		||||
                           GLsizeiptr(
 | 
			
		||||
                               this->vertices_and_normals_interleaved.size() *
 | 
			
		||||
                               4),
 | 
			
		||||
                           this->vertices_and_normals_interleaved.data(),
 | 
			
		||||
                           GL_STATIC_DRAW));
 | 
			
		||||
        glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
 | 
			
		||||
        this->vertices_and_normals_interleaved.clear();
 | 
			
		||||
    }
 | 
			
		||||
    if (!this->triangle_indices.empty()) {
 | 
			
		||||
        glsafe(::glGenBuffers(1, &this->triangle_indices_VBO_id));
 | 
			
		||||
        glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,
 | 
			
		||||
                              this->triangle_indices_VBO_id));
 | 
			
		||||
        glsafe(::glBufferData(GL_ELEMENT_ARRAY_BUFFER,
 | 
			
		||||
                              GLsizeiptr(this->triangle_indices.size() * 4),
 | 
			
		||||
                              this->triangle_indices.data(), GL_STATIC_DRAW));
 | 
			
		||||
        glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
 | 
			
		||||
        this->triangle_indices.clear();
 | 
			
		||||
    }
 | 
			
		||||
    if (!this->quad_indices.empty()) {
 | 
			
		||||
        glsafe(::glGenBuffers(1, &this->quad_indices_VBO_id));
 | 
			
		||||
        glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,
 | 
			
		||||
                              this->quad_indices_VBO_id));
 | 
			
		||||
        glsafe(::glBufferData(GL_ELEMENT_ARRAY_BUFFER,
 | 
			
		||||
                              GLsizeiptr(this->quad_indices.size() * 4),
 | 
			
		||||
                              this->quad_indices.data(), GL_STATIC_DRAW));
 | 
			
		||||
        glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
 | 
			
		||||
        this->quad_indices.clear();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void IndexedVertexArray::release_geometry()
 | 
			
		||||
{
 | 
			
		||||
    if (this->vertices_and_normals_interleaved_VBO_id) {
 | 
			
		||||
        glsafe(
 | 
			
		||||
            ::glDeleteBuffers(1,
 | 
			
		||||
                              &this->vertices_and_normals_interleaved_VBO_id));
 | 
			
		||||
        this->vertices_and_normals_interleaved_VBO_id = 0;
 | 
			
		||||
    }
 | 
			
		||||
    if (this->triangle_indices_VBO_id) {
 | 
			
		||||
        glsafe(::glDeleteBuffers(1, &this->triangle_indices_VBO_id));
 | 
			
		||||
        this->triangle_indices_VBO_id = 0;
 | 
			
		||||
    }
 | 
			
		||||
    if (this->quad_indices_VBO_id) {
 | 
			
		||||
        glsafe(::glDeleteBuffers(1, &this->quad_indices_VBO_id));
 | 
			
		||||
        this->quad_indices_VBO_id = 0;
 | 
			
		||||
    }
 | 
			
		||||
    this->clear();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void IndexedVertexArray::render() const
 | 
			
		||||
{
 | 
			
		||||
    assert(this->vertices_and_normals_interleaved_VBO_id != 0);
 | 
			
		||||
    assert(this->triangle_indices_VBO_id != 0 ||
 | 
			
		||||
           this->quad_indices_VBO_id != 0);
 | 
			
		||||
 | 
			
		||||
    glsafe(::glBindBuffer(GL_ARRAY_BUFFER,
 | 
			
		||||
                          this->vertices_and_normals_interleaved_VBO_id));
 | 
			
		||||
    glsafe(::glVertexPointer(3, GL_FLOAT, 6 * sizeof(float),
 | 
			
		||||
                             reinterpret_cast<const void *>(3 * sizeof(float))));
 | 
			
		||||
    glsafe(::glNormalPointer(GL_FLOAT, 6 * sizeof(float), nullptr));
 | 
			
		||||
 | 
			
		||||
    glsafe(::glEnableClientState(GL_VERTEX_ARRAY));
 | 
			
		||||
    glsafe(::glEnableClientState(GL_NORMAL_ARRAY));
 | 
			
		||||
 | 
			
		||||
    // Render using the Vertex Buffer Objects.
 | 
			
		||||
    if (this->triangle_indices_size > 0) {
 | 
			
		||||
        glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,
 | 
			
		||||
                              this->triangle_indices_VBO_id));
 | 
			
		||||
        glsafe(::glDrawElements(GL_TRIANGLES,
 | 
			
		||||
                                GLsizei(this->triangle_indices_size),
 | 
			
		||||
                                GL_UNSIGNED_INT, nullptr));
 | 
			
		||||
        glsafe(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
 | 
			
		||||
    }
 | 
			
		||||
    if (this->quad_indices_size > 0) {
 | 
			
		||||
        glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,
 | 
			
		||||
                              this->quad_indices_VBO_id));
 | 
			
		||||
        glsafe(::glDrawElements(GL_QUADS, GLsizei(this->quad_indices_size),
 | 
			
		||||
                                GL_UNSIGNED_INT, nullptr));
 | 
			
		||||
        glsafe(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    glsafe(::glDisableClientState(GL_VERTEX_ARRAY));
 | 
			
		||||
    glsafe(::glDisableClientState(GL_NORMAL_ARRAY));
 | 
			
		||||
 | 
			
		||||
    glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void IndexedVertexArray::clear() {
 | 
			
		||||
    this->vertices_and_normals_interleaved.clear();
 | 
			
		||||
    this->triangle_indices.clear();
 | 
			
		||||
    this->quad_indices.clear();
 | 
			
		||||
    vertices_and_normals_interleaved_size = 0;
 | 
			
		||||
    triangle_indices_size = 0;
 | 
			
		||||
    quad_indices_size = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void IndexedVertexArray::shrink_to_fit() {
 | 
			
		||||
    this->vertices_and_normals_interleaved.shrink_to_fit();
 | 
			
		||||
    this->triangle_indices.shrink_to_fit();
 | 
			
		||||
    this->quad_indices.shrink_to_fit();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Volume::render()
 | 
			
		||||
{
 | 
			
		||||
    glsafe(::glPushMatrix());
 | 
			
		||||
    glsafe(::glMultMatrixd(m_trafo.get_matrix().data()));
 | 
			
		||||
    m_geom.render();
 | 
			
		||||
    glsafe(::glPopMatrix());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Display::clear_screen()
 | 
			
		||||
{
 | 
			
		||||
    glViewport(0, 0, GLsizei(m_size.x()), GLsizei(m_size.y()));
 | 
			
		||||
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Display::~Display()
 | 
			
		||||
{
 | 
			
		||||
    OpenCSG::freeResources();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Display::set_active(long width, long height)
 | 
			
		||||
{   
 | 
			
		||||
    if (!m_initialized) {
 | 
			
		||||
        glewInit();
 | 
			
		||||
        m_initialized = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // gray background
 | 
			
		||||
    glClearColor(0.9f, 0.9f, 0.9f, 1.0f);
 | 
			
		||||
 | 
			
		||||
    // Enable two OpenGL lights
 | 
			
		||||
    GLfloat light_diffuse[]   = { 1.0f,  1.0f,  0.0f,  1.0f};  // White diffuse light
 | 
			
		||||
    GLfloat light_position0[] = {-1.0f, -1.0f, -1.0f,  0.0f};  // Infinite light location
 | 
			
		||||
    GLfloat light_position1[] = { 1.0f,  1.0f,  1.0f,  0.0f};  // Infinite light location
 | 
			
		||||
    
 | 
			
		||||
    glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
 | 
			
		||||
    glLightfv(GL_LIGHT0, GL_POSITION, light_position0);
 | 
			
		||||
    glEnable(GL_LIGHT0);  
 | 
			
		||||
    glLightfv(GL_LIGHT1, GL_DIFFUSE, light_diffuse);
 | 
			
		||||
    glLightfv(GL_LIGHT1, GL_POSITION, light_position1);
 | 
			
		||||
    glEnable(GL_LIGHT1);
 | 
			
		||||
    glEnable(GL_LIGHTING);
 | 
			
		||||
    glEnable(GL_NORMALIZE);
 | 
			
		||||
    
 | 
			
		||||
    // Use depth buffering for hidden surface elimination
 | 
			
		||||
    glEnable(GL_DEPTH_TEST);
 | 
			
		||||
    glEnable(GL_STENCIL_TEST);
 | 
			
		||||
    
 | 
			
		||||
    set_screen_size(width, height);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Display::set_screen_size(long width, long height)
 | 
			
		||||
{
 | 
			
		||||
    if (m_size.x() != width || m_size.y() != height)
 | 
			
		||||
        m_camera->set_screen(width, height);
 | 
			
		||||
    
 | 
			
		||||
    m_size = {width, height};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Display::repaint()
 | 
			
		||||
{
 | 
			
		||||
    clear_screen();
 | 
			
		||||
    
 | 
			
		||||
    m_camera->view();
 | 
			
		||||
    render_scene();
 | 
			
		||||
    
 | 
			
		||||
    m_fps_counter.update();
 | 
			
		||||
    
 | 
			
		||||
    swap_buffers();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Controller::on_scene_updated(const Scene &scene)
 | 
			
		||||
{
 | 
			
		||||
    const SLAPrint *print = scene.get_print();
 | 
			
		||||
    if (!print) return;
 | 
			
		||||
    
 | 
			
		||||
    auto bb = scene.get_bounding_box();
 | 
			
		||||
    double d = std::max(std::max(bb.size().x(), bb.size().y()), bb.size().z());
 | 
			
		||||
    m_wheel_pos = long(2 * d);
 | 
			
		||||
    
 | 
			
		||||
    call_cameras(&Camera::set_zoom, m_wheel_pos);
 | 
			
		||||
    call(&Display::on_scene_updated, m_displays, scene);    
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Controller::on_scroll(long v, long d, MouseInput::WheelAxis /*wa*/)
 | 
			
		||||
{
 | 
			
		||||
    m_wheel_pos += v / d;
 | 
			
		||||
    
 | 
			
		||||
    call_cameras(&Camera::set_zoom, m_wheel_pos);
 | 
			
		||||
    call(&Display::repaint, m_displays);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Controller::on_moved_to(long x, long y)
 | 
			
		||||
{
 | 
			
		||||
    if (m_left_btn) {
 | 
			
		||||
        call_cameras(&Camera::rotate, (Vec2i{x, y} - m_mouse_pos).cast<float>());
 | 
			
		||||
        call(&Display::repaint, m_displays);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    m_mouse_pos = {x, y};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void CSGDisplay::apply_csgsettings(const CSGSettings &settings)
 | 
			
		||||
{
 | 
			
		||||
    using namespace OpenCSG;
 | 
			
		||||
    
 | 
			
		||||
    bool needupdate = m_csgsettings.get_convexity() != settings.get_convexity();
 | 
			
		||||
    
 | 
			
		||||
    m_csgsettings = settings;
 | 
			
		||||
    setOption(AlgorithmSetting, m_csgsettings.get_algo());
 | 
			
		||||
    setOption(DepthComplexitySetting, m_csgsettings.get_depth_algo());
 | 
			
		||||
    setOption(DepthBoundsOptimization, m_csgsettings.get_optimization());
 | 
			
		||||
    
 | 
			
		||||
    if (needupdate) {
 | 
			
		||||
        for (OpenCSG::Primitive * p : m_scene_cache.primitives_csg)
 | 
			
		||||
            if (p->getConvexity() > 1)
 | 
			
		||||
                p->setConvexity(m_csgsettings.get_convexity());
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void CSGDisplay::on_scene_updated(const Scene &scene)
 | 
			
		||||
{
 | 
			
		||||
    const SLAPrint *print = scene.get_print();
 | 
			
		||||
    if (!print) return;
 | 
			
		||||
    
 | 
			
		||||
    m_scene_cache.clear();
 | 
			
		||||
    
 | 
			
		||||
    for (const SLAPrintObject *po : print->objects()) {
 | 
			
		||||
        const ModelObject *mo = po->model_object();
 | 
			
		||||
        TriangleMesh msh = mo->raw_mesh();
 | 
			
		||||
        
 | 
			
		||||
        sla::DrainHoles holedata = mo->sla_drain_holes;
 | 
			
		||||
        
 | 
			
		||||
        for (const ModelInstance *mi : mo->instances) {
 | 
			
		||||
            
 | 
			
		||||
            TriangleMesh mshinst = msh;
 | 
			
		||||
            auto interior = po->hollowed_interior_mesh();
 | 
			
		||||
            interior.transform(po->trafo().inverse());
 | 
			
		||||
            
 | 
			
		||||
            mshinst.merge(interior);
 | 
			
		||||
            
 | 
			
		||||
            mi->transform_mesh(&mshinst);
 | 
			
		||||
            
 | 
			
		||||
            auto bb = mshinst.bounding_box();
 | 
			
		||||
            auto center = bb.center().cast<float>();
 | 
			
		||||
            mshinst.translate(-center);
 | 
			
		||||
            
 | 
			
		||||
            m_scene_cache.add_mesh(mshinst, OpenCSG::Intersection,
 | 
			
		||||
                                   m_csgsettings.get_convexity());
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        for (const sla::DrainHole &holept : holedata) {
 | 
			
		||||
            TriangleMesh holemesh = sla::to_triangle_mesh(holept.to_mesh());
 | 
			
		||||
            m_scene_cache.add_mesh(holemesh, OpenCSG::Subtraction, 1);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    repaint();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Camera::view()
 | 
			
		||||
{
 | 
			
		||||
    glMatrixMode(GL_MODELVIEW);
 | 
			
		||||
    glLoadIdentity();
 | 
			
		||||
    gluLookAt(0.0, m_zoom, 0.0,  /* eye is at (0,zoom,0) */
 | 
			
		||||
              m_referene.x(), m_referene.y(), m_referene.z(),
 | 
			
		||||
              0.0, 0.0, 1.0); /* up is in positive Y direction */
 | 
			
		||||
    
 | 
			
		||||
    // TODO Could have been set in prevoius gluLookAt in first argument
 | 
			
		||||
    glRotatef(m_rot.y(), 1.0, 0.0, 0.0);
 | 
			
		||||
    glRotatef(m_rot.x(), 0.0, 0.0, 1.0);
 | 
			
		||||
    
 | 
			
		||||
    if (m_clip_z > 0.) {
 | 
			
		||||
        GLdouble plane[] = {0., 0., 1., m_clip_z};
 | 
			
		||||
        glClipPlane(GL_CLIP_PLANE0, plane);
 | 
			
		||||
        glEnable(GL_CLIP_PLANE0);
 | 
			
		||||
    } else {
 | 
			
		||||
        glDisable(GL_CLIP_PLANE0);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void PerspectiveCamera::set_screen(long width, long height)
 | 
			
		||||
{
 | 
			
		||||
    // Setup the view of the CSG shape
 | 
			
		||||
    glMatrixMode(GL_PROJECTION);
 | 
			
		||||
    glLoadIdentity();
 | 
			
		||||
    gluPerspective(45.0, width / double(height), .1, 200.0);
 | 
			
		||||
    glMatrixMode(GL_MODELVIEW);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool enable_multisampling(bool e)
 | 
			
		||||
{
 | 
			
		||||
    if (!e) { glDisable(GL_MULTISAMPLE); return false; }
 | 
			
		||||
    
 | 
			
		||||
    GLint is_ms_context;
 | 
			
		||||
    glGetIntegerv(GL_SAMPLE_BUFFERS, &is_ms_context);
 | 
			
		||||
    
 | 
			
		||||
    if (is_ms_context) { glEnable(GL_MULTISAMPLE); return true; }
 | 
			
		||||
    else return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
MouseInput::Listener::~Listener() = default;
 | 
			
		||||
 | 
			
		||||
void FpsCounter::update()
 | 
			
		||||
{
 | 
			
		||||
    ++m_frames;
 | 
			
		||||
    
 | 
			
		||||
    TimePoint msec = Clock::now();
 | 
			
		||||
    
 | 
			
		||||
    double seconds_window = to_sec(msec - m_window);
 | 
			
		||||
    m_fps = 0.5 * m_fps + 0.5 * (m_frames / seconds_window);
 | 
			
		||||
    
 | 
			
		||||
    if (to_sec(msec - m_last) >= m_resolution) {
 | 
			
		||||
        m_last = msec;
 | 
			
		||||
        for (auto &l : m_listeners) l(m_fps);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    if (seconds_window >= m_window_size) {
 | 
			
		||||
        m_frames = 0;
 | 
			
		||||
        m_window = msec;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}} // namespace Slic3r::GL
 | 
			
		||||
							
								
								
									
										488
									
								
								sandboxes/opencsg/Engine.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										488
									
								
								sandboxes/opencsg/Engine.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,488 @@
 | 
			
		|||
#ifndef SLIC3R_OCSG_EXMP_ENGINE_HPP
 | 
			
		||||
#define SLIC3R_OCSG_EXMP_ENGINE_HPP
 | 
			
		||||
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <chrono>
 | 
			
		||||
 | 
			
		||||
#include <libslic3r/Geometry.hpp>
 | 
			
		||||
#include <libslic3r/Model.hpp>
 | 
			
		||||
#include <libslic3r/TriangleMesh.hpp>
 | 
			
		||||
#include <libslic3r/SLA/Hollowing.hpp>
 | 
			
		||||
#include <opencsg/opencsg.h>
 | 
			
		||||
 | 
			
		||||
namespace Slic3r {
 | 
			
		||||
 | 
			
		||||
class SLAPrint;
 | 
			
		||||
 | 
			
		||||
namespace GL {
 | 
			
		||||
 | 
			
		||||
template<class T, class A = std::allocator<T>> using vector = std::vector<T, A>;
 | 
			
		||||
 | 
			
		||||
// remove empty weak pointers from a vector
 | 
			
		||||
template<class L> inline void cleanup(vector<std::weak_ptr<L>> &listeners) {
 | 
			
		||||
    auto it = std::remove_if(listeners.begin(), listeners.end(),
 | 
			
		||||
                             [](auto &l) { return !l.lock(); });
 | 
			
		||||
    listeners.erase(it, listeners.end());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Call a class method on each element of a vector of objects (weak pointers)
 | 
			
		||||
// of the same type.
 | 
			
		||||
template<class F, class L, class...Args>
 | 
			
		||||
inline void call(F &&f, vector<std::weak_ptr<L>> &listeners, Args&&... args) {
 | 
			
		||||
    for (auto &l : listeners)
 | 
			
		||||
        if (auto p = l.lock()) ((p.get())->*f)(std::forward<Args>(args)...);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// A representation of a mouse input for the engine.
 | 
			
		||||
class MouseInput
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    enum WheelAxis { waVertical, waHorizontal };
 | 
			
		||||
    
 | 
			
		||||
    // Interface to implement if an object wants to receive notifications
 | 
			
		||||
    // about mouse events.
 | 
			
		||||
    class Listener {
 | 
			
		||||
    public:
 | 
			
		||||
        virtual ~Listener();
 | 
			
		||||
        
 | 
			
		||||
        virtual void on_left_click_down() {}
 | 
			
		||||
        virtual void on_left_click_up() {}
 | 
			
		||||
        virtual void on_right_click_down() {}
 | 
			
		||||
        virtual void on_right_click_up() {}
 | 
			
		||||
        virtual void on_double_click() {}
 | 
			
		||||
        virtual void on_scroll(long /*v*/, long /*delta*/, WheelAxis ) {}
 | 
			
		||||
        virtual void on_moved_to(long /*x*/, long /*y*/) {}
 | 
			
		||||
    };
 | 
			
		||||
    
 | 
			
		||||
private:
 | 
			
		||||
    vector<std::weak_ptr<Listener>> m_listeners;
 | 
			
		||||
        
 | 
			
		||||
public:
 | 
			
		||||
    virtual ~MouseInput() = default;
 | 
			
		||||
 | 
			
		||||
    virtual void left_click_down()
 | 
			
		||||
    {
 | 
			
		||||
        call(&Listener::on_left_click_down, m_listeners);
 | 
			
		||||
    }
 | 
			
		||||
    virtual void left_click_up()
 | 
			
		||||
    {
 | 
			
		||||
        call(&Listener::on_left_click_up, m_listeners);
 | 
			
		||||
    }
 | 
			
		||||
    virtual void right_click_down()
 | 
			
		||||
    {
 | 
			
		||||
        call(&Listener::on_right_click_down, m_listeners);
 | 
			
		||||
    }
 | 
			
		||||
    virtual void right_click_up()
 | 
			
		||||
    {
 | 
			
		||||
        call(&Listener::on_right_click_up, m_listeners);
 | 
			
		||||
    }
 | 
			
		||||
    virtual void double_click()
 | 
			
		||||
    {
 | 
			
		||||
        call(&Listener::on_double_click, m_listeners);
 | 
			
		||||
    }
 | 
			
		||||
    virtual void scroll(long v, long d, WheelAxis wa)
 | 
			
		||||
    {
 | 
			
		||||
        call(&Listener::on_scroll, m_listeners, v, d, wa);
 | 
			
		||||
    }
 | 
			
		||||
    virtual void move_to(long x, long y)
 | 
			
		||||
    {
 | 
			
		||||
        call(&Listener::on_moved_to, m_listeners, x, y);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    void add_listener(std::shared_ptr<Listener> listener)
 | 
			
		||||
    {
 | 
			
		||||
        m_listeners.emplace_back(listener);
 | 
			
		||||
        cleanup(m_listeners);
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// This is a stripped down version of Slic3r::IndexedVertexArray
 | 
			
		||||
class IndexedVertexArray {
 | 
			
		||||
public:
 | 
			
		||||
    ~IndexedVertexArray() { release_geometry(); }
 | 
			
		||||
 | 
			
		||||
    // Vertices and their normals, interleaved to be used by void
 | 
			
		||||
    // glInterleavedArrays(GL_N3F_V3F, 0, x)
 | 
			
		||||
    vector<float> vertices_and_normals_interleaved;
 | 
			
		||||
    vector<int>   triangle_indices;
 | 
			
		||||
    vector<int>   quad_indices;
 | 
			
		||||
 | 
			
		||||
    // When the geometry data is loaded into the graphics card as Vertex
 | 
			
		||||
    // Buffer Objects, the above mentioned std::vectors are cleared and the
 | 
			
		||||
    // following variables keep their original length.
 | 
			
		||||
    size_t vertices_and_normals_interleaved_size{ 0 };
 | 
			
		||||
    size_t triangle_indices_size{ 0 };
 | 
			
		||||
    size_t quad_indices_size{ 0 };
 | 
			
		||||
    
 | 
			
		||||
    // IDs of the Vertex Array Objects, into which the geometry has been loaded.
 | 
			
		||||
    // Zero if the VBOs are not sent to GPU yet.
 | 
			
		||||
    unsigned int       vertices_and_normals_interleaved_VBO_id{ 0 };
 | 
			
		||||
    unsigned int       triangle_indices_VBO_id{ 0 };
 | 
			
		||||
    unsigned int       quad_indices_VBO_id{ 0 };
 | 
			
		||||
    
 | 
			
		||||
    
 | 
			
		||||
    void push_geometry(float x, float y, float z, float nx, float ny, float nz);
 | 
			
		||||
 | 
			
		||||
    inline void push_geometry(
 | 
			
		||||
        double x, double y, double z, double nx, double ny, double nz)
 | 
			
		||||
    {
 | 
			
		||||
        push_geometry(float(x), float(y), float(z), float(nx), float(ny), float(nz));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    inline void push_geometry(const Vec3d &p, const Vec3d &n)
 | 
			
		||||
    {
 | 
			
		||||
        push_geometry(p(0), p(1), p(2), n(0), n(1), n(2));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void push_triangle(int idx1, int idx2, int idx3);
 | 
			
		||||
    
 | 
			
		||||
    void load_mesh(const TriangleMesh &mesh);
 | 
			
		||||
 | 
			
		||||
    inline bool has_VBOs() const
 | 
			
		||||
    {
 | 
			
		||||
        return vertices_and_normals_interleaved_VBO_id != 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Finalize the initialization of the geometry & indices,
 | 
			
		||||
    // upload the geometry and indices to OpenGL VBO objects
 | 
			
		||||
    // and shrink the allocated data, possibly relasing it if it has been
 | 
			
		||||
    // loaded into the VBOs.
 | 
			
		||||
    void finalize_geometry();
 | 
			
		||||
    // Release the geometry data, release OpenGL VBOs.
 | 
			
		||||
    void release_geometry();
 | 
			
		||||
    
 | 
			
		||||
    void render() const;
 | 
			
		||||
    
 | 
			
		||||
    // Is there any geometry data stored?
 | 
			
		||||
    bool empty() const { return vertices_and_normals_interleaved_size == 0; }
 | 
			
		||||
    
 | 
			
		||||
    void clear();
 | 
			
		||||
    
 | 
			
		||||
    // Shrink the internal storage to tighly fit the data stored.
 | 
			
		||||
    void shrink_to_fit();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Try to enable or disable multisampling.
 | 
			
		||||
bool enable_multisampling(bool e = true);
 | 
			
		||||
 | 
			
		||||
class Volume {
 | 
			
		||||
    IndexedVertexArray m_geom;
 | 
			
		||||
    Geometry::Transformation m_trafo;
 | 
			
		||||
    
 | 
			
		||||
public:
 | 
			
		||||
    
 | 
			
		||||
    void render();
 | 
			
		||||
    
 | 
			
		||||
    void translation(const Vec3d &offset) { m_trafo.set_offset(offset); }
 | 
			
		||||
    void rotation(const Vec3d &rot) { m_trafo.set_rotation(rot); }
 | 
			
		||||
    void scale(const Vec3d &scaleing) { m_trafo.set_scaling_factor(scaleing); }
 | 
			
		||||
    void scale(double s) { scale({s, s, s}); }
 | 
			
		||||
    
 | 
			
		||||
    inline void load_mesh(const TriangleMesh &mesh)
 | 
			
		||||
    {
 | 
			
		||||
        m_geom.load_mesh(mesh);
 | 
			
		||||
        m_geom.finalize_geometry();
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// A primitive that can be used with OpenCSG rendering algorithms.
 | 
			
		||||
// Does a similar job to GLVolume.
 | 
			
		||||
class Primitive : public Volume, public OpenCSG::Primitive
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    using OpenCSG::Primitive::Primitive;
 | 
			
		||||
    
 | 
			
		||||
    Primitive() : OpenCSG::Primitive(OpenCSG::Intersection, 1) {}
 | 
			
		||||
    
 | 
			
		||||
    void render() override { Volume::render(); }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// A simple representation of a camera in a 3D scene
 | 
			
		||||
class Camera {
 | 
			
		||||
protected:
 | 
			
		||||
    Vec2f m_rot = {0., 0.};
 | 
			
		||||
    Vec3d m_referene = {0., 0., 0.};
 | 
			
		||||
    double m_zoom = 0.;
 | 
			
		||||
    double m_clip_z = 0.;
 | 
			
		||||
public:
 | 
			
		||||
    
 | 
			
		||||
    virtual ~Camera() = default;
 | 
			
		||||
    
 | 
			
		||||
    virtual void view();
 | 
			
		||||
    virtual void set_screen(long width, long height) = 0;
 | 
			
		||||
    
 | 
			
		||||
    void set_rotation(const Vec2f &rotation) { m_rot = rotation; }    
 | 
			
		||||
    void rotate(const Vec2f &rotation) { m_rot += rotation; }
 | 
			
		||||
    void set_zoom(double z) { m_zoom = z; }
 | 
			
		||||
    void set_reference_point(const Vec3d &p) { m_referene = p; }
 | 
			
		||||
    void set_clip_z(double z) { m_clip_z = z; }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Reset a camera object
 | 
			
		||||
inline void reset(Camera &cam)
 | 
			
		||||
{
 | 
			
		||||
    cam.set_rotation({0., 0.});
 | 
			
		||||
    cam.set_zoom(0.);
 | 
			
		||||
    cam.set_reference_point({0., 0., 0.});
 | 
			
		||||
    cam.set_clip_z(0.);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Specialization of a camera which shows in perspective projection
 | 
			
		||||
class PerspectiveCamera: public Camera {
 | 
			
		||||
public:
 | 
			
		||||
    
 | 
			
		||||
    void set_screen(long width, long height) override;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// A simple counter of FPS. Subscribed objects will receive updates of the
 | 
			
		||||
// current fps.
 | 
			
		||||
class FpsCounter {
 | 
			
		||||
    vector<std::function<void(double)>> m_listeners;
 | 
			
		||||
    
 | 
			
		||||
    using Clock = std::chrono::high_resolution_clock;
 | 
			
		||||
    using Duration = Clock::duration;
 | 
			
		||||
    using TimePoint = Clock::time_point;
 | 
			
		||||
    
 | 
			
		||||
    int m_frames = 0;
 | 
			
		||||
    TimePoint m_last = Clock::now(), m_window = m_last;
 | 
			
		||||
    
 | 
			
		||||
    double m_resolution = 0.1, m_window_size = 1.0;
 | 
			
		||||
    double m_fps = 0.;
 | 
			
		||||
    
 | 
			
		||||
    static double to_sec(Duration d)
 | 
			
		||||
    {
 | 
			
		||||
        return d.count() * double(Duration::period::num) / Duration::period::den;
 | 
			
		||||
    }    
 | 
			
		||||
    
 | 
			
		||||
public:
 | 
			
		||||
    
 | 
			
		||||
    void update();
 | 
			
		||||
 | 
			
		||||
    void add_listener(std::function<void(double)> lst)
 | 
			
		||||
    {
 | 
			
		||||
        m_listeners.emplace_back(lst);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    void clear_listeners() { m_listeners = {}; }
 | 
			
		||||
 | 
			
		||||
    void set_notification_interval(double seconds);
 | 
			
		||||
    void set_measure_window_size(double seconds);
 | 
			
		||||
    
 | 
			
		||||
    double get_notification_interval() const { return m_resolution; }
 | 
			
		||||
    double get_mesure_window_size() const { return m_window_size; }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Collection of the used OpenCSG library settings.
 | 
			
		||||
class CSGSettings {
 | 
			
		||||
public:
 | 
			
		||||
    static const constexpr unsigned DEFAULT_CONVEXITY = 10;
 | 
			
		||||
    
 | 
			
		||||
private:
 | 
			
		||||
    OpenCSG::Algorithm m_csgalg = OpenCSG::Algorithm::Automatic;
 | 
			
		||||
    OpenCSG::DepthComplexityAlgorithm m_depth_algo = OpenCSG::NoDepthComplexitySampling;
 | 
			
		||||
    OpenCSG::Optimization m_optim = OpenCSG::OptimizationDefault;
 | 
			
		||||
    bool m_enable = true;
 | 
			
		||||
    unsigned int m_convexity = DEFAULT_CONVEXITY;
 | 
			
		||||
    
 | 
			
		||||
public:
 | 
			
		||||
    int get_algo() const { return int(m_csgalg); }
 | 
			
		||||
    void set_algo(int alg)
 | 
			
		||||
    {
 | 
			
		||||
        if (alg < OpenCSG::Algorithm::AlgorithmUnused)
 | 
			
		||||
            m_csgalg = OpenCSG::Algorithm(alg);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    int get_depth_algo() const { return int(m_depth_algo); }
 | 
			
		||||
    void set_depth_algo(int alg)
 | 
			
		||||
    {
 | 
			
		||||
        if (alg < OpenCSG::DepthComplexityAlgorithmUnused)
 | 
			
		||||
            m_depth_algo = OpenCSG::DepthComplexityAlgorithm(alg);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    int  get_optimization() const { return int(m_optim); }
 | 
			
		||||
    void set_optimization(int o)
 | 
			
		||||
    {
 | 
			
		||||
        if (o < OpenCSG::Optimization::OptimizationUnused)
 | 
			
		||||
            m_optim = OpenCSG::Optimization(o);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    void enable_csg(bool en = true) { m_enable = en; }
 | 
			
		||||
    bool is_enabled() const { return m_enable; }
 | 
			
		||||
    
 | 
			
		||||
    unsigned get_convexity() const { return m_convexity; }
 | 
			
		||||
    void set_convexity(unsigned c) { m_convexity = c; }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// The scene is a wrapper around SLAPrint which holds the data to be visualized.
 | 
			
		||||
class Scene
 | 
			
		||||
{
 | 
			
		||||
    std::unique_ptr<SLAPrint> m_print;
 | 
			
		||||
public:
 | 
			
		||||
    
 | 
			
		||||
    // Subscribers will be notified if the model is changed. This might be a
 | 
			
		||||
    // display which will have to load the meshes and repaint itself when
 | 
			
		||||
    // the scene data changes.
 | 
			
		||||
    // eg. We load a new 3mf through the UI, this will notify the controller
 | 
			
		||||
    // associated with the scene and all the displays that the controller is
 | 
			
		||||
    // connected with.
 | 
			
		||||
    class Listener {
 | 
			
		||||
    public:
 | 
			
		||||
        virtual ~Listener() = default;
 | 
			
		||||
        virtual void on_scene_updated(const Scene &scene) = 0;
 | 
			
		||||
    };
 | 
			
		||||
    
 | 
			
		||||
    Scene();
 | 
			
		||||
    ~Scene();
 | 
			
		||||
    
 | 
			
		||||
    void set_print(std::unique_ptr<SLAPrint> &&print);
 | 
			
		||||
    const SLAPrint * get_print() const { return m_print.get(); }
 | 
			
		||||
    
 | 
			
		||||
    BoundingBoxf3 get_bounding_box() const;
 | 
			
		||||
    
 | 
			
		||||
    void add_listener(std::shared_ptr<Listener> listener)
 | 
			
		||||
    {
 | 
			
		||||
        m_listeners.emplace_back(listener);
 | 
			
		||||
        cleanup(m_listeners);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
private:
 | 
			
		||||
    vector<std::weak_ptr<Listener>> m_listeners;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// The basic Display. This is almost just an interface but will do all the
 | 
			
		||||
// initialization and show the fps values. Overriding the render_scene is
 | 
			
		||||
// needed to show the scene content. The specific method of displaying the
 | 
			
		||||
// scene is up the the particular implementation (OpenCSG or other screen space
 | 
			
		||||
// boolean algorithms)
 | 
			
		||||
class Display : public Scene::Listener
 | 
			
		||||
{
 | 
			
		||||
protected:
 | 
			
		||||
    Vec2i m_size;
 | 
			
		||||
    bool m_initialized = false;
 | 
			
		||||
    
 | 
			
		||||
    std::shared_ptr<Camera>  m_camera;
 | 
			
		||||
    FpsCounter m_fps_counter;
 | 
			
		||||
    
 | 
			
		||||
public:
 | 
			
		||||
    
 | 
			
		||||
    explicit Display(std::shared_ptr<Camera> camera = nullptr)
 | 
			
		||||
        : m_camera(camera ? camera : std::make_shared<PerspectiveCamera>())
 | 
			
		||||
    {}
 | 
			
		||||
    
 | 
			
		||||
    ~Display() override;
 | 
			
		||||
    
 | 
			
		||||
    std::shared_ptr<const Camera> get_camera() const { return m_camera; }
 | 
			
		||||
    std::shared_ptr<Camera> get_camera() { return m_camera; }
 | 
			
		||||
    void set_camera(std::shared_ptr<Camera> cam) { m_camera = cam; }
 | 
			
		||||
    
 | 
			
		||||
    virtual void swap_buffers() = 0;
 | 
			
		||||
    virtual void set_active(long width, long height);
 | 
			
		||||
    virtual void set_screen_size(long width, long height);
 | 
			
		||||
    Vec2i get_screen_size() const { return m_size; }
 | 
			
		||||
    
 | 
			
		||||
    virtual void repaint();
 | 
			
		||||
    
 | 
			
		||||
    bool is_initialized() const { return m_initialized; }
 | 
			
		||||
    
 | 
			
		||||
    virtual void clear_screen();
 | 
			
		||||
    virtual void render_scene() {}
 | 
			
		||||
    
 | 
			
		||||
    template<class _FpsCounter> void set_fps_counter(_FpsCounter &&fpsc)
 | 
			
		||||
    {
 | 
			
		||||
        m_fps_counter = std::forward<_FpsCounter>(fpsc);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const FpsCounter &get_fps_counter() const { return m_fps_counter; }
 | 
			
		||||
    FpsCounter &get_fps_counter() { return m_fps_counter; }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Special dispaly using OpenCSG for rendering the scene.
 | 
			
		||||
class CSGDisplay : public Display {
 | 
			
		||||
protected:
 | 
			
		||||
    CSGSettings m_csgsettings;
 | 
			
		||||
    
 | 
			
		||||
    // Cache the renderable primitives. These will be fetched when the scene
 | 
			
		||||
    // is modified.
 | 
			
		||||
    struct SceneCache {
 | 
			
		||||
        vector<std::shared_ptr<Primitive>> primitives;
 | 
			
		||||
        vector<Primitive *> primitives_free;
 | 
			
		||||
        vector<OpenCSG::Primitive *> primitives_csg;
 | 
			
		||||
        
 | 
			
		||||
        void clear();
 | 
			
		||||
        
 | 
			
		||||
        std::shared_ptr<Primitive> add_mesh(const TriangleMesh &mesh);
 | 
			
		||||
        std::shared_ptr<Primitive> add_mesh(const TriangleMesh &mesh,
 | 
			
		||||
                                  OpenCSG::Operation  op,
 | 
			
		||||
                                  unsigned            covexity);
 | 
			
		||||
    } m_scene_cache;
 | 
			
		||||
    
 | 
			
		||||
public:
 | 
			
		||||
    
 | 
			
		||||
    // Receive or apply the new settings.
 | 
			
		||||
    const CSGSettings & get_csgsettings() const { return m_csgsettings; }
 | 
			
		||||
    void apply_csgsettings(const CSGSettings &settings);
 | 
			
		||||
    
 | 
			
		||||
    void render_scene() override;
 | 
			
		||||
    
 | 
			
		||||
    void on_scene_updated(const Scene &scene) override;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// The controller is a hub which dispatches mouse events to the connected
 | 
			
		||||
// displays. It keeps track of the mouse wheel position, the states whether
 | 
			
		||||
// the mouse is being held, dragged, etc... All the connected displays will
 | 
			
		||||
// mirror the camera movement (if there is more than one display).
 | 
			
		||||
class Controller : public std::enable_shared_from_this<Controller>,
 | 
			
		||||
                   public MouseInput::Listener,
 | 
			
		||||
                   public Scene::Listener
 | 
			
		||||
{
 | 
			
		||||
    long m_wheel_pos = 0;
 | 
			
		||||
    Vec2i m_mouse_pos, m_mouse_pos_rprev, m_mouse_pos_lprev;
 | 
			
		||||
    bool m_left_btn = false, m_right_btn = false;
 | 
			
		||||
 | 
			
		||||
    std::shared_ptr<Scene>           m_scene;
 | 
			
		||||
    vector<std::weak_ptr<Display>> m_displays;
 | 
			
		||||
    
 | 
			
		||||
    // Call a method of Camera on all the cameras of the attached displays
 | 
			
		||||
    template<class F, class...Args>
 | 
			
		||||
    void call_cameras(F &&f, Args&&... args) {
 | 
			
		||||
        for (std::weak_ptr<Display> &l : m_displays)
 | 
			
		||||
            if (auto disp = l.lock()) if (auto cam = disp->get_camera())
 | 
			
		||||
                (cam.get()->*f)(std::forward<Args>(args)...);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
public:
 | 
			
		||||
    
 | 
			
		||||
    // Set the scene that will be controlled.
 | 
			
		||||
    void set_scene(std::shared_ptr<Scene> scene)
 | 
			
		||||
    {
 | 
			
		||||
        m_scene = scene;
 | 
			
		||||
        m_scene->add_listener(shared_from_this());
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    const Scene * get_scene() const { return m_scene.get(); }
 | 
			
		||||
 | 
			
		||||
    void add_display(std::shared_ptr<Display> disp)
 | 
			
		||||
    {
 | 
			
		||||
        m_displays.emplace_back(disp);
 | 
			
		||||
        cleanup(m_displays);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    void remove_displays() { m_displays = {}; }
 | 
			
		||||
    
 | 
			
		||||
    void on_scene_updated(const Scene &scene) override;
 | 
			
		||||
    
 | 
			
		||||
    void on_left_click_down() override { m_left_btn = true; }
 | 
			
		||||
    void on_left_click_up() override { m_left_btn = false;  }
 | 
			
		||||
    void on_right_click_down() override { m_right_btn = true;  }
 | 
			
		||||
    void on_right_click_up() override { m_right_btn = false; }
 | 
			
		||||
    
 | 
			
		||||
    void on_scroll(long v, long d, MouseInput::WheelAxis wa) override;
 | 
			
		||||
    void on_moved_to(long x, long y) override;
 | 
			
		||||
 | 
			
		||||
    void move_clip_plane(double z) { call_cameras(&Camera::set_clip_z, z); }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}}     // namespace Slic3r::GL
 | 
			
		||||
#endif // SLIC3R_OCSG_EXMP_ENGINE_HPP
 | 
			
		||||
							
								
								
									
										63
									
								
								sandboxes/opencsg/ShaderCSGDisplay.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								sandboxes/opencsg/ShaderCSGDisplay.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,63 @@
 | 
			
		|||
#include "ShaderCSGDisplay.hpp"
 | 
			
		||||
#include "libslic3r/SLAPrint.hpp"
 | 
			
		||||
#include <GL/glew.h>
 | 
			
		||||
 | 
			
		||||
namespace Slic3r { namespace GL {
 | 
			
		||||
 | 
			
		||||
void ShaderCSGDisplay::add_mesh(const TriangleMesh &mesh)
 | 
			
		||||
{
 | 
			
		||||
    auto v = std::make_shared<CSGVolume>();
 | 
			
		||||
    v->load_mesh(mesh);
 | 
			
		||||
    m_volumes.emplace_back(v);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ShaderCSGDisplay::render_scene()
 | 
			
		||||
{
 | 
			
		||||
    GLfloat color[] = {1.f, 1.f, 0.f, 0.f};
 | 
			
		||||
    glColor4fv(color);
 | 
			
		||||
    glDepthFunc(GL_LESS);
 | 
			
		||||
    for (auto &v : m_volumes) v->render();
 | 
			
		||||
    glFlush();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ShaderCSGDisplay::on_scene_updated(const Scene &scene)
 | 
			
		||||
{
 | 
			
		||||
    // TriangleMesh mesh = print->objects().front()->hollowed_interior_mesh();
 | 
			
		||||
    // Look at CSGDisplay::on_scene_updated to see how its done there.
 | 
			
		||||
    
 | 
			
		||||
    const SLAPrint *print = scene.get_print();
 | 
			
		||||
    if (!print) return;
 | 
			
		||||
    
 | 
			
		||||
    m_volumes.clear();
 | 
			
		||||
    
 | 
			
		||||
    for (const SLAPrintObject *po : print->objects()) {
 | 
			
		||||
        const ModelObject *mo = po->model_object();
 | 
			
		||||
        TriangleMesh msh = mo->raw_mesh();
 | 
			
		||||
        
 | 
			
		||||
        sla::DrainHoles holedata = mo->sla_drain_holes;
 | 
			
		||||
        
 | 
			
		||||
        for (const ModelInstance *mi : mo->instances) {
 | 
			
		||||
            
 | 
			
		||||
            TriangleMesh mshinst = msh;
 | 
			
		||||
            auto interior = po->hollowed_interior_mesh();
 | 
			
		||||
            interior.transform(po->trafo().inverse());
 | 
			
		||||
            
 | 
			
		||||
            mshinst.merge(interior);
 | 
			
		||||
            
 | 
			
		||||
            mi->transform_mesh(&mshinst);
 | 
			
		||||
            
 | 
			
		||||
            auto bb = mshinst.bounding_box();
 | 
			
		||||
            auto center = bb.center().cast<float>();
 | 
			
		||||
            mshinst.translate(-center);
 | 
			
		||||
            
 | 
			
		||||
            add_mesh(mshinst);
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        for (const sla::DrainHole &holept : holedata)
 | 
			
		||||
            add_mesh(sla::to_triangle_mesh(holept.to_mesh()));
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    repaint();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}} // namespace Slic3r::GL
 | 
			
		||||
							
								
								
									
										27
									
								
								sandboxes/opencsg/ShaderCSGDisplay.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								sandboxes/opencsg/ShaderCSGDisplay.hpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,27 @@
 | 
			
		|||
#ifndef SHADERCSGDISPLAY_HPP
 | 
			
		||||
#define SHADERCSGDISPLAY_HPP
 | 
			
		||||
 | 
			
		||||
#include "Engine.hpp"
 | 
			
		||||
 | 
			
		||||
namespace Slic3r { namespace GL {
 | 
			
		||||
 | 
			
		||||
class CSGVolume: public Volume
 | 
			
		||||
{
 | 
			
		||||
    // Extend...    
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class ShaderCSGDisplay: public Display {
 | 
			
		||||
protected:
 | 
			
		||||
    vector<std::shared_ptr<CSGVolume>> m_volumes;
 | 
			
		||||
    
 | 
			
		||||
    void add_mesh(const TriangleMesh &mesh);
 | 
			
		||||
public:
 | 
			
		||||
    
 | 
			
		||||
    void render_scene() override;
 | 
			
		||||
    
 | 
			
		||||
    void on_scene_updated(const Scene &scene) override;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}}
 | 
			
		||||
 | 
			
		||||
#endif // SHADERCSGDISPLAY_HPP
 | 
			
		||||
							
								
								
									
										734
									
								
								sandboxes/opencsg/main.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										734
									
								
								sandboxes/opencsg/main.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,734 @@
 | 
			
		|||
#include <iostream>
 | 
			
		||||
#include <utility>
 | 
			
		||||
#include <memory>
 | 
			
		||||
 | 
			
		||||
#include "Engine.hpp"
 | 
			
		||||
#include "ShaderCSGDisplay.hpp"
 | 
			
		||||
 | 
			
		||||
#include <GL/glew.h>
 | 
			
		||||
 | 
			
		||||
#include <opencsg/opencsg.h>
 | 
			
		||||
// For compilers that support precompilation, includes "wx/wx.h".
 | 
			
		||||
#include <wx/wxprec.h>
 | 
			
		||||
#ifndef WX_PRECOMP
 | 
			
		||||
#include <wx/wx.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include <wx/slider.h>
 | 
			
		||||
#include <wx/tglbtn.h>
 | 
			
		||||
#include <wx/combobox.h>
 | 
			
		||||
#include <wx/spinctrl.h>
 | 
			
		||||
#include <wx/msgdlg.h>
 | 
			
		||||
#include <wx/glcanvas.h>
 | 
			
		||||
#include <wx/cmdline.h>
 | 
			
		||||
 | 
			
		||||
#include "libslic3r/Model.hpp"
 | 
			
		||||
#include "libslic3r/Format/3mf.hpp"
 | 
			
		||||
#include "libslic3r/SLAPrint.hpp"
 | 
			
		||||
 | 
			
		||||
#include "slic3r/GUI/Jobs/Job.hpp"
 | 
			
		||||
#include "slic3r/GUI/ProgressStatusBar.hpp"
 | 
			
		||||
 | 
			
		||||
using namespace Slic3r::GL;
 | 
			
		||||
 | 
			
		||||
class Renderer {
 | 
			
		||||
protected:
 | 
			
		||||
    wxGLCanvas *m_canvas;
 | 
			
		||||
    std::shared_ptr<wxGLContext> m_context;
 | 
			
		||||
public:
 | 
			
		||||
    
 | 
			
		||||
    Renderer(wxGLCanvas *c): m_canvas{c} {
 | 
			
		||||
        auto ctx = new wxGLContext(m_canvas);
 | 
			
		||||
        if (!ctx || !ctx->IsOK()) {
 | 
			
		||||
            wxMessageBox("Could not create OpenGL context.", "Error",
 | 
			
		||||
                         wxOK | wxICON_ERROR);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        m_context.reset(ctx);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    wxGLContext * context() { return m_context.get(); }
 | 
			
		||||
    const wxGLContext * context() const { return m_context.get(); }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Tell the CSGDisplay how to swap buffers and set the gl context.
 | 
			
		||||
class OCSGRenderer: public Renderer, public Slic3r::GL::CSGDisplay {
 | 
			
		||||
public:
 | 
			
		||||
    
 | 
			
		||||
    OCSGRenderer(wxGLCanvas *c): Renderer{c} {}
 | 
			
		||||
    
 | 
			
		||||
    void set_active(long w, long h) override
 | 
			
		||||
    {
 | 
			
		||||
        m_canvas->SetCurrent(*m_context);
 | 
			
		||||
        Slic3r::GL::Display::set_active(w, h);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    void swap_buffers() override { m_canvas->SwapBuffers(); }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Tell the CSGDisplay how to swap buffers and set the gl context.
 | 
			
		||||
class ShaderCSGRenderer : public Renderer, public Slic3r::GL::ShaderCSGDisplay {
 | 
			
		||||
public:
 | 
			
		||||
    
 | 
			
		||||
    ShaderCSGRenderer(wxGLCanvas *c): Renderer{c} {}
 | 
			
		||||
    
 | 
			
		||||
    void set_active(long w, long h) override
 | 
			
		||||
    {
 | 
			
		||||
        m_canvas->SetCurrent(*m_context);
 | 
			
		||||
        Slic3r::GL::Display::set_active(w, h);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    void swap_buffers() override { m_canvas->SwapBuffers(); }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// The opengl rendering facility. Here we implement the rendering objects.
 | 
			
		||||
class Canvas: public wxGLCanvas
 | 
			
		||||
{    
 | 
			
		||||
    // One display is active at a time, the OCSGRenderer by default.
 | 
			
		||||
    std::shared_ptr<Slic3r::GL::Display> m_display;
 | 
			
		||||
    
 | 
			
		||||
public:
 | 
			
		||||
    
 | 
			
		||||
    template<class...Args>
 | 
			
		||||
    Canvas(Args &&...args): wxGLCanvas(std::forward<Args>(args)...) {}
 | 
			
		||||
    
 | 
			
		||||
    std::shared_ptr<Slic3r::GL::Display> get_display() const { return m_display; }
 | 
			
		||||
 | 
			
		||||
    void set_display(std::shared_ptr<Slic3r::GL::Display> d) { m_display = d; }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Enumerate possible mouse events, we will record them.
 | 
			
		||||
enum EEvents { LCLK_U, RCLK_U, LCLK_D, RCLK_D, DDCLK, SCRL, MV };
 | 
			
		||||
struct Event
 | 
			
		||||
{
 | 
			
		||||
    EEvents type;
 | 
			
		||||
    long    a, b;
 | 
			
		||||
    Event(EEvents t, long x = 0, long y = 0) : type{t}, a{x}, b{y} {}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Create a special mouse input adapter, which can store (record) the received
 | 
			
		||||
// mouse signals into a file and play back the stored events later.
 | 
			
		||||
class RecorderMouseInput: public MouseInput {
 | 
			
		||||
    std::vector<Event> m_events;
 | 
			
		||||
    bool m_recording = false, m_playing = false;
 | 
			
		||||
    
 | 
			
		||||
public:
 | 
			
		||||
    void left_click_down() override
 | 
			
		||||
    {
 | 
			
		||||
        if (m_recording) m_events.emplace_back(LCLK_D);
 | 
			
		||||
        if (!m_playing) MouseInput::left_click_down();
 | 
			
		||||
    }
 | 
			
		||||
    void left_click_up() override
 | 
			
		||||
    {
 | 
			
		||||
        if (m_recording) m_events.emplace_back(LCLK_U);
 | 
			
		||||
        if (!m_playing) MouseInput::left_click_up();
 | 
			
		||||
    }
 | 
			
		||||
    void right_click_down() override
 | 
			
		||||
    {
 | 
			
		||||
        if (m_recording) m_events.emplace_back(RCLK_D);
 | 
			
		||||
        if (!m_playing) MouseInput::right_click_down();
 | 
			
		||||
    }
 | 
			
		||||
    void right_click_up() override
 | 
			
		||||
    {
 | 
			
		||||
        if (m_recording) m_events.emplace_back(RCLK_U);
 | 
			
		||||
        if (!m_playing) MouseInput::right_click_up();
 | 
			
		||||
    }
 | 
			
		||||
    void double_click() override
 | 
			
		||||
    {
 | 
			
		||||
        if (m_recording) m_events.emplace_back(DDCLK);
 | 
			
		||||
        if (!m_playing) MouseInput::double_click();
 | 
			
		||||
    }
 | 
			
		||||
    void scroll(long v, long d, WheelAxis wa) override
 | 
			
		||||
    {
 | 
			
		||||
        if (m_recording) m_events.emplace_back(SCRL, v, d);
 | 
			
		||||
        if (!m_playing) MouseInput::scroll(v, d, wa);
 | 
			
		||||
    }
 | 
			
		||||
    void move_to(long x, long y) override
 | 
			
		||||
    {
 | 
			
		||||
        if (m_recording) m_events.emplace_back(MV, x, y);
 | 
			
		||||
        if (!m_playing) MouseInput::move_to(x, y);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    void save(std::ostream &stream)
 | 
			
		||||
    {
 | 
			
		||||
        for (const Event &evt : m_events)
 | 
			
		||||
            stream << evt.type << " " << evt.a << " " << evt.b << std::endl;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    void load(std::istream &stream)
 | 
			
		||||
    {
 | 
			
		||||
        m_events.clear();
 | 
			
		||||
        while (stream.good()) {
 | 
			
		||||
            int type; long a, b;
 | 
			
		||||
            stream >> type >> a >> b;
 | 
			
		||||
            m_events.emplace_back(EEvents(type), a, b);
 | 
			
		||||
        }
 | 
			
		||||
    }    
 | 
			
		||||
    
 | 
			
		||||
    void record(bool r) { m_recording = r; if (r) m_events.clear(); }
 | 
			
		||||
    
 | 
			
		||||
    void play()
 | 
			
		||||
    {
 | 
			
		||||
        m_playing = true;
 | 
			
		||||
        for (const Event &evt : m_events) {
 | 
			
		||||
            switch (evt.type) {
 | 
			
		||||
            case LCLK_U: MouseInput::left_click_up(); break;
 | 
			
		||||
            case LCLK_D: MouseInput::left_click_down(); break;
 | 
			
		||||
            case RCLK_U: MouseInput::right_click_up(); break;
 | 
			
		||||
            case RCLK_D: MouseInput::right_click_down(); break;
 | 
			
		||||
            case DDCLK:  MouseInput::double_click(); break;
 | 
			
		||||
            case SCRL:   MouseInput::scroll(evt.a, evt.b, WheelAxis::waVertical); break;
 | 
			
		||||
            case MV:     MouseInput::move_to(evt.a, evt.b); break;
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            wxTheApp->Yield();
 | 
			
		||||
            if (!m_playing)
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
        m_playing = false;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    void stop() { m_playing = false; }
 | 
			
		||||
    bool is_playing() const { return m_playing; }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// The top level frame of the application.
 | 
			
		||||
class MyFrame: public wxFrame
 | 
			
		||||
{
 | 
			
		||||
    // Instantiate the 3D engine.
 | 
			
		||||
    std::shared_ptr<Scene>             m_scene;             // Model
 | 
			
		||||
    std::shared_ptr<Canvas>            m_canvas;            // Views store
 | 
			
		||||
    std::shared_ptr<OCSGRenderer>      m_ocsgdisplay;       // View
 | 
			
		||||
    std::shared_ptr<ShaderCSGRenderer> m_shadercsg_display; // Another view
 | 
			
		||||
    std::shared_ptr<Controller>        m_ctl;               // Controller
 | 
			
		||||
 | 
			
		||||
    // Add a status bar with progress indication.
 | 
			
		||||
    std::shared_ptr<Slic3r::GUI::ProgressStatusBar> m_stbar;
 | 
			
		||||
    
 | 
			
		||||
    RecorderMouseInput m_mouse;
 | 
			
		||||
    
 | 
			
		||||
    // When loading a Model from 3mf and preparing it, we use a separate thread.
 | 
			
		||||
    class SLAJob: public Slic3r::GUI::Job {
 | 
			
		||||
        MyFrame *m_parent;
 | 
			
		||||
        std::unique_ptr<Slic3r::SLAPrint> m_print;
 | 
			
		||||
        std::string m_fname;
 | 
			
		||||
        
 | 
			
		||||
    public:
 | 
			
		||||
        SLAJob(MyFrame *frame, const std::string &fname)
 | 
			
		||||
            : Slic3r::GUI::Job{frame->m_stbar}
 | 
			
		||||
            , m_parent{frame}
 | 
			
		||||
            , m_fname{fname}
 | 
			
		||||
        {}
 | 
			
		||||
        
 | 
			
		||||
        // Runs in separate thread
 | 
			
		||||
        void process() override;
 | 
			
		||||
        
 | 
			
		||||
        const std::string & get_project_fname() const { return m_fname; }
 | 
			
		||||
        
 | 
			
		||||
    protected:
 | 
			
		||||
        
 | 
			
		||||
        // Runs in the UI thread.
 | 
			
		||||
        void finalize() override 
 | 
			
		||||
        {
 | 
			
		||||
            m_parent->m_scene->set_print(std::move(m_print));
 | 
			
		||||
            m_parent->m_stbar->set_status_text(
 | 
			
		||||
                        wxString::Format("Model %s loaded.", m_fname));
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
    
 | 
			
		||||
    std::unique_ptr<SLAJob> m_ui_job;
 | 
			
		||||
    
 | 
			
		||||
    // To keep track of the running average of measured fps values.
 | 
			
		||||
    double m_fps_avg = 0.;
 | 
			
		||||
    
 | 
			
		||||
    // We need the record button across methods
 | 
			
		||||
    wxToggleButton *m_record_btn;
 | 
			
		||||
    wxComboBox *    m_alg_select;
 | 
			
		||||
    wxComboBox *    m_depth_select;
 | 
			
		||||
    wxComboBox *    m_optimization_select;
 | 
			
		||||
    wxSpinCtrl *    m_convexity_spin;
 | 
			
		||||
    wxToggleButton *m_csg_toggle;
 | 
			
		||||
    wxToggleButton *m_ms_toggle;
 | 
			
		||||
    wxStaticText   *m_fpstext;
 | 
			
		||||
    
 | 
			
		||||
    CSGSettings     m_csg_settings;
 | 
			
		||||
    
 | 
			
		||||
    void read_csg_settings(const wxCmdLineParser &parser);
 | 
			
		||||
    
 | 
			
		||||
    void set_renderer_algorithm(const wxString &alg);
 | 
			
		||||
    
 | 
			
		||||
    void activate_canvas_display();
 | 
			
		||||
    
 | 
			
		||||
public:
 | 
			
		||||
    MyFrame(const wxString &       title,
 | 
			
		||||
            const wxPoint &        pos,
 | 
			
		||||
            const wxSize &         size,
 | 
			
		||||
            const wxCmdLineParser &parser);
 | 
			
		||||
 | 
			
		||||
    // Grab a 3mf and load (hollow it out) within the UI job.
 | 
			
		||||
    void load_model(const std::string &fname) {
 | 
			
		||||
        m_ui_job = std::make_unique<SLAJob>(this, fname);
 | 
			
		||||
        m_ui_job->start();
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // Load a previously stored mouse event log and play it back.
 | 
			
		||||
    void play_back_mouse(const std::string &events_fname)
 | 
			
		||||
    {
 | 
			
		||||
        std::fstream stream(events_fname, std::fstream::in);
 | 
			
		||||
 | 
			
		||||
        if (stream.good()) {
 | 
			
		||||
            std::string model_name;
 | 
			
		||||
            std::getline(stream, model_name);
 | 
			
		||||
            load_model(model_name);
 | 
			
		||||
            
 | 
			
		||||
            while (!m_ui_job->is_finalized())
 | 
			
		||||
                wxTheApp->Yield();;
 | 
			
		||||
            
 | 
			
		||||
            int w, h;
 | 
			
		||||
            stream >> w >> h;
 | 
			
		||||
            SetSize(w, h);
 | 
			
		||||
            
 | 
			
		||||
            m_mouse.load(stream);
 | 
			
		||||
            if (m_record_btn) m_record_btn->Disable();
 | 
			
		||||
            m_mouse.play();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    Canvas * canvas() { return m_canvas.get(); }
 | 
			
		||||
    const Canvas * canvas() const { return m_canvas.get(); }
 | 
			
		||||
    
 | 
			
		||||
    // Bind the canvas mouse events to a class implementing MouseInput interface
 | 
			
		||||
    void bind_canvas_events(MouseInput &msinput);
 | 
			
		||||
    
 | 
			
		||||
    double get_fps_average() const { return m_fps_avg; }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Possible OpenCSG configuration values. Will be used on the command line and
 | 
			
		||||
// on the UI widgets.
 | 
			
		||||
static const std::vector<wxString> CSG_ALGS  = {"Auto", "Goldfeather", "SCS", "EnricoShader"};
 | 
			
		||||
static const std::vector<wxString> CSG_DEPTH = {"Off", "OcclusionQuery", "On"};
 | 
			
		||||
static const std::vector<wxString> CSG_OPT   = { "Default", "ForceOn", "On", "Off" };
 | 
			
		||||
 | 
			
		||||
inline long get_idx(const wxString &a, const std::vector<wxString> &v)
 | 
			
		||||
{
 | 
			
		||||
    auto it = std::find(v.begin(), v.end(), a.ToStdString());
 | 
			
		||||
    return it - v.begin();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class App : public wxApp {
 | 
			
		||||
    MyFrame *m_frame = nullptr;
 | 
			
		||||
    wxString m_fname;
 | 
			
		||||
public:
 | 
			
		||||
    bool OnInit() override {
 | 
			
		||||
        
 | 
			
		||||
        wxCmdLineParser parser(argc, argv);
 | 
			
		||||
        
 | 
			
		||||
        parser.AddOption("p", "play", "play back file", wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL);
 | 
			
		||||
        parser.AddOption("a", "algorithm", "OpenCSG algorithm [Auto|Goldfeather|SCS]", wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL);
 | 
			
		||||
        parser.AddOption("d", "depth", "OpenCSG depth strategy [Off|OcclusionQuery|On]", wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL);
 | 
			
		||||
        parser.AddOption("o", "optimization", "OpenCSG optimization strategy [Default|ForceOn|On|Off]", wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL);
 | 
			
		||||
        parser.AddOption("c", "convexity", "OpenCSG convexity parameter for generic meshes", wxCMD_LINE_VAL_NUMBER, wxCMD_LINE_PARAM_OPTIONAL);
 | 
			
		||||
        parser.AddSwitch("", "disable-csg", "Disable csg rendering", wxCMD_LINE_PARAM_OPTIONAL);
 | 
			
		||||
        
 | 
			
		||||
        parser.Parse();
 | 
			
		||||
        
 | 
			
		||||
        bool is_play = parser.Found("play", &m_fname);
 | 
			
		||||
        
 | 
			
		||||
        m_frame = new MyFrame("BambuStudio OpenCSG Demo", wxDefaultPosition, wxSize(1024, 768), parser);
 | 
			
		||||
 | 
			
		||||
        if (is_play) {
 | 
			
		||||
            Bind(wxEVT_IDLE, &App::Play, this);
 | 
			
		||||
            m_frame->Show( true );
 | 
			
		||||
        } else m_frame->Show( true );
 | 
			
		||||
        
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    void Play(wxIdleEvent &) {
 | 
			
		||||
        Unbind(wxEVT_IDLE, &App::Play, this);
 | 
			
		||||
        m_frame->play_back_mouse(m_fname.ToStdString());
 | 
			
		||||
        m_frame->Destroy();
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
wxIMPLEMENT_APP(App);
 | 
			
		||||
 | 
			
		||||
void MyFrame::read_csg_settings(const wxCmdLineParser &parser)
 | 
			
		||||
{
 | 
			
		||||
    wxString alg;
 | 
			
		||||
    parser.Found("algorithm", &alg);
 | 
			
		||||
    
 | 
			
		||||
    wxString depth;
 | 
			
		||||
    parser.Found("depth", &depth);
 | 
			
		||||
    
 | 
			
		||||
    wxString opt;
 | 
			
		||||
    parser.Found("optimization", &opt);
 | 
			
		||||
    
 | 
			
		||||
    long convexity = 1;
 | 
			
		||||
    parser.Found("convexity", &convexity);
 | 
			
		||||
    
 | 
			
		||||
    bool csg_off = parser.Found("disable-csg");
 | 
			
		||||
    
 | 
			
		||||
    if (auto a = get_idx(alg, CSG_ALGS) < OpenCSG::AlgorithmUnused) 
 | 
			
		||||
            m_csg_settings.set_algo(OpenCSG::Algorithm(a));
 | 
			
		||||
    
 | 
			
		||||
    if (auto a = get_idx(depth, CSG_DEPTH) < OpenCSG::DepthComplexityAlgorithmUnused) 
 | 
			
		||||
            m_csg_settings.set_depth_algo(OpenCSG::DepthComplexityAlgorithm(a));
 | 
			
		||||
    
 | 
			
		||||
    if (auto a = get_idx(opt, CSG_OPT) < OpenCSG::OptimizationUnused) 
 | 
			
		||||
            m_csg_settings.set_optimization(OpenCSG::Optimization(a));
 | 
			
		||||
    
 | 
			
		||||
    m_csg_settings.set_convexity(unsigned(convexity));
 | 
			
		||||
    m_csg_settings.enable_csg(!csg_off);
 | 
			
		||||
    
 | 
			
		||||
    if (m_ocsgdisplay) m_ocsgdisplay->apply_csgsettings(m_csg_settings);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MyFrame::set_renderer_algorithm(const wxString &alg)
 | 
			
		||||
{
 | 
			
		||||
    long alg_idx = get_idx(alg, CSG_ALGS);
 | 
			
		||||
    if (alg_idx < 0 || alg_idx >= long(CSG_ALGS.size())) return;
 | 
			
		||||
    
 | 
			
		||||
    // If there is a valid display in place, save its camera.
 | 
			
		||||
    auto cam = m_canvas->get_display() ?
 | 
			
		||||
                   m_canvas->get_display()->get_camera() : nullptr;
 | 
			
		||||
 | 
			
		||||
    if (alg == "EnricoShader") {
 | 
			
		||||
        m_alg_select->SetSelection(int(alg_idx));
 | 
			
		||||
        m_depth_select->Disable();
 | 
			
		||||
        m_optimization_select->Disable();
 | 
			
		||||
        m_csg_toggle->Disable();
 | 
			
		||||
        
 | 
			
		||||
        m_ocsgdisplay.reset();
 | 
			
		||||
        canvas()->set_display(nullptr);
 | 
			
		||||
        m_shadercsg_display = std::make_shared<ShaderCSGRenderer>(canvas());
 | 
			
		||||
        canvas()->set_display(m_shadercsg_display);
 | 
			
		||||
    } else {
 | 
			
		||||
        if (m_csg_settings.get_algo() > 0) m_depth_select->Enable(true);
 | 
			
		||||
        m_alg_select->SetSelection(m_csg_settings.get_algo());
 | 
			
		||||
        m_depth_select->SetSelection(m_csg_settings.get_depth_algo());
 | 
			
		||||
        m_optimization_select->SetSelection(m_csg_settings.get_optimization());
 | 
			
		||||
        m_convexity_spin->SetValue(int(m_csg_settings.get_convexity()));
 | 
			
		||||
        m_csg_toggle->SetValue(m_csg_settings.is_enabled());
 | 
			
		||||
        m_optimization_select->Enable();
 | 
			
		||||
        m_csg_toggle->Enable();
 | 
			
		||||
        
 | 
			
		||||
        m_shadercsg_display.reset();
 | 
			
		||||
        canvas()->set_display(nullptr);
 | 
			
		||||
        m_ocsgdisplay = std::make_shared<OCSGRenderer>(canvas());
 | 
			
		||||
        m_ocsgdisplay->apply_csgsettings(m_csg_settings);
 | 
			
		||||
        canvas()->set_display(m_ocsgdisplay);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    if (cam)
 | 
			
		||||
        m_canvas->get_display()->set_camera(cam);
 | 
			
		||||
    
 | 
			
		||||
    m_ctl->remove_displays();
 | 
			
		||||
    m_ctl->add_display(m_canvas->get_display());
 | 
			
		||||
    m_canvas->get_display()->get_fps_counter().add_listener([this](double fps) {
 | 
			
		||||
        m_fpstext->SetLabel(wxString::Format("fps: %.2f", fps));
 | 
			
		||||
        m_fps_avg = 0.9 * m_fps_avg + 0.1 * fps;
 | 
			
		||||
    });
 | 
			
		||||
    
 | 
			
		||||
    if (IsShown()) {
 | 
			
		||||
        activate_canvas_display();
 | 
			
		||||
        m_canvas->get_display()->on_scene_updated(*m_scene);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MyFrame::activate_canvas_display()
 | 
			
		||||
{
 | 
			
		||||
    const wxSize ClientSize = m_canvas->GetClientSize();
 | 
			
		||||
    m_canvas->get_display()->set_active(ClientSize.x, ClientSize.y);
 | 
			
		||||
    enable_multisampling(m_ms_toggle->GetValue());
 | 
			
		||||
    
 | 
			
		||||
    m_canvas->Bind(wxEVT_PAINT, [this](wxPaintEvent &) {
 | 
			
		||||
        // This is required even though dc is not used otherwise.
 | 
			
		||||
        wxPaintDC dc(m_canvas.get());
 | 
			
		||||
        const wxSize csize = m_canvas->GetClientSize();
 | 
			
		||||
        m_canvas->get_display()->set_screen_size(csize.x, csize.y);
 | 
			
		||||
        m_canvas->get_display()->repaint();
 | 
			
		||||
    });
 | 
			
		||||
    
 | 
			
		||||
    m_canvas->Bind(wxEVT_SIZE, [this](wxSizeEvent &) {
 | 
			
		||||
        const wxSize csize = m_canvas->GetClientSize();
 | 
			
		||||
        m_canvas->get_display()->set_screen_size(csize.x, csize.y);
 | 
			
		||||
        m_canvas->get_display()->repaint();
 | 
			
		||||
    });
 | 
			
		||||
    
 | 
			
		||||
    // Do the repaint continuously
 | 
			
		||||
    m_canvas->Bind(wxEVT_IDLE, [this](wxIdleEvent &evt) {
 | 
			
		||||
        m_canvas->get_display()->repaint();
 | 
			
		||||
        evt.RequestMore();
 | 
			
		||||
    });
 | 
			
		||||
    
 | 
			
		||||
    bind_canvas_events(m_mouse);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
MyFrame::MyFrame(const wxString &title, const wxPoint &pos, const wxSize &size, 
 | 
			
		||||
                 const wxCmdLineParser &parser):
 | 
			
		||||
    wxFrame(nullptr, wxID_ANY, title, pos, size)
 | 
			
		||||
{
 | 
			
		||||
    wxMenu *menuFile = new wxMenu;
 | 
			
		||||
    menuFile->Append(wxID_OPEN);
 | 
			
		||||
    menuFile->Append(wxID_EXIT);
 | 
			
		||||
    wxMenuBar *menuBar = new wxMenuBar;
 | 
			
		||||
    menuBar->Append( menuFile, "&File" );
 | 
			
		||||
    SetMenuBar( menuBar );
 | 
			
		||||
    
 | 
			
		||||
    m_stbar = std::make_shared<Slic3r::GUI::ProgressStatusBar>(this);
 | 
			
		||||
    m_stbar->embed(this);
 | 
			
		||||
    
 | 
			
		||||
    SetStatusText( "Welcome to wxWidgets!" );
 | 
			
		||||
    
 | 
			
		||||
    int attribList[] =
 | 
			
		||||
    {WX_GL_RGBA, WX_GL_DOUBLEBUFFER,
 | 
			
		||||
     // RGB channels each should be allocated with 8 bit depth. One
 | 
			
		||||
     // should almost certainly get these bit depths by default.
 | 
			
		||||
     WX_GL_MIN_RED, 8, WX_GL_MIN_GREEN, 8, WX_GL_MIN_BLUE, 8,
 | 
			
		||||
     // Requesting an 8 bit alpha channel. Interestingly, the NVIDIA
 | 
			
		||||
     // drivers would most likely work with some alpha plane, but
 | 
			
		||||
     // glReadPixels would not return the alpha channel on NVIDIA if
 | 
			
		||||
     // not requested when the GL context is created.
 | 
			
		||||
     WX_GL_MIN_ALPHA, 8, WX_GL_DEPTH_SIZE, 8, WX_GL_STENCIL_SIZE, 8,
 | 
			
		||||
     WX_GL_SAMPLE_BUFFERS, GL_TRUE, WX_GL_SAMPLES, 4, 0};
 | 
			
		||||
    
 | 
			
		||||
    m_scene = std::make_shared<Scene>();
 | 
			
		||||
    m_ctl = std::make_shared<Controller>();
 | 
			
		||||
    m_ctl->set_scene(m_scene);
 | 
			
		||||
    
 | 
			
		||||
    m_canvas = std::make_shared<Canvas>(this, wxID_ANY, attribList,
 | 
			
		||||
                                        wxDefaultPosition, wxDefaultSize,
 | 
			
		||||
                                        wxWANTS_CHARS | wxFULL_REPAINT_ON_RESIZE);
 | 
			
		||||
    
 | 
			
		||||
    read_csg_settings(parser);
 | 
			
		||||
 | 
			
		||||
    wxPanel *control_panel = new wxPanel(this);
 | 
			
		||||
 | 
			
		||||
    auto controlsizer = new wxBoxSizer(wxHORIZONTAL);
 | 
			
		||||
    auto slider_sizer = new wxBoxSizer(wxVERTICAL);
 | 
			
		||||
    auto console_sizer = new wxBoxSizer(wxVERTICAL);
 | 
			
		||||
    
 | 
			
		||||
    auto slider = new wxSlider(control_panel, wxID_ANY, 0, 0, 100,
 | 
			
		||||
                               wxDefaultPosition, wxDefaultSize,
 | 
			
		||||
                               wxSL_VERTICAL);
 | 
			
		||||
    slider_sizer->Add(slider, 1, wxEXPAND);
 | 
			
		||||
    
 | 
			
		||||
    m_ms_toggle = new wxToggleButton(control_panel, wxID_ANY, "Multisampling");
 | 
			
		||||
    console_sizer->Add(m_ms_toggle, 0, wxALL | wxEXPAND, 5);
 | 
			
		||||
    
 | 
			
		||||
    m_csg_toggle = new wxToggleButton(control_panel, wxID_ANY, "CSG");
 | 
			
		||||
    m_csg_toggle->SetValue(true);
 | 
			
		||||
    console_sizer->Add(m_csg_toggle, 0, wxALL | wxEXPAND, 5);
 | 
			
		||||
    
 | 
			
		||||
    auto add_combobox = [control_panel, console_sizer]
 | 
			
		||||
            (const wxString &label, const std::vector<wxString> &list)
 | 
			
		||||
    {
 | 
			
		||||
        auto widget = new wxComboBox(control_panel, wxID_ANY, list[0],
 | 
			
		||||
                wxDefaultPosition, wxDefaultSize,
 | 
			
		||||
                int(list.size()), list.data());
 | 
			
		||||
        
 | 
			
		||||
        auto sz = new wxBoxSizer(wxHORIZONTAL);
 | 
			
		||||
        sz->Add(new wxStaticText(control_panel, wxID_ANY, label), 0,
 | 
			
		||||
                wxALL | wxALIGN_CENTER, 5);
 | 
			
		||||
        sz->Add(widget, 1, wxALL | wxEXPAND, 5);
 | 
			
		||||
        console_sizer->Add(sz, 0, wxEXPAND);
 | 
			
		||||
        return widget;
 | 
			
		||||
    };
 | 
			
		||||
    
 | 
			
		||||
    auto add_spinctl = [control_panel, console_sizer]
 | 
			
		||||
            (const wxString &label, int initial, int min, int max)
 | 
			
		||||
    {    
 | 
			
		||||
        auto widget = new wxSpinCtrl(
 | 
			
		||||
                    control_panel, wxID_ANY,
 | 
			
		||||
                    wxString::Format("%d", initial),
 | 
			
		||||
                    wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, min, max,
 | 
			
		||||
                    initial);
 | 
			
		||||
        
 | 
			
		||||
        auto sz = new wxBoxSizer(wxHORIZONTAL);
 | 
			
		||||
        sz->Add(new wxStaticText(control_panel, wxID_ANY, label), 0,
 | 
			
		||||
                wxALL | wxALIGN_CENTER, 5);
 | 
			
		||||
        sz->Add(widget, 1, wxALL | wxEXPAND, 5);
 | 
			
		||||
        console_sizer->Add(sz, 0, wxEXPAND);
 | 
			
		||||
        return widget;
 | 
			
		||||
    };
 | 
			
		||||
    
 | 
			
		||||
    m_convexity_spin = add_spinctl("Convexity", CSGSettings::DEFAULT_CONVEXITY, 0, 100);
 | 
			
		||||
    
 | 
			
		||||
    m_alg_select = add_combobox("Algorithm", CSG_ALGS);
 | 
			
		||||
    m_depth_select = add_combobox("Depth Complexity", CSG_DEPTH);
 | 
			
		||||
    m_optimization_select = add_combobox("Optimization", CSG_OPT);
 | 
			
		||||
    
 | 
			
		||||
    m_fpstext = new wxStaticText(control_panel, wxID_ANY, "");
 | 
			
		||||
    console_sizer->Add(m_fpstext, 0, wxALL, 5);
 | 
			
		||||
    
 | 
			
		||||
    m_record_btn = new wxToggleButton(control_panel, wxID_ANY, "Record");
 | 
			
		||||
    console_sizer->Add(m_record_btn, 0, wxALL | wxEXPAND, 5);
 | 
			
		||||
    
 | 
			
		||||
    controlsizer->Add(slider_sizer, 0, wxEXPAND);
 | 
			
		||||
    controlsizer->Add(console_sizer, 1, wxEXPAND);
 | 
			
		||||
    
 | 
			
		||||
    control_panel->SetSizer(controlsizer);
 | 
			
		||||
    
 | 
			
		||||
    auto sizer = new wxBoxSizer(wxHORIZONTAL);
 | 
			
		||||
    sizer->Add(m_canvas.get(), 1, wxEXPAND);
 | 
			
		||||
    sizer->Add(control_panel, 0, wxEXPAND);
 | 
			
		||||
    SetSizer(sizer);
 | 
			
		||||
    
 | 
			
		||||
    wxString alg;
 | 
			
		||||
    if (!parser.Found("algorithm", &alg)) alg = "Auto";
 | 
			
		||||
    
 | 
			
		||||
    set_renderer_algorithm(alg);
 | 
			
		||||
    
 | 
			
		||||
    Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent &evt){
 | 
			
		||||
        if (m_canvas) RemoveChild(m_canvas.get());
 | 
			
		||||
        m_canvas.reset();
 | 
			
		||||
        if (!m_mouse.is_playing()) evt.Skip();
 | 
			
		||||
        else m_mouse.stop();
 | 
			
		||||
    });    
 | 
			
		||||
    
 | 
			
		||||
    Bind(wxEVT_MENU, [this](wxCommandEvent &) {
 | 
			
		||||
        wxFileDialog dlg(this, "Select project file",  wxEmptyString,
 | 
			
		||||
                         wxEmptyString, "*.3mf", wxFD_OPEN|wxFD_FILE_MUST_EXIST);
 | 
			
		||||
 | 
			
		||||
        if (dlg.ShowModal() == wxID_OK) load_model(dlg.GetPath().ToStdString());
 | 
			
		||||
    }, wxID_OPEN);
 | 
			
		||||
    
 | 
			
		||||
    Bind(wxEVT_MENU, [this](wxCommandEvent &) { Close(true); }, wxID_EXIT);
 | 
			
		||||
    
 | 
			
		||||
    Bind(wxEVT_SHOW, [this](wxShowEvent &) {
 | 
			
		||||
        activate_canvas_display();
 | 
			
		||||
    });
 | 
			
		||||
    
 | 
			
		||||
    Bind(wxEVT_SLIDER, [this, slider](wxCommandEvent &) {
 | 
			
		||||
        m_ctl->move_clip_plane(double(slider->GetValue()));
 | 
			
		||||
    });
 | 
			
		||||
    
 | 
			
		||||
    m_ms_toggle->Bind(wxEVT_TOGGLEBUTTON, [this](wxCommandEvent &){
 | 
			
		||||
        enable_multisampling(m_ms_toggle->GetValue());
 | 
			
		||||
        m_canvas->get_display()->repaint();
 | 
			
		||||
    });
 | 
			
		||||
    
 | 
			
		||||
    m_csg_toggle->Bind(wxEVT_TOGGLEBUTTON, [this](wxCommandEvent &){
 | 
			
		||||
        CSGSettings stt = m_ocsgdisplay->get_csgsettings();
 | 
			
		||||
        stt.enable_csg(m_csg_toggle->GetValue());
 | 
			
		||||
        m_ocsgdisplay->apply_csgsettings(stt);
 | 
			
		||||
    });
 | 
			
		||||
    
 | 
			
		||||
    m_alg_select->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent &) {
 | 
			
		||||
        wxString alg = m_alg_select->GetValue();
 | 
			
		||||
        int sel = m_alg_select->GetSelection();
 | 
			
		||||
        m_csg_settings.set_algo(sel);
 | 
			
		||||
        set_renderer_algorithm(alg);
 | 
			
		||||
    });
 | 
			
		||||
    
 | 
			
		||||
    m_depth_select->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent &) {
 | 
			
		||||
        int sel = m_depth_select->GetSelection();
 | 
			
		||||
        m_csg_settings.set_depth_algo(sel);
 | 
			
		||||
        if (m_ocsgdisplay) m_ocsgdisplay->apply_csgsettings(m_csg_settings);
 | 
			
		||||
    });
 | 
			
		||||
    
 | 
			
		||||
    m_optimization_select->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent &) {
 | 
			
		||||
        int sel = m_optimization_select->GetSelection();
 | 
			
		||||
        m_csg_settings.set_optimization(sel);
 | 
			
		||||
        if (m_ocsgdisplay) m_ocsgdisplay->apply_csgsettings(m_csg_settings);
 | 
			
		||||
    });
 | 
			
		||||
    
 | 
			
		||||
    m_convexity_spin->Bind(wxEVT_SPINCTRL, [this](wxSpinEvent &) {
 | 
			
		||||
        int c = m_convexity_spin->GetValue();
 | 
			
		||||
        if (c > 0) {
 | 
			
		||||
            m_csg_settings.set_convexity(unsigned(c));
 | 
			
		||||
            if (m_ocsgdisplay) m_ocsgdisplay->apply_csgsettings(m_csg_settings);
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
    
 | 
			
		||||
    m_record_btn->Bind(wxEVT_TOGGLEBUTTON, [this](wxCommandEvent &) {
 | 
			
		||||
        if (!m_ui_job) {
 | 
			
		||||
            m_stbar->set_status_text("No project loaded!");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        if (m_record_btn->GetValue()) {
 | 
			
		||||
            if (auto c = m_canvas->get_display()->get_camera()) reset(*c);
 | 
			
		||||
            m_ctl->on_scene_updated(*m_scene);
 | 
			
		||||
            m_mouse.record(true);
 | 
			
		||||
        } else {
 | 
			
		||||
            m_mouse.record(false);
 | 
			
		||||
            wxFileDialog dlg(this, "Select output file",
 | 
			
		||||
                             wxEmptyString, wxEmptyString, "*.events",
 | 
			
		||||
                             wxFD_SAVE|wxFD_OVERWRITE_PROMPT);
 | 
			
		||||
            
 | 
			
		||||
            if (dlg.ShowModal() == wxID_OK) {
 | 
			
		||||
                std::fstream stream(dlg.GetPath().ToStdString(),
 | 
			
		||||
                                    std::fstream::out);
 | 
			
		||||
                
 | 
			
		||||
                if (stream.good()) {
 | 
			
		||||
                    stream << m_ui_job->get_project_fname() << "\n";
 | 
			
		||||
                    wxSize winsize = GetSize();
 | 
			
		||||
                    stream << winsize.x << " " << winsize.y << "\n";
 | 
			
		||||
                    m_mouse.save(stream);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MyFrame::bind_canvas_events(MouseInput &ms)
 | 
			
		||||
{
 | 
			
		||||
    m_canvas->Bind(wxEVT_MOUSEWHEEL, [&ms](wxMouseEvent &evt) {
 | 
			
		||||
        ms.scroll(evt.GetWheelRotation(), evt.GetWheelDelta(),
 | 
			
		||||
                  evt.GetWheelAxis() == wxMOUSE_WHEEL_VERTICAL ?
 | 
			
		||||
                      Slic3r::GL::MouseInput::waVertical :
 | 
			
		||||
                      Slic3r::GL::MouseInput::waHorizontal);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    m_canvas->Bind(wxEVT_MOTION, [&ms](wxMouseEvent &evt) {
 | 
			
		||||
        ms.move_to(evt.GetPosition().x, evt.GetPosition().y);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    m_canvas->Bind(wxEVT_RIGHT_DOWN, [&ms](wxMouseEvent & /*evt*/) {
 | 
			
		||||
        ms.right_click_down();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    m_canvas->Bind(wxEVT_RIGHT_UP, [&ms](wxMouseEvent & /*evt*/) {
 | 
			
		||||
        ms.right_click_up();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    m_canvas->Bind(wxEVT_LEFT_DOWN, [&ms](wxMouseEvent & /*evt*/) {
 | 
			
		||||
        ms.left_click_down();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    m_canvas->Bind(wxEVT_LEFT_UP, [&ms](wxMouseEvent & /*evt*/) {
 | 
			
		||||
        ms.left_click_up();
 | 
			
		||||
    });
 | 
			
		||||
    
 | 
			
		||||
    ms.add_listener(m_ctl);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MyFrame::SLAJob::process() 
 | 
			
		||||
{
 | 
			
		||||
    using SlStatus = Slic3r::PrintBase::SlicingStatus;
 | 
			
		||||
    
 | 
			
		||||
    Slic3r::DynamicPrintConfig cfg;
 | 
			
		||||
    auto model = Slic3r::Model::read_from_file(m_fname, &cfg);
 | 
			
		||||
    
 | 
			
		||||
    m_print = std::make_unique<Slic3r::SLAPrint>();
 | 
			
		||||
    m_print->apply(model, cfg);
 | 
			
		||||
    
 | 
			
		||||
    Slic3r::PrintBase::TaskParams params;
 | 
			
		||||
    params.to_object_step = Slic3r::slaposHollowing;
 | 
			
		||||
    m_print->set_task(params);
 | 
			
		||||
    
 | 
			
		||||
    m_print->set_status_callback([this](const SlStatus &status) {
 | 
			
		||||
        update_status(status.percent, status.text);
 | 
			
		||||
    });
 | 
			
		||||
    
 | 
			
		||||
    try {
 | 
			
		||||
        m_print->process();
 | 
			
		||||
    } catch(std::exception &e) {
 | 
			
		||||
        update_status(0, wxString("Exception during processing: ") + e.what());
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//int main() {}
 | 
			
		||||
							
								
								
									
										7
									
								
								sandboxes/openvdb/CMakeLists.txt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								sandboxes/openvdb/CMakeLists.txt
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,7 @@
 | 
			
		|||
if(TARGET OpenVDB::openvdb)
 | 
			
		||||
    add_executable(openvdb_example openvdb_example.cpp)
 | 
			
		||||
 | 
			
		||||
    target_link_libraries(openvdb_example libslic3r)
 | 
			
		||||
    target_link_libraries(openvdb_example OpenVDB::openvdb)
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										37
									
								
								sandboxes/openvdb/openvdb_example.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								sandboxes/openvdb/openvdb_example.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,37 @@
 | 
			
		|||
#include <openvdb/openvdb.h>
 | 
			
		||||
#include <iostream>
 | 
			
		||||
 | 
			
		||||
int main()
 | 
			
		||||
{
 | 
			
		||||
    // Initialize the OpenVDB library.  This must be called at least
 | 
			
		||||
    // once per program and may safely be called multiple times.
 | 
			
		||||
    openvdb::initialize();
 | 
			
		||||
    // Create an empty floating-point grid with background value 0.
 | 
			
		||||
    openvdb::FloatGrid::Ptr grid = openvdb::FloatGrid::create();
 | 
			
		||||
    std::cout << "Testing random access:" << std::endl;
 | 
			
		||||
    // Get an accessor for coordinate-based access to voxels.
 | 
			
		||||
    openvdb::FloatGrid::Accessor accessor = grid->getAccessor();
 | 
			
		||||
    // Define a coordinate with large signed indices.
 | 
			
		||||
    openvdb::Coord xyz(1000, -200000000, 30000000);
 | 
			
		||||
    // Set the voxel value at (1000, -200000000, 30000000) to 1.
 | 
			
		||||
    accessor.setValue(xyz, 1.0);
 | 
			
		||||
    // Verify that the voxel value at (1000, -200000000, 30000000) is 1.
 | 
			
		||||
    std::cout << "Grid" << xyz << " = " << accessor.getValue(xyz) << std::endl;
 | 
			
		||||
    // Reset the coordinates to those of a different voxel.
 | 
			
		||||
    xyz.reset(1000, 200000000, -30000000);
 | 
			
		||||
    // Verify that the voxel value at (1000, 200000000, -30000000) is
 | 
			
		||||
    // the background value, 0.
 | 
			
		||||
    std::cout << "Grid" << xyz << " = " << accessor.getValue(xyz) << std::endl;
 | 
			
		||||
    // Set the voxel value at (1000, 200000000, -30000000) to 2.
 | 
			
		||||
    accessor.setValue(xyz, 2.0);
 | 
			
		||||
    // Set the voxels at the two extremes of the available coordinate space.
 | 
			
		||||
    // For 32-bit signed coordinates these are (-2147483648, -2147483648, -2147483648)
 | 
			
		||||
    // and (2147483647, 2147483647, 2147483647).
 | 
			
		||||
    accessor.setValue(openvdb::Coord::min(), 3.0f);
 | 
			
		||||
    accessor.setValue(openvdb::Coord::max(), 4.0f);
 | 
			
		||||
    std::cout << "Testing sequential access:" << std::endl;
 | 
			
		||||
    // Print all active ("on") voxels by means of an iterator.
 | 
			
		||||
    for (openvdb::FloatGrid::ValueOnCIter iter = grid->cbeginValueOn(); iter; ++iter) {
 | 
			
		||||
        std::cout << "Grid" << iter.getCoord() << " = " << *iter << std::endl;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										2
									
								
								sandboxes/slasupporttree/CMakeLists.txt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								sandboxes/slasupporttree/CMakeLists.txt
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,2 @@
 | 
			
		|||
add_executable(slasupporttree slasupporttree.cpp)
 | 
			
		||||
target_link_libraries(slasupporttree libslic3r ${Boost_LIBRARIES} ${TBB_LIBRARIES} ${Boost_LIBRARIES} ${CMAKE_DL_LIBS})
 | 
			
		||||
							
								
								
									
										42
									
								
								sandboxes/slasupporttree/slasupporttree.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								sandboxes/slasupporttree/slasupporttree.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,42 @@
 | 
			
		|||
#include <iostream>
 | 
			
		||||
#include <fstream>
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
#include <libslic3r/libslic3r.h>
 | 
			
		||||
#include <libslic3r/Model.hpp>
 | 
			
		||||
#include <libslic3r/Tesselate.hpp>
 | 
			
		||||
#include <libslic3r/ClipperUtils.hpp>
 | 
			
		||||
#include <libslic3r/SLA/SLAAutoSupports.hpp>
 | 
			
		||||
#include <libslic3r/SLA/SLASupportTree.hpp>
 | 
			
		||||
#include <libslic3r/SLAPrint.hpp>
 | 
			
		||||
#include <libslic3r/MTUtils.hpp>
 | 
			
		||||
 | 
			
		||||
#include <tbb/parallel_for.h>
 | 
			
		||||
#include <tbb/mutex.h>
 | 
			
		||||
#include <future>
 | 
			
		||||
 | 
			
		||||
const std::string USAGE_STR = {
 | 
			
		||||
    "Usage: slasupporttree stlfilename.stl"
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
int main(const int argc, const char *argv[]) {
 | 
			
		||||
    using namespace Slic3r;
 | 
			
		||||
    using std::cout; using std::endl;
 | 
			
		||||
 | 
			
		||||
    if(argc < 2) {
 | 
			
		||||
        cout << USAGE_STR << endl;
 | 
			
		||||
        return EXIT_SUCCESS;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    DynamicPrintConfig config;
 | 
			
		||||
 | 
			
		||||
    Model model = Model::read_from_file(argv[1], &config);
 | 
			
		||||
 | 
			
		||||
    SLAPrint print;
 | 
			
		||||
 | 
			
		||||
    print.apply(model, config);
 | 
			
		||||
    print.process();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    return EXIT_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue