mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-11-02 20:51:23 -07:00 
			
		
		
		
	Merge remote-tracking branch 'origin/master' into tm_autoplacement
This commit is contained in:
		
						commit
						8c57541fe9
					
				
					 20 changed files with 338 additions and 110 deletions
				
			
		
							
								
								
									
										
											BIN
										
									
								
								resources/models/sl1_bed.stl
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								resources/models/sl1_bed.stl
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
						 | 
				
			
			@ -1125,7 +1125,7 @@ std::string Print::validate() const
 | 
			
		|||
        // Check horizontal clearance.
 | 
			
		||||
        {
 | 
			
		||||
            Polygons convex_hulls_other;
 | 
			
		||||
            for (PrintObject *object : m_objects) {
 | 
			
		||||
            for (const PrintObject *object : m_objects) {
 | 
			
		||||
                // Get convex hull of all meshes assigned to this print object.
 | 
			
		||||
                Polygon convex_hull;
 | 
			
		||||
                {
 | 
			
		||||
| 
						 | 
				
			
			@ -1186,46 +1186,63 @@ std::string Print::validate() const
 | 
			
		|||
            return L("The Wipe Tower is currently only supported for the Marlin, RepRap/Sprinter and Repetier G-code flavors.");
 | 
			
		||||
        if (! m_config.use_relative_e_distances)
 | 
			
		||||
            return L("The Wipe Tower is currently only supported with the relative extruder addressing (use_relative_e_distances=1).");
 | 
			
		||||
        SlicingParameters slicing_params0 = m_objects.front()->slicing_parameters();
 | 
			
		||||
 | 
			
		||||
        const PrintObject* tallest_object = m_objects.front(); // let's find the tallest object
 | 
			
		||||
        for (const auto* object : m_objects)
 | 
			
		||||
            if (*(object->layer_height_profile.end()-2) > *(tallest_object->layer_height_profile.end()-2) )
 | 
			
		||||
                    tallest_object = object;
 | 
			
		||||
 | 
			
		||||
        for (PrintObject *object : m_objects) {
 | 
			
		||||
            SlicingParameters slicing_params = object->slicing_parameters();
 | 
			
		||||
            if (std::abs(slicing_params.first_print_layer_height - slicing_params0.first_print_layer_height) > EPSILON ||
 | 
			
		||||
                std::abs(slicing_params.layer_height             - slicing_params0.layer_height            ) > EPSILON)
 | 
			
		||||
                return L("The Wipe Tower is only supported for multiple objects if they have equal layer heigths");
 | 
			
		||||
            if (slicing_params.raft_layers() != slicing_params0.raft_layers())
 | 
			
		||||
                return L("The Wipe Tower is only supported for multiple objects if they are printed over an equal number of raft layers");
 | 
			
		||||
            if (object->config().support_material_contact_distance != m_objects.front()->config().support_material_contact_distance)
 | 
			
		||||
                return L("The Wipe Tower is only supported for multiple objects if they are printed with the same support_material_contact_distance");
 | 
			
		||||
            if (! equal_layering(slicing_params, slicing_params0))
 | 
			
		||||
                return L("The Wipe Tower is only supported for multiple objects if they are sliced equally.");
 | 
			
		||||
 | 
			
		||||
            if (m_config.variable_layer_height) { // comparing layer height profiles
 | 
			
		||||
                bool failed = false;
 | 
			
		||||
                // layer_height_profile should be set by Print::apply().
 | 
			
		||||
                if (tallest_object->layer_height_profile.size() >= object->layer_height_profile.size()) {
 | 
			
		||||
                    int i = 0;
 | 
			
		||||
                    while (i < object->layer_height_profile.size() && i < tallest_object->layer_height_profile.size()) {
 | 
			
		||||
                        if (std::abs(tallest_object->layer_height_profile[i] - object->layer_height_profile[i])) {
 | 
			
		||||
                            failed = true;
 | 
			
		||||
                            break;
 | 
			
		||||
                        }
 | 
			
		||||
                        ++i;
 | 
			
		||||
                        if (i == object->layer_height_profile.size()-2) // this element contains this objects max z
 | 
			
		||||
                            if (tallest_object->layer_height_profile[i] > object->layer_height_profile[i]) // the difference does not matter in this case
 | 
			
		||||
                                ++i;
 | 
			
		||||
                    }
 | 
			
		||||
        if (m_objects.size() > 1) {
 | 
			
		||||
            bool                                has_custom_layering = false;
 | 
			
		||||
            std::vector<std::vector<coordf_t>>  layer_height_profiles;
 | 
			
		||||
            for (const PrintObject *object : m_objects) {
 | 
			
		||||
                has_custom_layering = ! object->model_object()->layer_height_ranges.empty() || ! object->model_object()->layer_height_profile.empty();
 | 
			
		||||
                if (has_custom_layering) {
 | 
			
		||||
                    layer_height_profiles.assign(m_objects.size(), std::vector<coordf_t>());
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                    failed = true;
 | 
			
		||||
            }
 | 
			
		||||
            SlicingParameters slicing_params0    = m_objects.front()->slicing_parameters();
 | 
			
		||||
            size_t            tallest_object_idx = 0;
 | 
			
		||||
            if (has_custom_layering)
 | 
			
		||||
                PrintObject::update_layer_height_profile(*m_objects.front()->model_object(), slicing_params0, layer_height_profiles.front());
 | 
			
		||||
            for (size_t i = 1; i < m_objects.size(); ++ i) {
 | 
			
		||||
                const PrintObject      *object         = m_objects[i];
 | 
			
		||||
                const SlicingParameters slicing_params = object->slicing_parameters();
 | 
			
		||||
                if (std::abs(slicing_params.first_print_layer_height - slicing_params0.first_print_layer_height) > EPSILON ||
 | 
			
		||||
                    std::abs(slicing_params.layer_height             - slicing_params0.layer_height            ) > EPSILON)
 | 
			
		||||
                    return L("The Wipe Tower is only supported for multiple objects if they have equal layer heigths");
 | 
			
		||||
                if (slicing_params.raft_layers() != slicing_params0.raft_layers())
 | 
			
		||||
                    return L("The Wipe Tower is only supported for multiple objects if they are printed over an equal number of raft layers");
 | 
			
		||||
                if (object->config().support_material_contact_distance != m_objects.front()->config().support_material_contact_distance)
 | 
			
		||||
                    return L("The Wipe Tower is only supported for multiple objects if they are printed with the same support_material_contact_distance");
 | 
			
		||||
                if (! equal_layering(slicing_params, slicing_params0))
 | 
			
		||||
                    return L("The Wipe Tower is only supported for multiple objects if they are sliced equally.");
 | 
			
		||||
                if (has_custom_layering) {
 | 
			
		||||
                    PrintObject::update_layer_height_profile(*object->model_object(), slicing_params, layer_height_profiles[i]);
 | 
			
		||||
                    if (*(layer_height_profiles[i].end()-2) > *(layer_height_profiles[tallest_object_idx].end()-2))
 | 
			
		||||
                        tallest_object_idx = i;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
                if (failed)
 | 
			
		||||
                    return L("The Wipe tower is only supported if all objects have the same layer height profile");
 | 
			
		||||
            if (has_custom_layering) {
 | 
			
		||||
                const std::vector<coordf_t> &layer_height_profile_tallest = layer_height_profiles[tallest_object_idx];
 | 
			
		||||
                for (size_t idx_object = 0; idx_object < m_objects.size(); ++ idx_object) {
 | 
			
		||||
                    const PrintObject           *object               = m_objects[idx_object];
 | 
			
		||||
                    const std::vector<coordf_t> &layer_height_profile = layer_height_profiles[idx_object];
 | 
			
		||||
                    bool                         failed               = false;
 | 
			
		||||
                    if (layer_height_profile_tallest.size() >= layer_height_profile.size()) {
 | 
			
		||||
                        int i = 0;
 | 
			
		||||
                        while (i < layer_height_profile.size() && i < layer_height_profile_tallest.size()) {
 | 
			
		||||
                            if (std::abs(layer_height_profile_tallest[i] - layer_height_profile[i])) {
 | 
			
		||||
                                failed = true;
 | 
			
		||||
                                break;
 | 
			
		||||
                            }
 | 
			
		||||
                            ++ i;
 | 
			
		||||
                            if (i == layer_height_profile.size() - 2) // this element contains this objects max z
 | 
			
		||||
                                if (layer_height_profile_tallest[i] > layer_height_profile[i]) // the difference does not matter in this case
 | 
			
		||||
                                    ++ i;
 | 
			
		||||
                        }
 | 
			
		||||
                    } else
 | 
			
		||||
                        failed = true;
 | 
			
		||||
                    if (failed)
 | 
			
		||||
                        return L("The Wipe tower is only supported if all objects have the same layer height profile");
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -83,10 +83,6 @@ public:
 | 
			
		|||
    // vector of (vectors of volume ids), indexed by region_id
 | 
			
		||||
    std::vector<std::vector<int>> region_volumes;
 | 
			
		||||
 | 
			
		||||
    // Profile of increasing z to a layer height, to be linearly interpolated when calculating the layers.
 | 
			
		||||
    // The pairs of <z, layer_height> are packed into a 1D array.
 | 
			
		||||
    std::vector<coordf_t>   layer_height_profile;
 | 
			
		||||
    
 | 
			
		||||
    // this is set to true when LayerRegion->slices is split in top/internal/bottom
 | 
			
		||||
    // so that next call to make_perimeters() performs a union() before computing loops
 | 
			
		||||
    bool                    typed_slices;
 | 
			
		||||
| 
						 | 
				
			
			@ -175,7 +171,7 @@ private:
 | 
			
		|||
    void infill();
 | 
			
		||||
    void generate_support_material();
 | 
			
		||||
 | 
			
		||||
    void _slice();
 | 
			
		||||
    void _slice(const std::vector<coordf_t> &layer_height_profile);
 | 
			
		||||
    std::string _fix_slicing_errors();
 | 
			
		||||
    void _simplify_slices(double distance);
 | 
			
		||||
    void _make_perimeters();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -64,8 +64,6 @@ PrintObject::PrintObject(Print* print, ModelObject* model_object, bool add_insta
 | 
			
		|||
        }
 | 
			
		||||
        this->set_copies(copies);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this->layer_height_profile = model_object->layer_height_profile;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PrintBase::ApplyStatus PrintObject::set_copies(const Points &points)
 | 
			
		||||
| 
						 | 
				
			
			@ -105,9 +103,10 @@ void PrintObject::slice()
 | 
			
		|||
    if (! this->set_started(posSlice))
 | 
			
		||||
        return;
 | 
			
		||||
    m_print->set_status(10, "Processing triangulated mesh");
 | 
			
		||||
    this->update_layer_height_profile(*this->model_object(), this->slicing_parameters(), this->layer_height_profile);
 | 
			
		||||
    std::vector<coordf_t> layer_height_profile;
 | 
			
		||||
    this->update_layer_height_profile(*this->model_object(), this->slicing_parameters(), layer_height_profile);
 | 
			
		||||
    m_print->throw_if_canceled();
 | 
			
		||||
    this->_slice();
 | 
			
		||||
    this->_slice(layer_height_profile);
 | 
			
		||||
    m_print->throw_if_canceled();
 | 
			
		||||
    // Fix the model.
 | 
			
		||||
    //FIXME is this the right place to do? It is done repeateadly at the UI and now here at the backend.
 | 
			
		||||
| 
						 | 
				
			
			@ -1438,7 +1437,7 @@ bool PrintObject::update_layer_height_profile(const ModelObject &model_object, c
 | 
			
		|||
// Resulting expolygons of layer regions are marked as Internal.
 | 
			
		||||
//
 | 
			
		||||
// this should be idempotent
 | 
			
		||||
void PrintObject::_slice()
 | 
			
		||||
void PrintObject::_slice(const std::vector<coordf_t> &layer_height_profile)
 | 
			
		||||
{
 | 
			
		||||
    BOOST_LOG_TRIVIAL(info) << "Slicing objects..." << log_memory_info();
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1457,7 +1456,7 @@ void PrintObject::_slice()
 | 
			
		|||
    {
 | 
			
		||||
        this->clear_layers();
 | 
			
		||||
        // Object layers (pairs of bottom/top Z coordinate), without the raft.
 | 
			
		||||
        std::vector<coordf_t> object_layers = generate_object_layers(slicing_params, this->layer_height_profile);
 | 
			
		||||
        std::vector<coordf_t> object_layers = generate_object_layers(slicing_params, layer_height_profile);
 | 
			
		||||
        // Reserve object layers for the raft. Last layer of the raft is the contact layer.
 | 
			
		||||
        int id = int(slicing_params.raft_layers());
 | 
			
		||||
        slice_zs.reserve(object_layers.size());
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -738,45 +738,46 @@ public:
 | 
			
		|||
    const TriangleMesh& merged_mesh() const {
 | 
			
		||||
        if(meshcache_valid) return meshcache;
 | 
			
		||||
 | 
			
		||||
        meshcache = TriangleMesh();
 | 
			
		||||
        Contour3D merged;
 | 
			
		||||
 | 
			
		||||
        for(auto& head : heads()) {
 | 
			
		||||
            if(m_ctl.stopcondition()) break;
 | 
			
		||||
            if(head.is_valid()) {
 | 
			
		||||
                auto&& m = mesh(head.mesh);
 | 
			
		||||
                meshcache.merge(m);
 | 
			
		||||
            }
 | 
			
		||||
            if(head.is_valid())
 | 
			
		||||
                merged.merge(head.mesh);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for(auto& stick : pillars()) {
 | 
			
		||||
            if(m_ctl.stopcondition()) break;
 | 
			
		||||
            meshcache.merge(mesh(stick.mesh));
 | 
			
		||||
            meshcache.merge(mesh(stick.base));
 | 
			
		||||
            merged.merge(stick.mesh);
 | 
			
		||||
            merged.merge(stick.base);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for(auto& j : junctions()) {
 | 
			
		||||
            if(m_ctl.stopcondition()) break;
 | 
			
		||||
            meshcache.merge(mesh(j.mesh));
 | 
			
		||||
            merged.merge(j.mesh);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for(auto& cb : compact_bridges()) {
 | 
			
		||||
            if(m_ctl.stopcondition()) break;
 | 
			
		||||
            meshcache.merge(mesh(cb.mesh));
 | 
			
		||||
            merged.merge(cb.mesh);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for(auto& bs : bridges()) {
 | 
			
		||||
            if(m_ctl.stopcondition()) break;
 | 
			
		||||
            meshcache.merge(mesh(bs.mesh));
 | 
			
		||||
            merged.merge(bs.mesh);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        if(m_ctl.stopcondition()) {
 | 
			
		||||
            // In case of failure we have to return an empty mesh
 | 
			
		||||
            meshcache = TriangleMesh();
 | 
			
		||||
            return meshcache;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        meshcache = mesh(merged);
 | 
			
		||||
 | 
			
		||||
        // TODO: Is this necessary?
 | 
			
		||||
        meshcache.repair();
 | 
			
		||||
        //meshcache.repair();
 | 
			
		||||
 | 
			
		||||
        BoundingBoxf3&& bb = meshcache.bounding_box();
 | 
			
		||||
        model_height = bb.max(Z) - bb.min(Z);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -45,7 +45,7 @@
 | 
			
		|||
// Improves navigation between sidebar fields
 | 
			
		||||
#define ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION (1 && ENABLE_1_42_0_ALPHA2)
 | 
			
		||||
// Adds print bed models to 3D scene
 | 
			
		||||
#define ENABLE_PRINT_BED_MODELS (0 && ENABLE_1_42_0_ALPHA2)
 | 
			
		||||
#define ENABLE_PRINT_BED_MODELS (1 && ENABLE_1_42_0_ALPHA2)
 | 
			
		||||
#endif // _technologies_h_
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -62,3 +62,5 @@
 | 
			
		|||
#define ENABLE_GENERIC_SUBPARTS_PLACEMENT (1 && ENABLE_1_42_0_ALPHA4)
 | 
			
		||||
// Reworked management of bed shape changes
 | 
			
		||||
#define ENABLE_REWORKED_BED_SHAPE_CHANGE (1 && ENABLE_1_42_0_ALPHA4)
 | 
			
		||||
// Use anisotropic filtering on bed plate texture
 | 
			
		||||
#define ENABLE_ANISOTROPIC_FILTER_ON_BED_TEXTURES (1 && ENABLE_1_42_0_ALPHA4)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -99,6 +99,8 @@ TriangleMesh& TriangleMesh::operator=(const TriangleMesh &other)
 | 
			
		|||
    return *this;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// #define SLIC3R_TRACE_REPAIR
 | 
			
		||||
 | 
			
		||||
void TriangleMesh::repair()
 | 
			
		||||
{
 | 
			
		||||
    if (this->repaired) return;
 | 
			
		||||
| 
						 | 
				
			
			@ -109,7 +111,9 @@ void TriangleMesh::repair()
 | 
			
		|||
    BOOST_LOG_TRIVIAL(debug) << "TriangleMesh::repair() started";
 | 
			
		||||
    
 | 
			
		||||
    // checking exact
 | 
			
		||||
#ifdef SLIC3R_TRACE_REPAIR
 | 
			
		||||
	BOOST_LOG_TRIVIAL(trace) << "\tstl_check_faces_exact";
 | 
			
		||||
#endif /* SLIC3R_TRACE_REPAIR */
 | 
			
		||||
	stl_check_facets_exact(&stl);
 | 
			
		||||
    stl.stats.facets_w_1_bad_edge = (stl.stats.connected_facets_2_edge - stl.stats.connected_facets_3_edge);
 | 
			
		||||
    stl.stats.facets_w_2_bad_edge = (stl.stats.connected_facets_1_edge - stl.stats.connected_facets_2_edge);
 | 
			
		||||
| 
						 | 
				
			
			@ -124,7 +128,9 @@ void TriangleMesh::repair()
 | 
			
		|||
        for (int i = 0; i < iterations; i++) {
 | 
			
		||||
            if (stl.stats.connected_facets_3_edge < stl.stats.number_of_facets) {
 | 
			
		||||
                //printf("Checking nearby. Tolerance= %f Iteration=%d of %d...", tolerance, i + 1, iterations);
 | 
			
		||||
#ifdef SLIC3R_TRACE_REPAIR
 | 
			
		||||
				BOOST_LOG_TRIVIAL(trace) << "\tstl_check_faces_nearby";
 | 
			
		||||
#endif /* SLIC3R_TRACE_REPAIR */
 | 
			
		||||
				stl_check_facets_nearby(&stl, tolerance);
 | 
			
		||||
                //printf("  Fixed %d edges.\n", stl.stats.edges_fixed - last_edges_fixed);
 | 
			
		||||
                //last_edges_fixed = stl.stats.edges_fixed;
 | 
			
		||||
| 
						 | 
				
			
			@ -137,7 +143,9 @@ void TriangleMesh::repair()
 | 
			
		|||
    
 | 
			
		||||
    // remove_unconnected
 | 
			
		||||
    if (stl.stats.connected_facets_3_edge <  stl.stats.number_of_facets) {
 | 
			
		||||
#ifdef SLIC3R_TRACE_REPAIR
 | 
			
		||||
        BOOST_LOG_TRIVIAL(trace) << "\tstl_remove_unconnected_facets";
 | 
			
		||||
#endif /* SLIC3R_TRACE_REPAIR */
 | 
			
		||||
        stl_remove_unconnected_facets(&stl);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
| 
						 | 
				
			
			@ -146,26 +154,36 @@ void TriangleMesh::repair()
 | 
			
		|||
    // Don't fill holes, the current algorithm does more harm than good on complex holes.
 | 
			
		||||
    // Rather let the slicing algorithm close gaps in 2D slices.
 | 
			
		||||
    if (stl.stats.connected_facets_3_edge < stl.stats.number_of_facets) {
 | 
			
		||||
#ifdef SLIC3R_TRACE_REPAIR
 | 
			
		||||
        BOOST_LOG_TRIVIAL(trace) << "\tstl_fill_holes";
 | 
			
		||||
#endif /* SLIC3R_TRACE_REPAIR */
 | 
			
		||||
        stl_fill_holes(&stl);
 | 
			
		||||
        stl_clear_error(&stl);
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    // normal_directions
 | 
			
		||||
#ifdef SLIC3R_TRACE_REPAIR
 | 
			
		||||
    BOOST_LOG_TRIVIAL(trace) << "\tstl_fix_normal_directions";
 | 
			
		||||
#endif /* SLIC3R_TRACE_REPAIR */
 | 
			
		||||
    stl_fix_normal_directions(&stl);
 | 
			
		||||
 | 
			
		||||
    // normal_values
 | 
			
		||||
#ifdef SLIC3R_TRACE_REPAIR
 | 
			
		||||
    BOOST_LOG_TRIVIAL(trace) << "\tstl_fix_normal_values";
 | 
			
		||||
#endif /* SLIC3R_TRACE_REPAIR */
 | 
			
		||||
    stl_fix_normal_values(&stl);
 | 
			
		||||
    
 | 
			
		||||
    // always calculate the volume and reverse all normals if volume is negative
 | 
			
		||||
#ifdef SLIC3R_TRACE_REPAIR
 | 
			
		||||
    BOOST_LOG_TRIVIAL(trace) << "\tstl_calculate_volume";
 | 
			
		||||
#endif /* SLIC3R_TRACE_REPAIR */
 | 
			
		||||
    stl_calculate_volume(&stl);
 | 
			
		||||
    
 | 
			
		||||
    // neighbors
 | 
			
		||||
#ifdef SLIC3R_TRACE_REPAIR
 | 
			
		||||
    BOOST_LOG_TRIVIAL(trace) << "\tstl_verify_neighbors";
 | 
			
		||||
#endif /* SLIC3R_TRACE_REPAIR */
 | 
			
		||||
    stl_verify_neighbors(&stl);
 | 
			
		||||
 | 
			
		||||
    this->repaired = true;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -545,6 +545,11 @@ void GLCanvas3D::Bed::_render_prusa(const std::string &key, float theta) const
 | 
			
		|||
    std::string model_path = resources_dir() + "/models/" + key;
 | 
			
		||||
#endif // ENABLE_PRINT_BED_MODELS
 | 
			
		||||
 | 
			
		||||
#if ENABLE_ANISOTROPIC_FILTER_ON_BED_TEXTURES
 | 
			
		||||
    GLfloat max_anisotropy = 0.0f;
 | 
			
		||||
    ::glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &max_anisotropy);
 | 
			
		||||
#endif // ENABLE_ANISOTROPIC_FILTER_ON_BED_TEXTURES
 | 
			
		||||
 | 
			
		||||
    std::string filename = tex_path + "_top.png";
 | 
			
		||||
    if ((m_top_texture.get_id() == 0) || (m_top_texture.get_source() != filename))
 | 
			
		||||
    {
 | 
			
		||||
| 
						 | 
				
			
			@ -553,6 +558,14 @@ void GLCanvas3D::Bed::_render_prusa(const std::string &key, float theta) const
 | 
			
		|||
            _render_custom();
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
#if ENABLE_ANISOTROPIC_FILTER_ON_BED_TEXTURES
 | 
			
		||||
        if (max_anisotropy > 0.0f)
 | 
			
		||||
        {
 | 
			
		||||
            ::glBindTexture(GL_TEXTURE_2D, m_top_texture.get_id());
 | 
			
		||||
            ::glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_anisotropy);
 | 
			
		||||
            ::glBindTexture(GL_TEXTURE_2D, 0);
 | 
			
		||||
        }
 | 
			
		||||
#endif // ENABLE_ANISOTROPIC_FILTER_ON_BED_TEXTURES
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    filename = tex_path + "_bottom.png";
 | 
			
		||||
| 
						 | 
				
			
			@ -563,6 +576,14 @@ void GLCanvas3D::Bed::_render_prusa(const std::string &key, float theta) const
 | 
			
		|||
            _render_custom();
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
#if ENABLE_ANISOTROPIC_FILTER_ON_BED_TEXTURES
 | 
			
		||||
        if (max_anisotropy > 0.0f)
 | 
			
		||||
        {
 | 
			
		||||
            ::glBindTexture(GL_TEXTURE_2D, m_bottom_texture.get_id());
 | 
			
		||||
            ::glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_anisotropy);
 | 
			
		||||
            ::glBindTexture(GL_TEXTURE_2D, 0);
 | 
			
		||||
        }
 | 
			
		||||
#endif // ENABLE_ANISOTROPIC_FILTER_ON_BED_TEXTURES
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#if ENABLE_PRINT_BED_MODELS
 | 
			
		||||
| 
						 | 
				
			
			@ -570,7 +591,7 @@ void GLCanvas3D::Bed::_render_prusa(const std::string &key, float theta) const
 | 
			
		|||
    {
 | 
			
		||||
        filename = model_path + "_bed.stl";
 | 
			
		||||
        if ((m_model.get_filename() != filename) && m_model.init_from_file(filename, useVBOs))
 | 
			
		||||
            m_model.center_around(m_bounding_box.center() - Vec3d(0.0, 0.0, 1.0 + 0.5 * m_model.get_bounding_box().size()(2)));
 | 
			
		||||
            m_model.center_around(m_bounding_box.center() - Vec3d(0.0, 0.0, 0.1 + 0.5 * m_model.get_bounding_box().size()(2)));
 | 
			
		||||
 | 
			
		||||
        if (!m_model.get_filename().empty())
 | 
			
		||||
        {
 | 
			
		||||
| 
						 | 
				
			
			@ -1179,11 +1200,12 @@ void GLCanvas3D::LayersEditing::adjust_layer_height_profile()
 | 
			
		|||
    m_layers_texture.valid = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GLCanvas3D::LayersEditing::reset_layer_height_profile()
 | 
			
		||||
void GLCanvas3D::LayersEditing::reset_layer_height_profile(GLCanvas3D& canvas)
 | 
			
		||||
{
 | 
			
		||||
	const_cast<ModelObject*>(m_model_object)->layer_height_profile.clear();
 | 
			
		||||
    m_layer_height_profile.clear();
 | 
			
		||||
    m_layers_texture.valid = false;
 | 
			
		||||
    canvas.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GLCanvas3D::LayersEditing::generate_layer_height_texture()
 | 
			
		||||
| 
						 | 
				
			
			@ -5117,7 +5139,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
 | 
			
		|||
            if (evt.LeftDown())
 | 
			
		||||
            {
 | 
			
		||||
                // A volume is selected and the mouse is inside the reset button. Reset the ModelObject's layer height profile.
 | 
			
		||||
				m_layers_editing.reset_layer_height_profile();
 | 
			
		||||
				m_layers_editing.reset_layer_height_profile(*this);
 | 
			
		||||
                // Index 2 means no editing, just wait for mouse up event.
 | 
			
		||||
                m_layers_editing.state = LayersEditing::Completed;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -5251,7 +5273,8 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
 | 
			
		|||
                        if (m_volumes.volumes[m_hover_volume_id]->hover && !m_volumes.volumes[m_hover_volume_id]->is_wipe_tower)
 | 
			
		||||
                        {
 | 
			
		||||
                            // forces the selection of the volume
 | 
			
		||||
                            m_selection.add(m_hover_volume_id);
 | 
			
		||||
                            if (!m_selection.is_multiple_full_instance())
 | 
			
		||||
                                m_selection.add(m_hover_volume_id);
 | 
			
		||||
                            m_gizmos.update_on_off_state(m_selection);
 | 
			
		||||
                            post_event(SimpleEvent(EVT_GLCANVAS_OBJECT_SELECT));
 | 
			
		||||
                            _update_gizmos_data();
 | 
			
		||||
| 
						 | 
				
			
			@ -5822,6 +5845,7 @@ void GLCanvas3D::set_camera_zoom(float zoom)
 | 
			
		|||
void GLCanvas3D::update_gizmos_on_off_state()
 | 
			
		||||
{
 | 
			
		||||
    set_as_dirty();
 | 
			
		||||
    _update_gizmos_data();
 | 
			
		||||
    m_gizmos.update_on_off_state(get_selection());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -360,7 +360,7 @@ class GLCanvas3D
 | 
			
		|||
 | 
			
		||||
		void adjust_layer_height_profile();
 | 
			
		||||
		void accept_changes(GLCanvas3D& canvas);
 | 
			
		||||
        void reset_layer_height_profile();
 | 
			
		||||
        void reset_layer_height_profile(GLCanvas3D& canvas);
 | 
			
		||||
 | 
			
		||||
        static float get_cursor_z_relative(const GLCanvas3D& canvas);
 | 
			
		||||
        static bool bar_rect_contains(const GLCanvas3D& canvas, float x, float y);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1511,7 +1511,7 @@ void GLGizmoFlatten::set_flattening_data(const ModelObject* model_object)
 | 
			
		|||
    bool object_changed = m_model_object != model_object;
 | 
			
		||||
    m_model_object = model_object;
 | 
			
		||||
 | 
			
		||||
    if (object_changed && is_plane_update_necessary())
 | 
			
		||||
    if (model_object && (object_changed || is_plane_update_necessary()))
 | 
			
		||||
        update_planes();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1585,15 +1585,17 @@ void GLGizmoFlatten::update_planes()
 | 
			
		|||
            m_planes.pop_back();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Let's prepare transformation of the normal vector from mesh to instance coordinates.
 | 
			
		||||
    Geometry::Transformation t(inst_matrix);
 | 
			
		||||
    Vec3d scaling = t.get_scaling_factor();
 | 
			
		||||
    t.set_scaling_factor(Vec3d(1./scaling(0), 1./scaling(1), 1./scaling(2)));
 | 
			
		||||
 | 
			
		||||
    // Now we'll go through all the polygons, transform the points into xy plane to process them:
 | 
			
		||||
    for (unsigned int polygon_id=0; polygon_id < m_planes.size(); ++polygon_id) {
 | 
			
		||||
        Pointf3s& polygon = m_planes[polygon_id].vertices;
 | 
			
		||||
        const Vec3d& normal = m_planes[polygon_id].normal;
 | 
			
		||||
 | 
			
		||||
        // let's transform the normal accodring to the instance matrix:
 | 
			
		||||
        Geometry::Transformation t(inst_matrix);
 | 
			
		||||
        Vec3d scaling = t.get_scaling_factor();
 | 
			
		||||
        t.set_scaling_factor(Vec3d(1./(scaling(0)*scaling(0)), 1./(scaling(0)*scaling(0)), 1./(scaling(0)*scaling(0))));
 | 
			
		||||
        // transform the normal according to the instance matrix:
 | 
			
		||||
        Vec3d normal_transformed = t.get_matrix() * normal;
 | 
			
		||||
 | 
			
		||||
        // We are going to rotate about z and y to flatten the plane
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -194,6 +194,8 @@ bool GUI_App::OnInit()
 | 
			
		|||
            preset_updater->slic3r_update_notify();
 | 
			
		||||
        }
 | 
			
		||||
        preset_updater->sync(preset_bundle);
 | 
			
		||||
 | 
			
		||||
        load_current_presets();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -51,6 +51,7 @@ ObjectList::ObjectList(wxWindow* parent) :
 | 
			
		|||
    create_object_popupmenu(&m_menu_object);
 | 
			
		||||
    create_part_popupmenu(&m_menu_part);
 | 
			
		||||
    create_sla_object_popupmenu(&m_menu_sla_object);
 | 
			
		||||
    create_instance_popupmenu(&m_menu_instance);
 | 
			
		||||
 | 
			
		||||
    // describe control behavior 
 | 
			
		||||
    Bind(wxEVT_DATAVIEW_SELECTION_CHANGED, [this](wxEvent& event) {
 | 
			
		||||
| 
						 | 
				
			
			@ -97,7 +98,7 @@ void ObjectList::create_objects_ctrl()
 | 
			
		|||
    // temporary workaround for the correct behavior of the Scrolled sidebar panel:
 | 
			
		||||
    // 1. set a height of the list to some big value 
 | 
			
		||||
    // 2. change it to the normal min value (200) after first whole App updating/layouting
 | 
			
		||||
    SetMinSize(wxSize(-1, 1500));   // #ys_FIXME 
 | 
			
		||||
    SetMinSize(wxSize(-1, 3000));   // #ys_FIXME 
 | 
			
		||||
 | 
			
		||||
    m_sizer = new wxBoxSizer(wxVERTICAL);
 | 
			
		||||
    m_sizer->Add(this, 1, wxGROW | wxLEFT, 20);
 | 
			
		||||
| 
						 | 
				
			
			@ -400,15 +401,28 @@ void ObjectList::OnContextMenu(wxDataViewEvent&)
 | 
			
		|||
 | 
			
		||||
void ObjectList::show_context_menu()
 | 
			
		||||
{
 | 
			
		||||
    if (multiple_selection() && selected_instances_of_same_object())
 | 
			
		||||
    {
 | 
			
		||||
        wxGetApp().plater()->PopupMenu(&m_menu_instance);
 | 
			
		||||
 | 
			
		||||
        wxGetApp().plater()->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) {
 | 
			
		||||
            evt.Enable(can_split_instances()); }, m_menu_item_split_instances->GetId());
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const auto item = GetSelection();
 | 
			
		||||
    if (item)
 | 
			
		||||
    {
 | 
			
		||||
        if (!(m_objects_model->GetItemType(item) & (itObject | itVolume)))
 | 
			
		||||
        const ItemType type = m_objects_model->GetItemType(item);
 | 
			
		||||
        if (!(type & (itObject | itVolume | itInstance)))
 | 
			
		||||
            return;
 | 
			
		||||
        wxMenu* menu = m_objects_model->GetParent(item) != wxDataViewItem(0) ? &m_menu_part :
 | 
			
		||||
 | 
			
		||||
        wxMenu* menu = type & itInstance ? &m_menu_instance :
 | 
			
		||||
                       m_objects_model->GetParent(item) != wxDataViewItem(0) ? &m_menu_part :
 | 
			
		||||
                       wxGetApp().plater()->printer_technology() == ptFFF ? &m_menu_object : &m_menu_sla_object;
 | 
			
		||||
 | 
			
		||||
        append_menu_item_settings(menu);
 | 
			
		||||
        if (!(type & itInstance))
 | 
			
		||||
            append_menu_item_settings(menu);
 | 
			
		||||
 | 
			
		||||
        wxGetApp().plater()->PopupMenu(menu);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -443,15 +457,35 @@ void ObjectList::OnBeginDrag(wxDataViewEvent &event)
 | 
			
		|||
{
 | 
			
		||||
    const wxDataViewItem item(event.GetItem());
 | 
			
		||||
 | 
			
		||||
    // only allow drags for item, not containers
 | 
			
		||||
    if (multiple_selection() || GetSelection()!=item || 
 | 
			
		||||
        m_objects_model->GetParent(item) == wxDataViewItem(0) ||
 | 
			
		||||
        m_objects_model->GetItemType(item) != itVolume ) {
 | 
			
		||||
    const bool mult_sel = multiple_selection();
 | 
			
		||||
 | 
			
		||||
    if (mult_sel && !selected_instances_of_same_object() ||
 | 
			
		||||
        !mult_sel && (GetSelection() != item ||
 | 
			
		||||
        m_objects_model->GetParent(item) == wxDataViewItem(0) ) ) {
 | 
			
		||||
        event.Veto();
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
   
 | 
			
		||||
    m_dragged_data.init(m_objects_model->GetObjectIdByItem(item), m_objects_model->GetVolumeIdByItem(item));
 | 
			
		||||
    const ItemType& type = m_objects_model->GetItemType(item);
 | 
			
		||||
    if (!(type & (itVolume | itInstance))) {
 | 
			
		||||
        event.Veto();
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (mult_sel)
 | 
			
		||||
    {
 | 
			
		||||
        m_dragged_data.init(m_objects_model->GetObjectIdByItem(item),type);
 | 
			
		||||
        std::set<int>& sub_obj_idxs = m_dragged_data.inst_idxs();
 | 
			
		||||
        wxDataViewItemArray sels;
 | 
			
		||||
        GetSelections(sels);
 | 
			
		||||
        for (auto sel : sels )
 | 
			
		||||
            sub_obj_idxs.insert(m_objects_model->GetInstanceIdByItem(sel));
 | 
			
		||||
    }
 | 
			
		||||
    else 
 | 
			
		||||
        m_dragged_data.init(m_objects_model->GetObjectIdByItem(item), 
 | 
			
		||||
                        type&itVolume ? m_objects_model->GetVolumeIdByItem(item) :
 | 
			
		||||
                                        m_objects_model->GetInstanceIdByItem(item), 
 | 
			
		||||
                        type);
 | 
			
		||||
 | 
			
		||||
    /* Under MSW or OSX, DnD moves an item to the place of another selected item
 | 
			
		||||
    * But under GTK, DnD moves an item between another two items.
 | 
			
		||||
| 
						 | 
				
			
			@ -470,31 +504,41 @@ void ObjectList::OnBeginDrag(wxDataViewEvent &event)
 | 
			
		|||
    event.SetDragFlags(wxDrag_DefaultMove); // allows both copy and move;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ObjectList::can_drop(const wxDataViewItem& item) const 
 | 
			
		||||
{
 | 
			
		||||
    return  m_dragged_data.type() == itInstance && !item.IsOk()     ||
 | 
			
		||||
            m_dragged_data.type() == itVolume && item.IsOk() &&
 | 
			
		||||
            m_objects_model->GetItemType(item) == itVolume &&
 | 
			
		||||
            m_dragged_data.obj_idx() == m_objects_model->GetObjectIdByItem(item);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ObjectList::OnDropPossible(wxDataViewEvent &event)
 | 
			
		||||
{
 | 
			
		||||
    wxDataViewItem item(event.GetItem());
 | 
			
		||||
    const wxDataViewItem& item = event.GetItem();
 | 
			
		||||
 | 
			
		||||
    // only allow drags for item or background, not containers
 | 
			
		||||
    if (!item.IsOk() ||
 | 
			
		||||
        m_objects_model->GetParent(item) == wxDataViewItem(0) || 
 | 
			
		||||
        m_objects_model->GetItemType(item) != itVolume ||
 | 
			
		||||
        m_dragged_data.obj_idx() != m_objects_model->GetObjectIdByItem(item))
 | 
			
		||||
    if (!can_drop(item))
 | 
			
		||||
        event.Veto();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ObjectList::OnDrop(wxDataViewEvent &event)
 | 
			
		||||
{
 | 
			
		||||
    wxDataViewItem item(event.GetItem());
 | 
			
		||||
    const wxDataViewItem& item = event.GetItem();
 | 
			
		||||
 | 
			
		||||
    if (!item.IsOk() || m_objects_model->GetParent(item) == wxDataViewItem(0) ||
 | 
			
		||||
                        m_objects_model->GetItemType(item) != itVolume ||
 | 
			
		||||
                        m_dragged_data.obj_idx() != m_objects_model->GetObjectIdByItem(item)) {
 | 
			
		||||
    if (!can_drop(item))
 | 
			
		||||
    {
 | 
			
		||||
        event.Veto();
 | 
			
		||||
        m_dragged_data.clear();
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const int from_volume_id = m_dragged_data.vol_idx();
 | 
			
		||||
    if (m_dragged_data.type() == itInstance)
 | 
			
		||||
    {
 | 
			
		||||
        instances_to_separated_object(m_dragged_data.obj_idx(), m_dragged_data.inst_idxs());
 | 
			
		||||
        m_dragged_data.clear();
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const int from_volume_id = m_dragged_data.sub_obj_idx();
 | 
			
		||||
    int to_volume_id = m_objects_model->GetVolumeIdByItem(item);
 | 
			
		||||
 | 
			
		||||
// It looks like a fixed in current version of the wxWidgets
 | 
			
		||||
| 
						 | 
				
			
			@ -506,7 +550,7 @@ void ObjectList::OnDrop(wxDataViewEvent &event)
 | 
			
		|||
//     if (to_volume_id > from_volume_id) to_volume_id--;
 | 
			
		||||
// #endif // __WXGTK__
 | 
			
		||||
 | 
			
		||||
    auto& volumes = (*m_objects)[/*m_selected_object_id*/m_dragged_data.obj_idx()]->volumes;
 | 
			
		||||
    auto& volumes = (*m_objects)[m_dragged_data.obj_idx()]->volumes;
 | 
			
		||||
    auto delta = to_volume_id < from_volume_id ? -1 : 1;
 | 
			
		||||
    int cnt = 0;
 | 
			
		||||
    for (int id = from_volume_id; cnt < abs(from_volume_id - to_volume_id); id += delta, cnt++)
 | 
			
		||||
| 
						 | 
				
			
			@ -516,7 +560,7 @@ void ObjectList::OnDrop(wxDataViewEvent &event)
 | 
			
		|||
                                                    m_objects_model->GetParent(item)));
 | 
			
		||||
 | 
			
		||||
    m_parts_changed = true;
 | 
			
		||||
    parts_changed(/*m_selected_object_id*/m_dragged_data.obj_idx());
 | 
			
		||||
    parts_changed(m_dragged_data.obj_idx());
 | 
			
		||||
 | 
			
		||||
    m_dragged_data.clear();
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -741,6 +785,12 @@ wxMenuItem* ObjectList::append_menu_item_change_type(wxMenu* menu)
 | 
			
		|||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
wxMenuItem* ObjectList::append_menu_item_instance_to_object(wxMenu* menu)
 | 
			
		||||
{
 | 
			
		||||
    return append_menu_item(menu, wxID_ANY, _(L("Set as a Separated Object")), "",
 | 
			
		||||
        [this](wxCommandEvent&) { split_instances(); }, "", menu);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ObjectList::create_object_popupmenu(wxMenu *menu)
 | 
			
		||||
{
 | 
			
		||||
    append_menu_items_add_volume(menu);
 | 
			
		||||
| 
						 | 
				
			
			@ -769,6 +819,11 @@ void ObjectList::create_part_popupmenu(wxMenu *menu)
 | 
			
		|||
    menu->AppendSeparator();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ObjectList::create_instance_popupmenu(wxMenu*menu)
 | 
			
		||||
{
 | 
			
		||||
    m_menu_item_split_instances = append_menu_item_instance_to_object(menu);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
wxMenu* ObjectList::create_settings_popupmenu(wxMenu *parent_menu)
 | 
			
		||||
{
 | 
			
		||||
    wxMenu *menu = new wxMenu;
 | 
			
		||||
| 
						 | 
				
			
			@ -1116,6 +1171,27 @@ bool ObjectList::is_splittable()
 | 
			
		|||
    return splittable;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ObjectList::selected_instances_of_same_object()
 | 
			
		||||
{
 | 
			
		||||
    wxDataViewItemArray sels;
 | 
			
		||||
    GetSelections(sels);
 | 
			
		||||
 | 
			
		||||
    const int obj_idx = m_objects_model->GetObjectIdByItem(sels.front());
 | 
			
		||||
 | 
			
		||||
    for (auto item : sels) {
 | 
			
		||||
        if (! (m_objects_model->GetItemType(item) & itInstance) ||
 | 
			
		||||
            obj_idx != m_objects_model->GetObjectIdByItem(item))
 | 
			
		||||
            return false;
 | 
			
		||||
    }
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ObjectList::can_split_instances()
 | 
			
		||||
{
 | 
			
		||||
    const GLCanvas3D::Selection& selection = wxGetApp().plater()->canvas3D()->get_selection();
 | 
			
		||||
    return selection.is_multiple_full_instance() || selection.is_single_full_instance();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ObjectList::part_settings_changed()
 | 
			
		||||
{
 | 
			
		||||
    m_part_settings_changed = true;
 | 
			
		||||
| 
						 | 
				
			
			@ -1419,7 +1495,7 @@ bool ObjectList::multiple_selection() const
 | 
			
		|||
 | 
			
		||||
void ObjectList::update_selections()
 | 
			
		||||
{
 | 
			
		||||
    auto& selection = wxGetApp().plater()->canvas3D()->get_selection();
 | 
			
		||||
    const GLCanvas3D::Selection& selection = wxGetApp().plater()->canvas3D()->get_selection();
 | 
			
		||||
    wxDataViewItemArray sels;
 | 
			
		||||
 | 
			
		||||
    // We doesn't update selection if SettingsItem for the current object/part is selected
 | 
			
		||||
| 
						 | 
				
			
			@ -1505,7 +1581,7 @@ void ObjectList::update_selections()
 | 
			
		|||
 | 
			
		||||
void ObjectList::update_selections_on_canvas()
 | 
			
		||||
{
 | 
			
		||||
    auto& selection = wxGetApp().plater()->canvas3D()->get_selection();
 | 
			
		||||
    GLCanvas3D::Selection& selection = wxGetApp().plater()->canvas3D()->get_selection();
 | 
			
		||||
 | 
			
		||||
    const int sel_cnt = GetSelectedItemsCount();
 | 
			
		||||
    if (sel_cnt == 0) {
 | 
			
		||||
| 
						 | 
				
			
			@ -1729,6 +1805,43 @@ void ObjectList::update_settings_items()
 | 
			
		|||
    UnselectAll();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ObjectList::instances_to_separated_object(const int obj_idx, const std::set<int>& inst_idxs)
 | 
			
		||||
{
 | 
			
		||||
    // create new object from selected instance  
 | 
			
		||||
    ModelObject* model_object = (*m_objects)[obj_idx]->get_model()->add_object(*(*m_objects)[obj_idx]);
 | 
			
		||||
    for (int inst_idx = model_object->instances.size() - 1; inst_idx >= 0; inst_idx--)
 | 
			
		||||
    {
 | 
			
		||||
        if (find(inst_idxs.begin(), inst_idxs.end(), inst_idx) != inst_idxs.end())
 | 
			
		||||
            continue;
 | 
			
		||||
        model_object->delete_instance(inst_idx);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Add new object to the object_list
 | 
			
		||||
    add_object_to_list(m_objects->size() - 1);
 | 
			
		||||
 | 
			
		||||
    for (std::set<int>::const_reverse_iterator it = inst_idxs.rbegin(); it != inst_idxs.rend(); ++it)
 | 
			
		||||
    {
 | 
			
		||||
        // delete selected instance from the object
 | 
			
		||||
        del_subobject_from_object(obj_idx, *it, itInstance);
 | 
			
		||||
        delete_instance_from_list(obj_idx, *it);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ObjectList::split_instances()
 | 
			
		||||
{
 | 
			
		||||
    const GLCanvas3D::Selection& selection = wxGetApp().plater()->canvas3D()->get_selection();
 | 
			
		||||
    const int obj_idx = selection.get_object_idx();
 | 
			
		||||
    if (obj_idx == -1)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    const int inst_idx = selection.get_instance_idx();
 | 
			
		||||
    const std::set<int> inst_idxs = inst_idx < 0 ?
 | 
			
		||||
                                    selection.get_instance_idxs() :
 | 
			
		||||
                                    std::set<int>{ inst_idx };
 | 
			
		||||
 | 
			
		||||
    instances_to_separated_object(obj_idx, inst_idxs);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ObjectList::ItemValueChanged(wxDataViewEvent &event)
 | 
			
		||||
{
 | 
			
		||||
    if (event.GetColumn() == 0)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -56,22 +56,38 @@ class ObjectList : public wxDataViewCtrl
 | 
			
		|||
 | 
			
		||||
    struct dragged_item_data
 | 
			
		||||
    {
 | 
			
		||||
        void init(const int obj_idx, const int vol_idx) {
 | 
			
		||||
        void init(const int obj_idx, const int subobj_idx, const ItemType type) {
 | 
			
		||||
            m_obj_idx = obj_idx;
 | 
			
		||||
            m_vol_idx = vol_idx;            
 | 
			
		||||
            m_type = type;
 | 
			
		||||
            if (m_type&itVolume)
 | 
			
		||||
                m_vol_idx = subobj_idx;
 | 
			
		||||
            else
 | 
			
		||||
                m_inst_idxs.insert(subobj_idx);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void init(const int obj_idx, const ItemType type) {
 | 
			
		||||
            m_obj_idx = obj_idx;
 | 
			
		||||
            m_type = type;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void clear() {
 | 
			
		||||
            m_obj_idx = -1;
 | 
			
		||||
            m_vol_idx = -1;
 | 
			
		||||
            m_inst_idxs.clear();
 | 
			
		||||
            m_type = itUndef;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        int obj_idx() const  { return m_obj_idx; }
 | 
			
		||||
        int vol_idx() const  { return m_vol_idx; }
 | 
			
		||||
        int sub_obj_idx() const  { return m_vol_idx; }
 | 
			
		||||
        ItemType type() const { return m_type; }
 | 
			
		||||
        std::set<int>& inst_idxs() { return m_inst_idxs; }
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
        int m_obj_idx = -1;
 | 
			
		||||
        int m_vol_idx = -1;
 | 
			
		||||
        std::set<int> m_inst_idxs{};
 | 
			
		||||
        ItemType m_type = itUndef;
 | 
			
		||||
 | 
			
		||||
    } m_dragged_data;
 | 
			
		||||
 | 
			
		||||
    wxBoxSizer          *m_sizer {nullptr};
 | 
			
		||||
| 
						 | 
				
			
			@ -91,9 +107,11 @@ class ObjectList : public wxDataViewCtrl
 | 
			
		|||
    wxMenu      m_menu_object;
 | 
			
		||||
    wxMenu      m_menu_part;
 | 
			
		||||
    wxMenu      m_menu_sla_object;
 | 
			
		||||
    wxMenu      m_menu_instance;
 | 
			
		||||
    wxMenuItem* m_menu_item_split { nullptr };
 | 
			
		||||
    wxMenuItem* m_menu_item_split_part { nullptr };
 | 
			
		||||
    wxMenuItem* m_menu_item_settings { nullptr };
 | 
			
		||||
    wxMenuItem* m_menu_item_split_instances { nullptr };
 | 
			
		||||
 | 
			
		||||
    std::vector<wxBitmap*> m_bmp_vector;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -148,9 +166,11 @@ public:
 | 
			
		|||
    wxMenuItem*         append_menu_item_split(wxMenu* menu);
 | 
			
		||||
    wxMenuItem*         append_menu_item_settings(wxMenu* menu);
 | 
			
		||||
    wxMenuItem*         append_menu_item_change_type(wxMenu* menu);
 | 
			
		||||
    wxMenuItem*         append_menu_item_instance_to_object(wxMenu* menu);
 | 
			
		||||
    void                create_object_popupmenu(wxMenu *menu);
 | 
			
		||||
    void                create_sla_object_popupmenu(wxMenu*menu);
 | 
			
		||||
    void                create_part_popupmenu(wxMenu*menu);
 | 
			
		||||
    void                create_instance_popupmenu(wxMenu*menu);
 | 
			
		||||
    wxMenu*             create_settings_popupmenu(wxMenu *parent_menu);
 | 
			
		||||
 | 
			
		||||
    void                update_opt_keys(t_config_option_keys& t_optopt_keys);
 | 
			
		||||
| 
						 | 
				
			
			@ -166,6 +186,8 @@ public:
 | 
			
		|||
    void                split();
 | 
			
		||||
    bool                get_volume_by_item(const wxDataViewItem& item, ModelVolume*& volume);
 | 
			
		||||
    bool                is_splittable();
 | 
			
		||||
    bool                selected_instances_of_same_object();
 | 
			
		||||
    bool                can_split_instances();
 | 
			
		||||
 | 
			
		||||
    wxPoint             get_mouse_position_in_control();
 | 
			
		||||
    wxBoxSizer*         get_sizer() {return  m_sizer;}
 | 
			
		||||
| 
						 | 
				
			
			@ -222,6 +244,9 @@ public:
 | 
			
		|||
    bool has_multi_part_objects();
 | 
			
		||||
    void update_settings_items();
 | 
			
		||||
 | 
			
		||||
    void instances_to_separated_object(const int obj_idx, const std::set<int>& inst_idx);
 | 
			
		||||
    void split_instances();
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    void OnChar(wxKeyEvent& event);
 | 
			
		||||
    void OnContextMenu(wxDataViewEvent &event);
 | 
			
		||||
| 
						 | 
				
			
			@ -229,6 +254,7 @@ private:
 | 
			
		|||
    void OnBeginDrag(wxDataViewEvent &event);
 | 
			
		||||
    void OnDropPossible(wxDataViewEvent &event);
 | 
			
		||||
    void OnDrop(wxDataViewEvent &event);
 | 
			
		||||
    bool can_drop(const wxDataViewItem& item) const ;
 | 
			
		||||
 | 
			
		||||
    void ItemValueChanged(wxDataViewEvent &event);
 | 
			
		||||
    void OnEditingDone(wxDataViewEvent &event);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,6 +2,10 @@
 | 
			
		|||
#define _(s)    Slic3r::GUI::I18N::translate((s))
 | 
			
		||||
#endif /* _ */
 | 
			
		||||
 | 
			
		||||
#ifndef _CTX
 | 
			
		||||
#define _CTX(s, ctx) Slic3r::GUI::I18N::translate((s), (ctx))
 | 
			
		||||
#endif /* _ */
 | 
			
		||||
 | 
			
		||||
#ifndef L
 | 
			
		||||
// !!! If you needed to translate some wxString,
 | 
			
		||||
// !!! please use _(L(string))
 | 
			
		||||
| 
						 | 
				
			
			@ -21,6 +25,7 @@
 | 
			
		|||
#define slic3r_GUI_I18N_hpp_
 | 
			
		||||
 | 
			
		||||
#include <wx/intl.h>
 | 
			
		||||
#include <wx/version.h>
 | 
			
		||||
 | 
			
		||||
namespace Slic3r { namespace GUI { 
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -29,6 +34,19 @@ namespace I18N {
 | 
			
		|||
	inline wxString translate(const wchar_t *s) 	 { return wxGetTranslation(s); }
 | 
			
		||||
	inline wxString translate(const std::string &s)  { return wxGetTranslation(wxString(s.c_str(), wxConvUTF8)); }
 | 
			
		||||
	inline wxString translate(const std::wstring &s) { return wxGetTranslation(s.c_str()); }
 | 
			
		||||
 | 
			
		||||
#if wxCHECK_VERSION(3, 1, 1)
 | 
			
		||||
	#define _wxGetTranslation_ctx(S, CTX) wxGetTranslation((S), wxEmptyString, (CTX))
 | 
			
		||||
#else
 | 
			
		||||
	#define _wxGetTranslation_ctx(S, CTX) ((void)(CTX), wxGetTranslation((S)))
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	inline wxString translate(const char *s, const char* ctx)         { return _wxGetTranslation_ctx(wxString(s, wxConvUTF8), ctx); }
 | 
			
		||||
	inline wxString translate(const wchar_t *s, const char* ctx)      { return _wxGetTranslation_ctx(s, ctx); }
 | 
			
		||||
	inline wxString translate(const std::string &s, const char* ctx)  { return _wxGetTranslation_ctx(wxString(s.c_str(), wxConvUTF8), ctx); }
 | 
			
		||||
	inline wxString translate(const std::wstring &s, const char* ctx) { return _wxGetTranslation_ctx(s.c_str(), ctx); }
 | 
			
		||||
 | 
			
		||||
#undef _wxGetTranslation_ctx
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Return translated std::string as a wxString
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -155,7 +155,6 @@ void MainFrame::create_preset_tabs()
 | 
			
		|||
    add_created_tab(new TabSLAPrint(m_tabpanel));
 | 
			
		||||
    add_created_tab(new TabSLAMaterial(m_tabpanel));
 | 
			
		||||
    add_created_tab(new TabPrinter(m_tabpanel));
 | 
			
		||||
    GUI::wxGetApp().load_current_presets();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MainFrame::add_created_tab(Tab* panel)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -234,7 +234,7 @@ void OptionsGroup::append_line(const Line& line, wxStaticText**	full_Label/* = n
 | 
			
		|||
// 			wxString str_label = _(option.label);
 | 
			
		||||
//!			To correct translation by context have to use wxGETTEXT_IN_CONTEXT macro from wxWidget 3.1.1
 | 
			
		||||
			wxString str_label = (option.label == "Top" || option.label == "Bottom") ?
 | 
			
		||||
								wxGETTEXT_IN_CONTEXT("Layers", wxString(option.label)) :
 | 
			
		||||
								_CTX(option.label, "Layers") :
 | 
			
		||||
								_(option.label);
 | 
			
		||||
			label = new wxStaticText(parent(), wxID_ANY, str_label + ":", wxDefaultPosition, wxDefaultSize);
 | 
			
		||||
			label->SetFont(label_font);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1063,6 +1063,7 @@ private:
 | 
			
		|||
    bool can_delete_object() const;
 | 
			
		||||
    bool can_increase_instances() const;
 | 
			
		||||
    bool can_decrease_instances() const;
 | 
			
		||||
    bool can_set_instance_to_object() const;
 | 
			
		||||
    bool can_split_to_objects() const;
 | 
			
		||||
    bool can_split_to_volumes() const;
 | 
			
		||||
    bool can_split() const;
 | 
			
		||||
| 
						 | 
				
			
			@ -2365,11 +2366,16 @@ bool Plater::priv::init_common_menu(wxMenu* menu, const bool is_part/* = false*/
 | 
			
		|||
            [this](wxCommandEvent&) { q->decrease_instances(); }, "delete.png");
 | 
			
		||||
        wxMenuItem* item_set_number_of_copies = append_menu_item(menu, wxID_ANY, _(L("Set number of copies")) + dots, _(L("Change the number of copies of the selected object")),
 | 
			
		||||
            [this](wxCommandEvent&) { q->set_number_of_copies(); }, "textfield.png");
 | 
			
		||||
 | 
			
		||||
        menu->AppendSeparator();
 | 
			
		||||
        wxMenuItem* item_instance_to_object = sidebar->obj_list()->append_menu_item_instance_to_object(menu);
 | 
			
		||||
 | 
			
		||||
        if (q != nullptr)
 | 
			
		||||
        {
 | 
			
		||||
            q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_increase_instances()); }, item_increase->GetId());
 | 
			
		||||
            q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_decrease_instances()); }, item_decrease->GetId());
 | 
			
		||||
            q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_increase_instances()); }, item_set_number_of_copies->GetId());
 | 
			
		||||
            q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_set_instance_to_object()); }, item_instance_to_object->GetId());
 | 
			
		||||
        }
 | 
			
		||||
        menu->AppendSeparator();
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -2432,9 +2438,9 @@ bool Plater::priv::complit_init_object_menu()
 | 
			
		|||
    // ui updates needs to be binded to the parent panel
 | 
			
		||||
    if (q != nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split/*_to_objects() || can_split_to_volumes*/()); }, item_split->GetId());
 | 
			
		||||
        q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split/*_to_objects*/()); }, item_split_objects->GetId());
 | 
			
		||||
        q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split/*_to_volumes*/()); }, item_split_volumes->GetId());
 | 
			
		||||
        q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split()); }, item_split->GetId());
 | 
			
		||||
        q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split()); }, item_split_objects->GetId());
 | 
			
		||||
        q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split()); }, item_split_volumes->GetId());
 | 
			
		||||
    }
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -2453,7 +2459,7 @@ bool Plater::priv::complit_init_sla_object_menu()
 | 
			
		|||
    // ui updates needs to be binded to the parent panel
 | 
			
		||||
    if (q != nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split/*_to_objects*/()); }, item_split->GetId());
 | 
			
		||||
        q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split()); }, item_split->GetId());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
| 
						 | 
				
			
			@ -2472,7 +2478,7 @@ bool Plater::priv::complit_init_part_menu()
 | 
			
		|||
    // ui updates needs to be binded to the parent panel
 | 
			
		||||
    if (q != nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split/*_to_volumes*/()); },  item_split->GetId());
 | 
			
		||||
        q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split()); },  item_split->GetId());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
| 
						 | 
				
			
			@ -2540,6 +2546,12 @@ bool Plater::priv::can_increase_instances() const
 | 
			
		|||
    return (0 <= obj_idx) && (obj_idx < (int)model.objects.size());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Plater::priv::can_set_instance_to_object() const
 | 
			
		||||
{
 | 
			
		||||
    const int obj_idx = get_selected_object_idx();
 | 
			
		||||
    return (0 <= obj_idx) && (obj_idx < (int)model.objects.size()) && (model.objects[obj_idx]->instances.size() > 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Plater::priv::can_decrease_instances() const
 | 
			
		||||
{
 | 
			
		||||
    int obj_idx = get_selected_object_idx();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -406,7 +406,7 @@ void PrusaObjectDataViewModelNode::set_object_action_icon() {
 | 
			
		|||
	m_action_icon = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("add_object.png")), wxBITMAP_TYPE_PNG);
 | 
			
		||||
}
 | 
			
		||||
void  PrusaObjectDataViewModelNode::set_part_action_icon() {
 | 
			
		||||
	m_action_icon = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("cog.png")), wxBITMAP_TYPE_PNG);
 | 
			
		||||
	m_action_icon = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var(m_type == itVolume ? "cog.png" : "brick_go.png")), wxBITMAP_TYPE_PNG);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Slic3r::GUI::BitmapCache *m_bitmap_cache = nullptr;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -275,6 +275,7 @@ public:
 | 
			
		|||
        else if (type == itInstance) {
 | 
			
		||||
            m_idx = parent->GetChildCount();
 | 
			
		||||
            m_name = wxString::Format("Instance_%d", m_idx+1);
 | 
			
		||||
            set_part_action_icon();
 | 
			
		||||
        }
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -49,8 +49,6 @@ _constant()
 | 
			
		|||
    Ref<StaticPrintConfig> config()
 | 
			
		||||
        %code%{ RETVAL = &THIS->config(); %};
 | 
			
		||||
    Points copies();
 | 
			
		||||
    std::vector<double> layer_height_profile()
 | 
			
		||||
        %code%{ RETVAL = THIS->layer_height_profile; %};
 | 
			
		||||
    Clone<BoundingBox> bounding_box();
 | 
			
		||||
    
 | 
			
		||||
    Points _shifted_copies()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue