mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-10-30 20:21:12 -06:00 
			
		
		
		
	Merge branch 'master' of https://github.com/prusa3d/PrusaSlicer into et_labels
This commit is contained in:
		
						commit
						dc393e2f0a
					
				
					 26 changed files with 581 additions and 386 deletions
				
			
		|  | @ -635,13 +635,30 @@ inline bool igl::copyleft::cgal::SelfIntersectMesh< | |||
| { | ||||
|   using namespace std; | ||||
| 
 | ||||
|   auto opposite_vertex = [](const Index a0, const Index a1) { | ||||
|     // get opposite index of A
 | ||||
|     int a2=-1; | ||||
| 	for(int c=0;c<3;++c) | ||||
|       if(c!=a0 && c!=a1) { | ||||
|         a2 = c; | ||||
|         break; | ||||
|       } | ||||
|       assert(a2 != -1); | ||||
|       return a2; | ||||
|   }; | ||||
| 
 | ||||
|   // must be co-planar
 | ||||
|   if( | ||||
|     A.supporting_plane() != B.supporting_plane() && | ||||
|     A.supporting_plane() != B.supporting_plane().opposite()) | ||||
|   { | ||||
|   Index a2 = opposite_vertex(shared[0].first, shared[1].first); | ||||
|   if (! B.supporting_plane().has_on(A.vertex(a2))) | ||||
|     return false; | ||||
|   } | ||||
|    | ||||
|   Index b2 = opposite_vertex(shared[0].second, shared[1].second); | ||||
| 
 | ||||
|   if (int(CGAL::coplanar_orientation(A.vertex(shared[0].first), A.vertex(shared[1].first), A.vertex(a2))) *  | ||||
| 	  int(CGAL::coplanar_orientation(B.vertex(shared[0].second), B.vertex(shared[1].second), B.vertex(b2))) < 0) | ||||
|     // There is certainly no self intersection as the non-shared triangle vertices lie on opposite sides of the shared edge.
 | ||||
|     return false; | ||||
| 
 | ||||
|   // Since A and B are non-degenerate the intersection must be a polygon
 | ||||
|   // (triangle). Either
 | ||||
|   //   - the vertex of A (B) opposite the shared edge of lies on B (A), or
 | ||||
|  | @ -650,22 +667,10 @@ inline bool igl::copyleft::cgal::SelfIntersectMesh< | |||
|   // Determine if the vertex opposite edge (a0,a1) in triangle A lies in
 | ||||
|   // (intersects) triangle B
 | ||||
|   const auto & opposite_point_inside = []( | ||||
|     const Triangle_3 & A, const Index a0, const Index a1, const Triangle_3 & B)  | ||||
|     const Triangle_3 & A, const Index a2, const Triangle_3 & B)  | ||||
|     -> bool | ||||
|   { | ||||
|     // get opposite index
 | ||||
|     Index a2 = -1; | ||||
|     for(int c = 0;c<3;c++) | ||||
|     { | ||||
|       if(c != a0 && c != a1) | ||||
|       { | ||||
|         a2 = c; | ||||
|         break; | ||||
|       } | ||||
|     } | ||||
|     assert(a2 != -1); | ||||
|     bool ret = CGAL::do_intersect(A.vertex(a2),B); | ||||
|     return ret; | ||||
|     return CGAL::do_intersect(A.vertex(a2),B); | ||||
|   }; | ||||
| 
 | ||||
|   // Determine if edge opposite vertex va in triangle A intersects edge
 | ||||
|  | @ -681,8 +686,8 @@ inline bool igl::copyleft::cgal::SelfIntersectMesh< | |||
|   }; | ||||
| 
 | ||||
|   if(  | ||||
|     !opposite_point_inside(A,shared[0].first,shared[1].first,B) && | ||||
|     !opposite_point_inside(B,shared[0].second,shared[1].second,A) && | ||||
|     !opposite_point_inside(A,a2,B) && | ||||
|     !opposite_point_inside(B,b2,A) && | ||||
|     !opposite_edges_intersect(A,shared[0].first,B,shared[1].second) &&  | ||||
|     !opposite_edges_intersect(A,shared[1].first,B,shared[0].second)) | ||||
|   { | ||||
|  | @ -936,4 +941,4 @@ inline void igl::copyleft::cgal::SelfIntersectMesh< | |||
|   //process_chunk(0, candidate_triangle_pairs.size());
 | ||||
| } | ||||
| 
 | ||||
| #endif | ||||
| #endif | ||||
|  | @ -150,8 +150,17 @@ void minus(TriangleMesh &A, const TriangleMesh &B) | |||
|     triangle_mesh_to_cgal(B, meshB.m); | ||||
|      | ||||
|     CGALMesh meshResult; | ||||
|     CGALProc::corefine_and_compute_difference(meshA.m, meshB.m, meshResult.m); | ||||
|      | ||||
|     bool success = false; | ||||
|     try { | ||||
|         success = CGALProc::corefine_and_compute_difference(meshA.m, meshB.m, meshResult.m, | ||||
|             CGALParams::throw_on_self_intersection(true), CGALParams::throw_on_self_intersection(true)); | ||||
|     } | ||||
|     catch (const CGAL::Polygon_mesh_processing::Corefinement::Self_intersection_exception&) { | ||||
|         success = false; | ||||
|     } | ||||
|     if (! success) | ||||
|         throw std::runtime_error("CGAL corefine_and_compute_difference failed"); | ||||
| 
 | ||||
|     A = cgal_to_triangle_mesh(meshResult.m); | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -122,10 +122,10 @@ void SLAPrint::clear() | |||
| } | ||||
| 
 | ||||
| // Transformation without rotation around Z and without a shift by X and Y.
 | ||||
| static Transform3d sla_trafo(const SLAPrint& p, const ModelObject &model_object) | ||||
| Transform3d SLAPrint::sla_trafo(const ModelObject &model_object) const | ||||
| { | ||||
| 
 | ||||
|     Vec3d corr = p.relative_correction(); | ||||
|     Vec3d corr = this->relative_correction(); | ||||
| 
 | ||||
|     ModelInstance &model_instance = *model_object.instances.front(); | ||||
|     Vec3d          offset         = model_instance.get_offset(); | ||||
|  | @ -376,7 +376,7 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, DynamicPrintConfig con | |||
|             bool sla_trafo_differs  = | ||||
|                 model_object.instances.empty() != model_object_new.instances.empty() || | ||||
|                 (! model_object.instances.empty() && | ||||
|                   (! sla_trafo(*this, model_object).isApprox(sla_trafo(*this, model_object_new)) || | ||||
|                   (! sla_trafo(model_object).isApprox(sla_trafo(model_object_new)) || | ||||
|                     model_object.instances.front()->is_left_handed() != model_object_new.instances.front()->is_left_handed())); | ||||
|             if (model_parts_differ || sla_trafo_differs) { | ||||
|                 // The very first step (the slicing step) is invalidated. One may freely remove all associated PrintObjects.
 | ||||
|  | @ -419,7 +419,7 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, DynamicPrintConfig con | |||
|                 if (model_object.sla_drain_holes != model_object_new.sla_drain_holes) | ||||
|                 { | ||||
|                     model_object.sla_drain_holes = model_object_new.sla_drain_holes; | ||||
|                     update_apply_status(it_print_object_status->print_object->invalidate_step(slaposHollowing)); | ||||
|                     update_apply_status(it_print_object_status->print_object->invalidate_step(slaposDrillHoles)); | ||||
|                 } | ||||
| 
 | ||||
|                 // Copy the ModelObject name, input_file and instances. The instances will compared against PrintObject instances in the next step.
 | ||||
|  | @ -453,7 +453,7 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, DynamicPrintConfig con | |||
| 
 | ||||
|             // FIXME: this invalidates the transformed mesh in SLAPrintObject
 | ||||
|             // which is expensive to calculate (especially the raw_mesh() call)
 | ||||
|             print_object->set_trafo(sla_trafo(*this, model_object), model_object.instances.front()->is_left_handed()); | ||||
|             print_object->set_trafo(sla_trafo(model_object), model_object.instances.front()->is_left_handed()); | ||||
| 
 | ||||
|             print_object->set_instances(std::move(new_instances)); | ||||
| 
 | ||||
|  | @ -1101,6 +1101,8 @@ const ExPolygons &SliceRecord::get_slice(SliceOrigin o) const | |||
| bool SLAPrintObject::has_mesh(SLAPrintObjectStep step) const | ||||
| { | ||||
|     switch (step) { | ||||
|     case slaposDrillHoles: | ||||
|         return m_hollowing_data && !m_hollowing_data->hollow_mesh_with_holes.empty(); | ||||
|     case slaposSupportTree: | ||||
|         return ! this->support_mesh().empty(); | ||||
|     case slaposPad: | ||||
|  | @ -1117,7 +1119,7 @@ TriangleMesh SLAPrintObject::get_mesh(SLAPrintObjectStep step) const | |||
|         return this->support_mesh(); | ||||
|     case slaposPad: | ||||
|         return this->pad_mesh(); | ||||
|     case slaposHollowing: | ||||
|     case slaposDrillHoles: | ||||
|         if (m_hollowing_data) | ||||
|             return m_hollowing_data->hollow_mesh_with_holes; | ||||
|         [[fallthrough]]; | ||||
|  |  | |||
|  | @ -78,13 +78,13 @@ public: | |||
|     // Support mesh is only valid if this->is_step_done(slaposPad) is true.
 | ||||
|     const TriangleMesh&     pad_mesh() const; | ||||
|      | ||||
|     // Ready after this->is_step_done(slaposHollowing) is true
 | ||||
|     // Ready after this->is_step_done(slaposDrillHoles) is true
 | ||||
|     const TriangleMesh&     hollowed_interior_mesh() const; | ||||
|      | ||||
|     // Get the mesh that is going to be printed with all the modifications
 | ||||
|     // like hollowing and drilled holes.
 | ||||
|     const TriangleMesh & get_mesh_to_print() const { | ||||
|         return m_hollowing_data ? m_hollowing_data->hollow_mesh_with_holes : transformed_mesh(); | ||||
|         return (m_hollowing_data && is_step_done(slaposDrillHoles)) ? m_hollowing_data->hollow_mesh_with_holes : transformed_mesh(); | ||||
|     } | ||||
| 
 | ||||
|     // This will return the transformed mesh which is cached
 | ||||
|  | @ -421,6 +421,9 @@ public: | |||
|     // Extracted value from the configuration objects
 | ||||
|     Vec3d                       relative_correction() const; | ||||
| 
 | ||||
|     // Return sla tansformation for a given model_object
 | ||||
|     Transform3d sla_trafo(const ModelObject &model_object) const; | ||||
| 
 | ||||
| 	std::string                 output_filename(const std::string &filename_base = std::string()) const override; | ||||
| 
 | ||||
|     const SLAPrintStatistics&   print_statistics() const { return m_print_statistics; } | ||||
|  |  | |||
|  | @ -27,7 +27,7 @@ namespace { | |||
| 
 | ||||
| const std::array<unsigned, slaposCount> OBJ_STEP_LEVELS = { | ||||
|     10, // slaposHollowing,
 | ||||
|     10, // slaposDrillHolesIfHollowed
 | ||||
|     10, // slaposDrillHoles
 | ||||
|     10, // slaposObjectSlice,
 | ||||
|     20, // slaposSupportPoints,
 | ||||
|     10, // slaposSupportTree,
 | ||||
|  | @ -39,7 +39,7 @@ std::string OBJ_STEP_LABELS(size_t idx) | |||
| { | ||||
|     switch (idx) { | ||||
|     case slaposHollowing:            return L("Hollowing model"); | ||||
|     case slaposDrillHoles:           return L("Drilling holes into hollowed model."); | ||||
|     case slaposDrillHoles:           return L("Drilling holes into model."); | ||||
|     case slaposObjectSlice:          return L("Slicing model"); | ||||
|     case slaposSupportPoints:        return L("Generating support points"); | ||||
|     case slaposSupportTree:          return L("Generating support tree"); | ||||
|  | @ -80,6 +80,7 @@ SLAPrint::Steps::Steps(SLAPrint *print) | |||
| void SLAPrint::Steps::hollow_model(SLAPrintObject &po) | ||||
| { | ||||
|     po.m_hollowing_data.reset(); | ||||
| 
 | ||||
|     if (! po.m_config.hollowing_enable.getBool()) { | ||||
|         BOOST_LOG_TRIVIAL(info) << "Skipping hollowing step!"; | ||||
|         return; | ||||
|  | @ -98,17 +99,37 @@ void SLAPrint::Steps::hollow_model(SLAPrintObject &po) | |||
|     else { | ||||
|         po.m_hollowing_data.reset(new SLAPrintObject::HollowingData()); | ||||
|         po.m_hollowing_data->interior = *meshptr; | ||||
|         auto &hollowed_mesh = po.m_hollowing_data->hollow_mesh_with_holes; | ||||
|         hollowed_mesh = po.transformed_mesh(); | ||||
|         hollowed_mesh.merge(po.m_hollowing_data->interior); | ||||
|         hollowed_mesh.require_shared_vertices(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| // Drill holes into the hollowed/original mesh.
 | ||||
| void SLAPrint::Steps::drill_holes(SLAPrintObject &po) | ||||
| { | ||||
|     // Drill holes into the hollowed/original mesh.
 | ||||
|     if (po.m_model_object->sla_drain_holes.empty()) { | ||||
|     bool needs_drilling = ! po.m_model_object->sla_drain_holes.empty(); | ||||
|     bool is_hollowed = (po.m_hollowing_data && ! po.m_hollowing_data->interior.empty()); | ||||
| 
 | ||||
|     if (! is_hollowed && ! needs_drilling) { | ||||
|         // In this case we can dump any data that might have been
 | ||||
|         // generated on previous runs.
 | ||||
|         po.m_hollowing_data.reset(); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     if (! po.m_hollowing_data) | ||||
|         po.m_hollowing_data.reset(new SLAPrintObject::HollowingData()); | ||||
| 
 | ||||
|     // Hollowing and/or drilling is active, m_hollowing_data is valid.
 | ||||
| 
 | ||||
|     // Regenerate hollowed mesh, even if it was there already. It may contain
 | ||||
|     // holes that are no longer on the frontend.
 | ||||
|     TriangleMesh &hollowed_mesh = po.m_hollowing_data->hollow_mesh_with_holes; | ||||
|     hollowed_mesh = po.transformed_mesh(); | ||||
|     if (! po.m_hollowing_data->interior.empty()) { | ||||
|         hollowed_mesh.merge(po.m_hollowing_data->interior); | ||||
|         hollowed_mesh.require_shared_vertices(); | ||||
|     } | ||||
| 
 | ||||
|     if (! needs_drilling) { | ||||
|         BOOST_LOG_TRIVIAL(info) << "Drilling skipped (no holes)."; | ||||
|         return; | ||||
|     } | ||||
|  | @ -124,17 +145,9 @@ void SLAPrint::Steps::drill_holes(SLAPrintObject &po) | |||
|     holes_mesh.require_shared_vertices(); | ||||
|     MeshBoolean::cgal::self_union(holes_mesh); //FIXME-fix and use the cgal version
 | ||||
|      | ||||
|     // If there is no hollowed mesh yet, copy the original mesh.
 | ||||
|     if (! po.m_hollowing_data) { | ||||
|         po.m_hollowing_data.reset(new SLAPrintObject::HollowingData()); | ||||
|         po.m_hollowing_data->hollow_mesh_with_holes = po.transformed_mesh(); | ||||
|     } | ||||
|      | ||||
|     TriangleMesh &hollowed_mesh = po.m_hollowing_data->hollow_mesh_with_holes; | ||||
|      | ||||
|     try { | ||||
|         MeshBoolean::cgal::minus(hollowed_mesh, holes_mesh); | ||||
|     } catch (const std::runtime_error &ex) { | ||||
|     } catch (const std::runtime_error&) { | ||||
|         throw std::runtime_error(L( | ||||
|             "Drilling holes into the mesh failed. " | ||||
|             "This is usually caused by broken model. Try to fix it first.")); | ||||
|  |  | |||
|  | @ -93,6 +93,8 @@ namespace PerlUtils { | |||
|     extern std::string path_to_parent_path(const char *src); | ||||
| }; | ||||
| 
 | ||||
| std::string string_printf(const char *format, ...); | ||||
| 
 | ||||
| // Standard "generated by Slic3r version xxx timestamp xxx" header string, 
 | ||||
| // to be placed at the top of Slic3r generated files.
 | ||||
| std::string header_slic3r_generated(); | ||||
|  |  | |||
|  | @ -230,23 +230,6 @@ static inline bool is_approx(Number value, Number test_value) | |||
|     return std::fabs(double(value) - double(test_value)) < double(EPSILON); | ||||
| } | ||||
| 
 | ||||
| template<class...Args> | ||||
| std::string string_printf(const char *const _fmt, Args &&...args) | ||||
| { | ||||
|     static const size_t INITIAL_LEN = 1024; | ||||
|     std::vector<char> buffer(INITIAL_LEN, '\0'); | ||||
|      | ||||
|     auto fmt = std::string("%s") + _fmt; | ||||
|     int bufflen = snprintf(buffer.data(), INITIAL_LEN - 1, fmt.c_str(), "", std::forward<Args>(args)...); | ||||
|      | ||||
|     if (bufflen >= int(INITIAL_LEN)) { | ||||
|         buffer.resize(size_t(bufflen) + 1); | ||||
|         snprintf(buffer.data(), buffer.size(), fmt.c_str(), "", std::forward<Args>(args)...); | ||||
|     } | ||||
|      | ||||
|     return std::string(buffer.begin(), buffer.begin() + bufflen); | ||||
| } | ||||
| 
 | ||||
| } // namespace Slic3r
 | ||||
| 
 | ||||
| #endif | ||||
|  |  | |||
|  | @ -577,6 +577,29 @@ namespace PerlUtils { | |||
|     std::string path_to_parent_path(const char *src)    { return boost::filesystem::path(src).parent_path().string(); } | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| std::string string_printf(const char *format, ...) | ||||
| { | ||||
|     va_list args1; | ||||
|     va_start(args1, format); | ||||
|     va_list args2; | ||||
|     va_copy(args2, args1); | ||||
|      | ||||
|     static const size_t INITIAL_LEN = 200; | ||||
|     std::string buffer(INITIAL_LEN, '\0'); | ||||
|      | ||||
|     int bufflen = ::vsnprintf(buffer.data(), INITIAL_LEN - 1, format, args1); | ||||
|      | ||||
|     if (bufflen >= int(INITIAL_LEN)) { | ||||
|         buffer.resize(size_t(bufflen) + 1); | ||||
|         ::vsnprintf(buffer.data(), buffer.size(), format, args2); | ||||
|     } | ||||
|      | ||||
|     buffer.resize(bufflen); | ||||
|      | ||||
|     return buffer; | ||||
| } | ||||
| 
 | ||||
| std::string header_slic3r_generated() | ||||
| { | ||||
|     return std::string("generated by " SLIC3R_APP_NAME " " SLIC3R_VERSION " on " ) + Utils::utc_timestamp(); | ||||
|  |  | |||
|  | @ -313,33 +313,38 @@ public: | |||
|     // Valid geometry_id should always be positive.
 | ||||
|     std::pair<size_t, size_t> geometry_id; | ||||
|     // An ID containing the extruder ID (used to select color).
 | ||||
|     int                 extruder_id; | ||||
|     // Is this object selected?
 | ||||
|     bool                selected; | ||||
|     // Is this object disabled from selection?
 | ||||
|     bool                disabled; | ||||
|     // Is this object printable?
 | ||||
|     bool                printable; | ||||
|     // Whether or not this volume is active for rendering
 | ||||
|     bool                is_active; | ||||
|     // Whether or not to use this volume when applying zoom_to_volumes()
 | ||||
|     bool                zoom_to_volumes; | ||||
|     // Wheter or not this volume is enabled for outside print volume detection in shader.
 | ||||
|     bool                shader_outside_printer_detection_enabled; | ||||
|     // Wheter or not this volume is outside print volume.
 | ||||
|     bool                is_outside; | ||||
|     int                 	extruder_id; | ||||
| 
 | ||||
|     // Various boolean flags.
 | ||||
|     struct { | ||||
| 	    // Is this object selected?
 | ||||
| 	    bool                selected : 1; | ||||
| 	    // Is this object disabled from selection?
 | ||||
| 	    bool                disabled : 1; | ||||
| 	    // Is this object printable?
 | ||||
| 	    bool                printable : 1; | ||||
| 	    // Whether or not this volume is active for rendering
 | ||||
| 	    bool                is_active : 1; | ||||
| 	    // Whether or not to use this volume when applying zoom_to_volumes()
 | ||||
| 	    bool                zoom_to_volumes : 1; | ||||
| 	    // Wheter or not this volume is enabled for outside print volume detection in shader.
 | ||||
| 	    bool                shader_outside_printer_detection_enabled : 1; | ||||
| 	    // Wheter or not this volume is outside print volume.
 | ||||
| 	    bool                is_outside : 1; | ||||
| 	    // Wheter or not this volume has been generated from a modifier
 | ||||
| 	    bool                is_modifier : 1; | ||||
| 	    // Wheter or not this volume has been generated from the wipe tower
 | ||||
| 	    bool                is_wipe_tower : 1; | ||||
| 	    // Wheter or not this volume has been generated from an extrusion path
 | ||||
| 	    bool                is_extrusion_path : 1; | ||||
| 	    // Wheter or not to always render this volume using its own alpha 
 | ||||
| 	    bool                force_transparent : 1; | ||||
| 	    // Whether or not always use the volume's own color (not using SELECTED/HOVER/DISABLED/OUTSIDE)
 | ||||
| 	    bool                force_native_color : 1; | ||||
| 	}; | ||||
| 
 | ||||
|     // Is mouse or rectangle selection over this object to select/deselect it ?
 | ||||
|     EHoverState         hover; | ||||
|     // Wheter or not this volume has been generated from a modifier
 | ||||
|     bool                is_modifier; | ||||
|     // Wheter or not this volume has been generated from the wipe tower
 | ||||
|     bool                is_wipe_tower; | ||||
|     // Wheter or not this volume has been generated from an extrusion path
 | ||||
|     bool                is_extrusion_path; | ||||
|     // Wheter or not to always render this volume using its own alpha 
 | ||||
|     bool                force_transparent; | ||||
|     // Whether or not always use the volume's own color (not using SELECTED/HOVER/DISABLED/OUTSIDE)
 | ||||
|     bool                force_native_color; | ||||
|     EHoverState         	hover; | ||||
| 
 | ||||
|     // Interleaved triangles & normals with indexed triangles & quads.
 | ||||
|     GLIndexedVertexArray        indexed_vertex_array; | ||||
|  |  | |||
|  | @ -35,7 +35,7 @@ public: | |||
|     wxBitmap* 		load_svg(const std::string &bitmap_key, unsigned width = 0, unsigned height = 0, const bool grayscale = false, const bool dark_mode = false); | ||||
| 
 | ||||
| 	/*static */wxBitmap mksolid(size_t width, size_t height, unsigned char r, unsigned char g, unsigned char b, unsigned char transparency, bool suppress_scaling = false); | ||||
| 	/*static */wxBitmap mksolid(size_t width, size_t height, const unsigned char rgb[3], bool suppress_scaling = false) { return mksolid(width, height, rgb[0], rgb[1], rgb[2], wxALPHA_OPAQUE); } | ||||
| 	/*static */wxBitmap mksolid(size_t width, size_t height, const unsigned char rgb[3], bool suppress_scaling = false) { return mksolid(width, height, rgb[0], rgb[1], rgb[2], wxALPHA_OPAQUE, suppress_scaling); } | ||||
| 	/*static */wxBitmap mkclear(size_t width, size_t height) { return mksolid(width, height, 0, 0, 0, wxALPHA_TRANSPARENT); } | ||||
| 
 | ||||
| 	static bool                 parse_color(const std::string& scolor, unsigned char* rgb_out); | ||||
|  |  | |||
|  | @ -1,4 +1,5 @@ | |||
| #include "wxExtensions.hpp" | ||||
| #include "libslic3r/GCode/PreviewData.hpp" | ||||
| #include "GUI.hpp" | ||||
| #include "GUI_App.hpp" | ||||
| #include "I18N.hpp" | ||||
|  | @ -322,7 +323,8 @@ void Control::SetTicksValues(const CustomGCode::Info& custom_gcode_per_print_z) | |||
|         // Switch to the "Feature type"/"Tool" from the very beginning of a new object slicing after deleting of the old one
 | ||||
|         post_ticks_changed_event(); | ||||
| 
 | ||||
|     m_ticks.mode = custom_gcode_per_print_z.mode; | ||||
|     if (custom_gcode_per_print_z.mode) | ||||
|         m_ticks.mode = custom_gcode_per_print_z.mode; | ||||
| 
 | ||||
|     Refresh(); | ||||
|     Update(); | ||||
|  | @ -1169,6 +1171,8 @@ void Control::OnKeyDown(wxKeyEvent &event) | |||
|         m_ticks.suppress_minus(true); | ||||
|         delete_current_tick(); | ||||
|     } | ||||
|     else if (event.GetKeyCode() == WXK_SHIFT) | ||||
|         UseDefaultColors(false); | ||||
|     else if (is_horizontal()) | ||||
|     { | ||||
|         if (key == WXK_LEFT || key == WXK_RIGHT) | ||||
|  | @ -1194,6 +1198,9 @@ void Control::OnKeyUp(wxKeyEvent &event) | |||
| { | ||||
|     if (event.GetKeyCode() == WXK_CONTROL) | ||||
|         m_is_one_layer = false; | ||||
|     else if (event.GetKeyCode() == WXK_SHIFT) | ||||
|         UseDefaultColors(true); | ||||
| 
 | ||||
|     Refresh(); | ||||
|     Update(); | ||||
|     event.Skip(); | ||||
|  | @ -1278,9 +1285,11 @@ std::array<int, 2> Control::get_active_extruders_for_tick(int tick) const | |||
| 
 | ||||
| // Get used extruders for tick. 
 | ||||
| // Means all extruders(tools) which will be used during printing from current tick to the end
 | ||||
| std::set<int> TickCodeInfo::get_used_extruders_for_tick(int tick, int only_extruder, double print_z) const | ||||
| std::set<int> TickCodeInfo::get_used_extruders_for_tick(int tick, int only_extruder, double print_z, t_mode force_mode/* = t_mode::Undef*/) const | ||||
| { | ||||
|     if (mode == t_mode::MultiExtruder) | ||||
|     t_mode e_mode = !force_mode ? mode : force_mode; | ||||
| 
 | ||||
|     if (e_mode == t_mode::MultiExtruder) | ||||
|     { | ||||
|         // #ys_FIXME: get tool ordering from _correct_ place
 | ||||
|         const ToolOrdering& tool_ordering = GUI::wxGetApp().plater()->fff_print().get_tool_ordering(); | ||||
|  | @ -1301,8 +1310,8 @@ std::set<int> TickCodeInfo::get_used_extruders_for_tick(int tick, int only_extru | |||
|         return used_extruders; | ||||
|     } | ||||
| 
 | ||||
|     const int default_initial_extruder = mode == t_mode::MultiAsSingle ? std::max(only_extruder, 1) : 1; | ||||
|     if (ticks.empty()) | ||||
|     const int default_initial_extruder = e_mode == t_mode::MultiAsSingle ? std::max(only_extruder, 1) : 1; | ||||
|     if (ticks.empty() || e_mode == t_mode::SingleExtruder) | ||||
|         return {default_initial_extruder}; | ||||
| 
 | ||||
|     std::set<int> used_extruders; | ||||
|  | @ -1346,10 +1355,13 @@ void Control::OnRightUp(wxMouseEvent& event) | |||
|     if (m_show_context_menu) { | ||||
|         wxMenu menu; | ||||
| 
 | ||||
|         if (m_mode == t_mode::SingleExtruder) | ||||
|         if (m_mode == t_mode::SingleExtruder) { | ||||
|             append_menu_item(&menu, wxID_ANY, _(L("Add color change")) + " (M600)", "", | ||||
|                 [this](wxCommandEvent&) { add_code_as_tick(ColorChangeCode); }, "colorchange_add_m", &menu, | ||||
|                 [](){return true;}, this); | ||||
| 
 | ||||
|             UseDefaultColors(false); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             append_change_extruder_menu_item(&menu); | ||||
|  | @ -1688,9 +1700,18 @@ bool Control::check_ticks_changed_event(const std::string& gcode) | |||
|     return true; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| std::string TickCodeInfo::get_color_for_tick(TickCode tick, const std::string& code, const int extruder) | ||||
| { | ||||
|     if (mode == t_mode::SingleExtruder && code == ColorChangeCode && m_use_default_colors) | ||||
|     { | ||||
|         const std::vector<std::string>& colors = GCodePreviewData::ColorPrintColors(); | ||||
|         if (ticks.empty()) | ||||
|             return colors[0]; | ||||
|         m_default_color_idx++; | ||||
| 
 | ||||
|         return colors[m_default_color_idx % colors.size()]; | ||||
|     } | ||||
| 
 | ||||
|     std::vector<std::string> colors = GUI::wxGetApp().plater()->get_extruder_colors_from_plater_config(); | ||||
|     std::string color = colors[extruder - 1]; | ||||
| 
 | ||||
|  | @ -1740,6 +1761,9 @@ bool TickCodeInfo::add_tick(const int tick, std::string& code, const int extrude | |||
|             return false; | ||||
|     } | ||||
| 
 | ||||
|     if (mode == t_mode::SingleExtruder) | ||||
|         m_use_default_colors = true; | ||||
| 
 | ||||
|     ticks.emplace(TickCode{ tick, code, extruder, color }); | ||||
|     return true; | ||||
| } | ||||
|  | @ -1840,7 +1864,7 @@ ConflictType TickCodeInfo::is_conflict_tick(const TickCode& tick, t_mode out_mod | |||
|     { | ||||
|         // We should mark a tick as a "MeaninglessColorChange", 
 | ||||
|         // if it has a ColorChange for unused extruder from current print to end of the print
 | ||||
|         std::set<int> used_extruders_for_tick = get_used_extruders_for_tick(tick.tick, only_extruder, print_z); | ||||
|         std::set<int> used_extruders_for_tick = get_used_extruders_for_tick(tick.tick, only_extruder, print_z, out_mode); | ||||
| 
 | ||||
|         if (used_extruders_for_tick.find(tick.extruder) == used_extruders_for_tick.end()) | ||||
|             return ctMeaninglessColorChange; | ||||
|  | @ -1868,7 +1892,6 @@ ConflictType TickCodeInfo::is_conflict_tick(const TickCode& tick, t_mode out_mod | |||
|     { | ||||
|         // We should mark a tick as a "MeaninglessToolChange", 
 | ||||
|         // if it has a ToolChange to the same extruder
 | ||||
| 
 | ||||
|         auto it = ticks.find(tick); | ||||
|         if (it == ticks.begin()) | ||||
|             return tick.extruder == std::max<int>(only_extruder, 1) ? ctMeaninglessToolChange : ctNone; | ||||
|  |  | |||
|  | @ -67,12 +67,14 @@ class TickCodeInfo | |||
|     std::string pause_print_msg; | ||||
|     bool        m_suppress_plus     = false; | ||||
|     bool        m_suppress_minus    = false; | ||||
|     bool        m_use_default_colors= false; | ||||
|     int         m_default_color_idx = 0; | ||||
| 
 | ||||
|     std::string get_color_for_tick(TickCode tick, const std::string& code, const int extruder); | ||||
| 
 | ||||
| public: | ||||
|     std::set<TickCode> ticks {}; | ||||
|     t_mode              mode = t_mode::SingleExtruder; | ||||
|     std::set<TickCode>  ticks {}; | ||||
|     t_mode              mode = t_mode::Undef; | ||||
| 
 | ||||
|     bool empty() const { return ticks.empty(); } | ||||
|     void set_pause_print_msg(const std::string& message) { pause_print_msg = message; } | ||||
|  | @ -88,12 +90,13 @@ public: | |||
| 
 | ||||
|     // Get used extruders for tick.
 | ||||
|     // Means all extruders(tools) which will be used during printing from current tick to the end
 | ||||
|     std::set<int>   get_used_extruders_for_tick(int tick, int only_extruder, double print_z) const; | ||||
|     std::set<int>   get_used_extruders_for_tick(int tick, int only_extruder, double print_z, t_mode force_mode = t_mode::Undef) const; | ||||
| 
 | ||||
|     void suppress_plus (bool suppress) { m_suppress_plus = suppress; } | ||||
|     void suppress_minus(bool suppress) { m_suppress_minus = suppress; } | ||||
|     bool suppressed_plus () { return m_suppress_plus; } | ||||
|     bool suppressed_minus() { return m_suppress_minus; } | ||||
|     void set_default_colors(bool default_colors_on)  { m_use_default_colors = default_colors_on; } | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
|  | @ -186,7 +189,11 @@ public: | |||
|         m_mode = !is_one_extruder_printed_model ? t_mode::MultiExtruder : | ||||
|                  only_extruder < 0              ? t_mode::SingleExtruder : | ||||
|                                                   t_mode::MultiAsSingle; | ||||
|         if (!m_ticks.mode) | ||||
|             m_ticks.mode = m_mode; | ||||
|         m_only_extruder = only_extruder; | ||||
| 
 | ||||
|         UseDefaultColors(m_mode == t_mode::SingleExtruder); | ||||
|     } | ||||
| 
 | ||||
|     bool is_horizontal() const      { return m_style == wxSL_HORIZONTAL; } | ||||
|  | @ -201,6 +208,7 @@ public: | |||
|     void OnLeftUp(wxMouseEvent& event); | ||||
|     void OnEnterWin(wxMouseEvent& event) { enter_window(event, true); } | ||||
|     void OnLeaveWin(wxMouseEvent& event) { enter_window(event, false); } | ||||
|     void UseDefaultColors(bool def_colors_on)   { m_ticks.set_default_colors(def_colors_on); } | ||||
|     void OnWheel(wxMouseEvent& event); | ||||
|     void OnKeyDown(wxKeyEvent &event); | ||||
|     void OnKeyUp(wxKeyEvent &event); | ||||
|  |  | |||
|  | @ -2136,8 +2136,8 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re | |||
|     }; | ||||
| 
 | ||||
|     // SLA steps to pull the preview meshes for.
 | ||||
| 	typedef std::array<SLAPrintObjectStep, 2> SLASteps; | ||||
| 	SLASteps sla_steps = { slaposSupportTree, slaposPad }; | ||||
| 	typedef std::array<SLAPrintObjectStep, 3> SLASteps; | ||||
|     SLASteps sla_steps = { slaposDrillHoles, slaposSupportTree, slaposPad }; | ||||
|     struct SLASupportState { | ||||
|         std::array<PrintStateBase::StateWithTimeStamp, std::tuple_size<SLASteps>::value> step; | ||||
|     }; | ||||
|  | @ -2184,7 +2184,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re | |||
|                         // Consider the DONE step without a valid mesh as invalid for the purpose
 | ||||
|                         // of mesh visualization.
 | ||||
|                         state.step[istep].state = PrintStateBase::INVALID; | ||||
|                     else | ||||
|                     else if (sla_steps[istep] != slaposDrillHoles) | ||||
|                         for (const ModelInstance* model_instance : print_object->model_object()->instances) | ||||
|                             // Only the instances, which are currently printable, will have the SLA support structures kept.
 | ||||
|                             // The instances outside the print bed will have the GLVolumes of their support structures released.
 | ||||
|  | @ -2197,7 +2197,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re | |||
|     } | ||||
|     std::sort(model_volume_state.begin(), model_volume_state.end(), model_volume_state_lower); | ||||
|     std::sort(aux_volume_state.begin(), aux_volume_state.end(), model_volume_state_lower); | ||||
|     // Release all ModelVolume based GLVolumes not found in the current Model.
 | ||||
|     // Release all ModelVolume based GLVolumes not found in the current Model. Find the GLVolume of a hollowed mesh.
 | ||||
|     for (size_t volume_id = 0; volume_id < m_volumes.volumes.size(); ++volume_id) { | ||||
|         GLVolume* volume = m_volumes.volumes[volume_id]; | ||||
|         ModelVolumeState  key(volume); | ||||
|  | @ -2279,6 +2279,9 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re | |||
|                     if (it_old_volume != deleted_volumes.end() && it_old_volume->composite_id == it->composite_id) | ||||
|                         // If a volume changed its ObjectID, but it reuses a GLVolume's CompositeID, maintain its selection.
 | ||||
|                         map_glvolume_old_to_new[it_old_volume->volume_idx] = m_volumes.volumes.size(); | ||||
|                     // Note the index of the loaded volume, so that we can reload the main model GLVolume with the hollowed mesh
 | ||||
|                     // later in this function.
 | ||||
|                     it->volume_idx = m_volumes.volumes.size(); | ||||
|                     m_volumes.load_object_volume(&model_object, obj_idx, volume_idx, instance_idx, m_color_by, m_initialized); | ||||
|                     m_volumes.volumes.back()->geometry_id = key.geometry_id; | ||||
|                     update_object_list = true; | ||||
|  | @ -2307,8 +2310,6 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re | |||
|             const ModelObject *model_object = print_object->model_object(); | ||||
|             // Find an index of the ModelObject
 | ||||
|             int object_idx; | ||||
| 			if (std::all_of(state.step.begin(), state.step.end(), [](const PrintStateBase::StateWithTimeStamp &state){ return state.state != PrintStateBase::DONE; })) | ||||
| 				continue; | ||||
|             // There may be new SLA volumes added to the scene for this print_object.
 | ||||
|             // Find the object index of this print_object in the Model::objects list.
 | ||||
|             auto it = std::find(sla_print->model().objects.begin(), sla_print->model().objects.end(), model_object); | ||||
|  | @ -2327,29 +2328,53 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re | |||
|                 assert(it != model_object->instances.end()); | ||||
|                 int instance_idx = it - model_object->instances.begin(); | ||||
|                 for (size_t istep = 0; istep < sla_steps.size(); ++ istep) | ||||
|                     if (state.step[istep].state == PrintStateBase::DONE) { | ||||
|                         ModelVolumeState key(state.step[istep].timestamp, instance.instance_id.id); | ||||
|                         auto it = std::lower_bound(aux_volume_state.begin(), aux_volume_state.end(), key, model_volume_state_lower); | ||||
|                         assert(it != aux_volume_state.end() && it->geometry_id == key.geometry_id); | ||||
|                         if (it->new_geometry()) { | ||||
|                     if (sla_steps[istep] == slaposDrillHoles) { | ||||
|                     	// Hollowing is a special case, where the mesh from the backend is being loaded into the 1st volume of an instance,
 | ||||
|                     	// not into its own GLVolume.
 | ||||
|                         // There shall always be such a GLVolume allocated.
 | ||||
|                         ModelVolumeState key(model_object->volumes.front()->id(), instance.instance_id); | ||||
|                         auto it = std::lower_bound(model_volume_state.begin(), model_volume_state.end(), key, model_volume_state_lower); | ||||
|                         assert(it != model_volume_state.end() && it->geometry_id == key.geometry_id); | ||||
|                         assert(!it->new_geometry()); | ||||
|                         GLVolume &volume = *m_volumes.volumes[it->volume_idx]; | ||||
|                         if (! volume.offsets.empty() && state.step[istep].timestamp != volume.offsets.front()) { | ||||
|                         	// The backend either produced a new hollowed mesh, or it invalidated the one that the front end has seen.
 | ||||
|                             volume.indexed_vertex_array.release_geometry(); | ||||
|                         	if (state.step[istep].state == PrintStateBase::DONE) { | ||||
|                                 TriangleMesh mesh = print_object->get_mesh(slaposDrillHoles); | ||||
| 	                            assert(! mesh.empty()); | ||||
|                                 mesh.transform(sla_print->sla_trafo(*m_model->objects[volume.object_idx()]).inverse()); | ||||
|                                 volume.indexed_vertex_array.load_mesh(mesh); | ||||
| 	                        } else { | ||||
| 	                        	// Reload the original volume.
 | ||||
|                                 volume.indexed_vertex_array.load_mesh(m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh()); | ||||
| 	                        } | ||||
|                             volume.finalize_geometry(true); | ||||
| 	                    } | ||||
|                     	//FIXME it is an ugly hack to write the timestamp into the "offsets" field to not have to add another member variable
 | ||||
|                     	// to the GLVolume. We should refactor GLVolume significantly, so that the GLVolume will not contain member variables
 | ||||
|                     	// of various concenrs (model vs. 3D print path).
 | ||||
|                     	volume.offsets = { state.step[istep].timestamp }; | ||||
|                     } else if (state.step[istep].state == PrintStateBase::DONE) { | ||||
|                         // Check whether there is an existing auxiliary volume to be updated, or a new auxiliary volume to be created.
 | ||||
| 						ModelVolumeState key(state.step[istep].timestamp, instance.instance_id.id); | ||||
| 						auto it = std::lower_bound(aux_volume_state.begin(), aux_volume_state.end(), key, model_volume_state_lower); | ||||
| 						assert(it != aux_volume_state.end() && it->geometry_id == key.geometry_id); | ||||
|                     	if (it->new_geometry()) { | ||||
|                             // This can be an SLA support structure that should not be rendered (in case someone used undo
 | ||||
|                             // to revert to before it was generated). If that's the case, we should not generate anything.
 | ||||
|                             if (model_object->sla_points_status != sla::PointsStatus::NoPoints) | ||||
|                                 instances[istep].emplace_back(std::pair<size_t, size_t>(instance_idx, print_instance_idx)); | ||||
|                             else | ||||
|                                 shift_zs[object_idx] = 0.; | ||||
|                         } else { | ||||
|                             // Recycling an old GLVolume. Update the Object/Instance indices into the current Model.
 | ||||
|                             m_volumes.volumes[it->volume_idx]->composite_id = GLVolume::CompositeID(object_idx, m_volumes.volumes[it->volume_idx]->volume_idx(), instance_idx); | ||||
|                             m_volumes.volumes[it->volume_idx]->set_instance_transformation(model_object->instances[instance_idx]->get_transformation()); | ||||
|                         } | ||||
| 						else { | ||||
| 							// Recycling an old GLVolume. Update the Object/Instance indices into the current Model.
 | ||||
| 							m_volumes.volumes[it->volume_idx]->composite_id = GLVolume::CompositeID(object_idx, m_volumes.volumes[it->volume_idx]->volume_idx(), instance_idx); | ||||
| 							m_volumes.volumes[it->volume_idx]->set_instance_transformation(model_object->instances[instance_idx]->get_transformation()); | ||||
| 						} | ||||
|                     } | ||||
|             } | ||||
| 
 | ||||
| //            // stores the current volumes count
 | ||||
| //            size_t volumes_count = m_volumes.volumes.size();
 | ||||
| 
 | ||||
|             for (size_t istep = 0; istep < sla_steps.size(); ++istep) | ||||
|                 if (!instances[istep].empty()) | ||||
|                     m_volumes.load_object_auxiliary(print_object, object_idx, instances[istep], sla_steps[istep], state.step[istep].timestamp, m_initialized); | ||||
|  | @ -2881,6 +2906,46 @@ void GLCanvas3D::on_key(wxKeyEvent& evt) | |||
|                 } | ||||
|                 else if (keyCode == WXK_CONTROL) | ||||
|                     m_dirty = true; | ||||
|                 else if (m_gizmos.is_enabled() && !m_selection.is_empty()) { | ||||
|                     switch (keyCode) | ||||
|                     { | ||||
|                     case WXK_NUMPAD_LEFT:  case WXK_LEFT: | ||||
|                     case WXK_NUMPAD_RIGHT: case WXK_RIGHT: | ||||
|                     case WXK_NUMPAD_UP:    case WXK_UP: | ||||
|                     case WXK_NUMPAD_DOWN:  case WXK_DOWN: | ||||
|                     { | ||||
|                         do_move(L("Gizmo-Move")); | ||||
|                         m_gizmos.update_data(); | ||||
| 
 | ||||
|                         wxGetApp().obj_manipul()->set_dirty(); | ||||
|                         // Let the plater know that the dragging finished, so a delayed refresh
 | ||||
|                         // of the scene with the background processing data should be performed.
 | ||||
|                         post_event(SimpleEvent(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED)); | ||||
|                         // updates camera target constraints
 | ||||
|                         refresh_camera_scene_box(); | ||||
|                         m_dirty = true; | ||||
| 
 | ||||
|                         break; | ||||
|                     } | ||||
|                     case WXK_NUMPAD_PAGEUP:   case WXK_PAGEUP: | ||||
|                     case WXK_NUMPAD_PAGEDOWN: case WXK_PAGEDOWN: | ||||
|                     { | ||||
|                         do_rotate(L("Gizmo-Rotate")); | ||||
|                         m_gizmos.update_data(); | ||||
| 
 | ||||
|                         wxGetApp().obj_manipul()->set_dirty(); | ||||
|                         // Let the plater know that the dragging finished, so a delayed refresh
 | ||||
|                         // of the scene with the background processing data should be performed.
 | ||||
|                         post_event(SimpleEvent(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED)); | ||||
|                         // updates camera target constraints
 | ||||
|                         refresh_camera_scene_box(); | ||||
|                         m_dirty = true; | ||||
| 
 | ||||
|                         break; | ||||
|                     } | ||||
|                     default: { break; } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             else if (evt.GetEventType() == wxEVT_KEY_DOWN) { | ||||
|                 m_tab_down = keyCode == WXK_TAB && !evt.HasAnyModifiers(); | ||||
|  | @ -2902,14 +2967,43 @@ void GLCanvas3D::on_key(wxKeyEvent& evt) | |||
|                 } | ||||
|                 else if (keyCode == WXK_CONTROL) | ||||
|                     m_dirty = true; | ||||
|                 // DoubleSlider navigation in Preview
 | ||||
|                 else if (keyCode == WXK_LEFT    ||  | ||||
|                          keyCode == WXK_RIGHT   || | ||||
|                          keyCode == WXK_UP      ||  | ||||
|                          keyCode == WXK_DOWN    ) | ||||
|                 else if (m_gizmos.is_enabled() && !m_selection.is_empty()) | ||||
|                 { | ||||
|                     if (dynamic_cast<Preview*>(m_canvas->GetParent()) != nullptr) | ||||
|                         post_event(wxKeyEvent(EVT_GLCANVAS_MOVE_DOUBLE_SLIDER, evt)); | ||||
|                     auto do_move = [this](const Vec3d& displacement) { | ||||
|                         m_selection.start_dragging(); | ||||
|                         m_selection.translate(displacement); | ||||
|                         m_dirty = true; | ||||
| //                        wxGetApp().obj_manipul()->set_dirty();
 | ||||
|                     }; | ||||
|                     auto do_rotate = [this](double angle_z_rad) { | ||||
|                         m_selection.start_dragging(); | ||||
|                         m_selection.rotate(Vec3d(0.0, 0.0, angle_z_rad), TransformationType(TransformationType::World_Relative_Joint)); | ||||
|                         m_dirty = true; | ||||
| //                        wxGetApp().obj_manipul()->set_dirty();
 | ||||
|                     }; | ||||
| 
 | ||||
|                     switch (keyCode) | ||||
|                     { | ||||
|                     case WXK_NUMPAD_LEFT:     case WXK_LEFT:     { do_move(-Vec3d::UnitX()); break; } | ||||
|                     case WXK_NUMPAD_RIGHT:    case WXK_RIGHT:    { do_move(Vec3d::UnitX()); break; } | ||||
|                     case WXK_NUMPAD_UP:       case WXK_UP:       { do_move(Vec3d::UnitY()); break; } | ||||
|                     case WXK_NUMPAD_DOWN:     case WXK_DOWN:     { do_move(-Vec3d::UnitY()); break; } | ||||
|                     case WXK_NUMPAD_PAGEUP:   case WXK_PAGEUP:   { do_rotate(0.25 * M_PI); break; } | ||||
|                     case WXK_NUMPAD_PAGEDOWN: case WXK_PAGEDOWN: { do_rotate(-0.25 * M_PI); break; } | ||||
|                     default: { break; } | ||||
|                     } | ||||
|                 } | ||||
|                 else if (!m_gizmos.is_enabled()) | ||||
|                 { | ||||
|                     // DoubleSlider navigation in Preview
 | ||||
|                     if (keyCode == WXK_LEFT || | ||||
|                         keyCode == WXK_RIGHT || | ||||
|                         keyCode == WXK_UP || | ||||
|                         keyCode == WXK_DOWN) | ||||
|                     { | ||||
|                         if (dynamic_cast<Preview*>(m_canvas->GetParent()) != nullptr) | ||||
|                             post_event(wxKeyEvent(EVT_GLCANVAS_MOVE_DOUBLE_SLIDER, evt)); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | @ -6256,8 +6350,6 @@ void GLCanvas3D::_load_sla_shells() | |||
|             unsigned int initial_volumes_count = (unsigned int)m_volumes.volumes.size(); | ||||
|             for (const SLAPrintObject::Instance& instance : obj->instances()) { | ||||
|                 add_volume(*obj, 0, instance, obj->get_mesh_to_print(), GLVolume::MODEL_COLOR[0], true); | ||||
| //                if (! obj->hollowed_interior_mesh().empty())
 | ||||
| //                    add_volume(*obj, -int(slaposHollowing), instance, obj->hollowed_interior_mesh(), GLVolume::MODEL_COLOR[0], false);
 | ||||
|                 // Set the extruder_id and volume_id to achieve the same color as in the 3D scene when
 | ||||
|                 // through the update_volumes_colors_by_extruder() call.
 | ||||
|                 m_volumes.volumes.back()->extruder_id = obj->model_object()->volumes.front()->extruder_id(); | ||||
|  |  | |||
|  | @ -978,6 +978,8 @@ void ObjectList::key_event(wxKeyEvent& event) | |||
|         ) { | ||||
|         remove(); | ||||
|     } | ||||
|     else if (event.GetKeyCode() == WXK_F5) | ||||
|         wxGetApp().plater()->reload_all_from_disk(); | ||||
|     else if (wxGetKeyState(wxKeyCode('A')) && wxGetKeyState(WXK_CONTROL/*WXK_SHIFT*/)) | ||||
|         select_item_all_children(); | ||||
|     else if (wxGetKeyState(wxKeyCode('C')) && wxGetKeyState(WXK_CONTROL))  | ||||
|  |  | |||
|  | @ -594,6 +594,11 @@ void Preview::create_double_slider() | |||
| 
 | ||||
|     // sizer, m_canvas_widget
 | ||||
|     m_canvas_widget->Bind(wxEVT_KEY_DOWN, &Preview::update_double_slider_from_canvas, this); | ||||
|     m_canvas_widget->Bind(wxEVT_KEY_UP, [this](wxKeyEvent& event) { | ||||
|         if (event.GetKeyCode() == WXK_SHIFT) | ||||
|             m_slider->UseDefaultColors(true); | ||||
|         event.Skip(); | ||||
|     }); | ||||
| 
 | ||||
|     m_slider->Bind(wxEVT_SCROLL_CHANGED, &Preview::on_sliders_scroll_changed, this); | ||||
| 
 | ||||
|  | @ -779,6 +784,8 @@ void Preview::update_double_slider_from_canvas(wxKeyEvent& event) | |||
|     } | ||||
|     else if (key == 'S') | ||||
|         m_slider->ChangeOneLayerLock(); | ||||
|     else if (key == WXK_SHIFT) | ||||
|         m_slider->UseDefaultColors(false); | ||||
|     else | ||||
|         event.Skip(); | ||||
| } | ||||
|  |  | |||
|  | @ -4,6 +4,9 @@ | |||
| #include <GL/glew.h> | ||||
| 
 | ||||
| #include "slic3r/GUI/GUI_App.hpp" | ||||
| #include "slic3r/GUI/GLCanvas3D.hpp" | ||||
| #include "libslic3r/SLAPrint.hpp" | ||||
| #include "slic3r/GUI/MeshUtils.hpp" | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|  | @ -302,5 +305,87 @@ unsigned char picking_checksum_alpha_channel(unsigned char red, unsigned char gr | |||
| 	return b; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| bool CommonGizmosData::update_from_backend(GLCanvas3D& canvas, ModelObject* model_object) | ||||
| { | ||||
|     recent_update = false; | ||||
| 
 | ||||
|     if (m_model_object != model_object | ||||
|     || (model_object && m_model_object_id != model_object->id())) { | ||||
|         m_model_object = model_object; | ||||
|         m_print_object_idx = -1; | ||||
|         m_mesh_raycaster.reset(); | ||||
|         m_object_clipper.reset(); | ||||
|         m_supports_clipper.reset(); | ||||
|         m_old_mesh = nullptr; | ||||
|         m_mesh = nullptr; | ||||
|         m_backend_mesh_transformed.clear(); | ||||
|         if (m_model_object) { | ||||
|             m_active_instance = canvas.get_selection().get_instance_idx(); | ||||
|             m_active_instance_bb_radius = m_model_object->instance_bounding_box(m_active_instance).radius(); | ||||
|         } | ||||
| 
 | ||||
|         recent_update = true; | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     if (! m_model_object || ! canvas.get_selection().is_from_single_instance()) | ||||
|         return false; | ||||
| 
 | ||||
|     int old_po_idx = m_print_object_idx; | ||||
| 
 | ||||
|     // First we need a pointer to the respective SLAPrintObject. The index into objects vector is
 | ||||
|     // cached so we don't have todo it on each render. We only search for the po if needed:
 | ||||
|     if (m_print_object_idx < 0 || (int)canvas.sla_print()->objects().size() != m_print_objects_count) { | ||||
|         m_print_objects_count = canvas.sla_print()->objects().size(); | ||||
|         m_print_object_idx = -1; | ||||
|         for (const SLAPrintObject* po : canvas.sla_print()->objects()) { | ||||
|             ++m_print_object_idx; | ||||
|             if (po->model_object()->id() == m_model_object->id()) | ||||
|                 break; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     m_mesh = nullptr; | ||||
|     // Load either the model_object mesh, or one provided by the backend
 | ||||
|     // This mesh does not account for the possible Z up SLA offset.
 | ||||
|     // The backend mesh needs to be transformed and because a pointer to it is
 | ||||
|     // saved, a copy is stored as a member (FIXME)
 | ||||
|     if (m_print_object_idx >=0) { | ||||
|         const SLAPrintObject* po = canvas.sla_print()->objects()[m_print_object_idx]; | ||||
|         if (po->is_step_done(slaposDrillHoles)) { | ||||
|             m_backend_mesh_transformed = po->get_mesh_to_print(); | ||||
|             m_backend_mesh_transformed.transform(canvas.sla_print()->sla_trafo(*m_model_object).inverse()); | ||||
|             m_mesh = &m_backend_mesh_transformed; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     if (! m_mesh) { | ||||
|         m_mesh = &m_model_object->volumes.front()->mesh(); | ||||
|         m_backend_mesh_transformed.clear(); | ||||
|     } | ||||
| 
 | ||||
|     m_model_object_id = m_model_object->id(); | ||||
| 
 | ||||
|     if (m_mesh != m_old_mesh) { | ||||
|         wxBusyCursor wait; | ||||
|         m_mesh_raycaster.reset(new MeshRaycaster(*m_mesh)); | ||||
|         m_object_clipper.reset(); | ||||
|         m_supports_clipper.reset(); | ||||
|         m_old_mesh = m_mesh; | ||||
|         m_clipping_plane_distance = 0.f; | ||||
|         m_clipping_plane_distance_stash = 0.f; | ||||
|         recent_update = true; | ||||
|         return true; | ||||
|     } | ||||
|     if (! recent_update) | ||||
|         recent_update = m_print_object_idx < 0 && old_po_idx >= 0; | ||||
| 
 | ||||
|     return m_print_object_idx < 0 ? old_po_idx >=0 : false; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| } // namespace GUI
 | ||||
| } // namespace Slic3r
 | ||||
|  |  | |||
|  | @ -31,6 +31,8 @@ static const float CONSTRAINED_COLOR[4] = { 0.5f, 0.5f, 0.5f, 1.0f }; | |||
| 
 | ||||
| class ImGuiWrapper; | ||||
| class CommonGizmosData; | ||||
| class GLCanvas3D; | ||||
| class ClippingPlane; | ||||
| 
 | ||||
| class GLGizmoBase | ||||
| { | ||||
|  | @ -189,9 +191,13 @@ class MeshClipper; | |||
| class CommonGizmosData { | ||||
| public: | ||||
|     const TriangleMesh* mesh() const { | ||||
|         return (! m_mesh ? nullptr : (m_cavity_mesh ? m_cavity_mesh.get() : m_mesh)); | ||||
|         return (! m_mesh ? nullptr : m_mesh); //(m_cavity_mesh ? m_cavity_mesh.get() : m_mesh));
 | ||||
|     } | ||||
| 
 | ||||
|     bool update_from_backend(GLCanvas3D& canvas, ModelObject* model_object); | ||||
| 
 | ||||
|     bool recent_update = false; | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|     ModelObject* m_model_object = nullptr; | ||||
|  | @ -200,8 +206,8 @@ public: | |||
|     std::unique_ptr<MeshClipper> m_object_clipper; | ||||
|     std::unique_ptr<MeshClipper> m_supports_clipper; | ||||
| 
 | ||||
|     std::unique_ptr<TriangleMesh> m_cavity_mesh; | ||||
|     std::unique_ptr<GLVolume> m_volume_with_cavity; | ||||
|     //std::unique_ptr<TriangleMesh> m_cavity_mesh;
 | ||||
|     //std::unique_ptr<GLVolume> m_volume_with_cavity;
 | ||||
| 
 | ||||
|     int m_active_instance = -1; | ||||
|     float m_active_instance_bb_radius = 0; | ||||
|  | @ -209,6 +215,22 @@ public: | |||
|     int m_print_object_idx = -1; | ||||
|     int m_print_objects_count = -1; | ||||
|     int m_old_timestamp = -1; | ||||
| 
 | ||||
|     float m_clipping_plane_distance = 0.f; | ||||
|     std::unique_ptr<ClippingPlane> m_clipping_plane; | ||||
| 
 | ||||
|     void stash_clipping_plane() { | ||||
|         m_clipping_plane_distance_stash = m_clipping_plane_distance; | ||||
|     } | ||||
| 
 | ||||
|     void unstash_clipping_plane() { | ||||
|         m_clipping_plane_distance = m_clipping_plane_distance_stash; | ||||
|     } | ||||
| 
 | ||||
| private: | ||||
|     const TriangleMesh* m_old_mesh; | ||||
|     TriangleMesh m_backend_mesh_transformed; | ||||
|     float m_clipping_plane_distance_stash = 0.f; | ||||
| }; | ||||
| 
 | ||||
| } // namespace GUI
 | ||||
|  |  | |||
|  | @ -9,6 +9,8 @@ | |||
| #include <wx/stattext.h> | ||||
| #include <wx/sizer.h> | ||||
| 
 | ||||
| #include <algorithm> | ||||
| 
 | ||||
| #include "slic3r/GUI/GUI_App.hpp" | ||||
| 
 | ||||
| 
 | ||||
|  | @ -189,7 +191,7 @@ void GLGizmoCut::update_max_z(const Selection& selection) const | |||
| void GLGizmoCut::set_cut_z(double cut_z) const | ||||
| { | ||||
|     // Clamp the plane to the object's bounding box
 | ||||
|     m_cut_z = std::max(0.0, std::min(m_max_z, cut_z)); | ||||
|     m_cut_z = std::clamp(cut_z, 0.0, m_max_z); | ||||
| } | ||||
| 
 | ||||
| void GLGizmoCut::perform_cut(const Selection& selection) | ||||
|  |  | |||
|  | @ -25,6 +25,9 @@ class GLGizmoCut : public GLGizmoBase | |||
| public: | ||||
|     GLGizmoCut(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); | ||||
| 
 | ||||
|     double get_cut_z() const { return m_cut_z; } | ||||
|     void set_cut_z(double cut_z) const; | ||||
| 
 | ||||
| protected: | ||||
|     virtual bool on_init(); | ||||
|     virtual void on_load(cereal::BinaryInputArchive& ar) { ar(m_cut_z, m_keep_upper, m_keep_lower, m_rotate_lower); } | ||||
|  | @ -40,7 +43,6 @@ protected: | |||
| 
 | ||||
| private: | ||||
|     void update_max_z(const Selection& selection) const; | ||||
|     void set_cut_z(double cut_z) const; | ||||
|     void perform_cut(const Selection& selection); | ||||
|     double calc_projection(const Linef3& mouse_ray) const; | ||||
| }; | ||||
|  |  | |||
|  | @ -22,7 +22,7 @@ GLGizmoHollow::GLGizmoHollow(GLCanvas3D& parent, const std::string& icon_filenam | |||
|     : GLGizmoBase(parent, icon_filename, sprite_id, cd) | ||||
|     , m_quadric(nullptr) | ||||
| { | ||||
|     m_clipping_plane.reset(new ClippingPlane(Vec3d::Zero(), 0.)); | ||||
|     m_c->m_clipping_plane.reset(new ClippingPlane(Vec3d::Zero(), 0.)); | ||||
|     m_quadric = ::gluNewQuadric(); | ||||
|     if (m_quadric != nullptr) | ||||
|         // using GLU_FILL does not work when the instance's transformation
 | ||||
|  | @ -40,7 +40,7 @@ bool GLGizmoHollow::on_init() | |||
| { | ||||
|     m_shortcut_key = WXK_CONTROL_H; | ||||
|     m_desc["enable"]           = _(L("Hollow this object")); | ||||
|     m_desc["preview"]          = _(L("Preview")); | ||||
|     m_desc["preview"]          = _(L("Preview hollowed and drilled model")); | ||||
|     m_desc["offset"]           = _(L("Offset")) + ": "; | ||||
|     m_desc["quality"]          = _(L("Quality")) + ": "; | ||||
|     m_desc["closing_distance"] = _(L("Closing distance")) + ": "; | ||||
|  | @ -55,38 +55,16 @@ bool GLGizmoHollow::on_init() | |||
|     return true; | ||||
| } | ||||
| 
 | ||||
| void GLGizmoHollow::set_sla_support_data(ModelObject* model_object, const Selection& selection) | ||||
| void GLGizmoHollow::set_sla_support_data(ModelObject*, const Selection&) | ||||
| { | ||||
|     if (! model_object || selection.is_empty()) { | ||||
|         m_c->m_model_object = nullptr; | ||||
|         return; | ||||
|     } | ||||
|     if (m_c->recent_update) { | ||||
| 
 | ||||
|     bool something_changed = false; | ||||
| 
 | ||||
|     if (m_c->m_model_object != model_object | ||||
|      || m_c->m_model_object_id != model_object->id() | ||||
|      || m_c->m_active_instance != selection.get_instance_idx()) { | ||||
|         m_c->m_model_object = model_object; | ||||
|         m_c->m_print_object_idx = -1; | ||||
|         m_c->m_active_instance = selection.get_instance_idx(); | ||||
|         something_changed = true; | ||||
|     } | ||||
| 
 | ||||
|     if (model_object && something_changed && selection.is_from_single_instance()) | ||||
|     { | ||||
|         // Cache the bb - it's needed for dealing with the clipping plane quite often
 | ||||
|         // It could be done inside update_mesh but one has to account for scaling of the instance.
 | ||||
|         m_c->m_active_instance_bb_radius = m_c->m_model_object->instance_bounding_box(m_c->m_active_instance).radius(); | ||||
| 
 | ||||
|         if (is_mesh_update_necessary()) { | ||||
|             update_mesh(); | ||||
|         if (m_c->m_model_object) | ||||
|             reload_cache(); | ||||
|         } | ||||
| 
 | ||||
|         if (m_state == On) { | ||||
|             m_parent.toggle_model_objects_visibility(false); | ||||
|             m_parent.toggle_model_objects_visibility(! m_c->m_cavity_mesh, m_c->m_model_object, m_c->m_active_instance); | ||||
|             m_parent.toggle_model_objects_visibility(true, m_c->m_model_object, m_c->m_active_instance); | ||||
|             m_parent.toggle_sla_auxiliaries_visibility(m_show_supports, m_c->m_model_object, m_c->m_active_instance); | ||||
|         } | ||||
|         else | ||||
|  | @ -109,8 +87,8 @@ void GLGizmoHollow::on_render() const | |||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     if (! m_c->m_mesh) | ||||
|         const_cast<GLGizmoHollow*>(this)->update_mesh(); | ||||
|     // !!! is it necessary?
 | ||||
|     //const_cast<GLGizmoHollow*>(this)->m_c->update_from_backend(m_parent, m_c->m_model_object);
 | ||||
| 
 | ||||
|     glsafe(::glEnable(GL_BLEND)); | ||||
|     glsafe(::glEnable(GL_DEPTH_TEST)); | ||||
|  | @ -132,7 +110,7 @@ void GLGizmoHollow::on_render() const | |||
| 
 | ||||
| void GLGizmoHollow::render_hollowed_mesh() const | ||||
| { | ||||
|     if (m_c->m_volume_with_cavity) { | ||||
|     /*if (m_c->m_volume_with_cavity) {
 | ||||
|         m_c->m_volume_with_cavity->set_sla_shift_z(m_z_shift); | ||||
|         m_parent.get_shader().start_using(); | ||||
| 
 | ||||
|  | @ -148,14 +126,13 @@ void GLGizmoHollow::render_hollowed_mesh() const | |||
|         m_c->m_volume_with_cavity->set_instance_transformation(m_c->m_model_object->instances[size_t(m_c->m_active_instance)]->get_transformation()); | ||||
|         m_c->m_volume_with_cavity->render(color_id, print_box_detection_id, print_box_worldmatrix_id); | ||||
|         m_parent.get_shader().stop_using(); | ||||
|     } | ||||
|     }*/ | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| void GLGizmoHollow::render_clipping_plane(const Selection& selection) const | ||||
| { | ||||
|     if (m_clipping_plane_distance == 0.f || m_c->mesh()->empty()) | ||||
|     if (m_c->m_clipping_plane_distance == 0.f) | ||||
|         return; | ||||
| 
 | ||||
|     // Get transformation of the instance
 | ||||
|  | @ -177,22 +154,14 @@ void GLGizmoHollow::render_clipping_plane(const Selection& selection) const | |||
|         m_c->m_object_clipper.reset(new MeshClipper); | ||||
|         m_c->m_object_clipper->set_mesh(*m_c->mesh()); | ||||
|     } | ||||
|     m_c->m_object_clipper->set_plane(*m_clipping_plane); | ||||
|     m_c->m_object_clipper->set_plane(*m_c->m_clipping_plane); | ||||
|     m_c->m_object_clipper->set_transformation(trafo); | ||||
| 
 | ||||
| 
 | ||||
|     // Next, ask the backend if supports are already calculated. If so, we are gonna cut them too.
 | ||||
|     // First we need a pointer to the respective SLAPrintObject. The index into objects vector is
 | ||||
|     // cached so we don't have todo it on each render. We only search for the po if needed:
 | ||||
|     if (m_c->m_print_object_idx < 0 || (int)m_parent.sla_print()->objects().size() != m_c->m_print_objects_count) { | ||||
|         m_c->m_print_objects_count = m_parent.sla_print()->objects().size(); | ||||
|         m_c->m_print_object_idx = -1; | ||||
|         for (const SLAPrintObject* po : m_parent.sla_print()->objects()) { | ||||
|             ++m_c->m_print_object_idx; | ||||
|             if (po->model_object()->id() == m_c->m_model_object->id()) | ||||
|                 break; | ||||
|         } | ||||
|     } | ||||
|     //if (m_c->m_print_object_idx < 0)
 | ||||
|     //    m_c->update_from_backend(m_parent, m_c->m_model_object);
 | ||||
| 
 | ||||
|     if (m_c->m_print_object_idx >= 0) { | ||||
|         const SLAPrintObject* print_object = m_parent.sla_print()->objects()[m_c->m_print_object_idx]; | ||||
| 
 | ||||
|  | @ -208,7 +177,7 @@ void GLGizmoHollow::render_clipping_plane(const Selection& selection) const | |||
|                 m_c->m_supports_clipper->set_mesh(print_object->support_mesh()); | ||||
|                 m_c->m_old_timestamp = timestamp; | ||||
|             } | ||||
|             m_c->m_supports_clipper->set_plane(*m_clipping_plane); | ||||
|             m_c->m_supports_clipper->set_plane(*m_c->m_clipping_plane); | ||||
|             m_c->m_supports_clipper->set_transformation(supports_trafo); | ||||
|         } | ||||
|         else | ||||
|  | @ -345,46 +314,12 @@ void GLGizmoHollow::render_points(const Selection& selection, bool picking) cons | |||
| 
 | ||||
| bool GLGizmoHollow::is_mesh_point_clipped(const Vec3d& point) const | ||||
| { | ||||
|     if (m_clipping_plane_distance == 0.f) | ||||
|     if (m_c->m_clipping_plane_distance == 0.f) | ||||
|         return false; | ||||
| 
 | ||||
|     Vec3d transformed_point = m_c->m_model_object->instances[m_c->m_active_instance]->get_transformation().get_matrix() * point; | ||||
|     transformed_point(2) += m_z_shift; | ||||
|     return m_clipping_plane->is_point_clipped(transformed_point); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| bool GLGizmoHollow::is_mesh_update_necessary() const | ||||
| { | ||||
|     return ((m_state == On) && (m_c->m_model_object != nullptr) && !m_c->m_model_object->instances.empty()) | ||||
|         && ((m_c->m_model_object->id() != m_c->m_model_object_id) || ! m_c->m_mesh); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| void GLGizmoHollow::update_mesh() | ||||
| { | ||||
|     if (! m_c->m_model_object) | ||||
|         return; | ||||
| 
 | ||||
|     wxBusyCursor wait; | ||||
|     // this way we can use that mesh directly.
 | ||||
|     // This mesh does not account for the possible Z up SLA offset.
 | ||||
|     m_c->m_mesh = &m_c->m_model_object->volumes.front()->mesh(); | ||||
| 
 | ||||
|     // If this is different mesh than last time
 | ||||
|     if (m_c->m_model_object_id != m_c->m_model_object->id()) { | ||||
|         m_c->m_cavity_mesh.reset(); // dump the cavity
 | ||||
|         m_c->m_volume_with_cavity.reset(); | ||||
|         m_parent.toggle_model_objects_visibility(true, m_c->m_model_object, m_c->m_active_instance); | ||||
|         m_c->m_mesh_raycaster.reset(); | ||||
|     } | ||||
| 
 | ||||
|     if (! m_c->m_mesh_raycaster) | ||||
|         m_c->m_mesh_raycaster.reset(new MeshRaycaster(*m_c->mesh())); | ||||
| 
 | ||||
|     m_c->m_model_object_id = m_c->m_model_object->id(); | ||||
|     return m_c->m_clipping_plane->is_point_clipped(transformed_point); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
|  | @ -393,9 +328,11 @@ void GLGizmoHollow::update_mesh() | |||
| // Return false if no intersection was found, true otherwise.
 | ||||
| bool GLGizmoHollow::unproject_on_mesh(const Vec2d& mouse_pos, std::pair<Vec3f, Vec3f>& pos_and_normal) | ||||
| { | ||||
|     // if the gizmo doesn't have the V, F structures for igl, calculate them first:
 | ||||
|     if (! m_c->m_mesh_raycaster) | ||||
|         update_mesh(); | ||||
|         return false; | ||||
|     // if the gizmo doesn't have the V, F structures for igl, calculate them first:
 | ||||
|     // !!! is it really necessary?
 | ||||
|     //m_c->update_from_backend(m_parent, m_c->m_model_object);
 | ||||
| 
 | ||||
|     const Camera& camera = m_parent.get_camera(); | ||||
|     const Selection& selection = m_parent.get_selection(); | ||||
|  | @ -406,7 +343,7 @@ bool GLGizmoHollow::unproject_on_mesh(const Vec2d& mouse_pos, std::pair<Vec3f, V | |||
|     // The raycaster query
 | ||||
|     Vec3f hit; | ||||
|     Vec3f normal; | ||||
|     if (m_c->m_mesh_raycaster->unproject_on_mesh(mouse_pos, trafo.get_matrix(), camera, hit, normal, m_clipping_plane.get())) { | ||||
|     if (m_c->m_mesh_raycaster->unproject_on_mesh(mouse_pos, trafo.get_matrix(), camera, hit, normal, m_c->m_clipping_plane.get())) { | ||||
|         // Return both the point and the facet normal.
 | ||||
|         pos_and_normal = std::make_pair(hit, normal); | ||||
|         return true; | ||||
|  | @ -492,7 +429,7 @@ bool GLGizmoHollow::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_pos | |||
|             points_inside.push_back(points[idx].cast<float>()); | ||||
| 
 | ||||
|         // Only select/deselect points that are actually visible
 | ||||
|         for (size_t idx :  m_c->m_mesh_raycaster->get_unobscured_idxs(trafo, m_parent.get_camera(), points_inside, m_clipping_plane.get())) | ||||
|         for (size_t idx :  m_c->m_mesh_raycaster->get_unobscured_idxs(trafo, m_parent.get_camera(), points_inside, m_c->m_clipping_plane.get())) | ||||
|         { | ||||
|             if (rectangle_status == GLSelectionRectangle::Deselect) | ||||
|                 unselect_point(points_idxs[idx]); | ||||
|  | @ -546,13 +483,13 @@ bool GLGizmoHollow::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_pos | |||
|     } | ||||
| 
 | ||||
|     if (action == SLAGizmoEventType::MouseWheelUp && control_down) { | ||||
|         m_clipping_plane_distance = std::min(1.f, m_clipping_plane_distance + 0.01f); | ||||
|         m_c->m_clipping_plane_distance = std::min(1.f, m_c->m_clipping_plane_distance + 0.01f); | ||||
|         update_clipping_plane(true); | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     if (action == SLAGizmoEventType::MouseWheelDown && control_down) { | ||||
|         m_clipping_plane_distance = std::max(0.f, m_clipping_plane_distance - 0.01f); | ||||
|         m_c->m_clipping_plane_distance = std::max(0.f, m_c->m_clipping_plane_distance - 0.01f); | ||||
|         update_clipping_plane(true); | ||||
|         return true; | ||||
|     } | ||||
|  | @ -605,7 +542,7 @@ void GLGizmoHollow::update_mesh_raycaster(std::unique_ptr<MeshRaycaster> &&rc) | |||
| { | ||||
|     m_c->m_mesh_raycaster = std::move(rc); | ||||
|     m_c->m_object_clipper.reset(); | ||||
|     m_c->m_volume_with_cavity.reset(); | ||||
|     //m_c->m_volume_with_cavity.reset();
 | ||||
| } | ||||
| 
 | ||||
| void GLGizmoHollow::hollow_mesh(bool postpone_error_messages) | ||||
|  | @ -622,8 +559,8 @@ void GLGizmoHollow::hollow_mesh(bool postpone_error_messages) | |||
| void GLGizmoHollow::update_hollowed_mesh(std::unique_ptr<TriangleMesh> &&mesh) | ||||
| { | ||||
|     // Called from Plater when the UI job finishes
 | ||||
|     m_c->m_cavity_mesh = std::move(mesh); | ||||
|      | ||||
|     /*m_c->m_cavity_mesh = std::move(mesh);
 | ||||
| 
 | ||||
|     if(m_c->m_cavity_mesh) { | ||||
|         // First subtract the holes:
 | ||||
|         if (! m_c->m_model_object->sla_drain_holes.empty()) { | ||||
|  | @ -665,10 +602,10 @@ void GLGizmoHollow::update_hollowed_mesh(std::unique_ptr<TriangleMesh> &&mesh) | |||
|         m_c->m_mesh_raycaster.reset(new MeshRaycaster(*m_c->mesh())); | ||||
|     } | ||||
| 
 | ||||
|     if (m_clipping_plane_distance == 0.f) { | ||||
|         m_clipping_plane_distance = 0.5f; | ||||
|     if (m_c->m_clipping_plane_distance == 0.f) { | ||||
|         m_c->m_clipping_plane_distance = 0.5f; | ||||
|         update_clipping_plane(); | ||||
|     } | ||||
|     }*/ | ||||
| } | ||||
| 
 | ||||
| std::vector<std::pair<const ConfigOption*, const ConfigOptionDef*>> GLGizmoHollow::get_config_options(const std::vector<std::string>& keys) const | ||||
|  | @ -701,10 +638,10 @@ std::vector<std::pair<const ConfigOption*, const ConfigOptionDef*>> GLGizmoHollo | |||
| 
 | ||||
| ClippingPlane GLGizmoHollow::get_sla_clipping_plane() const | ||||
| { | ||||
|     if (!m_c->m_model_object || m_state == Off || m_clipping_plane_distance == 0.f) | ||||
|     if (!m_c->m_model_object || m_state == Off || m_c->m_clipping_plane_distance == 0.f) | ||||
|         return ClippingPlane::ClipsNothing(); | ||||
|     else | ||||
|         return ClippingPlane(-m_clipping_plane->get_normal(), m_clipping_plane->get_data()[3]); | ||||
|         return ClippingPlane(-m_c->m_clipping_plane->get_normal(), m_c->m_clipping_plane->get_data()[3]); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
|  | @ -724,18 +661,24 @@ RENDER_AGAIN: | |||
| 
 | ||||
|     // First calculate width of all the texts that are could possibly be shown. We will decide set the dialog width based on that:
 | ||||
|     const float settings_sliders_left = | ||||
|       std::max(std::max(m_imgui->calc_text_size(m_desc.at("offset")).x, | ||||
|                         m_imgui->calc_text_size(m_desc.at("quality")).x), | ||||
|                         m_imgui->calc_text_size(m_desc.at("closing_distance")).x) | ||||
|                         + m_imgui->scaled(1.f); | ||||
|       std::max({m_imgui->calc_text_size(m_desc.at("offset")).x, | ||||
|                m_imgui->calc_text_size(m_desc.at("quality")).x, | ||||
|                m_imgui->calc_text_size(m_desc.at("closing_distance")).x, | ||||
|                m_imgui->calc_text_size(m_desc.at("hole_diameter")).x, | ||||
|                m_imgui->calc_text_size(m_desc.at("hole_depth")).x}) | ||||
|            + m_imgui->scaled(1.f); | ||||
| 
 | ||||
|     const float clipping_slider_left = std::max(m_imgui->calc_text_size(m_desc.at("clipping_of_view")).x, m_imgui->calc_text_size(m_desc.at("reset_direction")).x) + m_imgui->scaled(1.5f); | ||||
|     const float diameter_slider_left = m_imgui->calc_text_size(m_desc.at("hole_diameter")).x + m_imgui->scaled(1.f); | ||||
|     const float diameter_slider_left = settings_sliders_left; //m_imgui->calc_text_size(m_desc.at("hole_diameter")).x + m_imgui->scaled(1.f);
 | ||||
|     const float minimal_slider_width = m_imgui->scaled(4.f); | ||||
|     //const float buttons_width_approx = m_imgui->calc_text_size(m_desc.at("apply_changes")).x + m_imgui->calc_text_size(m_desc.at("discard_changes")).x + m_imgui->scaled(1.5f);
 | ||||
| 
 | ||||
|     float window_width = minimal_slider_width + std::max(std::max(settings_sliders_left, clipping_slider_left), diameter_slider_left); | ||||
|     window_width = std::max(std::max(window_width, /*buttons_width_approx*/0.f), 0.f); | ||||
|     float window_width = minimal_slider_width + std::max({settings_sliders_left, clipping_slider_left, diameter_slider_left}); | ||||
|     window_width = std::max(window_width, m_imgui->calc_text_size(m_desc.at("preview")).x); | ||||
| 
 | ||||
|     if (m_imgui->button(m_desc["preview"])) | ||||
|         hollow_mesh(); | ||||
| 
 | ||||
|     ImGui::Separator(); | ||||
| 
 | ||||
|     { | ||||
|         auto opts = get_config_options({"hollowing_enable"}); | ||||
|  | @ -745,11 +688,8 @@ RENDER_AGAIN: | |||
|             wxGetApp().obj_list()->update_and_show_object_settings_item(); | ||||
|         } | ||||
|     } | ||||
|     m_imgui->disabled_begin(! m_enable_hollowing); | ||||
| 
 | ||||
|     ImGui::SameLine(); | ||||
|     if (m_imgui->button(m_desc["preview"])) | ||||
|         hollow_mesh(); | ||||
|     m_imgui->disabled_begin(! m_enable_hollowing); | ||||
| 
 | ||||
|     std::vector<std::string> opts_keys = {"hollowing_min_thickness", "hollowing_quality", "hollowing_closing_distance"}; | ||||
|     auto opts = get_config_options(opts_keys); | ||||
|  | @ -834,7 +774,7 @@ RENDER_AGAIN: | |||
|    // m_imgui->text(" "); // vertical gap
 | ||||
|     ImGui::Separator(); | ||||
| 
 | ||||
|     float diameter_upper_cap = 5.f; | ||||
|     float diameter_upper_cap = 15.; | ||||
|     if (m_new_hole_radius > diameter_upper_cap) | ||||
|         m_new_hole_radius = diameter_upper_cap; | ||||
|     m_imgui->text(m_desc.at("hole_diameter")); | ||||
|  | @ -904,7 +844,7 @@ RENDER_AGAIN: | |||
|     // Following is rendered in both editing and non-editing mode:
 | ||||
|    // m_imgui->text("");
 | ||||
|     ImGui::Separator(); | ||||
|     if (m_clipping_plane_distance == 0.f) | ||||
|     if (m_c->m_clipping_plane_distance == 0.f) | ||||
|         m_imgui->text(m_desc.at("clipping_of_view")); | ||||
|     else { | ||||
|         if (m_imgui->button(m_desc.at("reset_direction"))) { | ||||
|  | @ -916,7 +856,7 @@ RENDER_AGAIN: | |||
| 
 | ||||
|     ImGui::SameLine(clipping_slider_left); | ||||
|     ImGui::PushItemWidth(window_width - clipping_slider_left); | ||||
|     if (ImGui::SliderFloat("     ", &m_clipping_plane_distance, 0.f, 1.f, "%.2f")) | ||||
|     if (ImGui::SliderFloat("     ", &m_c->m_clipping_plane_distance, 0.f, 1.f, "%.2f")) | ||||
|         update_clipping_plane(true); | ||||
| 
 | ||||
|     // make sure supports are shown/hidden as appropriate
 | ||||
|  | @ -973,7 +913,7 @@ bool GLGizmoHollow::on_is_selectable() const | |||
| 
 | ||||
| std::string GLGizmoHollow::on_get_name() const | ||||
| { | ||||
|     return (_(L("Hollowing")) + " [H]").ToUTF8().data(); | ||||
|     return (_(L("Hollowing and drilling")) + " [H]").ToUTF8().data(); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
|  | @ -995,17 +935,19 @@ void GLGizmoHollow::on_set_state() | |||
| 
 | ||||
|     if (m_state == On && m_old_state != On) { // the gizmo was just turned on
 | ||||
|         //Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("SLA gizmo turned on")));
 | ||||
|         if (is_mesh_update_necessary()) | ||||
|             update_mesh(); | ||||
|         //m_c->update_from_backend(m_parent, m_c->m_model_object);
 | ||||
|         m_c->unstash_clipping_plane(); | ||||
|         update_clipping_plane(m_c->m_clipping_plane_distance != 0.f); | ||||
| 
 | ||||
|         // we'll now reload support points:
 | ||||
|         if (m_c->m_model_object) | ||||
|             reload_cache(); | ||||
| 
 | ||||
|         m_parent.toggle_model_objects_visibility(false); | ||||
|         if (m_c->m_model_object) | ||||
|             m_parent.toggle_model_objects_visibility(! m_c->m_cavity_mesh, m_c->m_model_object, m_c->m_active_instance); | ||||
|         m_parent.toggle_sla_auxiliaries_visibility(m_show_supports, m_c->m_model_object, m_c->m_active_instance); | ||||
|         if (m_c->m_model_object) { | ||||
|             m_parent.toggle_model_objects_visibility(true, m_c->m_model_object, m_c->m_active_instance); | ||||
|             m_parent.toggle_sla_auxiliaries_visibility(m_show_supports, m_c->m_model_object, m_c->m_active_instance); | ||||
|         } | ||||
| 
 | ||||
|         // Set default head diameter from config.
 | ||||
|         //const DynamicPrintConfig& cfg = wxGetApp().preset_bundle->sla_prints.get_edited_preset().config;
 | ||||
|  | @ -1014,8 +956,9 @@ void GLGizmoHollow::on_set_state() | |||
|     if (m_state == Off && m_old_state != Off) { // the gizmo was just turned Off
 | ||||
|         //Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("SLA gizmo turned off")));
 | ||||
|         m_parent.toggle_model_objects_visibility(true); | ||||
|         m_clipping_plane_distance = 0.f; | ||||
|         update_clipping_plane(); | ||||
|         m_c->stash_clipping_plane(); | ||||
|         m_c->m_clipping_plane_distance = 0.f; | ||||
|         update_clipping_plane(true); | ||||
|         // Release clippers and the AABB raycaster.
 | ||||
|         m_c->m_object_clipper.reset(); | ||||
|         m_c->m_supports_clipper.reset(); | ||||
|  | @ -1060,8 +1003,8 @@ void GLGizmoHollow::on_stop_dragging() | |||
| 
 | ||||
| void GLGizmoHollow::on_load(cereal::BinaryInputArchive& ar) | ||||
| { | ||||
|     ar(m_clipping_plane_distance, | ||||
|        *m_clipping_plane, | ||||
|     ar(m_c->m_clipping_plane_distance, | ||||
|        *m_c->m_clipping_plane, | ||||
|        m_c->m_model_object_id, | ||||
|        m_new_hole_radius, | ||||
|        m_new_hole_height, | ||||
|  | @ -1074,8 +1017,8 @@ void GLGizmoHollow::on_load(cereal::BinaryInputArchive& ar) | |||
| 
 | ||||
| void GLGizmoHollow::on_save(cereal::BinaryOutputArchive& ar) const | ||||
| { | ||||
|     ar(m_clipping_plane_distance, | ||||
|        *m_clipping_plane, | ||||
|     ar(m_c->m_clipping_plane_distance, | ||||
|        *m_c->m_clipping_plane, | ||||
|        m_c->m_model_object_id, | ||||
|        m_new_hole_radius, | ||||
|        m_new_hole_height, | ||||
|  | @ -1128,12 +1071,14 @@ void GLGizmoHollow::reload_cache() | |||
| 
 | ||||
| void GLGizmoHollow::update_clipping_plane(bool keep_normal) const | ||||
| { | ||||
|     Vec3d normal = (keep_normal && m_clipping_plane->get_normal() != Vec3d::Zero() ? | ||||
|                         m_clipping_plane->get_normal() : -m_parent.get_camera().get_dir_forward()); | ||||
|     if (! m_c->m_model_object) | ||||
|         return; | ||||
|     Vec3d normal = (keep_normal && m_c->m_clipping_plane->get_normal() != Vec3d::Zero() ? | ||||
|                         m_c->m_clipping_plane->get_normal() : -m_parent.get_camera().get_dir_forward()); | ||||
| 
 | ||||
|     const Vec3d& center = m_c->m_model_object->instances[m_c->m_active_instance]->get_offset() + Vec3d(0., 0., m_z_shift); | ||||
|     float dist = normal.dot(center); | ||||
|     *m_clipping_plane = ClippingPlane(normal, (dist - (-m_c->m_active_instance_bb_radius) - m_clipping_plane_distance * 2*m_c->m_active_instance_bb_radius)); | ||||
|     *m_c->m_clipping_plane = ClippingPlane(normal, (dist - (-m_c->m_active_instance_bb_radius) - m_c->m_clipping_plane_distance * 2*m_c->m_active_instance_bb_radius)); | ||||
|     m_parent.set_as_dirty(); | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -53,14 +53,12 @@ private: | |||
|     void render_points(const Selection& selection, bool picking = false) const; | ||||
|     void render_clipping_plane(const Selection& selection) const; | ||||
|     void render_hollowed_mesh() const; | ||||
|     bool is_mesh_update_necessary() const; | ||||
|     void update_mesh(); | ||||
|     void hollow_mesh(bool postpone_error_messages = false); | ||||
|     bool unsaved_changes() const; | ||||
| 
 | ||||
|     bool  m_show_supports = true; | ||||
|     float m_new_hole_radius = 2.f;        // Size of a new hole.
 | ||||
|     float m_new_hole_height = 5.f; | ||||
|     float m_new_hole_height = 6.f; | ||||
|     mutable std::vector<bool> m_selected; // which holes are currently selected
 | ||||
| 
 | ||||
|     bool m_enable_hollowing = true; | ||||
|  | @ -72,13 +70,9 @@ private: | |||
|     float m_closing_d_stash = 2.f; | ||||
|     Vec3f m_hole_before_drag = Vec3f::Zero(); | ||||
| 
 | ||||
| 
 | ||||
|     sla::DrainHoles m_holes_stash; | ||||
| 
 | ||||
| 
 | ||||
|      | ||||
|     float m_clipping_plane_distance = 0.f; | ||||
|     std::unique_ptr<ClippingPlane> m_clipping_plane; | ||||
|     //std::unique_ptr<ClippingPlane> m_clipping_plane;
 | ||||
|      | ||||
|     // This map holds all translated description texts, so they can be easily referenced during layout calculations
 | ||||
|     // etc. When language changes, GUI is recreated and this class constructed again, so the change takes effect.
 | ||||
|  |  | |||
|  | @ -27,7 +27,7 @@ GLGizmoSlaSupports::GLGizmoSlaSupports(GLCanvas3D& parent, const std::string& ic | |||
|     , m_quadric(nullptr) | ||||
|     , m_its(nullptr) | ||||
| { | ||||
|     m_clipping_plane.reset(new ClippingPlane(Vec3d::Zero(), 0.)); | ||||
|     m_c->m_clipping_plane.reset(new ClippingPlane(Vec3d::Zero(), 0.)); | ||||
|     m_quadric = ::gluNewQuadric(); | ||||
|     if (m_quadric != nullptr) | ||||
|         // using GLU_FILL does not work when the instance's transformation
 | ||||
|  | @ -63,43 +63,22 @@ bool GLGizmoSlaSupports::on_init() | |||
| 
 | ||||
| void GLGizmoSlaSupports::set_sla_support_data(ModelObject* model_object, const Selection& selection) | ||||
| { | ||||
|     if (! model_object || selection.is_empty()) { | ||||
|         m_c->m_model_object = nullptr; | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     bool something_changed = false; | ||||
| 
 | ||||
|     if (m_c->m_model_object != model_object | ||||
|      || m_c->m_model_object_id != model_object->id() | ||||
|      || m_c->m_active_instance != selection.get_instance_idx()) { | ||||
|         m_c->m_model_object = model_object; | ||||
|         m_c->m_print_object_idx = -1; | ||||
|         m_c->m_active_instance = selection.get_instance_idx(); | ||||
|         something_changed = true; | ||||
|     } | ||||
| 
 | ||||
|     if (model_object && selection.is_from_single_instance()) | ||||
|     { | ||||
|         // Cache the bb - it's needed for dealing with the clipping plane quite often
 | ||||
|         // It could be done inside update_mesh but one has to account for scaling of the instance.
 | ||||
|         if (something_changed) { | ||||
|             m_c->m_active_instance_bb_radius = m_c->m_model_object->instance_bounding_box(m_c->m_active_instance).radius(); | ||||
|             if (m_state == On) { | ||||
|                 m_parent.toggle_model_objects_visibility(false); | ||||
|                 m_parent.toggle_model_objects_visibility(! m_c->m_cavity_mesh, m_c->m_model_object, m_c->m_active_instance); | ||||
|                 m_parent.toggle_sla_auxiliaries_visibility(! m_editing_mode, m_c->m_model_object, m_c->m_active_instance); | ||||
|             } | ||||
|             else | ||||
|                 m_parent.toggle_model_objects_visibility(true, nullptr, -1); | ||||
|     if (m_c->recent_update) { | ||||
|         if (m_state == On) { | ||||
|             m_parent.toggle_model_objects_visibility(false); | ||||
|             m_parent.toggle_model_objects_visibility(/*! m_c->m_cavity_mesh*/ true, m_c->m_model_object, m_c->m_active_instance); | ||||
|             m_parent.toggle_sla_auxiliaries_visibility(! m_editing_mode, m_c->m_model_object, m_c->m_active_instance); | ||||
|         } | ||||
|         else | ||||
|             m_parent.toggle_model_objects_visibility(true, nullptr, -1); | ||||
| 
 | ||||
|         if (is_mesh_update_necessary()) { | ||||
|             update_mesh(); | ||||
|         disable_editing_mode(); | ||||
|         if (m_c->m_model_object) | ||||
|             reload_cache(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|         // If we triggered autogeneration before, check backend and fetch results if they are there
 | ||||
|     // If we triggered autogeneration before, check backend and fetch results if they are there
 | ||||
|     if (m_c->m_model_object) { | ||||
|         if (m_c->m_model_object->sla_points_status == sla::PointsStatus::Generating) | ||||
|             get_data_from_backend(); | ||||
|     } | ||||
|  | @ -120,9 +99,6 @@ void GLGizmoSlaSupports::on_render() const | |||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     if (! m_its || ! m_c->m_mesh) | ||||
|         const_cast<GLGizmoSlaSupports*>(this)->update_mesh(); | ||||
| 
 | ||||
|     glsafe(::glEnable(GL_BLEND)); | ||||
|     glsafe(::glEnable(GL_DEPTH_TEST)); | ||||
| 
 | ||||
|  | @ -143,7 +119,7 @@ void GLGizmoSlaSupports::on_render() const | |||
| 
 | ||||
| void GLGizmoSlaSupports::render_hollowed_mesh() const | ||||
| { | ||||
|     if (m_c->m_volume_with_cavity) { | ||||
|     /*if (m_c->m_volume_with_cavity) {
 | ||||
|         m_c->m_volume_with_cavity->set_sla_shift_z(m_z_shift); | ||||
|         m_parent.get_shader().start_using(); | ||||
| 
 | ||||
|  | @ -159,14 +135,14 @@ void GLGizmoSlaSupports::render_hollowed_mesh() const | |||
|         m_c->m_volume_with_cavity->set_instance_transformation(m_c->m_model_object->instances[size_t(m_c->m_active_instance)]->get_transformation()); | ||||
|         m_c->m_volume_with_cavity->render(color_id, print_box_detection_id, print_box_worldmatrix_id); | ||||
|         m_parent.get_shader().stop_using(); | ||||
|     } | ||||
|     }*/ | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| void GLGizmoSlaSupports::render_clipping_plane(const Selection& selection) const | ||||
| { | ||||
|     if (m_clipping_plane_distance == 0.f || m_c->m_mesh->empty()) | ||||
|     if (m_c->m_clipping_plane_distance == 0.f || m_c->m_mesh->empty()) | ||||
|         return; | ||||
| 
 | ||||
|     // Get transformation of the instance
 | ||||
|  | @ -188,7 +164,7 @@ void GLGizmoSlaSupports::render_clipping_plane(const Selection& selection) const | |||
|         m_c->m_object_clipper.reset(new MeshClipper); | ||||
|         m_c->m_object_clipper->set_mesh(*m_c->mesh()); | ||||
|     } | ||||
|     m_c->m_object_clipper->set_plane(*m_clipping_plane); | ||||
|     m_c->m_object_clipper->set_plane(*m_c->m_clipping_plane); | ||||
|     m_c->m_object_clipper->set_transformation(trafo); | ||||
| 
 | ||||
| 
 | ||||
|  | @ -219,7 +195,7 @@ void GLGizmoSlaSupports::render_clipping_plane(const Selection& selection) const | |||
|                 m_c->m_supports_clipper->set_mesh(print_object->support_mesh()); | ||||
|                 m_c->m_old_timestamp = timestamp; | ||||
|             } | ||||
|             m_c->m_supports_clipper->set_plane(*m_clipping_plane); | ||||
|             m_c->m_supports_clipper->set_plane(*m_c->m_clipping_plane); | ||||
|             m_c->m_supports_clipper->set_transformation(supports_trafo); | ||||
|         } | ||||
|         else | ||||
|  | @ -359,7 +335,7 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) | |||
|     } | ||||
| 
 | ||||
|     // Now render the drain holes:
 | ||||
|     if (! m_c->m_cavity_mesh) { | ||||
|     /*if (! m_c->m_cavity_mesh) {
 | ||||
|         render_color[0] = 0.7f; | ||||
|         render_color[1] = 0.7f; | ||||
|         render_color[2] = 0.7f; | ||||
|  | @ -394,7 +370,7 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) | |||
|                 glFrontFace(GL_CCW); | ||||
|             glsafe(::glPopMatrix()); | ||||
|         } | ||||
|     } | ||||
|     }*/ | ||||
| 
 | ||||
|     if (!picking) | ||||
|         glsafe(::glDisable(GL_LIGHTING)); | ||||
|  | @ -406,51 +382,22 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) | |||
| 
 | ||||
| bool GLGizmoSlaSupports::is_mesh_point_clipped(const Vec3d& point) const | ||||
| { | ||||
|     if (m_clipping_plane_distance == 0.f) | ||||
|     if (m_c->m_clipping_plane_distance == 0.f) | ||||
|         return false; | ||||
| 
 | ||||
|     Vec3d transformed_point = m_c->m_model_object->instances[m_c->m_active_instance]->get_transformation().get_matrix() * point; | ||||
|     transformed_point(2) += m_z_shift; | ||||
|     return m_clipping_plane->is_point_clipped(transformed_point); | ||||
|     return m_c->m_clipping_plane->is_point_clipped(transformed_point); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| bool GLGizmoSlaSupports::is_mesh_update_necessary() const | ||||
| { | ||||
|     return ((m_state == On) && (m_c->m_model_object != nullptr) && !m_c->m_model_object->instances.empty()) | ||||
|         && ((m_c->m_model_object->id() != m_c->m_model_object_id) || m_its == nullptr); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| void GLGizmoSlaSupports::update_mesh() | ||||
| { | ||||
|     if (! m_c->m_model_object) | ||||
|         return; | ||||
| 
 | ||||
|     wxBusyCursor wait; | ||||
|     // this way we can use that mesh directly.
 | ||||
|     // This mesh does not account for the possible Z up SLA offset.
 | ||||
|     m_c->m_mesh = &m_c->m_model_object->volumes.front()->mesh(); | ||||
|     m_its = &m_c->m_mesh->its; | ||||
| 
 | ||||
|     // If this is different mesh than last time or if the AABB tree is uninitialized, recalculate it.
 | ||||
|     if (m_c->m_model_object_id != m_c->m_model_object->id() || ! m_c->m_mesh_raycaster) | ||||
|         m_c->m_mesh_raycaster.reset(new MeshRaycaster(*m_c->mesh())); | ||||
| 
 | ||||
|     m_c->m_model_object_id = m_c->m_model_object->id(); | ||||
|     disable_editing_mode(); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| // Unprojects the mouse position on the mesh and saves hit point and normal of the facet into pos_and_normal
 | ||||
| // Return false if no intersection was found, true otherwise.
 | ||||
| bool GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse_pos, std::pair<Vec3f, Vec3f>& pos_and_normal) | ||||
| { | ||||
|     // if the gizmo doesn't have the V, F structures for igl, calculate them first:
 | ||||
|     if (! m_c->m_mesh_raycaster) | ||||
|         update_mesh(); | ||||
|         return false; | ||||
| 
 | ||||
|     const Camera& camera = m_parent.get_camera(); | ||||
|     const Selection& selection = m_parent.get_selection(); | ||||
|  | @ -461,20 +408,20 @@ bool GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse_pos, std::pair<Vec | |||
|     // The raycaster query
 | ||||
|     Vec3f hit; | ||||
|     Vec3f normal; | ||||
|     if (m_c->m_mesh_raycaster->unproject_on_mesh(mouse_pos, trafo.get_matrix(), camera, hit, normal, m_clipping_plane.get())) { | ||||
|     if (m_c->m_mesh_raycaster->unproject_on_mesh(mouse_pos, trafo.get_matrix(), camera, hit, normal, m_c->m_clipping_plane.get())) { | ||||
|         // Check whether the hit is in a hole
 | ||||
|         bool in_hole = false; | ||||
|         // In case the hollowed and drilled mesh is available, we can allow
 | ||||
|         // placing points in holes, because they should never end up
 | ||||
|         // on surface that's been drilled away.
 | ||||
|         if (! m_c->m_cavity_mesh) { | ||||
|         /*if (! m_c->m_cavity_mesh) {
 | ||||
|             for (const sla::DrainHole& hole : m_c->m_model_object->sla_drain_holes) { | ||||
|                 if (hole.is_inside(hit)) { | ||||
|                     in_hole = true; | ||||
|                     break; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         }*/ | ||||
|         if (! in_hole) { | ||||
|             // Return both the point and the facet normal.
 | ||||
|             pos_and_normal = std::make_pair(hit, normal); | ||||
|  | @ -555,7 +502,7 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous | |||
|                 points_inside.push_back(points[idx].cast<float>()); | ||||
| 
 | ||||
|             // Only select/deselect points that are actually visible
 | ||||
|             for (size_t idx :  m_c->m_mesh_raycaster->get_unobscured_idxs(trafo, m_parent.get_camera(), points_inside, m_clipping_plane.get())) | ||||
|             for (size_t idx :  m_c->m_mesh_raycaster->get_unobscured_idxs(trafo, m_parent.get_camera(), points_inside, m_c->m_clipping_plane.get())) | ||||
|             { | ||||
|                 if (rectangle_status == GLSelectionRectangle::Deselect) | ||||
|                     unselect_point(points_idxs[idx]); | ||||
|  | @ -632,13 +579,13 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous | |||
|     } | ||||
| 
 | ||||
|     if (action == SLAGizmoEventType::MouseWheelUp && control_down) { | ||||
|         m_clipping_plane_distance = std::min(1.f, m_clipping_plane_distance + 0.01f); | ||||
|         m_c->m_clipping_plane_distance = std::min(1.f, m_c->m_clipping_plane_distance + 0.01f); | ||||
|         update_clipping_plane(true); | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     if (action == SLAGizmoEventType::MouseWheelDown && control_down) { | ||||
|         m_clipping_plane_distance = std::max(0.f, m_clipping_plane_distance - 0.01f); | ||||
|         m_c->m_clipping_plane_distance = std::max(0.f, m_c->m_clipping_plane_distance - 0.01f); | ||||
|         update_clipping_plane(true); | ||||
|         return true; | ||||
|     } | ||||
|  | @ -719,10 +666,10 @@ std::vector<const ConfigOption*> GLGizmoSlaSupports::get_config_options(const st | |||
| 
 | ||||
| ClippingPlane GLGizmoSlaSupports::get_sla_clipping_plane() const | ||||
| { | ||||
|     if (!m_c->m_model_object || m_state == Off || m_clipping_plane_distance == 0.f) | ||||
|     if (!m_c->m_model_object || m_state == Off || m_c->m_clipping_plane_distance == 0.f) | ||||
|         return ClippingPlane::ClipsNothing(); | ||||
|     else | ||||
|         return ClippingPlane(-m_clipping_plane->get_normal(), m_clipping_plane->get_data()[3]); | ||||
|         return ClippingPlane(-m_c->m_clipping_plane->get_normal(), m_c->m_clipping_plane->get_data()[3]); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
|  | @ -944,7 +891,7 @@ RENDER_AGAIN: | |||
| 
 | ||||
|     // Following is rendered in both editing and non-editing mode:
 | ||||
|     ImGui::Separator(); | ||||
|     if (m_clipping_plane_distance == 0.f) | ||||
|     if (m_c->m_clipping_plane_distance == 0.f) | ||||
|     { | ||||
|         ImGui::AlignTextToFramePadding(); | ||||
|         m_imgui->text(m_desc.at("clipping_of_view")); | ||||
|  | @ -959,7 +906,7 @@ RENDER_AGAIN: | |||
| 
 | ||||
|     ImGui::SameLine(clipping_slider_left); | ||||
|     ImGui::PushItemWidth(window_width - clipping_slider_left); | ||||
|     if (ImGui::SliderFloat("  ", &m_clipping_plane_distance, 0.f, 1.f, "%.2f")) | ||||
|     if (ImGui::SliderFloat("  ", &m_c->m_clipping_plane_distance, 0.f, 1.f, "%.2f")) | ||||
|         update_clipping_plane(true); | ||||
| 
 | ||||
| 
 | ||||
|  | @ -1043,15 +990,17 @@ void GLGizmoSlaSupports::on_set_state() | |||
| 
 | ||||
|     if (m_state == On && m_old_state != On) { // the gizmo was just turned on
 | ||||
|         Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("SLA gizmo turned on"))); | ||||
|         if (is_mesh_update_necessary()) | ||||
|             update_mesh(); | ||||
| 
 | ||||
|         m_c->unstash_clipping_plane(); | ||||
|         update_clipping_plane(m_c->m_clipping_plane_distance != 0.f); | ||||
| 
 | ||||
| 
 | ||||
|         // we'll now reload support points:
 | ||||
|         if (m_c->m_model_object) | ||||
|             reload_cache(); | ||||
| 
 | ||||
|         m_parent.toggle_model_objects_visibility(false); | ||||
|         if (m_c->m_model_object && ! m_c->m_cavity_mesh) | ||||
|         if (m_c->m_model_object /*&& ! m_c->m_cavity_mesh*/) | ||||
|             m_parent.toggle_model_objects_visibility(true, m_c->m_model_object, m_c->m_active_instance); | ||||
|         m_parent.toggle_sla_auxiliaries_visibility(! m_editing_mode, m_c->m_model_object, m_c->m_active_instance); | ||||
| 
 | ||||
|  | @ -1081,13 +1030,14 @@ void GLGizmoSlaSupports::on_set_state() | |||
|             Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("SLA gizmo turned off"))); | ||||
|             m_parent.toggle_model_objects_visibility(true); | ||||
|             m_normal_cache.clear(); | ||||
|             m_clipping_plane_distance = 0.f; | ||||
|             update_clipping_plane(); | ||||
|             m_c->stash_clipping_plane(); | ||||
|             m_c->m_clipping_plane_distance = 0.f; | ||||
|             update_clipping_plane(true); | ||||
|             // Release clippers and the AABB raycaster.
 | ||||
|             m_its = nullptr; | ||||
|             m_c->m_object_clipper.reset(); | ||||
|             m_c->m_supports_clipper.reset(); | ||||
|             m_c->m_mesh_raycaster.reset(); | ||||
|             //m_c->m_mesh_raycaster.reset();
 | ||||
|         } | ||||
|     } | ||||
|     m_old_state = m_state; | ||||
|  | @ -1127,8 +1077,8 @@ void GLGizmoSlaSupports::on_stop_dragging() | |||
| 
 | ||||
| void GLGizmoSlaSupports::on_load(cereal::BinaryInputArchive& ar) | ||||
| { | ||||
|     ar(m_clipping_plane_distance, | ||||
|        *m_clipping_plane, | ||||
|     ar(m_c->m_clipping_plane_distance, | ||||
|        *m_c->m_clipping_plane, | ||||
|        m_c->m_model_object_id, | ||||
|        m_new_point_head_diameter, | ||||
|        m_normal_cache, | ||||
|  | @ -1141,8 +1091,8 @@ void GLGizmoSlaSupports::on_load(cereal::BinaryInputArchive& ar) | |||
| 
 | ||||
| void GLGizmoSlaSupports::on_save(cereal::BinaryOutputArchive& ar) const | ||||
| { | ||||
|     ar(m_clipping_plane_distance, | ||||
|        *m_clipping_plane, | ||||
|     ar(m_c->m_clipping_plane_distance, | ||||
|        *m_c->m_clipping_plane, | ||||
|        m_c->m_model_object_id, | ||||
|        m_new_point_head_diameter, | ||||
|        m_normal_cache, | ||||
|  | @ -1244,6 +1194,9 @@ void GLGizmoSlaSupports::reload_cache() | |||
| 
 | ||||
| bool GLGizmoSlaSupports::has_backend_supports() const | ||||
| { | ||||
|     if (! m_c->m_model_object) | ||||
|         return false; | ||||
| 
 | ||||
|     // find SlaPrintObject with this ID
 | ||||
|     for (const SLAPrintObject* po : m_parent.sla_print()->objects()) { | ||||
|         if (po->model_object()->id() == m_c->m_model_object->id()) | ||||
|  | @ -1338,12 +1291,12 @@ bool GLGizmoSlaSupports::unsaved_changes() const | |||
| 
 | ||||
| void GLGizmoSlaSupports::update_clipping_plane(bool keep_normal) const | ||||
| { | ||||
|     Vec3d normal = (keep_normal && m_clipping_plane->get_normal() != Vec3d::Zero() ? | ||||
|                         m_clipping_plane->get_normal() : -m_parent.get_camera().get_dir_forward()); | ||||
|     Vec3d normal = (keep_normal && m_c->m_clipping_plane->get_normal() != Vec3d::Zero() ? | ||||
|                         m_c->m_clipping_plane->get_normal() : -m_parent.get_camera().get_dir_forward()); | ||||
| 
 | ||||
|     const Vec3d& center = m_c->m_model_object->instances[m_c->m_active_instance]->get_offset() + Vec3d(0., 0., m_z_shift); | ||||
|     float dist = normal.dot(center); | ||||
|     *m_clipping_plane = ClippingPlane(normal, (dist - (-m_c->m_active_instance_bb_radius) - m_clipping_plane_distance * 2*m_c->m_active_instance_bb_radius)); | ||||
|     *m_c->m_clipping_plane = ClippingPlane(normal, (dist - (-m_c->m_active_instance_bb_radius) - m_c->m_clipping_plane_distance * 2*m_c->m_active_instance_bb_radius)); | ||||
|     m_parent.set_as_dirty(); | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -91,8 +91,6 @@ private: | |||
|     void render_points(const Selection& selection, bool picking = false) const; | ||||
|     void render_clipping_plane(const Selection& selection) const; | ||||
|     void render_hollowed_mesh() const; | ||||
|     bool is_mesh_update_necessary() const; | ||||
|     void update_mesh(); | ||||
|     bool unsaved_changes() const; | ||||
| 
 | ||||
|     bool m_lock_unique_islands = false; | ||||
|  | @ -105,8 +103,7 @@ private: | |||
|     mutable std::vector<CacheEntry> m_editing_cache; // a support point and whether it is currently selected
 | ||||
|     std::vector<sla::SupportPoint> m_normal_cache; // to restore after discarding changes or undo/redo
 | ||||
| 
 | ||||
|     float m_clipping_plane_distance = 0.f; | ||||
|     std::unique_ptr<ClippingPlane> m_clipping_plane; | ||||
|     //std::unique_ptr<ClippingPlane> m_clipping_plane;
 | ||||
| 
 | ||||
|     // This map holds all translated description texts, so they can be easily referenced during layout calculations
 | ||||
|     // etc. When language changes, GUI is recreated and this class constructed again, so the change takes effect.
 | ||||
|  |  | |||
|  | @ -348,6 +348,9 @@ void GLGizmosManager::set_sla_support_data(ModelObject* model_object) | |||
|     if (!m_enabled || m_gizmos.empty()) | ||||
|         return; | ||||
| 
 | ||||
|     // Update common data for hollowing and sla support gizmos.
 | ||||
|     m_common_gizmos_data->update_from_backend(m_parent, model_object); | ||||
| 
 | ||||
|     dynamic_cast<GLGizmoSlaSupports*>(m_gizmos[SlaSupports].get())->set_sla_support_data(model_object, m_parent.get_selection()); | ||||
|     dynamic_cast<GLGizmoHollow*>(m_gizmos[Hollow].get())->set_sla_support_data(model_object, m_parent.get_selection()); | ||||
| } | ||||
|  | @ -497,7 +500,7 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) | |||
|             processed = true; | ||||
|         } | ||||
|         else if (evt.Dragging() && (m_parent.get_move_volume_id() != -1) && (m_current == SlaSupports || m_current == Hollow)) | ||||
|                         // don't allow dragging objects with the Sla gizmo on
 | ||||
|             // don't allow dragging objects with the Sla gizmo on
 | ||||
|             processed = true; | ||||
|         else if (evt.Dragging() && (m_current == SlaSupports || m_current == Hollow) && gizmo_event(SLAGizmoEventType::Dragging, mouse_pos, evt.ShiftDown(), evt.AltDown(), evt.ControlDown())) | ||||
|         { | ||||
|  | @ -554,12 +557,9 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) | |||
|         else if (evt.LeftUp() && is_dragging()) | ||||
|         { | ||||
|             switch (m_current) { | ||||
|             case Move : m_parent.do_move(L("Gizmo-Move")); | ||||
|                         break; | ||||
|             case Scale : m_parent.do_scale(L("Gizmo-Scale")); | ||||
|                          break; | ||||
|             case Rotate : m_parent.do_rotate(L("Gizmo-Rotate")); | ||||
|                           break; | ||||
|             case Move : m_parent.do_move(L("Gizmo-Move")); break; | ||||
|             case Scale : m_parent.do_scale(L("Gizmo-Scale")); break; | ||||
|             case Rotate : m_parent.do_rotate(L("Gizmo-Rotate")); break; | ||||
|             default : break; | ||||
|             } | ||||
| 
 | ||||
|  | @ -788,6 +788,21 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt) | |||
| //            m_parent.set_cursor(GLCanvas3D::Cross);
 | ||||
|             processed = true; | ||||
|         } | ||||
|         else if (m_current == Cut) | ||||
|         { | ||||
|             auto do_move = [this, &processed](double delta_z) { | ||||
|                 GLGizmoCut* cut = dynamic_cast<GLGizmoCut*>(get_current()); | ||||
|                 cut->set_cut_z(delta_z + cut->get_cut_z()); | ||||
|                 processed = true; | ||||
|             }; | ||||
| 
 | ||||
|             switch (keyCode) | ||||
|             { | ||||
|             case WXK_NUMPAD_UP:   case WXK_UP:   { do_move(1.0); break; } | ||||
|             case WXK_NUMPAD_DOWN: case WXK_DOWN: { do_move(-1.0); break; } | ||||
|             default: { break; } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     if (processed) | ||||
|  |  | |||
|  | @ -2076,7 +2076,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) | |||
|     view3D_canvas->Bind(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE, [this](SimpleEvent&) { this->view3D->get_canvas3d()->reset_layer_height_profile(); }); | ||||
|     view3D_canvas->Bind(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, [this](Event<float>& evt) { this->view3D->get_canvas3d()->adaptive_layer_height_profile(evt.data); }); | ||||
|     view3D_canvas->Bind(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, [this](HeightProfileSmoothEvent& evt) { this->view3D->get_canvas3d()->smooth_layer_height_profile(evt.data); }); | ||||
|     view3D_canvas->Bind(EVT_GLCANVAS_RELOAD_FROM_DISK, [this](SimpleEvent&) { if (!this->model.objects.empty()) this->reload_all_from_disk(); }); | ||||
|     view3D_canvas->Bind(EVT_GLCANVAS_RELOAD_FROM_DISK, [this](SimpleEvent&) { this->reload_all_from_disk(); }); | ||||
| 
 | ||||
|     // 3DScene/Toolbar:
 | ||||
|     view3D_canvas->Bind(EVT_GLTOOLBAR_ADD, &priv::on_action_add, this); | ||||
|  | @ -3451,6 +3451,9 @@ void Plater::priv::reload_from_disk() | |||
| 
 | ||||
| void Plater::priv::reload_all_from_disk() | ||||
| { | ||||
|     if (model.objects.empty()) | ||||
|         return; | ||||
| 
 | ||||
|     Plater::TakeSnapshot snapshot(q, _(L("Reload all from disk"))); | ||||
|     Plater::SuppressSnapshots suppress(q); | ||||
| 
 | ||||
|  | @ -5130,7 +5133,7 @@ void Plater::reslice_SLA_supports(const ModelObject &object, bool postpone_error | |||
| 
 | ||||
| void Plater::reslice_SLA_hollowing(const ModelObject &object, bool postpone_error_messages) | ||||
| { | ||||
|     reslice_SLA_until_step(slaposHollowing, object, postpone_error_messages); | ||||
|     reslice_SLA_until_step(slaposDrillHoles, object, postpone_error_messages); | ||||
| } | ||||
| 
 | ||||
| void Plater::reslice_SLA_until_step(SLAPrintObjectStep step, const ModelObject &object, bool postpone_error_messages) | ||||
|  |  | |||
|  | @ -1,6 +1,6 @@ | |||
| #include <catch_main.hpp> | ||||
| 
 | ||||
| #include "libslic3r/libslic3r.h" | ||||
| #include "libslic3r/Utils.hpp" | ||||
| 
 | ||||
| namespace { | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Enrico Turri
						Enrico Turri