mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-10-31 04:31:15 -06:00 
			
		
		
		
	Merge branch 'master' of https://github.com/prusa3d/PrusaSlicer into et_custom_bed
This commit is contained in:
		
						commit
						610e0f7ee6
					
				
					 56 changed files with 1779 additions and 1171 deletions
				
			
		|  | @ -46,7 +46,7 @@ BreakConstructorInitializersBeforeComma: false | ||||||
| BreakConstructorInitializers: BeforeComma | BreakConstructorInitializers: BeforeComma | ||||||
| BreakAfterJavaFieldAnnotations: false | BreakAfterJavaFieldAnnotations: false | ||||||
| BreakStringLiterals: true | BreakStringLiterals: true | ||||||
| ColumnLimit:     75 | ColumnLimit:     78 | ||||||
| CommentPragmas:  '^ IWYU pragma:' | CommentPragmas:  '^ IWYU pragma:' | ||||||
| CompactNamespaces: true | CompactNamespaces: true | ||||||
| ConstructorInitializerAllOnOneLineOrOnePerLine: true | ConstructorInitializerAllOnOneLineOrOnePerLine: true | ||||||
|  |  | ||||||
							
								
								
									
										2
									
								
								deps/CMakeLists.txt
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								deps/CMakeLists.txt
									
										
									
									
										vendored
									
									
								
							|  | @ -105,7 +105,7 @@ else() | ||||||
|         dep_gtest |         dep_gtest | ||||||
|         dep_nlopt |         dep_nlopt | ||||||
|         dep_qhull |         dep_qhull | ||||||
|         dep_libigl |         # dep_libigl # Not working, static build has different Eigen | ||||||
|     ) |     ) | ||||||
| 
 | 
 | ||||||
| endif() | endif() | ||||||
|  |  | ||||||
							
								
								
									
										
											BIN
										
									
								
								resources/icons/drop_to_bed.png
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								resources/icons/drop_to_bed.png
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 528 B | 
|  | @ -15,7 +15,8 @@ const std::string USAGE_STR = { | ||||||
| 
 | 
 | ||||||
| namespace Slic3r { namespace sla { | namespace Slic3r { namespace sla { | ||||||
| 
 | 
 | ||||||
| Contour3D create_base_pool(const ExPolygons &ground_layer,  | Contour3D create_base_pool(const Polygons &ground_layer,  | ||||||
|  |                            const Polygons &holes = {}, | ||||||
|                            const PoolConfig& cfg = PoolConfig()); |                            const PoolConfig& cfg = PoolConfig()); | ||||||
| 
 | 
 | ||||||
| Contour3D walls(const Polygon& floor_plate, const Polygon& ceiling, | Contour3D walls(const Polygon& floor_plate, const Polygon& ceiling, | ||||||
|  | @ -42,37 +43,28 @@ int main(const int argc, const char *argv[]) { | ||||||
|     model.ReadSTLFile(argv[1]); |     model.ReadSTLFile(argv[1]); | ||||||
|     model.align_to_origin(); |     model.align_to_origin(); | ||||||
| 
 | 
 | ||||||
|     ExPolygons ground_slice; |     Polygons ground_slice; | ||||||
|     sla::Contour3D mesh; |  | ||||||
| //    TriangleMesh basepool;
 |  | ||||||
| 
 |  | ||||||
|     sla::base_plate(model, ground_slice, 0.1f); |     sla::base_plate(model, ground_slice, 0.1f); | ||||||
| 
 |  | ||||||
|     if(ground_slice.empty()) return EXIT_FAILURE; |     if(ground_slice.empty()) return EXIT_FAILURE; | ||||||
| 
 | 
 | ||||||
| //    ExPolygon bottom_plate = ground_slice.front();
 |     Polygon gndfirst; gndfirst = ground_slice.front(); | ||||||
| //    ExPolygon top_plate = bottom_plate;
 |     sla::offset_with_breakstick_holes(gndfirst, 0.5, 10, 0.3); | ||||||
| //    sla::offset(top_plate, coord_t(3.0/SCALING_FACTOR));
 | 
 | ||||||
| //    sla::offset(bottom_plate, coord_t(1.0/SCALING_FACTOR));
 |     sla::Contour3D mesh; | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
|     bench.start(); |     bench.start(); | ||||||
| 
 | 
 | ||||||
| //    TriangleMesh pool;
 |  | ||||||
|     sla::PoolConfig cfg; |     sla::PoolConfig cfg; | ||||||
|     cfg.min_wall_height_mm = 0; |     cfg.min_wall_height_mm = 0; | ||||||
|     cfg.edge_radius_mm = 0.2; |     cfg.edge_radius_mm = 0; | ||||||
|     mesh = sla::create_base_pool(ground_slice, cfg); |     mesh = sla::create_base_pool(ground_slice, {}, cfg); | ||||||
|      |  | ||||||
| //    mesh.merge(triangulate_expolygon_3d(top_plate, 3.0, false));
 |  | ||||||
| //    mesh.merge(triangulate_expolygon_3d(bottom_plate, 0.0, true));
 |  | ||||||
| //    mesh = sla::walls(bottom_plate.contour, top_plate.contour, 0, 3, 2.0, [](){});
 |  | ||||||
| 
 | 
 | ||||||
|     bench.stop(); |     bench.stop(); | ||||||
| 
 | 
 | ||||||
|     cout << "Base pool creation time: " << std::setprecision(10) |     cout << "Base pool creation time: " << std::setprecision(10) | ||||||
|          << bench.getElapsedSec() << " seconds." << endl; |          << bench.getElapsedSec() << " seconds." << endl; | ||||||
| 
 | 
 | ||||||
| //    auto point = []()
 |  | ||||||
|     for(auto& trind : mesh.indices) { |     for(auto& trind : mesh.indices) { | ||||||
|         Vec3d p0 = mesh.points[size_t(trind[0])]; |         Vec3d p0 = mesh.points[size_t(trind[0])]; | ||||||
|         Vec3d p1 = mesh.points[size_t(trind[1])]; |         Vec3d p1 = mesh.points[size_t(trind[1])]; | ||||||
|  |  | ||||||
|  | @ -586,6 +586,9 @@ void GCode::_do_export(Print &print, FILE *file) | ||||||
|     } |     } | ||||||
|     m_analyzer.set_extruder_offsets(extruder_offsets); |     m_analyzer.set_extruder_offsets(extruder_offsets); | ||||||
| 
 | 
 | ||||||
|  |     // tell analyzer about the gcode flavor
 | ||||||
|  |     m_analyzer.set_gcode_flavor(print.config().gcode_flavor); | ||||||
|  | 
 | ||||||
|     // resets analyzer's tracking data
 |     // resets analyzer's tracking data
 | ||||||
|     m_last_mm3_per_mm = GCodeAnalyzer::Default_mm3_per_mm; |     m_last_mm3_per_mm = GCodeAnalyzer::Default_mm3_per_mm; | ||||||
|     m_last_width = GCodeAnalyzer::Default_Width; |     m_last_width = GCodeAnalyzer::Default_Width; | ||||||
|  | @ -1057,6 +1060,10 @@ void GCode::_do_export(Print &print, FILE *file) | ||||||
|     print.m_print_statistics.clear(); |     print.m_print_statistics.clear(); | ||||||
|     print.m_print_statistics.estimated_normal_print_time = m_normal_time_estimator.get_time_dhms(); |     print.m_print_statistics.estimated_normal_print_time = m_normal_time_estimator.get_time_dhms(); | ||||||
|     print.m_print_statistics.estimated_silent_print_time = m_silent_time_estimator_enabled ? m_silent_time_estimator.get_time_dhms() : "N/A"; |     print.m_print_statistics.estimated_silent_print_time = m_silent_time_estimator_enabled ? m_silent_time_estimator.get_time_dhms() : "N/A"; | ||||||
|  |     print.m_print_statistics.estimated_normal_color_print_times = m_normal_time_estimator.get_color_times_dhms(); | ||||||
|  |     if (m_silent_time_estimator_enabled) | ||||||
|  |         print.m_print_statistics.estimated_silent_color_print_times = m_silent_time_estimator.get_color_times_dhms(); | ||||||
|  | 
 | ||||||
|     std::vector<Extruder> extruders = m_writer.extruders(); |     std::vector<Extruder> extruders = m_writer.extruders(); | ||||||
|     if (! extruders.empty()) { |     if (! extruders.empty()) { | ||||||
|         std::pair<std::string, unsigned int> out_filament_used_mm ("; filament used [mm] = ", 0); |         std::pair<std::string, unsigned int> out_filament_used_mm ("; filament used [mm] = ", 0); | ||||||
|  |  | ||||||
|  | @ -106,6 +106,11 @@ void GCodeAnalyzer::set_extruder_offsets(const GCodeAnalyzer::ExtruderOffsetsMap | ||||||
|     m_extruder_offsets = extruder_offsets; |     m_extruder_offsets = extruder_offsets; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void GCodeAnalyzer::set_gcode_flavor(const GCodeFlavor& flavor) | ||||||
|  | { | ||||||
|  |     m_gcode_flavor = flavor; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void GCodeAnalyzer::reset() | void GCodeAnalyzer::reset() | ||||||
| { | { | ||||||
|     _set_units(Millimeters); |     _set_units(Millimeters); | ||||||
|  | @ -249,6 +254,14 @@ void GCodeAnalyzer::_process_gcode_line(GCodeReader&, const GCodeReader::GCodeLi | ||||||
|                         _processM83(line); |                         _processM83(line); | ||||||
|                         break; |                         break; | ||||||
|                     } |                     } | ||||||
|  |                 case 108: | ||||||
|  |                 case 135: | ||||||
|  |                     { | ||||||
|  |                         // these are used by MakerWare and Sailfish firmwares
 | ||||||
|  |                         // for tool changing - we can process it in one place
 | ||||||
|  |                         _processM108orM135(line); | ||||||
|  |                         break; | ||||||
|  |                     } | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|                 break; |                 break; | ||||||
|  | @ -426,9 +439,27 @@ void GCodeAnalyzer::_processM600(const GCodeReader::GCodeLine& line) | ||||||
|     _set_cp_color_id(m_state.cur_cp_color_id); |     _set_cp_color_id(m_state.cur_cp_color_id); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GCodeAnalyzer::_processT(const GCodeReader::GCodeLine& line) | void GCodeAnalyzer::_processM108orM135(const GCodeReader::GCodeLine& line) | ||||||
|  | { | ||||||
|  |     // These M-codes are used by MakerWare and Sailfish to change active tool.
 | ||||||
|  |     // They have to be processed otherwise toolchanges will be unrecognised
 | ||||||
|  |     // by the analyzer - see https://github.com/prusa3d/PrusaSlicer/issues/2566
 | ||||||
|  | 
 | ||||||
|  |     size_t code = ::atoi(&(line.cmd()[1])); | ||||||
|  |     if ((code == 108 && m_gcode_flavor == gcfSailfish) | ||||||
|  |      || (code == 135 && m_gcode_flavor == gcfMakerWare)) { | ||||||
|  | 
 | ||||||
|  |         std::string cmd = line.raw(); | ||||||
|  |         size_t T_pos = cmd.find("T"); | ||||||
|  |         if (T_pos != std::string::npos) { | ||||||
|  |             cmd = cmd.substr(T_pos); | ||||||
|  |             _processT(cmd); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void GCodeAnalyzer::_processT(const std::string& cmd) | ||||||
| { | { | ||||||
|     std::string cmd = line.cmd(); |  | ||||||
|     if (cmd.length() > 1) |     if (cmd.length() > 1) | ||||||
|     { |     { | ||||||
|         unsigned int id = (unsigned int)::strtol(cmd.substr(1).c_str(), nullptr, 10); |         unsigned int id = (unsigned int)::strtol(cmd.substr(1).c_str(), nullptr, 10); | ||||||
|  | @ -442,6 +473,11 @@ void GCodeAnalyzer::_processT(const GCodeReader::GCodeLine& line) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void GCodeAnalyzer::_processT(const GCodeReader::GCodeLine& line) | ||||||
|  | { | ||||||
|  |     _processT(line.cmd()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| bool GCodeAnalyzer::_process_tags(const GCodeReader::GCodeLine& line) | bool GCodeAnalyzer::_process_tags(const GCodeReader::GCodeLine& line) | ||||||
| { | { | ||||||
|     std::string comment = line.comment(); |     std::string comment = line.comment(); | ||||||
|  |  | ||||||
|  | @ -106,6 +106,7 @@ private: | ||||||
|     GCodeReader m_parser; |     GCodeReader m_parser; | ||||||
|     TypeToMovesMap m_moves_map; |     TypeToMovesMap m_moves_map; | ||||||
|     ExtruderOffsetsMap m_extruder_offsets; |     ExtruderOffsetsMap m_extruder_offsets; | ||||||
|  |     GCodeFlavor m_gcode_flavor; | ||||||
| 
 | 
 | ||||||
|     // The output of process_layer()
 |     // The output of process_layer()
 | ||||||
|     std::string m_process_output; |     std::string m_process_output; | ||||||
|  | @ -115,6 +116,8 @@ public: | ||||||
| 
 | 
 | ||||||
|     void set_extruder_offsets(const ExtruderOffsetsMap& extruder_offsets); |     void set_extruder_offsets(const ExtruderOffsetsMap& extruder_offsets); | ||||||
| 
 | 
 | ||||||
|  |     void set_gcode_flavor(const GCodeFlavor& flavor); | ||||||
|  | 
 | ||||||
|     // Reinitialize the analyzer
 |     // Reinitialize the analyzer
 | ||||||
|     void reset(); |     void reset(); | ||||||
| 
 | 
 | ||||||
|  | @ -164,10 +167,14 @@ private: | ||||||
|     // Set extruder to relative mode
 |     // Set extruder to relative mode
 | ||||||
|     void _processM83(const GCodeReader::GCodeLine& line); |     void _processM83(const GCodeReader::GCodeLine& line); | ||||||
| 
 | 
 | ||||||
|  |     // Set tool (MakerWare and Sailfish flavor)
 | ||||||
|  |     void _processM108orM135(const GCodeReader::GCodeLine& line); | ||||||
|  | 
 | ||||||
|     // Set color change
 |     // Set color change
 | ||||||
|     void _processM600(const GCodeReader::GCodeLine& line); |     void _processM600(const GCodeReader::GCodeLine& line); | ||||||
| 
 | 
 | ||||||
|     // Processes T line (Select Tool)
 |     // Processes T line (Select Tool)
 | ||||||
|  |     void _processT(const std::string& command); | ||||||
|     void _processT(const GCodeReader::GCodeLine& line); |     void _processT(const GCodeReader::GCodeLine& line); | ||||||
| 
 | 
 | ||||||
|     // Processes the tags
 |     // Processes the tags
 | ||||||
|  |  | ||||||
|  | @ -466,7 +466,7 @@ GCodePreviewData::LegendItemsList GCodePreviewData::get_legend_items(const std:: | ||||||
|         } |         } | ||||||
|     case Extrusion::Tool: |     case Extrusion::Tool: | ||||||
|         { |         { | ||||||
|             unsigned int tools_colors_count = tool_colors.size() / 4; |             unsigned int tools_colors_count = (unsigned int)tool_colors.size() / 4; | ||||||
|             items.reserve(tools_colors_count); |             items.reserve(tools_colors_count); | ||||||
|             for (unsigned int i = 0; i < tools_colors_count; ++i) |             for (unsigned int i = 0; i < tools_colors_count; ++i) | ||||||
|             { |             { | ||||||
|  | @ -491,17 +491,20 @@ GCodePreviewData::LegendItemsList GCodePreviewData::get_legend_items(const std:: | ||||||
|                     items.emplace_back(Slic3r::I18N::translate(L("Default print color")), color); |                     items.emplace_back(Slic3r::I18N::translate(L("Default print color")), color); | ||||||
|                     break; |                     break; | ||||||
|                 } |                 } | ||||||
|  | 
 | ||||||
|  |                 std::string id_str = std::to_string(i + 1) + ": "; | ||||||
|  | 
 | ||||||
|                 if (i == 0) { |                 if (i == 0) { | ||||||
|                     items.emplace_back((boost::format(Slic3r::I18N::translate(L("up to %.2f mm"))) % cp_values[0].first).str(), color); |                     items.emplace_back(id_str + (boost::format(Slic3r::I18N::translate(L("up to %.2f mm"))) % cp_values[0].first).str(), color); | ||||||
|                     break; |                     break; | ||||||
|                 } |                 } | ||||||
|                 if (i == color_print_cnt) { |                 if (i == color_print_cnt) { | ||||||
|                     items.emplace_back((boost::format(Slic3r::I18N::translate(L("above %.2f mm"))) % cp_values[i-1].second).str(), color); |                     items.emplace_back(id_str + (boost::format(Slic3r::I18N::translate(L("above %.2f mm"))) % cp_values[i - 1].second).str(), color); | ||||||
|                     continue; |                     continue; | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
| //                 items.emplace_back((boost::format(Slic3r::I18N::translate(L("%.2f - %.2f mm"))) %  cp_values[i-1] % cp_values[i]).str(), color);
 | //                 items.emplace_back((boost::format(Slic3r::I18N::translate(L("%.2f - %.2f mm"))) %  cp_values[i-1] % cp_values[i]).str(), color);
 | ||||||
|                 items.emplace_back((boost::format(Slic3r::I18N::translate(L("%.2f - %.2f mm"))) %  cp_values[i-1].second % cp_values[i].first).str(), color); |                 items.emplace_back(id_str + (boost::format(Slic3r::I18N::translate(L("%.2f - %.2f mm"))) % cp_values[i - 1].second% cp_values[i].first).str(), color); | ||||||
|             } |             } | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  | @ -174,7 +174,7 @@ namespace Slic3r { | ||||||
|     const std::string GCodeTimeEstimator::Silent_Last_M73_Output_Placeholder_Tag = "; SILENT_LAST_M73_OUTPUT_PLACEHOLDER"; |     const std::string GCodeTimeEstimator::Silent_Last_M73_Output_Placeholder_Tag = "; SILENT_LAST_M73_OUTPUT_PLACEHOLDER"; | ||||||
| 
 | 
 | ||||||
|     GCodeTimeEstimator::GCodeTimeEstimator(EMode mode) |     GCodeTimeEstimator::GCodeTimeEstimator(EMode mode) | ||||||
|         : _mode(mode) |         : m_mode(mode) | ||||||
|     { |     { | ||||||
|         reset(); |         reset(); | ||||||
|         set_default(); |         set_default(); | ||||||
|  | @ -183,7 +183,7 @@ namespace Slic3r { | ||||||
|     void GCodeTimeEstimator::add_gcode_line(const std::string& gcode_line) |     void GCodeTimeEstimator::add_gcode_line(const std::string& gcode_line) | ||||||
|     { |     { | ||||||
|         PROFILE_FUNC(); |         PROFILE_FUNC(); | ||||||
|         _parser.parse_line(gcode_line,  |         m_parser.parse_line(gcode_line,  | ||||||
|             [this](GCodeReader &reader, const GCodeReader::GCodeLine &line) |             [this](GCodeReader &reader, const GCodeReader::GCodeLine &line) | ||||||
|         { this->_process_gcode_line(reader, line); }); |         { this->_process_gcode_line(reader, line); }); | ||||||
|     } |     } | ||||||
|  | @ -196,7 +196,7 @@ namespace Slic3r { | ||||||
|         { this->_process_gcode_line(reader, line); }; |         { this->_process_gcode_line(reader, line); }; | ||||||
|         for (; *ptr != 0;) { |         for (; *ptr != 0;) { | ||||||
|             gline.reset(); |             gline.reset(); | ||||||
|             ptr = _parser.parse_line(ptr, gline, action); |             ptr = m_parser.parse_line(ptr, gline, action); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -206,10 +206,13 @@ namespace Slic3r { | ||||||
|         if (start_from_beginning) |         if (start_from_beginning) | ||||||
|         { |         { | ||||||
|             _reset_time(); |             _reset_time(); | ||||||
|             _last_st_synchronized_block_id = -1; |             m_last_st_synchronized_block_id = -1; | ||||||
|         } |         } | ||||||
|         _calculate_time(); |         _calculate_time(); | ||||||
| 
 | 
 | ||||||
|  |         if (m_needs_color_times && (m_color_time_cache != 0.0f)) | ||||||
|  |             m_color_times.push_back(m_color_time_cache); | ||||||
|  | 
 | ||||||
| #if ENABLE_MOVE_STATS | #if ENABLE_MOVE_STATS | ||||||
|         _log_moves_stats(); |         _log_moves_stats(); | ||||||
| #endif // ENABLE_MOVE_STATS
 | #endif // ENABLE_MOVE_STATS
 | ||||||
|  | @ -219,12 +222,15 @@ namespace Slic3r { | ||||||
|     { |     { | ||||||
|         reset(); |         reset(); | ||||||
| 
 | 
 | ||||||
|         _parser.parse_buffer(gcode, |         m_parser.parse_buffer(gcode, | ||||||
|             [this](GCodeReader &reader, const GCodeReader::GCodeLine &line) |             [this](GCodeReader &reader, const GCodeReader::GCodeLine &line) | ||||||
|         { this->_process_gcode_line(reader, line); }); |         { this->_process_gcode_line(reader, line); }); | ||||||
| 
 | 
 | ||||||
|         _calculate_time(); |         _calculate_time(); | ||||||
| 
 | 
 | ||||||
|  |         if (m_needs_color_times && (m_color_time_cache != 0.0f)) | ||||||
|  |             m_color_times.push_back(m_color_time_cache); | ||||||
|  | 
 | ||||||
| #if ENABLE_MOVE_STATS | #if ENABLE_MOVE_STATS | ||||||
|         _log_moves_stats(); |         _log_moves_stats(); | ||||||
| #endif // ENABLE_MOVE_STATS
 | #endif // ENABLE_MOVE_STATS
 | ||||||
|  | @ -234,9 +240,12 @@ namespace Slic3r { | ||||||
|     { |     { | ||||||
|         reset(); |         reset(); | ||||||
| 
 | 
 | ||||||
|         _parser.parse_file(file, boost::bind(&GCodeTimeEstimator::_process_gcode_line, this, _1, _2)); |         m_parser.parse_file(file, boost::bind(&GCodeTimeEstimator::_process_gcode_line, this, _1, _2)); | ||||||
|         _calculate_time(); |         _calculate_time(); | ||||||
| 
 | 
 | ||||||
|  |         if (m_needs_color_times && (m_color_time_cache != 0.0f)) | ||||||
|  |             m_color_times.push_back(m_color_time_cache); | ||||||
|  | 
 | ||||||
| #if ENABLE_MOVE_STATS | #if ENABLE_MOVE_STATS | ||||||
|         _log_moves_stats(); |         _log_moves_stats(); | ||||||
| #endif // ENABLE_MOVE_STATS
 | #endif // ENABLE_MOVE_STATS
 | ||||||
|  | @ -249,9 +258,12 @@ namespace Slic3r { | ||||||
|         auto action = [this](GCodeReader &reader, const GCodeReader::GCodeLine &line) |         auto action = [this](GCodeReader &reader, const GCodeReader::GCodeLine &line) | ||||||
|         { this->_process_gcode_line(reader, line); }; |         { this->_process_gcode_line(reader, line); }; | ||||||
|         for (const std::string& line : gcode_lines) |         for (const std::string& line : gcode_lines) | ||||||
|             _parser.parse_line(line, action); |             m_parser.parse_line(line, action); | ||||||
|         _calculate_time(); |         _calculate_time(); | ||||||
| 
 | 
 | ||||||
|  |         if (m_needs_color_times && (m_color_time_cache != 0.0f)) | ||||||
|  |             m_color_times.push_back(m_color_time_cache); | ||||||
|  | 
 | ||||||
| #if ENABLE_MOVE_STATS | #if ENABLE_MOVE_STATS | ||||||
|         _log_moves_stats(); |         _log_moves_stats(); | ||||||
| #endif // ENABLE_MOVE_STATS
 | #endif // ENABLE_MOVE_STATS
 | ||||||
|  | @ -270,7 +282,7 @@ namespace Slic3r { | ||||||
|             throw std::runtime_error(std::string("Remaining times export failed.\nCannot open file for writing.\n")); |             throw std::runtime_error(std::string("Remaining times export failed.\nCannot open file for writing.\n")); | ||||||
| 
 | 
 | ||||||
|         std::string time_mask; |         std::string time_mask; | ||||||
|         switch (_mode) |         switch (m_mode) | ||||||
|         { |         { | ||||||
|         default: |         default: | ||||||
|         case Normal: |         case Normal: | ||||||
|  | @ -291,7 +303,7 @@ namespace Slic3r { | ||||||
|         // buffer line to export only when greater than 64K to reduce writing calls
 |         // buffer line to export only when greater than 64K to reduce writing calls
 | ||||||
|         std::string export_line; |         std::string export_line; | ||||||
|         char time_line[64]; |         char time_line[64]; | ||||||
| 		G1LineIdToBlockIdMap::const_iterator it_line_id = _g1_line_ids.begin(); | 		G1LineIdToBlockIdMap::const_iterator it_line_id = m_g1_line_ids.begin(); | ||||||
| 		while (std::getline(in, gcode_line)) | 		while (std::getline(in, gcode_line)) | ||||||
|         { |         { | ||||||
|             if (!in.good()) |             if (!in.good()) | ||||||
|  | @ -301,15 +313,15 @@ namespace Slic3r { | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             // replaces placeholders for initial line M73 with the real lines
 |             // replaces placeholders for initial line M73 with the real lines
 | ||||||
|             if (((_mode == Normal) && (gcode_line == Normal_First_M73_Output_Placeholder_Tag)) || |             if (((m_mode == Normal) && (gcode_line == Normal_First_M73_Output_Placeholder_Tag)) || | ||||||
|                 ((_mode == Silent) && (gcode_line == Silent_First_M73_Output_Placeholder_Tag))) |                 ((m_mode == Silent) && (gcode_line == Silent_First_M73_Output_Placeholder_Tag))) | ||||||
|             { |             { | ||||||
|                 sprintf(time_line, time_mask.c_str(), "0", _get_time_minutes(_time).c_str()); |                 sprintf(time_line, time_mask.c_str(), "0", _get_time_minutes(m_time).c_str()); | ||||||
|                 gcode_line = time_line; |                 gcode_line = time_line; | ||||||
|             } |             } | ||||||
|             // replaces placeholders for final line M73 with the real lines
 |             // replaces placeholders for final line M73 with the real lines
 | ||||||
|             else if (((_mode == Normal) && (gcode_line == Normal_Last_M73_Output_Placeholder_Tag)) || |             else if (((m_mode == Normal) && (gcode_line == Normal_Last_M73_Output_Placeholder_Tag)) || | ||||||
|                      ((_mode == Silent) && (gcode_line == Silent_Last_M73_Output_Placeholder_Tag))) |                      ((m_mode == Silent) && (gcode_line == Silent_Last_M73_Output_Placeholder_Tag))) | ||||||
|             { |             { | ||||||
|                 sprintf(time_line, time_mask.c_str(), "100", "0"); |                 sprintf(time_line, time_mask.c_str(), "100", "0"); | ||||||
|                 gcode_line = time_line; |                 gcode_line = time_line; | ||||||
|  | @ -319,27 +331,27 @@ namespace Slic3r { | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|             // add remaining time lines where needed
 |             // add remaining time lines where needed
 | ||||||
|             _parser.parse_line(gcode_line, |             m_parser.parse_line(gcode_line, | ||||||
|                 [this, &it_line_id, &g1_lines_count, &last_recorded_time, &time_line, &gcode_line, time_mask, interval](GCodeReader& reader, const GCodeReader::GCodeLine& line) |                 [this, &it_line_id, &g1_lines_count, &last_recorded_time, &time_line, &gcode_line, time_mask, interval](GCodeReader& reader, const GCodeReader::GCodeLine& line) | ||||||
|             { |             { | ||||||
|                 if (line.cmd_is("G1")) |                 if (line.cmd_is("G1")) | ||||||
|                 { |                 { | ||||||
|                     ++g1_lines_count; |                     ++g1_lines_count; | ||||||
| 
 | 
 | ||||||
| 					assert(it_line_id == _g1_line_ids.end() || it_line_id->first >= g1_lines_count); | 					assert(it_line_id == m_g1_line_ids.end() || it_line_id->first >= g1_lines_count); | ||||||
| 
 | 
 | ||||||
| 					const Block *block = nullptr; | 					const Block *block = nullptr; | ||||||
| 					if (it_line_id != _g1_line_ids.end() && it_line_id->first == g1_lines_count) { | 					if (it_line_id != m_g1_line_ids.end() && it_line_id->first == g1_lines_count) { | ||||||
| 						if (line.has_e() && it_line_id->second < (unsigned int)_blocks.size()) | 						if (line.has_e() && it_line_id->second < (unsigned int)m_blocks.size()) | ||||||
| 							block = &_blocks[it_line_id->second]; | 							block = &m_blocks[it_line_id->second]; | ||||||
| 						++it_line_id; | 						++it_line_id; | ||||||
| 					} | 					} | ||||||
| 
 | 
 | ||||||
| 					if (block != nullptr && block->elapsed_time != -1.0f) { | 					if (block != nullptr && block->elapsed_time != -1.0f) { | ||||||
|                         float block_remaining_time = _time - block->elapsed_time; |                         float block_remaining_time = m_time - block->elapsed_time; | ||||||
|                         if (std::abs(last_recorded_time - block_remaining_time) > interval) |                         if (std::abs(last_recorded_time - block_remaining_time) > interval) | ||||||
|                         { |                         { | ||||||
|                             sprintf(time_line, time_mask.c_str(), std::to_string((int)(100.0f * block->elapsed_time / _time)).c_str(), _get_time_minutes(block_remaining_time).c_str()); |                             sprintf(time_line, time_mask.c_str(), std::to_string((int)(100.0f * block->elapsed_time / m_time)).c_str(), _get_time_minutes(block_remaining_time).c_str()); | ||||||
|                             gcode_line += time_line; |                             gcode_line += time_line; | ||||||
| 
 | 
 | ||||||
|                             last_recorded_time = block_remaining_time; |                             last_recorded_time = block_remaining_time; | ||||||
|  | @ -387,240 +399,240 @@ namespace Slic3r { | ||||||
| 
 | 
 | ||||||
|     void GCodeTimeEstimator::set_axis_position(EAxis axis, float position) |     void GCodeTimeEstimator::set_axis_position(EAxis axis, float position) | ||||||
|     { |     { | ||||||
|         _state.axis[axis].position = position; |         m_state.axis[axis].position = position; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void GCodeTimeEstimator::set_axis_max_feedrate(EAxis axis, float feedrate_mm_sec) |     void GCodeTimeEstimator::set_axis_max_feedrate(EAxis axis, float feedrate_mm_sec) | ||||||
|     { |     { | ||||||
|         _state.axis[axis].max_feedrate = feedrate_mm_sec; |         m_state.axis[axis].max_feedrate = feedrate_mm_sec; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void GCodeTimeEstimator::set_axis_max_acceleration(EAxis axis, float acceleration) |     void GCodeTimeEstimator::set_axis_max_acceleration(EAxis axis, float acceleration) | ||||||
|     { |     { | ||||||
|         _state.axis[axis].max_acceleration = acceleration; |         m_state.axis[axis].max_acceleration = acceleration; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void GCodeTimeEstimator::set_axis_max_jerk(EAxis axis, float jerk) |     void GCodeTimeEstimator::set_axis_max_jerk(EAxis axis, float jerk) | ||||||
|     { |     { | ||||||
|         _state.axis[axis].max_jerk = jerk; |         m_state.axis[axis].max_jerk = jerk; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     float GCodeTimeEstimator::get_axis_position(EAxis axis) const |     float GCodeTimeEstimator::get_axis_position(EAxis axis) const | ||||||
|     { |     { | ||||||
|         return _state.axis[axis].position; |         return m_state.axis[axis].position; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     float GCodeTimeEstimator::get_axis_max_feedrate(EAxis axis) const |     float GCodeTimeEstimator::get_axis_max_feedrate(EAxis axis) const | ||||||
|     { |     { | ||||||
|         return _state.axis[axis].max_feedrate; |         return m_state.axis[axis].max_feedrate; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     float GCodeTimeEstimator::get_axis_max_acceleration(EAxis axis) const |     float GCodeTimeEstimator::get_axis_max_acceleration(EAxis axis) const | ||||||
|     { |     { | ||||||
|         return _state.axis[axis].max_acceleration; |         return m_state.axis[axis].max_acceleration; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     float GCodeTimeEstimator::get_axis_max_jerk(EAxis axis) const |     float GCodeTimeEstimator::get_axis_max_jerk(EAxis axis) const | ||||||
|     { |     { | ||||||
|         return _state.axis[axis].max_jerk; |         return m_state.axis[axis].max_jerk; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void GCodeTimeEstimator::set_feedrate(float feedrate_mm_sec) |     void GCodeTimeEstimator::set_feedrate(float feedrate_mm_sec) | ||||||
|     { |     { | ||||||
|         _state.feedrate = feedrate_mm_sec; |         m_state.feedrate = feedrate_mm_sec; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     float GCodeTimeEstimator::get_feedrate() const |     float GCodeTimeEstimator::get_feedrate() const | ||||||
|     { |     { | ||||||
|         return _state.feedrate; |         return m_state.feedrate; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void GCodeTimeEstimator::set_acceleration(float acceleration_mm_sec2) |     void GCodeTimeEstimator::set_acceleration(float acceleration_mm_sec2) | ||||||
|     { |     { | ||||||
|         _state.acceleration = (_state.max_acceleration == 0) ?  |         m_state.acceleration = (m_state.max_acceleration == 0) ?  | ||||||
|             acceleration_mm_sec2 :  |             acceleration_mm_sec2 :  | ||||||
|             // Clamp the acceleration with the maximum.
 |             // Clamp the acceleration with the maximum.
 | ||||||
|             std::min(_state.max_acceleration, acceleration_mm_sec2); |             std::min(m_state.max_acceleration, acceleration_mm_sec2); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     float GCodeTimeEstimator::get_acceleration() const |     float GCodeTimeEstimator::get_acceleration() const | ||||||
|     { |     { | ||||||
|         return _state.acceleration; |         return m_state.acceleration; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void GCodeTimeEstimator::set_max_acceleration(float acceleration_mm_sec2) |     void GCodeTimeEstimator::set_max_acceleration(float acceleration_mm_sec2) | ||||||
|     { |     { | ||||||
|         _state.max_acceleration = acceleration_mm_sec2; |         m_state.max_acceleration = acceleration_mm_sec2; | ||||||
|         if (acceleration_mm_sec2 > 0) |         if (acceleration_mm_sec2 > 0) | ||||||
|             _state.acceleration = acceleration_mm_sec2; |             m_state.acceleration = acceleration_mm_sec2; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     float GCodeTimeEstimator::get_max_acceleration() const |     float GCodeTimeEstimator::get_max_acceleration() const | ||||||
|     { |     { | ||||||
|         return _state.max_acceleration; |         return m_state.max_acceleration; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void GCodeTimeEstimator::set_retract_acceleration(float acceleration_mm_sec2) |     void GCodeTimeEstimator::set_retract_acceleration(float acceleration_mm_sec2) | ||||||
|     { |     { | ||||||
|         _state.retract_acceleration = acceleration_mm_sec2; |         m_state.retract_acceleration = acceleration_mm_sec2; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     float GCodeTimeEstimator::get_retract_acceleration() const |     float GCodeTimeEstimator::get_retract_acceleration() const | ||||||
|     { |     { | ||||||
|         return _state.retract_acceleration; |         return m_state.retract_acceleration; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void GCodeTimeEstimator::set_minimum_feedrate(float feedrate_mm_sec) |     void GCodeTimeEstimator::set_minimum_feedrate(float feedrate_mm_sec) | ||||||
|     { |     { | ||||||
|         _state.minimum_feedrate = feedrate_mm_sec; |         m_state.minimum_feedrate = feedrate_mm_sec; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     float GCodeTimeEstimator::get_minimum_feedrate() const |     float GCodeTimeEstimator::get_minimum_feedrate() const | ||||||
|     { |     { | ||||||
|         return _state.minimum_feedrate; |         return m_state.minimum_feedrate; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void GCodeTimeEstimator::set_minimum_travel_feedrate(float feedrate_mm_sec) |     void GCodeTimeEstimator::set_minimum_travel_feedrate(float feedrate_mm_sec) | ||||||
|     { |     { | ||||||
|         _state.minimum_travel_feedrate = feedrate_mm_sec; |         m_state.minimum_travel_feedrate = feedrate_mm_sec; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     float GCodeTimeEstimator::get_minimum_travel_feedrate() const |     float GCodeTimeEstimator::get_minimum_travel_feedrate() const | ||||||
|     { |     { | ||||||
|         return _state.minimum_travel_feedrate; |         return m_state.minimum_travel_feedrate; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void GCodeTimeEstimator::set_filament_load_times(const std::vector<double> &filament_load_times) |     void GCodeTimeEstimator::set_filament_load_times(const std::vector<double> &filament_load_times) | ||||||
|     { |     { | ||||||
|         _state.filament_load_times.clear(); |         m_state.filament_load_times.clear(); | ||||||
|         for (double t : filament_load_times) |         for (double t : filament_load_times) | ||||||
|             _state.filament_load_times.push_back((float)t); |             m_state.filament_load_times.push_back((float)t); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void GCodeTimeEstimator::set_filament_unload_times(const std::vector<double> &filament_unload_times) |     void GCodeTimeEstimator::set_filament_unload_times(const std::vector<double> &filament_unload_times) | ||||||
|     { |     { | ||||||
|         _state.filament_unload_times.clear(); |         m_state.filament_unload_times.clear(); | ||||||
|         for (double t : filament_unload_times) |         for (double t : filament_unload_times) | ||||||
|             _state.filament_unload_times.push_back((float)t); |             m_state.filament_unload_times.push_back((float)t); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     float GCodeTimeEstimator::get_filament_load_time(unsigned int id_extruder) |     float GCodeTimeEstimator::get_filament_load_time(unsigned int id_extruder) | ||||||
|     { |     { | ||||||
|         return |         return | ||||||
|             (_state.filament_load_times.empty() || id_extruder == _state.extruder_id_unloaded) ?  |             (m_state.filament_load_times.empty() || id_extruder == m_state.extruder_id_unloaded) ?  | ||||||
|                 0 : |                 0 : | ||||||
|                 (_state.filament_load_times.size() <= id_extruder) ? |                 (m_state.filament_load_times.size() <= id_extruder) ? | ||||||
|                     _state.filament_load_times.front() :  |                     m_state.filament_load_times.front() :  | ||||||
|                     _state.filament_load_times[id_extruder]; |                     m_state.filament_load_times[id_extruder]; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     float GCodeTimeEstimator::get_filament_unload_time(unsigned int id_extruder) |     float GCodeTimeEstimator::get_filament_unload_time(unsigned int id_extruder) | ||||||
|     { |     { | ||||||
|         return |         return | ||||||
|             (_state.filament_unload_times.empty() || id_extruder == _state.extruder_id_unloaded) ?  |             (m_state.filament_unload_times.empty() || id_extruder == m_state.extruder_id_unloaded) ?  | ||||||
|                 0 : |                 0 : | ||||||
|                 (_state.filament_unload_times.size() <= id_extruder) ? |                 (m_state.filament_unload_times.size() <= id_extruder) ? | ||||||
|                     _state.filament_unload_times.front() :  |                     m_state.filament_unload_times.front() :  | ||||||
|                     _state.filament_unload_times[id_extruder]; |                     m_state.filament_unload_times[id_extruder]; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void GCodeTimeEstimator::set_extrude_factor_override_percentage(float percentage) |     void GCodeTimeEstimator::set_extrude_factor_override_percentage(float percentage) | ||||||
|     { |     { | ||||||
|         _state.extrude_factor_override_percentage = percentage; |         m_state.extrude_factor_override_percentage = percentage; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     float GCodeTimeEstimator::get_extrude_factor_override_percentage() const |     float GCodeTimeEstimator::get_extrude_factor_override_percentage() const | ||||||
|     { |     { | ||||||
|         return _state.extrude_factor_override_percentage; |         return m_state.extrude_factor_override_percentage; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void GCodeTimeEstimator::set_dialect(GCodeFlavor dialect) |     void GCodeTimeEstimator::set_dialect(GCodeFlavor dialect) | ||||||
|     { |     { | ||||||
|         _state.dialect = dialect; |         m_state.dialect = dialect; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     GCodeFlavor GCodeTimeEstimator::get_dialect() const |     GCodeFlavor GCodeTimeEstimator::get_dialect() const | ||||||
|     { |     { | ||||||
|         PROFILE_FUNC(); |         PROFILE_FUNC(); | ||||||
|         return _state.dialect; |         return m_state.dialect; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void GCodeTimeEstimator::set_units(GCodeTimeEstimator::EUnits units) |     void GCodeTimeEstimator::set_units(GCodeTimeEstimator::EUnits units) | ||||||
|     { |     { | ||||||
|         _state.units = units; |         m_state.units = units; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     GCodeTimeEstimator::EUnits GCodeTimeEstimator::get_units() const |     GCodeTimeEstimator::EUnits GCodeTimeEstimator::get_units() const | ||||||
|     { |     { | ||||||
|         return _state.units; |         return m_state.units; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void GCodeTimeEstimator::set_global_positioning_type(GCodeTimeEstimator::EPositioningType type) |     void GCodeTimeEstimator::set_global_positioning_type(GCodeTimeEstimator::EPositioningType type) | ||||||
|     { |     { | ||||||
|         _state.global_positioning_type = type; |         m_state.global_positioning_type = type; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     GCodeTimeEstimator::EPositioningType GCodeTimeEstimator::get_global_positioning_type() const |     GCodeTimeEstimator::EPositioningType GCodeTimeEstimator::get_global_positioning_type() const | ||||||
|     { |     { | ||||||
|         return _state.global_positioning_type; |         return m_state.global_positioning_type; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void GCodeTimeEstimator::set_e_local_positioning_type(GCodeTimeEstimator::EPositioningType type) |     void GCodeTimeEstimator::set_e_local_positioning_type(GCodeTimeEstimator::EPositioningType type) | ||||||
|     { |     { | ||||||
|         _state.e_local_positioning_type = type; |         m_state.e_local_positioning_type = type; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     GCodeTimeEstimator::EPositioningType GCodeTimeEstimator::get_e_local_positioning_type() const |     GCodeTimeEstimator::EPositioningType GCodeTimeEstimator::get_e_local_positioning_type() const | ||||||
|     { |     { | ||||||
|         return _state.e_local_positioning_type; |         return m_state.e_local_positioning_type; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     int GCodeTimeEstimator::get_g1_line_id() const |     int GCodeTimeEstimator::get_g1_line_id() const | ||||||
|     { |     { | ||||||
|         return _state.g1_line_id; |         return m_state.g1_line_id; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void GCodeTimeEstimator::increment_g1_line_id() |     void GCodeTimeEstimator::increment_g1_line_id() | ||||||
|     { |     { | ||||||
|         ++_state.g1_line_id; |         ++m_state.g1_line_id; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void GCodeTimeEstimator::reset_g1_line_id() |     void GCodeTimeEstimator::reset_g1_line_id() | ||||||
|     { |     { | ||||||
|         _state.g1_line_id = 0; |         m_state.g1_line_id = 0; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void GCodeTimeEstimator::set_extruder_id(unsigned int id) |     void GCodeTimeEstimator::set_extruder_id(unsigned int id) | ||||||
|     { |     { | ||||||
|         _state.extruder_id = id; |         m_state.extruder_id = id; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     unsigned int GCodeTimeEstimator::get_extruder_id() const |     unsigned int GCodeTimeEstimator::get_extruder_id() const | ||||||
|     { |     { | ||||||
|         return _state.extruder_id; |         return m_state.extruder_id; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void GCodeTimeEstimator::reset_extruder_id() |     void GCodeTimeEstimator::reset_extruder_id() | ||||||
|     { |     { | ||||||
|         // Set the initial extruder ID to unknown. For the multi-material setup it means
 |         // Set the initial extruder ID to unknown. For the multi-material setup it means
 | ||||||
|         // that all the filaments are parked in the MMU and no filament is loaded yet.
 |         // that all the filaments are parked in the MMU and no filament is loaded yet.
 | ||||||
|         _state.extruder_id = _state.extruder_id_unloaded; |         m_state.extruder_id = m_state.extruder_id_unloaded; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void GCodeTimeEstimator::add_additional_time(float timeSec) |     void GCodeTimeEstimator::add_additional_time(float timeSec) | ||||||
|     { |     { | ||||||
|         PROFILE_FUNC(); |         PROFILE_FUNC(); | ||||||
|         _state.additional_time += timeSec; |         m_state.additional_time += timeSec; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void GCodeTimeEstimator::set_additional_time(float timeSec) |     void GCodeTimeEstimator::set_additional_time(float timeSec) | ||||||
|     { |     { | ||||||
|         _state.additional_time = timeSec; |         m_state.additional_time = timeSec; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     float GCodeTimeEstimator::get_additional_time() const |     float GCodeTimeEstimator::get_additional_time() const | ||||||
|     { |     { | ||||||
|         return _state.additional_time; |         return m_state.additional_time; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void GCodeTimeEstimator::set_default() |     void GCodeTimeEstimator::set_default() | ||||||
|  | @ -648,8 +660,8 @@ namespace Slic3r { | ||||||
|             set_axis_max_jerk(axis, DEFAULT_AXIS_MAX_JERK[a]); |             set_axis_max_jerk(axis, DEFAULT_AXIS_MAX_JERK[a]); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         _state.filament_load_times.clear(); |         m_state.filament_load_times.clear(); | ||||||
|         _state.filament_unload_times.clear(); |         m_state.filament_unload_times.clear(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void GCodeTimeEstimator::reset() |     void GCodeTimeEstimator::reset() | ||||||
|  | @ -664,7 +676,7 @@ namespace Slic3r { | ||||||
| 
 | 
 | ||||||
|     float GCodeTimeEstimator::get_time() const |     float GCodeTimeEstimator::get_time() const | ||||||
|     { |     { | ||||||
|         return _time; |         return m_time; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     std::string GCodeTimeEstimator::get_time_dhms() const |     std::string GCodeTimeEstimator::get_time_dhms() const | ||||||
|  | @ -677,19 +689,44 @@ namespace Slic3r { | ||||||
|         return _get_time_minutes(get_time()); |         return _get_time_minutes(get_time()); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     std::vector<float> GCodeTimeEstimator::get_color_times() const | ||||||
|  |     { | ||||||
|  |         return m_color_times; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     std::vector<std::string> GCodeTimeEstimator::get_color_times_dhms() const | ||||||
|  |     { | ||||||
|  |         std::vector<std::string> ret; | ||||||
|  |         for (float t : m_color_times) | ||||||
|  |         { | ||||||
|  |             ret.push_back(_get_time_dhms(t)); | ||||||
|  |         } | ||||||
|  |         return ret; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     std::vector<std::string> GCodeTimeEstimator::get_color_times_minutes() const | ||||||
|  |     { | ||||||
|  |         std::vector<std::string> ret; | ||||||
|  |         for (float t : m_color_times) | ||||||
|  |         { | ||||||
|  |             ret.push_back(_get_time_minutes(t)); | ||||||
|  |         } | ||||||
|  |         return ret; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     // Return an estimate of the memory consumed by the time estimator.
 |     // Return an estimate of the memory consumed by the time estimator.
 | ||||||
| 	size_t GCodeTimeEstimator::memory_used() const | 	size_t GCodeTimeEstimator::memory_used() const | ||||||
|     { |     { | ||||||
|         size_t out = sizeof(*this); |         size_t out = sizeof(*this); | ||||||
| 		out += SLIC3R_STDVEC_MEMSIZE(this->_blocks, Block); | 		out += SLIC3R_STDVEC_MEMSIZE(this->m_blocks, Block); | ||||||
| 		out += SLIC3R_STDVEC_MEMSIZE(this->_g1_line_ids, G1LineIdToBlockId); | 		out += SLIC3R_STDVEC_MEMSIZE(this->m_g1_line_ids, G1LineIdToBlockId); | ||||||
|         return out; |         return out; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void GCodeTimeEstimator::_reset() |     void GCodeTimeEstimator::_reset() | ||||||
|     { |     { | ||||||
|         _curr.reset(); |         m_curr.reset(); | ||||||
|         _prev.reset(); |         m_prev.reset(); | ||||||
| 
 | 
 | ||||||
|         set_axis_position(X, 0.0f); |         set_axis_position(X, 0.0f); | ||||||
|         set_axis_position(Y, 0.0f); |         set_axis_position(Y, 0.0f); | ||||||
|  | @ -701,19 +738,23 @@ namespace Slic3r { | ||||||
| 
 | 
 | ||||||
|         reset_extruder_id(); |         reset_extruder_id(); | ||||||
|         reset_g1_line_id(); |         reset_g1_line_id(); | ||||||
|         _g1_line_ids.clear(); |         m_g1_line_ids.clear(); | ||||||
| 
 | 
 | ||||||
|         _last_st_synchronized_block_id = -1; |         m_last_st_synchronized_block_id = -1; | ||||||
|  | 
 | ||||||
|  |         m_needs_color_times = false; | ||||||
|  |         m_color_times.clear(); | ||||||
|  |         m_color_time_cache = 0.0f; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void GCodeTimeEstimator::_reset_time() |     void GCodeTimeEstimator::_reset_time() | ||||||
|     { |     { | ||||||
|         _time = 0.0f; |         m_time = 0.0f; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void GCodeTimeEstimator::_reset_blocks() |     void GCodeTimeEstimator::_reset_blocks() | ||||||
|     { |     { | ||||||
|         _blocks.clear(); |         m_blocks.clear(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void GCodeTimeEstimator::_calculate_time() |     void GCodeTimeEstimator::_calculate_time() | ||||||
|  | @ -723,35 +764,32 @@ namespace Slic3r { | ||||||
|         _reverse_pass(); |         _reverse_pass(); | ||||||
|         _recalculate_trapezoids(); |         _recalculate_trapezoids(); | ||||||
| 
 | 
 | ||||||
|         _time += get_additional_time(); |         m_time += get_additional_time(); | ||||||
|  |         m_color_time_cache += get_additional_time(); | ||||||
| 
 | 
 | ||||||
|         for (int i = _last_st_synchronized_block_id + 1; i < (int)_blocks.size(); ++i) |         for (int i = m_last_st_synchronized_block_id + 1; i < (int)m_blocks.size(); ++i) | ||||||
|         { |         { | ||||||
|             Block& block = _blocks[i]; |             Block& block = m_blocks[i]; | ||||||
| 
 |  | ||||||
| #if ENABLE_MOVE_STATS |  | ||||||
|             float block_time = 0.0f; |             float block_time = 0.0f; | ||||||
|             block_time += block.acceleration_time(); |             block_time += block.acceleration_time(); | ||||||
|             block_time += block.cruise_time(); |             block_time += block.cruise_time(); | ||||||
|             block_time += block.deceleration_time(); |             block_time += block.deceleration_time(); | ||||||
|             _time += block_time; |             m_time += block_time; | ||||||
|             block.elapsed_time = _time; |             block.elapsed_time = m_time; | ||||||
| 
 | 
 | ||||||
|  | #if ENABLE_MOVE_STATS | ||||||
|             MovesStatsMap::iterator it = _moves_stats.find(block.move_type); |             MovesStatsMap::iterator it = _moves_stats.find(block.move_type); | ||||||
|             if (it == _moves_stats.end()) |             if (it == _moves_stats.end()) | ||||||
|                 it = _moves_stats.insert(MovesStatsMap::value_type(block.move_type, MoveStats())).first; |                 it = _moves_stats.insert(MovesStatsMap::value_type(block.move_type, MoveStats())).first; | ||||||
| 
 | 
 | ||||||
|             it->second.count += 1; |             it->second.count += 1; | ||||||
|             it->second.time += block_time; |             it->second.time += block_time; | ||||||
| #else |  | ||||||
|             _time += block.acceleration_time(); |  | ||||||
|             _time += block.cruise_time(); |  | ||||||
|             _time += block.deceleration_time(); |  | ||||||
|             block.elapsed_time = _time; |  | ||||||
| #endif // ENABLE_MOVE_STATS
 | #endif // ENABLE_MOVE_STATS
 | ||||||
|  | 
 | ||||||
|  |             m_color_time_cache += block_time; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         _last_st_synchronized_block_id = (int)_blocks.size() - 1; |         m_last_st_synchronized_block_id = (int)m_blocks.size() - 1; | ||||||
|         // The additional time has been consumed (added to the total time), reset it to zero.
 |         // The additional time has been consumed (added to the total time), reset it to zero.
 | ||||||
|         set_additional_time(0.); |         set_additional_time(0.); | ||||||
|     } |     } | ||||||
|  | @ -866,6 +904,11 @@ namespace Slic3r { | ||||||
|                             _processM566(line); |                             _processM566(line); | ||||||
|                             break; |                             break; | ||||||
|                         } |                         } | ||||||
|  |                     case 600: // Set color change
 | ||||||
|  |                         { | ||||||
|  |                             _processM600(line); | ||||||
|  |                             break; | ||||||
|  |                         } | ||||||
|                     case 702: // MK3 MMU2: Process the final filament unload.
 |                     case 702: // MK3 MMU2: Process the final filament unload.
 | ||||||
|                         { |                         { | ||||||
|                             _processM702(line); |                             _processM702(line); | ||||||
|  | @ -934,7 +977,7 @@ namespace Slic3r { | ||||||
|             return; |             return; | ||||||
| 
 | 
 | ||||||
|         // calculates block feedrate
 |         // calculates block feedrate
 | ||||||
|         _curr.feedrate = std::max(get_feedrate(), block.is_travel_move() ? get_minimum_travel_feedrate() : get_minimum_feedrate()); |         m_curr.feedrate = std::max(get_feedrate(), block.is_travel_move() ? get_minimum_travel_feedrate() : get_minimum_feedrate()); | ||||||
| 
 | 
 | ||||||
|         float distance = block.move_length(); |         float distance = block.move_length(); | ||||||
|         float invDistance = 1.0f / distance; |         float invDistance = 1.0f / distance; | ||||||
|  | @ -942,23 +985,23 @@ namespace Slic3r { | ||||||
|         float min_feedrate_factor = 1.0f; |         float min_feedrate_factor = 1.0f; | ||||||
|         for (unsigned char a = X; a < Num_Axis; ++a) |         for (unsigned char a = X; a < Num_Axis; ++a) | ||||||
|         { |         { | ||||||
|             _curr.axis_feedrate[a] = _curr.feedrate * block.delta_pos[a] * invDistance; |             m_curr.axis_feedrate[a] = m_curr.feedrate * block.delta_pos[a] * invDistance; | ||||||
|             if (a == E) |             if (a == E) | ||||||
|                 _curr.axis_feedrate[a] *= get_extrude_factor_override_percentage(); |                 m_curr.axis_feedrate[a] *= get_extrude_factor_override_percentage(); | ||||||
| 
 | 
 | ||||||
|             _curr.abs_axis_feedrate[a] = std::abs(_curr.axis_feedrate[a]); |             m_curr.abs_axis_feedrate[a] = std::abs(m_curr.axis_feedrate[a]); | ||||||
|             if (_curr.abs_axis_feedrate[a] > 0.0f) |             if (m_curr.abs_axis_feedrate[a] > 0.0f) | ||||||
|                 min_feedrate_factor = std::min(min_feedrate_factor, get_axis_max_feedrate((EAxis)a) / _curr.abs_axis_feedrate[a]); |                 min_feedrate_factor = std::min(min_feedrate_factor, get_axis_max_feedrate((EAxis)a) / m_curr.abs_axis_feedrate[a]); | ||||||
|         } |         } | ||||||
|          |          | ||||||
|         block.feedrate.cruise = min_feedrate_factor * _curr.feedrate; |         block.feedrate.cruise = min_feedrate_factor * m_curr.feedrate; | ||||||
| 
 | 
 | ||||||
|         if (min_feedrate_factor < 1.0f) |         if (min_feedrate_factor < 1.0f) | ||||||
|         { |         { | ||||||
|             for (unsigned char a = X; a < Num_Axis; ++a) |             for (unsigned char a = X; a < Num_Axis; ++a) | ||||||
|             { |             { | ||||||
|                 _curr.axis_feedrate[a] *= min_feedrate_factor; |                 m_curr.axis_feedrate[a] *= min_feedrate_factor; | ||||||
|                 _curr.abs_axis_feedrate[a] *= min_feedrate_factor; |                 m_curr.abs_axis_feedrate[a] *= min_feedrate_factor; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  | @ -975,25 +1018,25 @@ namespace Slic3r { | ||||||
|         block.acceleration = acceleration; |         block.acceleration = acceleration; | ||||||
| 
 | 
 | ||||||
|         // calculates block exit feedrate
 |         // calculates block exit feedrate
 | ||||||
|         _curr.safe_feedrate = block.feedrate.cruise; |         m_curr.safe_feedrate = block.feedrate.cruise; | ||||||
| 
 | 
 | ||||||
|         for (unsigned char a = X; a < Num_Axis; ++a) |         for (unsigned char a = X; a < Num_Axis; ++a) | ||||||
|         { |         { | ||||||
|             float axis_max_jerk = get_axis_max_jerk((EAxis)a); |             float axis_max_jerk = get_axis_max_jerk((EAxis)a); | ||||||
|             if (_curr.abs_axis_feedrate[a] > axis_max_jerk) |             if (m_curr.abs_axis_feedrate[a] > axis_max_jerk) | ||||||
|                 _curr.safe_feedrate = std::min(_curr.safe_feedrate, axis_max_jerk); |                 m_curr.safe_feedrate = std::min(m_curr.safe_feedrate, axis_max_jerk); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         block.feedrate.exit = _curr.safe_feedrate; |         block.feedrate.exit = m_curr.safe_feedrate; | ||||||
| 
 | 
 | ||||||
|         // calculates block entry feedrate
 |         // calculates block entry feedrate
 | ||||||
|         float vmax_junction = _curr.safe_feedrate; |         float vmax_junction = m_curr.safe_feedrate; | ||||||
|         if (!_blocks.empty() && (_prev.feedrate > PREVIOUS_FEEDRATE_THRESHOLD)) |         if (!m_blocks.empty() && (m_prev.feedrate > PREVIOUS_FEEDRATE_THRESHOLD)) | ||||||
|         { |         { | ||||||
|             bool prev_speed_larger = _prev.feedrate > block.feedrate.cruise; |             bool prev_speed_larger = m_prev.feedrate > block.feedrate.cruise; | ||||||
|             float smaller_speed_factor = prev_speed_larger ? (block.feedrate.cruise / _prev.feedrate) : (_prev.feedrate / block.feedrate.cruise); |             float smaller_speed_factor = prev_speed_larger ? (block.feedrate.cruise / m_prev.feedrate) : (m_prev.feedrate / block.feedrate.cruise); | ||||||
|             // Pick the smaller of the nominal speeds. Higher speed shall not be achieved at the junction during coasting.
 |             // Pick the smaller of the nominal speeds. Higher speed shall not be achieved at the junction during coasting.
 | ||||||
|             vmax_junction = prev_speed_larger ? block.feedrate.cruise : _prev.feedrate; |             vmax_junction = prev_speed_larger ? block.feedrate.cruise : m_prev.feedrate; | ||||||
| 
 | 
 | ||||||
|             float v_factor = 1.0f; |             float v_factor = 1.0f; | ||||||
|             bool limited = false; |             bool limited = false; | ||||||
|  | @ -1001,8 +1044,8 @@ namespace Slic3r { | ||||||
|             for (unsigned char a = X; a < Num_Axis; ++a) |             for (unsigned char a = X; a < Num_Axis; ++a) | ||||||
|             { |             { | ||||||
|                 // Limit an axis. We have to differentiate coasting from the reversal of an axis movement, or a full stop.
 |                 // Limit an axis. We have to differentiate coasting from the reversal of an axis movement, or a full stop.
 | ||||||
|                 float v_exit = _prev.axis_feedrate[a]; |                 float v_exit = m_prev.axis_feedrate[a]; | ||||||
|                 float v_entry = _curr.axis_feedrate[a]; |                 float v_entry = m_curr.axis_feedrate[a]; | ||||||
| 
 | 
 | ||||||
|                 if (prev_speed_larger) |                 if (prev_speed_larger) | ||||||
|                     v_exit *= smaller_speed_factor; |                     v_exit *= smaller_speed_factor; | ||||||
|  | @ -1044,23 +1087,23 @@ namespace Slic3r { | ||||||
|             float vmax_junction_threshold = vmax_junction * 0.99f; |             float vmax_junction_threshold = vmax_junction * 0.99f; | ||||||
| 
 | 
 | ||||||
|             // Not coasting. The machine will stop and start the movements anyway, better to start the segment from start.
 |             // Not coasting. The machine will stop and start the movements anyway, better to start the segment from start.
 | ||||||
|             if ((_prev.safe_feedrate > vmax_junction_threshold) && (_curr.safe_feedrate > vmax_junction_threshold)) |             if ((m_prev.safe_feedrate > vmax_junction_threshold) && (m_curr.safe_feedrate > vmax_junction_threshold)) | ||||||
|                 vmax_junction = _curr.safe_feedrate; |                 vmax_junction = m_curr.safe_feedrate; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         float v_allowable = Block::max_allowable_speed(-acceleration, _curr.safe_feedrate, distance); |         float v_allowable = Block::max_allowable_speed(-acceleration, m_curr.safe_feedrate, distance); | ||||||
|         block.feedrate.entry = std::min(vmax_junction, v_allowable); |         block.feedrate.entry = std::min(vmax_junction, v_allowable); | ||||||
| 
 | 
 | ||||||
|         block.max_entry_speed = vmax_junction; |         block.max_entry_speed = vmax_junction; | ||||||
|         block.flags.nominal_length = (block.feedrate.cruise <= v_allowable); |         block.flags.nominal_length = (block.feedrate.cruise <= v_allowable); | ||||||
|         block.flags.recalculate = true; |         block.flags.recalculate = true; | ||||||
|         block.safe_feedrate = _curr.safe_feedrate; |         block.safe_feedrate = m_curr.safe_feedrate; | ||||||
| 
 | 
 | ||||||
|         // calculates block trapezoid
 |         // calculates block trapezoid
 | ||||||
|         block.calculate_trapezoid(); |         block.calculate_trapezoid(); | ||||||
| 
 | 
 | ||||||
|         // updates previous
 |         // updates previous
 | ||||||
|         _prev = _curr; |         m_prev = m_curr; | ||||||
| 
 | 
 | ||||||
|         // updates axis positions
 |         // updates axis positions
 | ||||||
|         for (unsigned char a = X; a < Num_Axis; ++a) |         for (unsigned char a = X; a < Num_Axis; ++a) | ||||||
|  | @ -1091,8 +1134,8 @@ namespace Slic3r { | ||||||
| #endif // ENABLE_MOVE_STATS
 | #endif // ENABLE_MOVE_STATS
 | ||||||
| 
 | 
 | ||||||
|         // adds block to blocks list
 |         // adds block to blocks list
 | ||||||
|         _blocks.emplace_back(block); |         m_blocks.emplace_back(block); | ||||||
|         _g1_line_ids.emplace_back(G1LineIdToBlockIdMap::value_type(get_g1_line_id(), (unsigned int)_blocks.size() - 1)); |         m_g1_line_ids.emplace_back(G1LineIdToBlockIdMap::value_type(get_g1_line_id(), (unsigned int)m_blocks.size() - 1)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void GCodeTimeEstimator::_processG4(const GCodeReader::GCodeLine& line) |     void GCodeTimeEstimator::_processG4(const GCodeReader::GCodeLine& line) | ||||||
|  | @ -1336,6 +1379,18 @@ namespace Slic3r { | ||||||
|             set_axis_max_jerk(E, line.e() * MMMIN_TO_MMSEC); |             set_axis_max_jerk(E, line.e() * MMMIN_TO_MMSEC); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     void GCodeTimeEstimator::_processM600(const GCodeReader::GCodeLine& line) | ||||||
|  |     { | ||||||
|  |         PROFILE_FUNC(); | ||||||
|  |         m_needs_color_times = true; | ||||||
|  |         _calculate_time(); | ||||||
|  |         if (m_color_time_cache != 0.0f) | ||||||
|  |         { | ||||||
|  |             m_color_times.push_back(m_color_time_cache); | ||||||
|  |             m_color_time_cache = 0.0f; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     void GCodeTimeEstimator::_processM702(const GCodeReader::GCodeLine& line) |     void GCodeTimeEstimator::_processM702(const GCodeReader::GCodeLine& line) | ||||||
|     { |     { | ||||||
|         PROFILE_FUNC(); |         PROFILE_FUNC(); | ||||||
|  | @ -1376,11 +1431,11 @@ namespace Slic3r { | ||||||
|     void GCodeTimeEstimator::_forward_pass() |     void GCodeTimeEstimator::_forward_pass() | ||||||
|     { |     { | ||||||
|         PROFILE_FUNC(); |         PROFILE_FUNC(); | ||||||
|         if (_blocks.size() > 1) |         if (m_blocks.size() > 1) | ||||||
|         { |         { | ||||||
|             for (int i = _last_st_synchronized_block_id + 1; i < (int)_blocks.size() - 1; ++i) |             for (int i = m_last_st_synchronized_block_id + 1; i < (int)m_blocks.size() - 1; ++i) | ||||||
|             { |             { | ||||||
|                 _planner_forward_pass_kernel(_blocks[i], _blocks[i + 1]); |                 _planner_forward_pass_kernel(m_blocks[i], m_blocks[i + 1]); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | @ -1388,11 +1443,11 @@ namespace Slic3r { | ||||||
|     void GCodeTimeEstimator::_reverse_pass() |     void GCodeTimeEstimator::_reverse_pass() | ||||||
|     { |     { | ||||||
|         PROFILE_FUNC(); |         PROFILE_FUNC(); | ||||||
|         if (_blocks.size() > 1) |         if (m_blocks.size() > 1) | ||||||
|         { |         { | ||||||
|             for (int i = (int)_blocks.size() - 1; i >= _last_st_synchronized_block_id + 2; --i) |             for (int i = (int)m_blocks.size() - 1; i >= m_last_st_synchronized_block_id + 2; --i) | ||||||
|             { |             { | ||||||
|                 _planner_reverse_pass_kernel(_blocks[i - 1], _blocks[i]); |                 _planner_reverse_pass_kernel(m_blocks[i - 1], m_blocks[i]); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | @ -1444,9 +1499,9 @@ namespace Slic3r { | ||||||
|         Block* curr = nullptr; |         Block* curr = nullptr; | ||||||
|         Block* next = nullptr; |         Block* next = nullptr; | ||||||
| 
 | 
 | ||||||
|         for (int i = _last_st_synchronized_block_id + 1; i < (int)_blocks.size(); ++i) |         for (int i = m_last_st_synchronized_block_id + 1; i < (int)m_blocks.size(); ++i) | ||||||
|         { |         { | ||||||
|             Block& b = _blocks[i]; |             Block& b = m_blocks[i]; | ||||||
| 
 | 
 | ||||||
|             curr = next; |             curr = next; | ||||||
|             next = &b; |             next = &b; | ||||||
|  | @ -1517,7 +1572,7 @@ namespace Slic3r { | ||||||
|         { |         { | ||||||
|             std::cout << MOVE_TYPE_STR[move.first]; |             std::cout << MOVE_TYPE_STR[move.first]; | ||||||
|             std::cout << ": count " << move.second.count << " (" << 100.0f * (float)move.second.count / moves_count << "%)"; |             std::cout << ": count " << move.second.count << " (" << 100.0f * (float)move.second.count / moves_count << "%)"; | ||||||
|             std::cout << " - time: " << move.second.time << "s (" << 100.0f * move.second.time / _time << "%)"; |             std::cout << " - time: " << move.second.time << "s (" << 100.0f * move.second.time / m_time << "%)"; | ||||||
|             std::cout << std::endl; |             std::cout << std::endl; | ||||||
|         } |         } | ||||||
|         std::cout << std::endl; |         std::cout << std::endl; | ||||||
|  |  | ||||||
|  | @ -215,17 +215,22 @@ namespace Slic3r { | ||||||
|         typedef std::vector<G1LineIdToBlockId> G1LineIdToBlockIdMap; |         typedef std::vector<G1LineIdToBlockId> G1LineIdToBlockIdMap; | ||||||
| 
 | 
 | ||||||
|     private: |     private: | ||||||
|         EMode _mode; |         EMode m_mode; | ||||||
|         GCodeReader _parser; |         GCodeReader m_parser; | ||||||
|         State _state; |         State m_state; | ||||||
|         Feedrates _curr; |         Feedrates m_curr; | ||||||
|         Feedrates _prev; |         Feedrates m_prev; | ||||||
|         BlocksList _blocks; |         BlocksList m_blocks; | ||||||
|         // Map between g1 line id and blocks id, used to speed up export of remaining times
 |         // Map between g1 line id and blocks id, used to speed up export of remaining times
 | ||||||
|         G1LineIdToBlockIdMap _g1_line_ids; |         G1LineIdToBlockIdMap m_g1_line_ids; | ||||||
|         // Index of the last block already st_synchronized
 |         // Index of the last block already st_synchronized
 | ||||||
|         int _last_st_synchronized_block_id; |         int m_last_st_synchronized_block_id; | ||||||
|         float _time; // s
 |         float m_time; // s
 | ||||||
|  | 
 | ||||||
|  |         // data to calculate color print times
 | ||||||
|  |         bool m_needs_color_times; | ||||||
|  |         std::vector<float> m_color_times; | ||||||
|  |         float m_color_time_cache; | ||||||
| 
 | 
 | ||||||
| #if ENABLE_MOVE_STATS | #if ENABLE_MOVE_STATS | ||||||
|         MovesStatsMap _moves_stats; |         MovesStatsMap _moves_stats; | ||||||
|  | @ -341,6 +346,15 @@ namespace Slic3r { | ||||||
|         // Returns the estimated time, in minutes (integer)
 |         // Returns the estimated time, in minutes (integer)
 | ||||||
|         std::string get_time_minutes() const; |         std::string get_time_minutes() const; | ||||||
| 
 | 
 | ||||||
|  |        // Returns the estimated time, in seconds, for each color
 | ||||||
|  |         std::vector<float> get_color_times() const; | ||||||
|  | 
 | ||||||
|  |         // Returns the estimated time, in format DDd HHh MMm SSs, for each color
 | ||||||
|  |         std::vector<std::string> get_color_times_dhms() const; | ||||||
|  | 
 | ||||||
|  |         // Returns the estimated time, in minutes (integer), for each color
 | ||||||
|  |         std::vector<std::string> get_color_times_minutes() const; | ||||||
|  | 
 | ||||||
|         // Return an estimate of the memory consumed by the time estimator.
 |         // Return an estimate of the memory consumed by the time estimator.
 | ||||||
|         size_t memory_used() const; |         size_t memory_used() const; | ||||||
| 
 | 
 | ||||||
|  | @ -409,6 +423,9 @@ namespace Slic3r { | ||||||
|         // Set allowable instantaneous speed change
 |         // Set allowable instantaneous speed change
 | ||||||
|         void _processM566(const GCodeReader::GCodeLine& line); |         void _processM566(const GCodeReader::GCodeLine& line); | ||||||
| 
 | 
 | ||||||
|  |         // Set color change
 | ||||||
|  |         void _processM600(const GCodeReader::GCodeLine& line); | ||||||
|  | 
 | ||||||
|         // Unload the current filament into the MK3 MMU2 unit at the end of print.
 |         // Unload the current filament into the MK3 MMU2 unit at the end of print.
 | ||||||
|         void _processM702(const GCodeReader::GCodeLine& line); |         void _processM702(const GCodeReader::GCodeLine& line); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -5,7 +5,9 @@ | ||||||
| #include <mutex>        // for std::lock_guard
 | #include <mutex>        // for std::lock_guard
 | ||||||
| #include <functional>   // for std::function
 | #include <functional>   // for std::function
 | ||||||
| #include <utility>      // for std::forward
 | #include <utility>      // for std::forward
 | ||||||
|  | #include <vector> | ||||||
| #include <algorithm> | #include <algorithm> | ||||||
|  | #include <cmath> | ||||||
| 
 | 
 | ||||||
| #include "libslic3r.h" | #include "libslic3r.h" | ||||||
| #include "Point.hpp" | #include "Point.hpp" | ||||||
|  | @ -242,6 +244,58 @@ template<class C> bool all_of(const C &container) | ||||||
|                        }); |                        }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | template<class T> struct remove_cvref | ||||||
|  | { | ||||||
|  |     using type = | ||||||
|  |         typename std::remove_cv<typename std::remove_reference<T>::type>::type; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | template<class T> using remove_cvref_t = typename remove_cvref<T>::type; | ||||||
|  | 
 | ||||||
|  | template<template<class> class C, class T> | ||||||
|  | class Container : public C<remove_cvref_t<T>> | ||||||
|  | { | ||||||
|  | public: | ||||||
|  |     explicit Container(size_t count, T &&initval) | ||||||
|  |         : C<remove_cvref_t<T>>(count, initval) | ||||||
|  |     {} | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | template<class T> using DefaultContainer = std::vector<T>; | ||||||
|  | 
 | ||||||
|  | /// Exactly like Matlab https://www.mathworks.com/help/matlab/ref/linspace.html
 | ||||||
|  | template<class T, class I, template<class> class C = DefaultContainer> | ||||||
|  | inline C<remove_cvref_t<T>> linspace(const T &start, const T &stop, const I &n) | ||||||
|  | { | ||||||
|  |     Container<C, T> vals(n, T()); | ||||||
|  | 
 | ||||||
|  |     T      stride = (stop - start) / n; | ||||||
|  |     size_t i      = 0; | ||||||
|  |     std::generate(vals.begin(), vals.end(), [&i, start, stride] { | ||||||
|  |         return start + i++ * stride; | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     return vals; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// A set of equidistant values starting from 'start' (inclusive), ending
 | ||||||
|  | /// in the closest multiple of 'stride' less than or equal to 'end' and
 | ||||||
|  | /// leaving 'stride' space between each value. 
 | ||||||
|  | /// Very similar to Matlab [start:stride:end] notation.
 | ||||||
|  | template<class T, template<class> class C = DefaultContainer> | ||||||
|  | inline C<remove_cvref_t<T>> grid(const T &start, const T &stop, const T &stride) | ||||||
|  | { | ||||||
|  |     Container<C, T> vals(size_t(std::ceil((stop - start) / stride)), T()); | ||||||
|  |      | ||||||
|  |     int i = 0; | ||||||
|  |     std::generate(vals.begin(), vals.end(), [&i, start, stride] { | ||||||
|  |         return start + i++ * stride;  | ||||||
|  |     }); | ||||||
|  |       | ||||||
|  |     return vals; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| // A shorter C++14 style form of the enable_if metafunction
 | // A shorter C++14 style form of the enable_if metafunction
 | ||||||
| template<bool B, class T> | template<bool B, class T> | ||||||
| using enable_if_t = typename std::enable_if<B, T>::type; | using enable_if_t = typename std::enable_if<B, T>::type; | ||||||
|  | @ -304,7 +358,7 @@ inline SLIC3R_CONSTEXPR ScaledCoordOnly<Tout> scaled(const Tin &v) SLIC3R_NOEXCE | ||||||
| template<class Tout = coord_t, class Tin, int N, class = FloatingOnly<Tin>> | template<class Tout = coord_t, class Tin, int N, class = FloatingOnly<Tin>> | ||||||
| inline EigenVec<ArithmeticOnly<Tout>, N> scaled(const EigenVec<Tin, N> &v) | inline EigenVec<ArithmeticOnly<Tout>, N> scaled(const EigenVec<Tin, N> &v) | ||||||
| { | { | ||||||
|     return v.template cast<Tout>() / SCALING_FACTOR; |     return (v / SCALING_FACTOR).template cast<Tout>(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Conversion from arithmetic scaled type to floating point unscaled
 | // Conversion from arithmetic scaled type to floating point unscaled
 | ||||||
|  |  | ||||||
|  | @ -241,6 +241,8 @@ struct PrintStatistics | ||||||
|     PrintStatistics() { clear(); } |     PrintStatistics() { clear(); } | ||||||
|     std::string                     estimated_normal_print_time; |     std::string                     estimated_normal_print_time; | ||||||
|     std::string                     estimated_silent_print_time; |     std::string                     estimated_silent_print_time; | ||||||
|  |     std::vector<std::string>        estimated_normal_color_print_times; | ||||||
|  |     std::vector<std::string>        estimated_silent_color_print_times; | ||||||
|     double                          total_used_filament; |     double                          total_used_filament; | ||||||
|     double                          total_extruded_volume; |     double                          total_extruded_volume; | ||||||
|     double                          total_cost; |     double                          total_cost; | ||||||
|  | @ -259,6 +261,8 @@ struct PrintStatistics | ||||||
|     void clear() { |     void clear() { | ||||||
|         estimated_normal_print_time.clear(); |         estimated_normal_print_time.clear(); | ||||||
|         estimated_silent_print_time.clear(); |         estimated_silent_print_time.clear(); | ||||||
|  |         estimated_normal_color_print_times.clear(); | ||||||
|  |         estimated_silent_color_print_times.clear(); | ||||||
|         total_used_filament    = 0.; |         total_used_filament    = 0.; | ||||||
|         total_extruded_volume  = 0.; |         total_extruded_volume  = 0.; | ||||||
|         total_cost             = 0.; |         total_cost             = 0.; | ||||||
|  |  | ||||||
|  | @ -2505,6 +2505,19 @@ void PrintConfigDef::init_sla_params() | ||||||
|     def->mode = comAdvanced; |     def->mode = comAdvanced; | ||||||
|     def->set_default_value(new ConfigOptionFloat(1.0)); |     def->set_default_value(new ConfigOptionFloat(1.0)); | ||||||
|      |      | ||||||
|  |     def = this->add("support_base_safety_distance", coFloat); | ||||||
|  |     def->label = L("Support base safety distance"); | ||||||
|  |     def->category = L("Supports"); | ||||||
|  |     def->tooltip  = L( | ||||||
|  |         "The minimum distance of the pillar base from the model in mm. " | ||||||
|  |         "Makes sense in zero elevation mode where a gap according " | ||||||
|  |         "to this parameter is inserted between the model and the pad."); | ||||||
|  |     def->sidetext = L("mm"); | ||||||
|  |     def->min = 0; | ||||||
|  |     def->max = 10; | ||||||
|  |     def->mode = comExpert; | ||||||
|  |     def->set_default_value(new ConfigOptionFloat(1)); | ||||||
|  | 
 | ||||||
|     def = this->add("support_critical_angle", coFloat); |     def = this->add("support_critical_angle", coFloat); | ||||||
|     def->label = L("Critical angle"); |     def->label = L("Critical angle"); | ||||||
|     def->category = L("Supports"); |     def->category = L("Supports"); | ||||||
|  | @ -2537,7 +2550,9 @@ void PrintConfigDef::init_sla_params() | ||||||
|     def = this->add("support_object_elevation", coFloat); |     def = this->add("support_object_elevation", coFloat); | ||||||
|     def->label = L("Object elevation"); |     def->label = L("Object elevation"); | ||||||
|     def->category = L("Supports"); |     def->category = L("Supports"); | ||||||
|     def->tooltip = L("How much the supports should lift up the supported object."); |     def->tooltip = L("How much the supports should lift up the supported object. " | ||||||
|  |                      "If this value is zero, the bottom of the model geometry " | ||||||
|  |                      "will be considered as part of the pad."); | ||||||
|     def->sidetext = L("mm"); |     def->sidetext = L("mm"); | ||||||
|     def->min = 0; |     def->min = 0; | ||||||
|     def->max = 150; // This is the max height of print on SL1
 |     def->max = 150; // This is the max height of print on SL1
 | ||||||
|  | @ -2623,6 +2638,47 @@ void PrintConfigDef::init_sla_params() | ||||||
|     def->max = 90; |     def->max = 90; | ||||||
|     def->mode = comAdvanced; |     def->mode = comAdvanced; | ||||||
|     def->set_default_value(new ConfigOptionFloat(45.0)); |     def->set_default_value(new ConfigOptionFloat(45.0)); | ||||||
|  |      | ||||||
|  |     def = this->add("pad_object_gap", coFloat); | ||||||
|  |     def->label = L("Pad object gap"); | ||||||
|  |     def->category = L("Pad"); | ||||||
|  |     def->tooltip  = L("The gap between the object bottom and the generated " | ||||||
|  |                       "pad in zero elevation mode."); | ||||||
|  |     def->sidetext = L("mm"); | ||||||
|  |     def->min = 0; | ||||||
|  |     def->max = 10; | ||||||
|  |     def->mode = comExpert; | ||||||
|  |     def->set_default_value(new ConfigOptionFloat(1)); | ||||||
|  |      | ||||||
|  |     def = this->add("pad_object_connector_stride", coFloat); | ||||||
|  |     def->label = L("Pad object connector stride"); | ||||||
|  |     def->category = L("Pad"); | ||||||
|  |     def->tooltip = L("Distance between two connector sticks between " | ||||||
|  |                      "the object pad and the generated pad."); | ||||||
|  |     def->sidetext = L("mm"); | ||||||
|  |     def->min = 0; | ||||||
|  |     def->mode = comExpert; | ||||||
|  |     def->set_default_value(new ConfigOptionFloat(10)); | ||||||
|  |      | ||||||
|  |     def = this->add("pad_object_connector_width", coFloat); | ||||||
|  |     def->label = L("Pad object connector width"); | ||||||
|  |     def->category = L("Pad"); | ||||||
|  |     def->tooltip  = L("The width of the connectors sticks which connect the " | ||||||
|  |                       "object pad and the generated pad."); | ||||||
|  |     def->sidetext = L("mm"); | ||||||
|  |     def->min = 0; | ||||||
|  |     def->mode = comExpert; | ||||||
|  |     def->set_default_value(new ConfigOptionFloat(0.5)); | ||||||
|  |      | ||||||
|  |     def = this->add("pad_object_connector_penetration", coFloat); | ||||||
|  |     def->label = L("Pad object connector penetration"); | ||||||
|  |     def->category = L("Pad"); | ||||||
|  |     def->tooltip  = L( | ||||||
|  |         "How much should the tiny connectors penetrate into the model body."); | ||||||
|  |     def->sidetext = L("mm"); | ||||||
|  |     def->min = 0; | ||||||
|  |     def->mode = comExpert; | ||||||
|  |     def->set_default_value(new ConfigOptionFloat(0.3)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &value) | void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &value) | ||||||
|  |  | ||||||
|  | @ -984,6 +984,9 @@ public: | ||||||
|     // The height of the pillar base cone in mm.
 |     // The height of the pillar base cone in mm.
 | ||||||
|     ConfigOptionFloat support_base_height /*= 1.0*/; |     ConfigOptionFloat support_base_height /*= 1.0*/; | ||||||
|      |      | ||||||
|  |     // The minimum distance of the pillar base from the model in mm.
 | ||||||
|  |     ConfigOptionFloat support_base_safety_distance; /*= 1.0*/; | ||||||
|  | 
 | ||||||
|     // The default angle for connecting support sticks and junctions.
 |     // The default angle for connecting support sticks and junctions.
 | ||||||
|     ConfigOptionFloat support_critical_angle /*= 45*/; |     ConfigOptionFloat support_critical_angle /*= 45*/; | ||||||
| 
 | 
 | ||||||
|  | @ -1022,6 +1025,26 @@ public: | ||||||
|     // The slope of the pad wall...
 |     // The slope of the pad wall...
 | ||||||
|     ConfigOptionFloat pad_wall_slope; |     ConfigOptionFloat pad_wall_slope; | ||||||
|      |      | ||||||
|  |     // /////////////////////////////////////////////////////////////////////////
 | ||||||
|  |     // Zero elevation mode parameters:
 | ||||||
|  |     //    - The object pad will be derived from the the model geometry.
 | ||||||
|  |     //    - There will be a gap between the object pad and the generated pad
 | ||||||
|  |     //      according to the support_base_safety_distance parameter.
 | ||||||
|  |     //    - The two pads will be connected with tiny connector sticks
 | ||||||
|  |     // /////////////////////////////////////////////////////////////////////////
 | ||||||
|  |      | ||||||
|  |     // This is the gap between the object bottom and the generated pad
 | ||||||
|  |     ConfigOptionFloat pad_object_gap; | ||||||
|  |      | ||||||
|  |     // How far to place the connector sticks on the object pad perimeter
 | ||||||
|  |     ConfigOptionFloat pad_object_connector_stride; | ||||||
|  |      | ||||||
|  |     // The width of the connectors sticks
 | ||||||
|  |     ConfigOptionFloat pad_object_connector_width; | ||||||
|  |      | ||||||
|  |     // How much should the tiny connectors penetrate into the model body
 | ||||||
|  |     ConfigOptionFloat pad_object_connector_penetration; | ||||||
|  | 
 | ||||||
| protected: | protected: | ||||||
|     void initialize(StaticCacheBase &cache, const char *base_ptr) |     void initialize(StaticCacheBase &cache, const char *base_ptr) | ||||||
|     { |     { | ||||||
|  | @ -1038,6 +1061,7 @@ protected: | ||||||
|         OPT_PTR(support_pillar_widening_factor); |         OPT_PTR(support_pillar_widening_factor); | ||||||
|         OPT_PTR(support_base_diameter); |         OPT_PTR(support_base_diameter); | ||||||
|         OPT_PTR(support_base_height); |         OPT_PTR(support_base_height); | ||||||
|  |         OPT_PTR(support_base_safety_distance); | ||||||
|         OPT_PTR(support_critical_angle); |         OPT_PTR(support_critical_angle); | ||||||
|         OPT_PTR(support_max_bridge_length); |         OPT_PTR(support_max_bridge_length); | ||||||
|         OPT_PTR(support_max_pillar_link_distance); |         OPT_PTR(support_max_pillar_link_distance); | ||||||
|  | @ -1050,6 +1074,10 @@ protected: | ||||||
|         OPT_PTR(pad_max_merge_distance); |         OPT_PTR(pad_max_merge_distance); | ||||||
|         OPT_PTR(pad_edge_radius); |         OPT_PTR(pad_edge_radius); | ||||||
|         OPT_PTR(pad_wall_slope); |         OPT_PTR(pad_wall_slope); | ||||||
|  |         OPT_PTR(pad_object_gap); | ||||||
|  |         OPT_PTR(pad_object_connector_stride); | ||||||
|  |         OPT_PTR(pad_object_connector_width); | ||||||
|  |         OPT_PTR(pad_object_connector_penetration); | ||||||
|     } |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -184,9 +184,10 @@ Contour3D walls(const Polygon& lower, const Polygon& upper, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// Offsetting with clipper and smoothing the edges into a curvature.
 | /// Offsetting with clipper and smoothing the edges into a curvature.
 | ||||||
| void offset(ExPolygon& sh, coord_t distance) { | void offset(ExPolygon& sh, coord_t distance, bool edgerounding = true) { | ||||||
|     using ClipperLib::ClipperOffset; |     using ClipperLib::ClipperOffset; | ||||||
|     using ClipperLib::jtRound; |     using ClipperLib::jtRound; | ||||||
|  |     using ClipperLib::jtMiter; | ||||||
|     using ClipperLib::etClosedPolygon; |     using ClipperLib::etClosedPolygon; | ||||||
|     using ClipperLib::Paths; |     using ClipperLib::Paths; | ||||||
|     using ClipperLib::Path; |     using ClipperLib::Path; | ||||||
|  | @ -203,11 +204,13 @@ void offset(ExPolygon& sh, coord_t distance) { | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     auto jointype = edgerounding? jtRound : jtMiter; | ||||||
|  |      | ||||||
|     ClipperOffset offs; |     ClipperOffset offs; | ||||||
|     offs.ArcTolerance = scaled<double>(0.01); |     offs.ArcTolerance = scaled<double>(0.01); | ||||||
|     Paths result; |     Paths result; | ||||||
|     offs.AddPath(ctour, jtRound, etClosedPolygon); |     offs.AddPath(ctour, jointype, etClosedPolygon); | ||||||
|     offs.AddPaths(holes, jtRound, etClosedPolygon); |     offs.AddPaths(holes, jointype, etClosedPolygon); | ||||||
|     offs.Execute(result, static_cast<double>(distance)); |     offs.Execute(result, static_cast<double>(distance)); | ||||||
| 
 | 
 | ||||||
|     // Offsetting reverts the orientation and also removes the last vertex
 |     // Offsetting reverts the orientation and also removes the last vertex
 | ||||||
|  | @ -237,6 +240,50 @@ void offset(ExPolygon& sh, coord_t distance) { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void offset(Polygon &sh, coord_t distance, bool edgerounding = true) | ||||||
|  | { | ||||||
|  |     using ClipperLib::ClipperOffset; | ||||||
|  |     using ClipperLib::jtRound; | ||||||
|  |     using ClipperLib::jtMiter; | ||||||
|  |     using ClipperLib::etClosedPolygon; | ||||||
|  |     using ClipperLib::Paths; | ||||||
|  |     using ClipperLib::Path; | ||||||
|  | 
 | ||||||
|  |     auto &&ctour = Slic3rMultiPoint_to_ClipperPath(sh); | ||||||
|  | 
 | ||||||
|  |     // If the input is not at least a triangle, we can not do this algorithm
 | ||||||
|  |     if (ctour.size() < 3) { | ||||||
|  |         BOOST_LOG_TRIVIAL(error) << "Invalid geometry for offsetting!"; | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     ClipperOffset offs; | ||||||
|  |     offs.ArcTolerance = 0.01 * scaled(1.); | ||||||
|  |     Paths result; | ||||||
|  |     offs.AddPath(ctour, edgerounding ? jtRound : jtMiter, etClosedPolygon); | ||||||
|  |     offs.Execute(result, static_cast<double>(distance)); | ||||||
|  | 
 | ||||||
|  |     // Offsetting reverts the orientation and also removes the last vertex
 | ||||||
|  |     // so boost will not have a closed polygon.
 | ||||||
|  | 
 | ||||||
|  |     bool found_the_contour = false; | ||||||
|  |     for (auto &r : result) { | ||||||
|  |         if (ClipperLib::Orientation(r)) { | ||||||
|  |             // We don't like if the offsetting generates more than one contour
 | ||||||
|  |             // but throwing would be an overkill. Instead, we should warn the
 | ||||||
|  |             // caller about the inability to create correct geometries
 | ||||||
|  |             if (!found_the_contour) { | ||||||
|  |                 auto rr = ClipperPath_to_Slic3rPolygon(r); | ||||||
|  |                 sh.points.swap(rr.points); | ||||||
|  |                 found_the_contour = true; | ||||||
|  |             } else { | ||||||
|  |                 BOOST_LOG_TRIVIAL(warning) | ||||||
|  |                     << "Warning: offsetting result is invalid!"; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /// Unification of polygons (with clipper) preserving holes as well.
 | /// Unification of polygons (with clipper) preserving holes as well.
 | ||||||
| ExPolygons unify(const ExPolygons& shapes) { | ExPolygons unify(const ExPolygons& shapes) { | ||||||
|     using ClipperLib::ptSubject; |     using ClipperLib::ptSubject; | ||||||
|  | @ -307,6 +354,116 @@ ExPolygons unify(const ExPolygons& shapes) { | ||||||
|     return retv; |     return retv; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | Polygons unify(const Polygons& shapes) { | ||||||
|  |     using ClipperLib::ptSubject; | ||||||
|  |      | ||||||
|  |     bool closed = true; | ||||||
|  |     bool valid = true; | ||||||
|  | 
 | ||||||
|  |     ClipperLib::Clipper clipper; | ||||||
|  | 
 | ||||||
|  |     for(auto& path : shapes) { | ||||||
|  |         auto clipperpath = Slic3rMultiPoint_to_ClipperPath(path); | ||||||
|  | 
 | ||||||
|  |         if(!clipperpath.empty()) | ||||||
|  |             valid &= clipper.AddPath(clipperpath, ptSubject, closed); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if(!valid) BOOST_LOG_TRIVIAL(warning) << "Unification of invalid shapes!"; | ||||||
|  | 
 | ||||||
|  |     ClipperLib::Paths result; | ||||||
|  |     clipper.Execute(ClipperLib::ctUnion, result, ClipperLib::pftNonZero); | ||||||
|  | 
 | ||||||
|  |     Polygons ret; | ||||||
|  |     for (ClipperLib::Path &p : result) { | ||||||
|  |         Polygon pp = ClipperPath_to_Slic3rPolygon(p); | ||||||
|  |         if (!pp.is_clockwise()) ret.emplace_back(std::move(pp)); | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Function to cut tiny connector cavities for a given polygon. The input poly
 | ||||||
|  | // will be offsetted by "padding" and small rectangle shaped cavities will be
 | ||||||
|  | // inserted along the perimeter in every "stride" distance. The stick rectangles
 | ||||||
|  | // will have a with about "stick_width". The input dimensions are in world 
 | ||||||
|  | // measure, not the scaled clipper units.
 | ||||||
|  | void breakstick_holes(ExPolygon& poly, | ||||||
|  |                       double padding, | ||||||
|  |                       double stride, | ||||||
|  |                       double stick_width, | ||||||
|  |                       double penetration) | ||||||
|  | {    | ||||||
|  |     // SVG svg("bridgestick_plate.svg");
 | ||||||
|  |     // svg.draw(poly);
 | ||||||
|  | 
 | ||||||
|  |     auto transf = [stick_width, penetration, padding, stride](Points &pts) { | ||||||
|  |         // The connector stick will be a small rectangle with dimensions
 | ||||||
|  |         // stick_width x (penetration + padding) to have some penetration
 | ||||||
|  |         // into the input polygon.
 | ||||||
|  | 
 | ||||||
|  |         Points out; | ||||||
|  |         out.reserve(2 * pts.size()); // output polygon points
 | ||||||
|  | 
 | ||||||
|  |         // stick bottom and right edge dimensions
 | ||||||
|  |         double sbottom = scaled(stick_width); | ||||||
|  |         double sright  = scaled(penetration + padding); | ||||||
|  | 
 | ||||||
|  |         // scaled stride distance
 | ||||||
|  |         double sstride = scaled(stride); | ||||||
|  |         double t       = 0; | ||||||
|  | 
 | ||||||
|  |         // process pairs of vertices as an edge, start with the last and
 | ||||||
|  |         // first point
 | ||||||
|  |         for (size_t i = pts.size() - 1, j = 0; j < pts.size(); i = j, ++j) { | ||||||
|  |             // Get vertices and the direction vectors
 | ||||||
|  |             const Point &a = pts[i], &b = pts[j]; | ||||||
|  |             Vec2d        dir = b.cast<double>() - a.cast<double>(); | ||||||
|  |             double       nrm = dir.norm(); | ||||||
|  |             dir /= nrm; | ||||||
|  |             Vec2d dirp(-dir(Y), dir(X)); | ||||||
|  | 
 | ||||||
|  |             // Insert start point
 | ||||||
|  |             out.emplace_back(a); | ||||||
|  | 
 | ||||||
|  |             // dodge the start point, do not make sticks on the joins
 | ||||||
|  |             while (t < sbottom) t += sbottom; | ||||||
|  |             double tend = nrm - sbottom; | ||||||
|  | 
 | ||||||
|  |             while (t < tend) { // insert the stick on the polygon perimeter
 | ||||||
|  | 
 | ||||||
|  |                 // calculate the stick rectangle vertices and insert them
 | ||||||
|  |                 // into the output.
 | ||||||
|  |                 Point p1 = a + (t * dir).cast<coord_t>(); | ||||||
|  |                 Point p2 = p1 + (sright * dirp).cast<coord_t>(); | ||||||
|  |                 Point p3 = p2 + (sbottom * dir).cast<coord_t>(); | ||||||
|  |                 Point p4 = p3 + (sright * -dirp).cast<coord_t>(); | ||||||
|  |                 out.insert(out.end(), {p1, p2, p3, p4}); | ||||||
|  | 
 | ||||||
|  |                 // continue along the perimeter
 | ||||||
|  |                 t += sstride; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             t = t - nrm; | ||||||
|  | 
 | ||||||
|  |             // Insert edge endpoint
 | ||||||
|  |             out.emplace_back(b); | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         // move the new points
 | ||||||
|  |         out.shrink_to_fit(); | ||||||
|  |         pts.swap(out); | ||||||
|  |     }; | ||||||
|  |      | ||||||
|  |     if(stride > 0.0 && stick_width > 0.0 && padding > 0.0) { | ||||||
|  |         transf(poly.contour.points); | ||||||
|  |         for (auto &h : poly.holes) transf(h.points); | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     // svg.draw(poly);
 | ||||||
|  |     // svg.Close();
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /// This method will create a rounded edge around a flat polygon in 3d space.
 | /// This method will create a rounded edge around a flat polygon in 3d space.
 | ||||||
| /// 'base_plate' parameter is the target plate.
 | /// 'base_plate' parameter is the target plate.
 | ||||||
| /// 'radius' is the radius of the edges.
 | /// 'radius' is the radius of the edges.
 | ||||||
|  | @ -426,40 +583,37 @@ inline Point centroid(Points& pp) { | ||||||
|     return c; |     return c; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| inline Point centroid(const ExPolygon& poly) { | inline Point centroid(const Polygon& poly) { | ||||||
|     return poly.contour.centroid(); |     return poly.centroid(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// A fake concave hull that is constructed by connecting separate shapes
 | /// A fake concave hull that is constructed by connecting separate shapes
 | ||||||
| /// with explicit bridges. Bridges are generated from each shape's centroid
 | /// with explicit bridges. Bridges are generated from each shape's centroid
 | ||||||
| /// to the center of the "scene" which is the centroid calculated from the shape
 | /// to the center of the "scene" which is the centroid calculated from the shape
 | ||||||
| /// centroids (a star is created...)
 | /// centroids (a star is created...)
 | ||||||
| ExPolygons concave_hull(const ExPolygons& polys, double max_dist_mm = 50, | Polygons concave_hull(const Polygons& polys, double max_dist_mm = 50, | ||||||
|                       ThrowOnCancel throw_on_cancel = [](){}) |                       ThrowOnCancel throw_on_cancel = [](){}) | ||||||
| { | { | ||||||
|     namespace bgi = boost::geometry::index; |     namespace bgi = boost::geometry::index; | ||||||
|     using SpatElement = std::pair<BoundingBox, unsigned>; |     using SpatElement = std::pair<Point, unsigned>; | ||||||
|     using SpatIndex = bgi::rtree< SpatElement, bgi::rstar<16, 4> >; |     using SpatIndex = bgi::rtree< SpatElement, bgi::rstar<16, 4> >; | ||||||
| 
 | 
 | ||||||
|     if(polys.empty()) return ExPolygons(); |     if(polys.empty()) return Polygons(); | ||||||
|      |      | ||||||
|     ExPolygons punion = unify(polys);   // could be redundant
 |     const double max_dist = scaled(max_dist_mm); | ||||||
|  | 
 | ||||||
|  |     Polygons punion = unify(polys);   // could be redundant
 | ||||||
| 
 | 
 | ||||||
|     if(punion.size() == 1) return punion; |     if(punion.size() == 1) return punion; | ||||||
| 
 | 
 | ||||||
|     // We get the centroids of all the islands in the 2D slice
 |     // We get the centroids of all the islands in the 2D slice
 | ||||||
|     Points centroids; centroids.reserve(punion.size()); |     Points centroids; centroids.reserve(punion.size()); | ||||||
|     std::transform(punion.begin(), punion.end(), std::back_inserter(centroids), |     std::transform(punion.begin(), punion.end(), std::back_inserter(centroids), | ||||||
|                    [](const ExPolygon& poly) { return centroid(poly); }); |                    [](const Polygon& poly) { return centroid(poly); }); | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|     SpatIndex boxindex; unsigned idx = 0; |  | ||||||
|     std::for_each(punion.begin(), punion.end(), |  | ||||||
|                   [&boxindex, &idx](const ExPolygon& expo) { |  | ||||||
|         BoundingBox bb(expo); |  | ||||||
|         boxindex.insert(std::make_pair(bb, idx++)); |  | ||||||
|     }); |  | ||||||
| 
 | 
 | ||||||
|  |     SpatIndex ctrindex; | ||||||
|  |     unsigned  idx = 0; | ||||||
|  |     for(const Point &ct : centroids) ctrindex.insert(std::make_pair(ct, idx++)); | ||||||
|      |      | ||||||
|     // Centroid of the centroids of islands. This is where the additional
 |     // Centroid of the centroids of islands. This is where the additional
 | ||||||
|     // connector sticks are routed.
 |     // connector sticks are routed.
 | ||||||
|  | @ -470,25 +624,32 @@ ExPolygons concave_hull(const ExPolygons& polys, double max_dist_mm = 50, | ||||||
|     idx = 0; |     idx = 0; | ||||||
|     std::transform(centroids.begin(), centroids.end(), |     std::transform(centroids.begin(), centroids.end(), | ||||||
|                    std::back_inserter(punion), |                    std::back_inserter(punion), | ||||||
|                    [&punion, &boxindex, cc, max_dist_mm, &idx, throw_on_cancel] |                    [¢roids, &ctrindex, cc, max_dist, &idx, throw_on_cancel] | ||||||
|                    (const Point& c) |                    (const Point& c) | ||||||
|     { |     { | ||||||
|         throw_on_cancel(); |         throw_on_cancel(); | ||||||
|         double dx = x(c) - x(cc), dy = y(c) - y(cc); |         double dx = x(c) - x(cc), dy = y(c) - y(cc); | ||||||
|         double l = std::sqrt(dx * dx + dy * dy); |         double l = std::sqrt(dx * dx + dy * dy); | ||||||
|         double nx = dx / l, ny = dy / l; |         double nx = dx / l, ny = dy / l; | ||||||
|         double max_dist = scaled<double>(max_dist_mm); |  | ||||||
|          |          | ||||||
|         ExPolygon& expo = punion[idx++]; |         Point& ct = centroids[idx]; | ||||||
|         BoundingBox querybb(expo); |  | ||||||
|          |          | ||||||
|         querybb.offset(max_dist); |  | ||||||
|         std::vector<SpatElement> result; |         std::vector<SpatElement> result; | ||||||
|         boxindex.query(bgi::intersects(querybb), std::back_inserter(result)); |         ctrindex.query(bgi::nearest(ct, 2), std::back_inserter(result)); | ||||||
|         if(result.size() <= 1) return ExPolygon(); |  | ||||||
| 
 | 
 | ||||||
|         ExPolygon r; |         double dist = max_dist; | ||||||
|         auto& ctour = r.contour.points; |         for (const SpatElement &el : result) | ||||||
|  |             if (el.second != idx) { | ||||||
|  |                 dist = Line(el.first, ct).length(); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |          | ||||||
|  |         idx++; | ||||||
|  |          | ||||||
|  |         if (dist >= max_dist) return Polygon(); | ||||||
|  |          | ||||||
|  |         Polygon r; | ||||||
|  |         auto& ctour = r.points; | ||||||
| 
 | 
 | ||||||
|         ctour.reserve(3); |         ctour.reserve(3); | ||||||
|         ctour.emplace_back(cc); |         ctour.emplace_back(cc); | ||||||
|  | @ -507,20 +668,16 @@ ExPolygons concave_hull(const ExPolygons& polys, double max_dist_mm = 50, | ||||||
|     return punion; |     return punion; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void base_plate(const TriangleMesh &mesh, ExPolygons &output, float h, | void base_plate(const TriangleMesh &      mesh, | ||||||
|                 float layerh, ThrowOnCancel thrfn) |                 ExPolygons &              output, | ||||||
|  |                 const std::vector<float> &heights, | ||||||
|  |                 ThrowOnCancel             thrfn) | ||||||
| { | { | ||||||
|     TriangleMesh m = mesh; |     if (mesh.empty()) return; | ||||||
|     m.require_shared_vertices(); // TriangleMeshSlicer needs this
 |     //    m.require_shared_vertices(); // TriangleMeshSlicer needs this    
 | ||||||
|     TriangleMeshSlicer slicer(&m); |     TriangleMeshSlicer slicer(&mesh); | ||||||
|      |      | ||||||
|     auto bb = mesh.bounding_box(); |     std::vector<ExPolygons> out; out.reserve(heights.size()); | ||||||
|     float gnd = float(bb.min(Z)); |  | ||||||
|     std::vector<float> heights = {float(bb.min(Z))}; |  | ||||||
|     for(float hi = gnd + layerh; hi <= gnd + h; hi += layerh) |  | ||||||
|         heights.emplace_back(hi); |  | ||||||
| 
 |  | ||||||
|     std::vector<ExPolygons> out; out.reserve(size_t(std::ceil(h/layerh))); |  | ||||||
|     slicer.slice(heights, 0.f, &out, thrfn); |     slicer.slice(heights, 0.f, &out, thrfn); | ||||||
|      |      | ||||||
|     size_t count = 0; for(auto& o : out) count += o.size(); |     size_t count = 0; for(auto& o : out) count += o.size(); | ||||||
|  | @ -542,7 +699,24 @@ void base_plate(const TriangleMesh &mesh, ExPolygons &output, float h, | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| Contour3D create_base_pool(const ExPolygons &ground_layer,  | void base_plate(const TriangleMesh &mesh, | ||||||
|  |                 ExPolygons &        output, | ||||||
|  |                 float               h, | ||||||
|  |                 float               layerh, | ||||||
|  |                 ThrowOnCancel       thrfn) | ||||||
|  | { | ||||||
|  |     auto bb = mesh.bounding_box(); | ||||||
|  |     float gnd = float(bb.min(Z)); | ||||||
|  |     std::vector<float> heights = {float(bb.min(Z))}; | ||||||
|  |      | ||||||
|  |     for(float hi = gnd + layerh; hi <= gnd + h; hi += layerh) | ||||||
|  |         heights.emplace_back(hi); | ||||||
|  |      | ||||||
|  |     base_plate(mesh, output, heights, thrfn); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Contour3D create_base_pool(const Polygons &ground_layer,  | ||||||
|  |                            const ExPolygons &obj_self_pad = {}, | ||||||
|                            const PoolConfig& cfg = PoolConfig())  |                            const PoolConfig& cfg = PoolConfig())  | ||||||
| { | { | ||||||
|     // for debugging:
 |     // for debugging:
 | ||||||
|  | @ -557,7 +731,7 @@ Contour3D create_base_pool(const ExPolygons &ground_layer, | ||||||
|     // serve as the bottom plate of the pad. We will offset this concave hull
 |     // serve as the bottom plate of the pad. We will offset this concave hull
 | ||||||
|     // and then offset back the result with clipper with rounding edges ON. This
 |     // and then offset back the result with clipper with rounding edges ON. This
 | ||||||
|     // trick will create a nice rounded pad shape.
 |     // trick will create a nice rounded pad shape.
 | ||||||
|     ExPolygons concavehs = concave_hull(ground_layer, mergedist, cfg.throw_on_cancel); |     Polygons concavehs = concave_hull(ground_layer, mergedist, cfg.throw_on_cancel); | ||||||
| 
 | 
 | ||||||
|     const double thickness      = cfg.min_wall_thickness_mm; |     const double thickness      = cfg.min_wall_thickness_mm; | ||||||
|     const double wingheight     = cfg.min_wall_height_mm; |     const double wingheight     = cfg.min_wall_height_mm; | ||||||
|  | @ -577,42 +751,37 @@ Contour3D create_base_pool(const ExPolygons &ground_layer, | ||||||
| 
 | 
 | ||||||
|     Contour3D pool; |     Contour3D pool; | ||||||
| 
 | 
 | ||||||
|     for(ExPolygon& concaveh : concavehs) { |     for(Polygon& concaveh : concavehs) { | ||||||
|         if(concaveh.contour.points.empty()) return pool; |         if(concaveh.points.empty()) return pool; | ||||||
| 
 |  | ||||||
|         // Get rid of any holes in the concave hull output.
 |  | ||||||
|         concaveh.holes.clear(); |  | ||||||
| 
 | 
 | ||||||
|         // Here lies the trick that does the smoothing only with clipper offset
 |         // Here lies the trick that does the smoothing only with clipper offset
 | ||||||
|         // calls. The offset is configured to round edges. Inner edges will
 |         // calls. The offset is configured to round edges. Inner edges will
 | ||||||
|         // be rounded because we offset twice: ones to get the outer (top) plate
 |         // be rounded because we offset twice: ones to get the outer (top) plate
 | ||||||
|         // and again to get the inner (bottom) plate
 |         // and again to get the inner (bottom) plate
 | ||||||
|         auto outer_base = concaveh; |         auto outer_base = concaveh; | ||||||
|         outer_base.holes.clear(); |  | ||||||
|         offset(outer_base, s_safety_dist + s_wingdist + s_thickness); |         offset(outer_base, s_safety_dist + s_wingdist + s_thickness); | ||||||
| 
 | 
 | ||||||
|         ExPolygon bottom_poly = outer_base; |         ExPolygon bottom_poly; bottom_poly.contour = outer_base; | ||||||
|         bottom_poly.holes.clear(); |  | ||||||
|         offset(bottom_poly, -s_bottom_offs); |         offset(bottom_poly, -s_bottom_offs); | ||||||
| 
 | 
 | ||||||
|         // Punching a hole in the top plate for the cavity
 |         // Punching a hole in the top plate for the cavity
 | ||||||
|         ExPolygon top_poly; |         ExPolygon top_poly; | ||||||
|         ExPolygon middle_base; |         ExPolygon middle_base; | ||||||
|         ExPolygon inner_base; |         ExPolygon inner_base; | ||||||
|         top_poly.contour = outer_base.contour; |         top_poly.contour = outer_base; | ||||||
| 
 | 
 | ||||||
|         if(wingheight > 0) { |         if(wingheight > 0) { | ||||||
|             inner_base = outer_base; |             inner_base.contour = outer_base; | ||||||
|             offset(inner_base, -(s_thickness + s_wingdist + s_eradius)); |             offset(inner_base, -(s_thickness + s_wingdist + s_eradius)); | ||||||
| 
 | 
 | ||||||
|             middle_base = outer_base; |             middle_base.contour = outer_base; | ||||||
|             offset(middle_base, -s_thickness); |             offset(middle_base, -s_thickness); | ||||||
|             top_poly.holes.emplace_back(middle_base.contour); |             top_poly.holes.emplace_back(middle_base.contour); | ||||||
|             auto& tph = top_poly.holes.back().points; |             auto& tph = top_poly.holes.back().points; | ||||||
|             std::reverse(tph.begin(), tph.end()); |             std::reverse(tph.begin(), tph.end()); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         ExPolygon ob = outer_base; double wh = 0; |         ExPolygon ob; ob.contour = outer_base; double wh = 0; | ||||||
| 
 | 
 | ||||||
|         // now we will calculate the angle or portion of the circle from
 |         // now we will calculate the angle or portion of the circle from
 | ||||||
|         // pi/2 that will connect perfectly with the bottom plate.
 |         // pi/2 that will connect perfectly with the bottom plate.
 | ||||||
|  | @ -659,6 +828,7 @@ Contour3D create_base_pool(const ExPolygons &ground_layer, | ||||||
|         if(wingheight > 0) { |         if(wingheight > 0) { | ||||||
|             // Generate the smoothed edge geometry
 |             // Generate the smoothed edge geometry
 | ||||||
|             wh = 0; |             wh = 0; | ||||||
|  |             ob = middle_base; | ||||||
|             if(s_eradius) pool.merge(round_edges(middle_base, |             if(s_eradius) pool.merge(round_edges(middle_base, | ||||||
|                                    r, |                                    r, | ||||||
|                                    phi - 90, // from tangent lines
 |                                    phi - 90, // from tangent lines
 | ||||||
|  | @ -673,11 +843,59 @@ Contour3D create_base_pool(const ExPolygons &ground_layer, | ||||||
|                              wh, -wingdist, thrcl)); |                              wh, -wingdist, thrcl)); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         // Now we need to triangulate the top and bottom plates as well as the
 |         if (cfg.embed_object) { | ||||||
|         // cavity bottom plate which is the same as the bottom plate but it is
 |             ExPolygons bttms = diff_ex(to_polygons(bottom_poly), | ||||||
|         // elevated by the thickness.
 |                                        to_polygons(obj_self_pad)); | ||||||
|         pool.merge(triangulate_expolygon_3d(top_poly)); |              | ||||||
|  |             assert(!bttms.empty()); | ||||||
|  |              | ||||||
|  |             std::sort(bttms.begin(), bttms.end(), | ||||||
|  |                       [](const ExPolygon& e1, const ExPolygon& e2) { | ||||||
|  |                           return e1.contour.area() > e2.contour.area(); | ||||||
|  |                       }); | ||||||
|  |              | ||||||
|  |             if(wingheight > 0) inner_base.holes = bttms.front().holes; | ||||||
|  |             else top_poly.holes = bttms.front().holes; | ||||||
|  | 
 | ||||||
|  |             auto straight_walls = | ||||||
|  |                 [&pool](const Polygon &cntr, coord_t z_low, coord_t z_high) { | ||||||
|  |                      | ||||||
|  |                 auto lines = cntr.lines(); | ||||||
|  |                  | ||||||
|  |                 for (auto &l : lines) { | ||||||
|  |                     auto s = coord_t(pool.points.size()); | ||||||
|  |                     auto& pts = pool.points; | ||||||
|  |                     pts.emplace_back(unscale(l.a.x(), l.a.y(), z_low)); | ||||||
|  |                     pts.emplace_back(unscale(l.b.x(), l.b.y(), z_low)); | ||||||
|  |                     pts.emplace_back(unscale(l.a.x(), l.a.y(), z_high)); | ||||||
|  |                     pts.emplace_back(unscale(l.b.x(), l.b.y(), z_high)); | ||||||
|  |                      | ||||||
|  |                     pool.indices.emplace_back(s, s + 1, s + 3); | ||||||
|  |                     pool.indices.emplace_back(s, s + 3, s + 2); | ||||||
|  |                 } | ||||||
|  |             }; | ||||||
|  |              | ||||||
|  |             coord_t z_lo = -scaled(fullheight), z_hi = -scaled(wingheight); | ||||||
|  |             for (ExPolygon &ep : bttms) { | ||||||
|  |                 pool.merge(triangulate_expolygon_3d(ep, -fullheight, true)); | ||||||
|  |                 for (auto &h : ep.holes) straight_walls(h, z_lo, z_hi); | ||||||
|  |             } | ||||||
|  |              | ||||||
|  |             // Skip the outer contour, triangulate the holes
 | ||||||
|  |             for (auto it = std::next(bttms.begin()); it != bttms.end(); ++it) { | ||||||
|  |                 pool.merge(triangulate_expolygon_3d(*it, -wingheight)); | ||||||
|  |                 straight_walls(it->contour, z_lo, z_hi); | ||||||
|  |             } | ||||||
|  |              | ||||||
|  |         } else { | ||||||
|  |             // Now we need to triangulate the top and bottom plates as well as
 | ||||||
|  |             // the cavity bottom plate which is the same as the bottom plate
 | ||||||
|  |             // but it is elevated by the thickness.
 | ||||||
|  |              | ||||||
|             pool.merge(triangulate_expolygon_3d(bottom_poly, -fullheight, true)); |             pool.merge(triangulate_expolygon_3d(bottom_poly, -fullheight, true)); | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         pool.merge(triangulate_expolygon_3d(top_poly)); | ||||||
| 
 | 
 | ||||||
|         if(wingheight > 0) |         if(wingheight > 0) | ||||||
|             pool.merge(triangulate_expolygon_3d(inner_base, -wingheight)); |             pool.merge(triangulate_expolygon_3d(inner_base, -wingheight)); | ||||||
|  | @ -687,8 +905,8 @@ Contour3D create_base_pool(const ExPolygons &ground_layer, | ||||||
|     return pool; |     return pool; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void create_base_pool(const ExPolygons &ground_layer, TriangleMesh& out, | void create_base_pool(const Polygons &ground_layer, TriangleMesh& out, | ||||||
|                       const PoolConfig& cfg) |                       const ExPolygons &holes, const PoolConfig& cfg) | ||||||
| { | { | ||||||
|      |      | ||||||
| 
 | 
 | ||||||
|  | @ -698,7 +916,7 @@ void create_base_pool(const ExPolygons &ground_layer, TriangleMesh& out, | ||||||
|     // std::fstream fout("pad_debug.obj", std::fstream::out);
 |     // std::fstream fout("pad_debug.obj", std::fstream::out);
 | ||||||
|     // if(fout.good()) pool.to_obj(fout);
 |     // if(fout.good()) pool.to_obj(fout);
 | ||||||
| 
 | 
 | ||||||
|     out.merge(mesh(create_base_pool(ground_layer, cfg))); |     out.merge(mesh(create_base_pool(ground_layer, holes, cfg))); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -8,7 +8,9 @@ | ||||||
| namespace Slic3r { | namespace Slic3r { | ||||||
| 
 | 
 | ||||||
| class ExPolygon; | class ExPolygon; | ||||||
|  | class Polygon; | ||||||
| using ExPolygons = std::vector<ExPolygon>; | using ExPolygons = std::vector<ExPolygon>; | ||||||
|  | using Polygons = std::vector<Polygon>; | ||||||
| 
 | 
 | ||||||
| class TriangleMesh; | class TriangleMesh; | ||||||
| 
 | 
 | ||||||
|  | @ -19,16 +21,40 @@ using ThrowOnCancel = std::function<void(void)>; | ||||||
| /// Calculate the polygon representing the silhouette from the specified height
 | /// Calculate the polygon representing the silhouette from the specified height
 | ||||||
| void base_plate(const TriangleMesh& mesh,       // input mesh
 | void base_plate(const TriangleMesh& mesh,       // input mesh
 | ||||||
|                 ExPolygons& output,             // Output will be merged with
 |                 ExPolygons& output,             // Output will be merged with
 | ||||||
|                 float zlevel = 0.1f,            // Plate creation level
 |                 float samplingheight = 0.1f,    // The height range to sample
 | ||||||
|                 float layerheight = 0.05f,      // The sampling height
 |                 float layerheight = 0.05f,      // The sampling height
 | ||||||
|                 ThrowOnCancel thrfn = [](){});  // Will be called frequently
 |                 ThrowOnCancel thrfn = [](){});  // Will be called frequently
 | ||||||
| 
 | 
 | ||||||
|  | void base_plate(const TriangleMesh& mesh,       // input mesh
 | ||||||
|  |                 ExPolygons& output,             // Output will be merged with
 | ||||||
|  |                 const std::vector<float>&,      // Exact Z levels to sample
 | ||||||
|  |                 ThrowOnCancel thrfn = [](){});  // Will be called frequently
 | ||||||
|  | 
 | ||||||
|  | // Function to cut tiny connector cavities for a given polygon. The input poly
 | ||||||
|  | // will be offsetted by "padding" and small rectangle shaped cavities will be
 | ||||||
|  | // inserted along the perimeter in every "stride" distance. The stick rectangles
 | ||||||
|  | // will have a with about "stick_width". The input dimensions are in world 
 | ||||||
|  | // measure, not the scaled clipper units.
 | ||||||
|  | void breakstick_holes(ExPolygon &poly, | ||||||
|  |                       double     padding, | ||||||
|  |                       double     stride, | ||||||
|  |                       double     stick_width, | ||||||
|  |                       double     penetration = 0.0); | ||||||
|  | 
 | ||||||
| struct PoolConfig { | struct PoolConfig { | ||||||
|     double min_wall_thickness_mm = 2; |     double min_wall_thickness_mm = 2; | ||||||
|     double min_wall_height_mm = 5; |     double min_wall_height_mm = 5; | ||||||
|     double max_merge_distance_mm = 50; |     double max_merge_distance_mm = 50; | ||||||
|     double edge_radius_mm = 1; |     double edge_radius_mm = 1; | ||||||
|     double wall_slope = std::atan(1.0);          // Universal constant for Pi/4
 |     double wall_slope = std::atan(1.0);          // Universal constant for Pi/4
 | ||||||
|  |     struct EmbedObject { | ||||||
|  |         double object_gap_mm = 0.5; | ||||||
|  |         double stick_stride_mm = 10; | ||||||
|  |         double stick_width_mm = 0.3; | ||||||
|  |         double stick_penetration_mm = 0.1; | ||||||
|  |         bool enabled = false; | ||||||
|  |         operator bool() const { return enabled; } | ||||||
|  |     } embed_object; | ||||||
| 
 | 
 | ||||||
|     ThrowOnCancel throw_on_cancel = [](){}; |     ThrowOnCancel throw_on_cancel = [](){}; | ||||||
| 
 | 
 | ||||||
|  | @ -42,15 +68,12 @@ struct PoolConfig { | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /// Calculate the pool for the mesh for SLA printing
 | /// Calculate the pool for the mesh for SLA printing
 | ||||||
| void create_base_pool(const ExPolygons& base_plate, | void create_base_pool(const Polygons& base_plate, | ||||||
|                       TriangleMesh& output_mesh, |                       TriangleMesh& output_mesh, | ||||||
|  |                       const ExPolygons& holes, | ||||||
|                       const PoolConfig& = PoolConfig()); |                       const PoolConfig& = PoolConfig()); | ||||||
| 
 | 
 | ||||||
| /// TODO: Currently the base plate of the pool will have half the height of the
 | /// Returns the elevation needed for compensating the pad.
 | ||||||
| /// whole pool. So the carved out space has also half the height. This is not
 |  | ||||||
| /// a particularly elegant solution, the thickness should be exactly
 |  | ||||||
| /// min_wall_thickness and it should be corrected in the future. This method
 |  | ||||||
| /// will return the correct value for further processing.
 |  | ||||||
| inline double get_pad_elevation(const PoolConfig& cfg) { | inline double get_pad_elevation(const PoolConfig& cfg) { | ||||||
|     return cfg.min_wall_thickness_mm; |     return cfg.min_wall_thickness_mm; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -60,7 +60,7 @@ class EigenMesh3D { | ||||||
| 
 | 
 | ||||||
|     Eigen::MatrixXd m_V; |     Eigen::MatrixXd m_V; | ||||||
|     Eigen::MatrixXi m_F; |     Eigen::MatrixXi m_F; | ||||||
|     double m_ground_level = 0; |     double m_ground_level = 0, m_gnd_offset = 0; | ||||||
| 
 | 
 | ||||||
|     std::unique_ptr<AABBImpl> m_aabb; |     std::unique_ptr<AABBImpl> m_aabb; | ||||||
| public: | public: | ||||||
|  | @ -71,7 +71,9 @@ public: | ||||||
| 
 | 
 | ||||||
|     ~EigenMesh3D(); |     ~EigenMesh3D(); | ||||||
| 
 | 
 | ||||||
|     inline double ground_level() const { return m_ground_level; } |     inline double ground_level() const { return m_ground_level + m_gnd_offset; } | ||||||
|  |     inline void ground_level_offset(double o) { m_gnd_offset = o; } | ||||||
|  |     inline double ground_level_offset() const { return m_gnd_offset; } | ||||||
| 
 | 
 | ||||||
|     inline const Eigen::MatrixXd& V() const { return m_V; } |     inline const Eigen::MatrixXd& V() const { return m_V; } | ||||||
|     inline const Eigen::MatrixXi& F() const { return m_F; } |     inline const Eigen::MatrixXi& F() const { return m_F; } | ||||||
|  | @ -149,6 +151,12 @@ public: | ||||||
| #endif /* SLIC3R_SLA_NEEDS_WINDTREE */ | #endif /* SLIC3R_SLA_NEEDS_WINDTREE */ | ||||||
| 
 | 
 | ||||||
|     double squared_distance(const Vec3d& p, int& i, Vec3d& c) const; |     double squared_distance(const Vec3d& p, int& i, Vec3d& c) const; | ||||||
|  |     inline double squared_distance(const Vec3d &p) const | ||||||
|  |     { | ||||||
|  |         int   i; | ||||||
|  |         Vec3d c; | ||||||
|  |         return squared_distance(p, i, c); | ||||||
|  |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -7,13 +7,15 @@ | ||||||
| 
 | 
 | ||||||
| #include <Eigen/Geometry> | #include <Eigen/Geometry> | ||||||
| 
 | 
 | ||||||
|  | #include <libslic3r/BoundingBox.hpp> | ||||||
|  | 
 | ||||||
| namespace Slic3r { | namespace Slic3r { | ||||||
| namespace sla { | namespace sla { | ||||||
| 
 | 
 | ||||||
| typedef Eigen::Matrix<double,   3, 1, Eigen::DontAlign> Vec3d; | typedef Eigen::Matrix<double,   3, 1, Eigen::DontAlign> Vec3d; | ||||||
| using SpatElement = std::pair<Vec3d, unsigned>; | using PointIndexEl = std::pair<Vec3d, unsigned>; | ||||||
| 
 | 
 | ||||||
| class SpatIndex { | class PointIndex { | ||||||
|     class Impl; |     class Impl; | ||||||
| 
 | 
 | ||||||
|     // We use Pimpl because it takes a long time to compile boost headers which
 |     // We use Pimpl because it takes a long time to compile boost headers which
 | ||||||
|  | @ -21,30 +23,67 @@ class SpatIndex { | ||||||
|     std::unique_ptr<Impl> m_impl; |     std::unique_ptr<Impl> m_impl; | ||||||
| public: | public: | ||||||
| 
 | 
 | ||||||
|     SpatIndex(); |     PointIndex(); | ||||||
|     ~SpatIndex(); |     ~PointIndex(); | ||||||
| 
 | 
 | ||||||
|     SpatIndex(const SpatIndex&); |     PointIndex(const PointIndex&); | ||||||
|     SpatIndex(SpatIndex&&); |     PointIndex(PointIndex&&); | ||||||
|     SpatIndex& operator=(const SpatIndex&); |     PointIndex& operator=(const PointIndex&); | ||||||
|     SpatIndex& operator=(SpatIndex&&); |     PointIndex& operator=(PointIndex&&); | ||||||
| 
 | 
 | ||||||
|     void insert(const SpatElement&); |     void insert(const PointIndexEl&); | ||||||
|     bool remove(const SpatElement&); |     bool remove(const PointIndexEl&); | ||||||
| 
 | 
 | ||||||
|     inline void insert(const Vec3d& v, unsigned idx) |     inline void insert(const Vec3d& v, unsigned idx) | ||||||
|     { |     { | ||||||
|         insert(std::make_pair(v, unsigned(idx))); |         insert(std::make_pair(v, unsigned(idx))); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     std::vector<SpatElement> query(std::function<bool(const SpatElement&)>); |     std::vector<PointIndexEl> query(std::function<bool(const PointIndexEl&)>); | ||||||
|     std::vector<SpatElement> nearest(const Vec3d&, unsigned k); |     std::vector<PointIndexEl> nearest(const Vec3d&, unsigned k); | ||||||
| 
 | 
 | ||||||
|     // For testing
 |     // For testing
 | ||||||
|     size_t size() const; |     size_t size() const; | ||||||
|     bool empty() const { return size() == 0; } |     bool empty() const { return size() == 0; } | ||||||
| 
 | 
 | ||||||
|     void foreach(std::function<void(const SpatElement& el)> fn); |     void foreach(std::function<void(const PointIndexEl& el)> fn); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | using BoxIndexEl = std::pair<Slic3r::BoundingBox, unsigned>; | ||||||
|  | 
 | ||||||
|  | class BoxIndex { | ||||||
|  |     class Impl; | ||||||
|  |      | ||||||
|  |     // We use Pimpl because it takes a long time to compile boost headers which
 | ||||||
|  |     // is the engine of this class. We include it only in the cpp file.
 | ||||||
|  |     std::unique_ptr<Impl> m_impl; | ||||||
|  | public: | ||||||
|  |      | ||||||
|  |     BoxIndex(); | ||||||
|  |     ~BoxIndex(); | ||||||
|  |      | ||||||
|  |     BoxIndex(const BoxIndex&); | ||||||
|  |     BoxIndex(BoxIndex&&); | ||||||
|  |     BoxIndex& operator=(const BoxIndex&); | ||||||
|  |     BoxIndex& operator=(BoxIndex&&); | ||||||
|  |      | ||||||
|  |     void insert(const BoxIndexEl&); | ||||||
|  |     inline void insert(const BoundingBox& bb, unsigned idx) | ||||||
|  |     { | ||||||
|  |         insert(std::make_pair(bb, unsigned(idx))); | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     bool remove(const BoxIndexEl&); | ||||||
|  | 
 | ||||||
|  |     enum QueryType { qtIntersects, qtWithin }; | ||||||
|  | 
 | ||||||
|  |     std::vector<BoxIndexEl> query(const BoundingBox&, QueryType qt); | ||||||
|  |      | ||||||
|  |     // For testing
 | ||||||
|  |     size_t size() const; | ||||||
|  |     bool empty() const { return size() == 0; } | ||||||
|  |      | ||||||
|  |     void foreach(std::function<void(const BoxIndexEl& el)> fn); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -9,10 +9,12 @@ | ||||||
| #include "SLASpatIndex.hpp" | #include "SLASpatIndex.hpp" | ||||||
| #include "SLABasePool.hpp" | #include "SLABasePool.hpp" | ||||||
| 
 | 
 | ||||||
|  | #include <libslic3r/MTUtils.hpp> | ||||||
| #include <libslic3r/ClipperUtils.hpp> | #include <libslic3r/ClipperUtils.hpp> | ||||||
| #include <libslic3r/Model.hpp> | #include <libslic3r/Model.hpp> | ||||||
| 
 | 
 | ||||||
| #include <libnest2d/optimizers/nlopt/genetic.hpp> | #include <libnest2d/optimizers/nlopt/genetic.hpp> | ||||||
|  | #include <libnest2d/optimizers/nlopt/subplex.hpp> | ||||||
| #include <boost/log/trivial.hpp> | #include <boost/log/trivial.hpp> | ||||||
| #include <tbb/parallel_for.h> | #include <tbb/parallel_for.h> | ||||||
| #include <libslic3r/I18N.hpp> | #include <libslic3r/I18N.hpp> | ||||||
|  | @ -413,7 +415,7 @@ struct Pillar { | ||||||
|         assert(steps > 0); |         assert(steps > 0); | ||||||
| 
 | 
 | ||||||
|         height = jp(Z) - endp(Z); |         height = jp(Z) - endp(Z); | ||||||
|         if(height > 0) { // Endpoint is below the starting point
 |         if(height > EPSILON) { // Endpoint is below the starting point
 | ||||||
| 
 | 
 | ||||||
|             // We just create a bridge geometry with the pillar parameters and
 |             // We just create a bridge geometry with the pillar parameters and
 | ||||||
|             // move the data.
 |             // move the data.
 | ||||||
|  | @ -528,6 +530,7 @@ struct CompactBridge { | ||||||
|                   const Vec3d& ep, |                   const Vec3d& ep, | ||||||
|                   const Vec3d& n, |                   const Vec3d& n, | ||||||
|                   double r, |                   double r, | ||||||
|  |                   bool endball = true, | ||||||
|                   size_t steps = 45) |                   size_t steps = 45) | ||||||
|     { |     { | ||||||
|         Vec3d startp = sp + r * n; |         Vec3d startp = sp + r * n; | ||||||
|  | @ -542,11 +545,13 @@ struct CompactBridge { | ||||||
|         auto upperball = sphere(r, Portion{PI / 2 - fa, PI}, fa); |         auto upperball = sphere(r, Portion{PI / 2 - fa, PI}, fa); | ||||||
|         for(auto& p : upperball.points) p += startp; |         for(auto& p : upperball.points) p += startp; | ||||||
|          |          | ||||||
|  |         if(endball) { | ||||||
|             auto lowerball = sphere(r, Portion{0, PI/2 + 2*fa}, fa); |             auto lowerball = sphere(r, Portion{0, PI/2 + 2*fa}, fa); | ||||||
|             for(auto& p : lowerball.points) p += endp; |             for(auto& p : lowerball.points) p += endp; | ||||||
|  |             mesh.merge(lowerball); | ||||||
|  |         } | ||||||
|          |          | ||||||
|         mesh.merge(upperball); |         mesh.merge(upperball); | ||||||
|         mesh.merge(lowerball); |  | ||||||
|     } |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | @ -556,28 +561,111 @@ struct Pad { | ||||||
|     PoolConfig cfg; |     PoolConfig cfg; | ||||||
|     double zlevel = 0; |     double zlevel = 0; | ||||||
| 
 | 
 | ||||||
|     Pad() {} |     Pad() = default; | ||||||
| 
 | 
 | ||||||
|     Pad(const TriangleMesh& object_support_mesh, |     Pad(const TriangleMesh& support_mesh, | ||||||
|         const ExPolygons& baseplate, |         const ExPolygons& modelbase, | ||||||
|         double ground_level, |         double ground_level, | ||||||
|         const PoolConfig& pcfg) : |         const PoolConfig& pcfg) : | ||||||
|         cfg(pcfg), |         cfg(pcfg), | ||||||
|         zlevel(ground_level +  |         zlevel(ground_level +  | ||||||
|                (sla::get_pad_fullheight(pcfg) - sla::get_pad_elevation(pcfg)) ) |                sla::get_pad_fullheight(pcfg) - | ||||||
|  |                sla::get_pad_elevation(pcfg)) | ||||||
|     { |     { | ||||||
|         ExPolygons basep; |         Polygons basep; | ||||||
|         cfg.throw_on_cancel(); |         auto &thr = cfg.throw_on_cancel; | ||||||
|          |          | ||||||
|         // The 0.1f is the layer height with which the mesh is sampled and then
 |         thr(); | ||||||
|         // the layers are unified into one vector of polygons.
 |  | ||||||
|         base_plate(object_support_mesh, basep, |  | ||||||
|                    float(cfg.min_wall_height_mm + cfg.min_wall_thickness_mm), |  | ||||||
|                    0.1f, pcfg.throw_on_cancel); |  | ||||||
|          |          | ||||||
|         for(auto& bp : baseplate) basep.emplace_back(bp); |         // Get a sample for the pad from the support mesh
 | ||||||
|  |         { | ||||||
|  |             ExPolygons platetmp; | ||||||
|  | 
 | ||||||
|  |             float zstart = float(zlevel); | ||||||
|  |             float zend   = zstart + float(get_pad_fullheight(pcfg) + EPSILON); | ||||||
|  | 
 | ||||||
|  |             base_plate(support_mesh, platetmp, grid(zstart, zend, 0.1f), thr); | ||||||
|  | 
 | ||||||
|  |             // We don't need no... holes control...
 | ||||||
|  |             for (const ExPolygon &bp : platetmp) | ||||||
|  |                 basep.emplace_back(std::move(bp.contour)); | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         if(pcfg.embed_object) { | ||||||
|  |              | ||||||
|  |             // If the zero elevation mode is ON, we need to process the model
 | ||||||
|  |             // base silhouette. Create the offsetted version and punch the
 | ||||||
|  |             // breaksticks across its perimeter.
 | ||||||
|  |              | ||||||
|  |             ExPolygons modelbase_offs = modelbase; | ||||||
|  |              | ||||||
|  |             if (pcfg.embed_object.object_gap_mm > 0.0) | ||||||
|  |                 modelbase_offs | ||||||
|  |                     = offset_ex(modelbase_offs, | ||||||
|  |                                 float(scaled(pcfg.embed_object.object_gap_mm))); | ||||||
|  |              | ||||||
|  |             // Create a spatial index of the support silhouette polygons.
 | ||||||
|  |             // This will be used to check for intersections with the model
 | ||||||
|  |             // silhouette polygons. If there is no intersection, then a certain
 | ||||||
|  |             // part of the pad is redundant as it does not host any supports.
 | ||||||
|  |             BoxIndex bindex; | ||||||
|  |             { | ||||||
|  |                 unsigned idx = 0; | ||||||
|  |                 for(auto &bp : basep) { | ||||||
|  |                     auto bb = bp.bounding_box(); | ||||||
|  |                     bb.offset(float(scaled(pcfg.min_wall_thickness_mm))); | ||||||
|  |                     bindex.insert(bb, idx++); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |              | ||||||
|  |             // Punching the breaksticks across the offsetted polygon perimeters
 | ||||||
|  |             ExPolygons pad_stickholes; pad_stickholes.reserve(modelbase.size()); | ||||||
|  |             for(auto& poly : modelbase_offs) { | ||||||
|  |                  | ||||||
|  |                 std::vector<BoxIndexEl> qres = | ||||||
|  |                     bindex.query(poly.contour.bounding_box(), | ||||||
|  |                                  BoxIndex::qtIntersects); | ||||||
|  |                      | ||||||
|  |                 if (!qres.empty()) { | ||||||
|  |                      | ||||||
|  |                     // The model silhouette polygon 'poly' HAS an intersection
 | ||||||
|  |                     // with the support silhouettes. Include this polygon
 | ||||||
|  |                     // in the pad holes with the breaksticks and merge the
 | ||||||
|  |                     // original (offsetted) version with the rest of the pad
 | ||||||
|  |                     // base plate.
 | ||||||
|  |                      | ||||||
|  |                     basep.emplace_back(poly.contour); | ||||||
|  |                      | ||||||
|  |                     // The holes of 'poly' will become positive parts of the
 | ||||||
|  |                     // pad, so they has to be checked for intersections as well
 | ||||||
|  |                     // and erased if there is no intersection with the supports
 | ||||||
|  |                     auto it = poly.holes.begin(); | ||||||
|  |                     while(it != poly.holes.end()) { | ||||||
|  |                         if (bindex.query(it->bounding_box(), | ||||||
|  |                                          BoxIndex::qtIntersects).empty()) | ||||||
|  |                             it = poly.holes.erase(it); | ||||||
|  |                         else | ||||||
|  |                             ++it; | ||||||
|  |                     } | ||||||
|  |                      | ||||||
|  |                     // Punch the breaksticks
 | ||||||
|  |                     sla::breakstick_holes( | ||||||
|  |                         poly, | ||||||
|  |                         pcfg.embed_object.object_gap_mm,   // padding
 | ||||||
|  |                         pcfg.embed_object.stick_stride_mm, | ||||||
|  |                         pcfg.embed_object.stick_width_mm, | ||||||
|  |                         pcfg.embed_object.stick_penetration_mm); | ||||||
|  |                      | ||||||
|  |                     pad_stickholes.emplace_back(poly); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |              | ||||||
|  |             create_base_pool(basep, tmesh, pad_stickholes, cfg); | ||||||
|  |         } else { | ||||||
|  |             for (const ExPolygon &bp : modelbase) basep.emplace_back(bp.contour); | ||||||
|  |             create_base_pool(basep, tmesh, {}, cfg); | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
|         create_base_pool(basep, tmesh, cfg); |  | ||||||
|         tmesh.translate(0, 0, float(zlevel)); |         tmesh.translate(0, 0, float(zlevel)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -603,7 +691,7 @@ inline Vec2d to_vec2(const Vec3d& v3) { | ||||||
|     return {v3(X), v3(Y)}; |     return {v3(X), v3(Y)}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool operator==(const SpatElement& e1, const SpatElement& e2) { | bool operator==(const PointIndexEl& e1, const PointIndexEl& e2) { | ||||||
|     return e1.second == e2.second; |     return e1.second == e2.second; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -620,7 +708,7 @@ ClusteredPoints cluster(const PointSet& points, | ||||||
| ClusteredPoints cluster( | ClusteredPoints cluster( | ||||||
|         const std::vector<unsigned>& indices, |         const std::vector<unsigned>& indices, | ||||||
|         std::function<Vec3d(unsigned)> pointfn, |         std::function<Vec3d(unsigned)> pointfn, | ||||||
|         std::function<bool(const SpatElement&, const SpatElement&)> predicate, |         std::function<bool(const PointIndexEl&, const PointIndexEl&)> predicate, | ||||||
|         unsigned max_points); |         unsigned max_points); | ||||||
| 
 | 
 | ||||||
| // This class will hold the support tree meshes with some additional bookkeeping
 | // This class will hold the support tree meshes with some additional bookkeeping
 | ||||||
|  | @ -763,9 +851,9 @@ public: | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     const Pad& create_pad(const TriangleMesh& object_supports, |     const Pad& create_pad(const TriangleMesh& object_supports, | ||||||
|                           const ExPolygons& baseplate, |                           const ExPolygons& modelbase, | ||||||
|                           const PoolConfig& cfg) { |                           const PoolConfig& cfg) { | ||||||
|         m_pad = Pad(object_supports, baseplate, ground_level, cfg); |         m_pad = Pad(object_supports, modelbase, ground_level, cfg); | ||||||
|         return m_pad; |         return m_pad; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -808,7 +896,6 @@ public: | ||||||
|             merged.merge(bs.mesh); |             merged.merge(bs.mesh); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|         if(m_ctl.stopcondition()) { |         if(m_ctl.stopcondition()) { | ||||||
|             // In case of failure we have to return an empty mesh
 |             // In case of failure we have to return an empty mesh
 | ||||||
|             meshcache = TriangleMesh(); |             meshcache = TriangleMesh(); | ||||||
|  | @ -819,7 +906,7 @@ public: | ||||||
| 
 | 
 | ||||||
|         // The mesh will be passed by const-pointer to TriangleMeshSlicer,
 |         // The mesh will be passed by const-pointer to TriangleMeshSlicer,
 | ||||||
|         // which will need this.
 |         // which will need this.
 | ||||||
|         meshcache.require_shared_vertices(); |         if (!meshcache.empty()) meshcache.require_shared_vertices(); | ||||||
| 
 | 
 | ||||||
|         // TODO: Is this necessary?
 |         // TODO: Is this necessary?
 | ||||||
|         //meshcache.repair();
 |         //meshcache.repair();
 | ||||||
|  | @ -947,7 +1034,7 @@ class SLASupportTree::Algorithm { | ||||||
|     ThrowOnCancel m_thr; |     ThrowOnCancel m_thr; | ||||||
| 
 | 
 | ||||||
|     // A spatial index to easily find strong pillars to connect to.
 |     // A spatial index to easily find strong pillars to connect to.
 | ||||||
|     SpatIndex m_pillar_index; |     PointIndex m_pillar_index; | ||||||
| 
 | 
 | ||||||
|     inline double ray_mesh_intersect(const Vec3d& s, |     inline double ray_mesh_intersect(const Vec3d& s, | ||||||
|                                      const Vec3d& dir) |                                      const Vec3d& dir) | ||||||
|  | @ -1149,7 +1236,7 @@ class SLASupportTree::Algorithm { | ||||||
|             auto hr = m.query_ray_hit(p + sd*dir, dir); |             auto hr = m.query_ray_hit(p + sd*dir, dir); | ||||||
| 
 | 
 | ||||||
|             if(ins_check && hr.is_inside()) { |             if(ins_check && hr.is_inside()) { | ||||||
|                 if(hr.distance() > r + sd) hits[i] = HitResult(0.0); |                 if(hr.distance() > 2 * r + sd) hits[i] = HitResult(0.0); | ||||||
|                 else { |                 else { | ||||||
|                     // re-cast the ray from the outside of the object
 |                     // re-cast the ray from the outside of the object
 | ||||||
|                     auto hr2 = |                     auto hr2 = | ||||||
|  | @ -1265,7 +1352,10 @@ class SLASupportTree::Algorithm { | ||||||
|     // For connecting a head to a nearby pillar.
 |     // For connecting a head to a nearby pillar.
 | ||||||
|     bool connect_to_nearpillar(const Head& head, long nearpillar_id) { |     bool connect_to_nearpillar(const Head& head, long nearpillar_id) { | ||||||
|          |          | ||||||
|         auto nearpillar = [this, nearpillar_id]() { return m_result.pillar(nearpillar_id); }; |         auto nearpillar = [this, nearpillar_id]() { | ||||||
|  |             return m_result.pillar(nearpillar_id); | ||||||
|  |         }; | ||||||
|  |          | ||||||
|         if (nearpillar().bridges > m_cfg.max_bridges_on_pillar) return false; |         if (nearpillar().bridges > m_cfg.max_bridges_on_pillar) return false; | ||||||
| 
 | 
 | ||||||
|         Vec3d headjp = head.junction_point(); |         Vec3d headjp = head.junction_point(); | ||||||
|  | @ -1337,7 +1427,7 @@ class SLASupportTree::Algorithm { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     bool search_pillar_and_connect(const Head& head) { |     bool search_pillar_and_connect(const Head& head) { | ||||||
|         SpatIndex spindex = m_pillar_index; |         PointIndex spindex = m_pillar_index; | ||||||
| 
 | 
 | ||||||
|         long nearest_id = -1; |         long nearest_id = -1; | ||||||
| 
 | 
 | ||||||
|  | @ -1370,6 +1460,120 @@ class SLASupportTree::Algorithm { | ||||||
|         return nearest_id >= 0; |         return nearest_id >= 0; | ||||||
|     } |     } | ||||||
|      |      | ||||||
|  |     // This is a proxy function for pillar creation which will mind the gap
 | ||||||
|  |     // between the pad and the model bottom in zero elevation mode.
 | ||||||
|  |     void create_ground_pillar(const Vec3d &jp, | ||||||
|  |                               const Vec3d &sourcedir, | ||||||
|  |                               double       radius, | ||||||
|  |                               int          head_id = -1) | ||||||
|  |     { | ||||||
|  |         // People were killed for this number (seriously)
 | ||||||
|  |         static const double SQR2 = std::sqrt(2.0); | ||||||
|  |         static const Vec3d  DOWN = {0.0, 0.0, -1.0}; | ||||||
|  | 
 | ||||||
|  |         double gndlvl       = m_result.ground_level; | ||||||
|  |         Vec3d  endp         = {jp(X), jp(Y), gndlvl}; | ||||||
|  |         double sd           = m_cfg.pillar_base_safety_distance_mm; | ||||||
|  |         int    pillar_id    = -1; | ||||||
|  |         double min_dist     = sd + m_cfg.base_radius_mm + EPSILON; | ||||||
|  |         double dist         = 0; | ||||||
|  |         bool   can_add_base = true; | ||||||
|  |         bool   normal_mode  = true; | ||||||
|  | 
 | ||||||
|  |         if (m_cfg.object_elevation_mm < EPSILON | ||||||
|  |             && (dist = std::sqrt(m_mesh.squared_distance(endp))) < min_dist) { | ||||||
|  |             // Get the distance from the mesh. This can be later optimized
 | ||||||
|  |             // to get the distance in 2D plane because we are dealing with
 | ||||||
|  |             // the ground level only.
 | ||||||
|  | 
 | ||||||
|  |             normal_mode     = false; | ||||||
|  |             double mv       = min_dist - dist; | ||||||
|  |             double azimuth  = std::atan2(sourcedir(Y), sourcedir(X)); | ||||||
|  |             double sinpolar = std::sin(PI - m_cfg.bridge_slope); | ||||||
|  |             double cospolar = std::cos(PI - m_cfg.bridge_slope); | ||||||
|  |             double cosazm   = std::cos(azimuth); | ||||||
|  |             double sinazm   = std::sin(azimuth); | ||||||
|  | 
 | ||||||
|  |             auto dir = Vec3d(cosazm * sinpolar, sinazm * sinpolar, cospolar) | ||||||
|  |                            .normalized(); | ||||||
|  | 
 | ||||||
|  |             using namespace libnest2d::opt; | ||||||
|  |             StopCriteria scr; | ||||||
|  |             scr.stop_score = min_dist; | ||||||
|  |             SubplexOptimizer solver(scr); | ||||||
|  | 
 | ||||||
|  |             auto result = solver.optimize_max( | ||||||
|  |                 [this, dir, jp, gndlvl](double mv) { | ||||||
|  |                     Vec3d endp = jp + SQR2 * mv * dir; | ||||||
|  |                     endp(Z)    = gndlvl; | ||||||
|  |                     return std::sqrt(m_mesh.squared_distance(endp)); | ||||||
|  |                 }, | ||||||
|  |                 initvals(mv), bound(0.0, 2 * min_dist)); | ||||||
|  | 
 | ||||||
|  |             mv           = std::get<0>(result.optimum); | ||||||
|  |             endp         = jp + SQR2 * mv * dir; | ||||||
|  |             Vec3d pgnd   = {endp(X), endp(Y), gndlvl}; | ||||||
|  |             can_add_base = result.score > min_dist; | ||||||
|  |              | ||||||
|  |             double gnd_offs = m_mesh.ground_level_offset(); | ||||||
|  |             auto abort_in_shame = | ||||||
|  |                 [gnd_offs, &normal_mode, &can_add_base, &endp, jp, gndlvl]() | ||||||
|  |             { | ||||||
|  |                 normal_mode  = true; | ||||||
|  |                 can_add_base = false;   // Nothing left to do, hope for the best
 | ||||||
|  |                 endp         = {jp(X), jp(Y), gndlvl - gnd_offs }; | ||||||
|  |             }; | ||||||
|  | 
 | ||||||
|  |             // We have to check if the bridge is feasible.
 | ||||||
|  |             if (bridge_mesh_intersect(jp, dir, radius) < (endp - jp).norm()) | ||||||
|  |                 abort_in_shame(); | ||||||
|  |             else { | ||||||
|  |                 // If the new endpoint is below ground, do not make a pillar
 | ||||||
|  |                 if (endp(Z) < gndlvl) | ||||||
|  |                     endp = endp - SQR2 * (gndlvl - endp(Z)) * dir; // back off
 | ||||||
|  |                 else { | ||||||
|  |                      | ||||||
|  |                     auto hit = bridge_mesh_intersect(endp, DOWN, radius); | ||||||
|  |                     if (!std::isinf(hit.distance())) abort_in_shame(); | ||||||
|  | 
 | ||||||
|  |                     Pillar &plr = m_result.add_pillar(endp, pgnd, radius); | ||||||
|  | 
 | ||||||
|  |                     if (can_add_base) | ||||||
|  |                         plr.add_base(m_cfg.base_height_mm, | ||||||
|  |                                      m_cfg.base_radius_mm); | ||||||
|  | 
 | ||||||
|  |                     pillar_id = plr.id; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 m_result.add_bridge(jp, endp, radius); | ||||||
|  |                 m_result.add_junction(endp, radius); | ||||||
|  | 
 | ||||||
|  |                 // Add a degenerated pillar and the bridge.
 | ||||||
|  |                 // The degenerate pillar will have zero length and it will
 | ||||||
|  |                 // prevent from queries of head_pillar() to have non-existing
 | ||||||
|  |                 // pillar when the head should have one.
 | ||||||
|  |                 if (head_id >= 0) | ||||||
|  |                     m_result.add_pillar(unsigned(head_id), jp, radius); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         if (normal_mode) { | ||||||
|  |             Pillar &plr = head_id >= 0 | ||||||
|  |                               ? m_result.add_pillar(unsigned(head_id), | ||||||
|  |                                                     endp, | ||||||
|  |                                                     radius) | ||||||
|  |                               : m_result.add_pillar(jp, endp, radius); | ||||||
|  | 
 | ||||||
|  |             if (can_add_base) | ||||||
|  |                 plr.add_base(m_cfg.base_height_mm, m_cfg.base_radius_mm); | ||||||
|  | 
 | ||||||
|  |             pillar_id = plr.id; | ||||||
|  |         }  | ||||||
|  |              | ||||||
|  |         if(pillar_id >= 0) // Save the pillar endpoint in the spatial index
 | ||||||
|  |             m_pillar_index.insert(endp, pillar_id); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
| public: | public: | ||||||
| 
 | 
 | ||||||
|     Algorithm(const SupportConfig& config, |     Algorithm(const SupportConfig& config, | ||||||
|  | @ -1473,14 +1677,14 @@ public: | ||||||
|                                 std::cos(polar)).normalized(); |                                 std::cos(polar)).normalized(); | ||||||
| 
 | 
 | ||||||
|                 // check available distance
 |                 // check available distance
 | ||||||
|                 double t = pinhead_mesh_intersect( |                 EigenMesh3D::hit_result t | ||||||
|                                   hp, // touching point
 |                     = pinhead_mesh_intersect(hp, // touching point
 | ||||||
|                                              nn, // normal
 |                                              nn, // normal
 | ||||||
|                                              pin_r, |                                              pin_r, | ||||||
|                                              m_cfg.head_back_radius_mm, |                                              m_cfg.head_back_radius_mm, | ||||||
|                                              w); |                                              w); | ||||||
| 
 | 
 | ||||||
|                 if(t <= w) { |                 if(t.distance() <= w) { | ||||||
| 
 | 
 | ||||||
|                     // Let's try to optimize this angle, there might be a
 |                     // Let's try to optimize this angle, there might be a
 | ||||||
|                     // viable normal that doesn't collide with the model
 |                     // viable normal that doesn't collide with the model
 | ||||||
|  | @ -1523,12 +1727,17 @@ public: | ||||||
|                 // save the verified and corrected normal
 |                 // save the verified and corrected normal
 | ||||||
|                 m_support_nmls.row(fidx) = nn; |                 m_support_nmls.row(fidx) = nn; | ||||||
| 
 | 
 | ||||||
|                 if(t > w) { |                 if (t.distance() > w) { | ||||||
|  |                     // Check distance from ground, we might have zero elevation.
 | ||||||
|  |                     if (hp(Z) + w * nn(Z) < m_result.ground_level) { | ||||||
|  |                         m_iheadless.emplace_back(fidx); | ||||||
|  |                     } else { | ||||||
|                         // mark the point for needing a head.
 |                         // mark the point for needing a head.
 | ||||||
|                         m_iheads.emplace_back(fidx); |                         m_iheads.emplace_back(fidx); | ||||||
|  |                     } | ||||||
|                 } else if (polar >= 3 * PI / 4) { |                 } else if (polar >= 3 * PI / 4) { | ||||||
|                     // Headless supports do not tilt like the headed ones so
 |                     // Headless supports do not tilt like the headed ones
 | ||||||
|                     // the normal should point almost to the ground.
 |                     // so the normal should point almost to the ground.
 | ||||||
|                     m_iheadless.emplace_back(fidx); |                     m_iheadless.emplace_back(fidx); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|  | @ -1594,16 +1803,22 @@ public: | ||||||
|         // from each other in the XY plane to not cross their pillar bases
 |         // from each other in the XY plane to not cross their pillar bases
 | ||||||
|         // These clusters of support points will join in one pillar,
 |         // These clusters of support points will join in one pillar,
 | ||||||
|         // possibly in their centroid support point.
 |         // possibly in their centroid support point.
 | ||||||
|  |          | ||||||
|         auto pointfn = [this](unsigned i) { |         auto pointfn = [this](unsigned i) { | ||||||
|             return m_result.head(i).junction_point(); |             return m_result.head(i).junction_point(); | ||||||
|         }; |         }; | ||||||
|         auto predicate = [this](const SpatElement& e1, const SpatElement& e2) { | 
 | ||||||
|  |         auto predicate = [this](const PointIndexEl &e1, | ||||||
|  |                                 const PointIndexEl &e2) { | ||||||
|             double d2d = distance(to_2d(e1.first), to_2d(e2.first)); |             double d2d = distance(to_2d(e1.first), to_2d(e2.first)); | ||||||
|             double d3d = distance(e1.first, e2.first); |             double d3d = distance(e1.first, e2.first); | ||||||
|             return d2d < 2 * m_cfg.base_radius_mm && |             return d2d < 2 * m_cfg.base_radius_mm | ||||||
|                    d3d < m_cfg.max_bridge_length_mm; |                    && d3d < m_cfg.max_bridge_length_mm; | ||||||
|         }; |         }; | ||||||
|         m_pillar_clusters = cluster(ground_head_indices, pointfn, predicate, | 
 | ||||||
|  |         m_pillar_clusters = cluster(ground_head_indices, | ||||||
|  |                                     pointfn, | ||||||
|  |                                     predicate, | ||||||
|                                     m_cfg.max_bridges_on_pillar); |                                     m_cfg.max_bridges_on_pillar); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -1615,7 +1830,7 @@ public: | ||||||
|     void routing_to_ground() |     void routing_to_ground() | ||||||
|     { |     { | ||||||
|         const double pradius = m_cfg.head_back_radius_mm; |         const double pradius = m_cfg.head_back_radius_mm; | ||||||
|         const double gndlvl = m_result.ground_level; |         // const double gndlvl = m_result.ground_level;
 | ||||||
| 
 | 
 | ||||||
|         ClusterEl cl_centroids; |         ClusterEl cl_centroids; | ||||||
|         cl_centroids.reserve(m_pillar_clusters.size()); |         cl_centroids.reserve(m_pillar_clusters.size()); | ||||||
|  | @ -1648,13 +1863,8 @@ public: | ||||||
| 
 | 
 | ||||||
|             Head& h = m_result.head(hid); |             Head& h = m_result.head(hid); | ||||||
|             h.transform(); |             h.transform(); | ||||||
|             Vec3d p = h.junction_point(); p(Z) = gndlvl; |  | ||||||
|             auto& plr = m_result.add_pillar(hid, p, h.r_back_mm) |  | ||||||
|                                 .add_base(m_cfg.base_height_mm, |  | ||||||
|                                           m_cfg.base_radius_mm); |  | ||||||
| 
 | 
 | ||||||
|             // Save the pillar endpoint and the pillar id in the spatial index
 |             create_ground_pillar(h.junction_point(), h.dir, h.r_back_mm, h.id); | ||||||
|             m_pillar_index.insert(plr.endpoint(), unsigned(plr.id)); |  | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         // now we will go through the clusters ones again and connect the
 |         // now we will go through the clusters ones again and connect the
 | ||||||
|  | @ -1681,15 +1891,12 @@ public: | ||||||
|                    !search_pillar_and_connect(sidehead)) |                    !search_pillar_and_connect(sidehead)) | ||||||
|                 { |                 { | ||||||
|                     Vec3d pstart = sidehead.junction_point(); |                     Vec3d pstart = sidehead.junction_point(); | ||||||
|                     Vec3d pend = Vec3d{pstart(X), pstart(Y), gndlvl}; |                     //Vec3d pend = Vec3d{pstart(X), pstart(Y), gndlvl};
 | ||||||
|                     // Could not find a pillar, create one
 |                     // Could not find a pillar, create one
 | ||||||
|                     auto& pillar = m_result.add_pillar(unsigned(sidehead.id), |                     create_ground_pillar(pstart, | ||||||
|                                                        pend, pradius) |                                          sidehead.dir, | ||||||
|                                            .add_base(m_cfg.base_height_mm, |                                          pradius, | ||||||
|                                                      m_cfg.base_radius_mm); |                                          sidehead.id); | ||||||
| 
 |  | ||||||
|                     // connects to ground, eligible for bridging
 |  | ||||||
|                     m_pillar_index.insert(pend, unsigned(pillar.id)); |  | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  | @ -1718,12 +1925,7 @@ public: | ||||||
|             m_result.add_bridge(hjp, endp, head.r_back_mm); |             m_result.add_bridge(hjp, endp, head.r_back_mm); | ||||||
|             m_result.add_junction(endp, head.r_back_mm); |             m_result.add_junction(endp, head.r_back_mm); | ||||||
| 
 | 
 | ||||||
|             auto groundp = endp; |             this->create_ground_pillar(endp, dir, head.r_back_mm); | ||||||
|             groundp(Z) = m_result.ground_level; |  | ||||||
|             auto& newpillar = m_result.add_pillar(endp, groundp, head.r_back_mm) |  | ||||||
|                                       .add_base(m_cfg.base_height_mm, |  | ||||||
|                                                 m_cfg.base_radius_mm); |  | ||||||
|             m_pillar_index.insert(groundp, unsigned(newpillar.id)); |  | ||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|         std::vector<unsigned> modelpillars; |         std::vector<unsigned> modelpillars; | ||||||
|  | @ -1884,6 +2086,28 @@ public: | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|      |      | ||||||
|  |     // Helper function for interconnect_pillars where pairs of already connected
 | ||||||
|  |     // pillars should be checked for not to be processed again. This can be done
 | ||||||
|  |     // in O(log) or even constant time with a set or an unordered set of hash
 | ||||||
|  |     // values uniquely representing a pair of integers. The order of numbers
 | ||||||
|  |     // within the pair should not matter, it has the same unique hash.
 | ||||||
|  |     template<class I> static I pairhash(I a, I b) | ||||||
|  |     { | ||||||
|  |         using std::ceil; using std::log2; using std::max; using std::min; | ||||||
|  |          | ||||||
|  |         static_assert(std::is_integral<I>::value, | ||||||
|  |                       "This function works only for integral types."); | ||||||
|  | 
 | ||||||
|  |         I g = min(a, b), l = max(a, b); | ||||||
|  |          | ||||||
|  |         auto bits_g = g ? int(ceil(log2(g))) : 0; | ||||||
|  | 
 | ||||||
|  |         // Assume the hash will fit into the output variable
 | ||||||
|  |         assert((l ? (ceil(log2(l))) : 0) + bits_g < int(sizeof(I) * CHAR_BIT)); | ||||||
|  |          | ||||||
|  |         return (l << bits_g) + g; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     void interconnect_pillars() { |     void interconnect_pillars() { | ||||||
|         // Now comes the algorithm that connects pillars with each other.
 |         // Now comes the algorithm that connects pillars with each other.
 | ||||||
|         // Ideally every pillar should be connected with at least one of its
 |         // Ideally every pillar should be connected with at least one of its
 | ||||||
|  | @ -1901,44 +2125,50 @@ public: | ||||||
| 
 | 
 | ||||||
|         std::set<unsigned long> pairs; |         std::set<unsigned long> pairs; | ||||||
|          |          | ||||||
|  |         // A function to connect one pillar with its neighbors. THe number of
 | ||||||
|  |         // neighbors is given in the configuration. This function if called
 | ||||||
|  |         // for every pillar in the pillar index. A pair of pillar will not
 | ||||||
|  |         // be connected multiple times this is ensured by the 'pairs' set which
 | ||||||
|  |         // remembers the processed pillar pairs
 | ||||||
|         auto cascadefn = |         auto cascadefn = | ||||||
|                 [this, d, &pairs, min_height_ratio, H1] (const SpatElement& el) |                 [this, d, &pairs, min_height_ratio, H1] (const PointIndexEl& el) | ||||||
|         { |         { | ||||||
|             Vec3d qp = el.first; |             Vec3d qp = el.first;    // endpoint of the pillar
 | ||||||
| 
 | 
 | ||||||
|             const Pillar& pillar = m_result.pillar(el.second); |             const Pillar& pillar = m_result.pillar(el.second); // actual pillar
 | ||||||
|              |              | ||||||
|  |             // Get the max number of neighbors a pillar should connect to
 | ||||||
|             unsigned neighbors = m_cfg.pillar_cascade_neighbors; |             unsigned neighbors = m_cfg.pillar_cascade_neighbors; | ||||||
| 
 | 
 | ||||||
|             // connections are enough for one pillar
 |             // connections are already enough for the pillar
 | ||||||
|             if(pillar.links >= neighbors) return; |             if(pillar.links >= neighbors) return; | ||||||
| 
 | 
 | ||||||
|             // Query all remaining points within reach
 |             // Query all remaining points within reach
 | ||||||
|             auto qres = m_pillar_index.query([qp, d](const SpatElement& e){ |             auto qres = m_pillar_index.query([qp, d](const PointIndexEl& e){ | ||||||
|                 return distance(e.first, qp) < d; |                 return distance(e.first, qp) < d; | ||||||
|             }); |             }); | ||||||
| 
 | 
 | ||||||
|             // sort the result by distance (have to check if this is needed)
 |             // sort the result by distance (have to check if this is needed)
 | ||||||
|             std::sort(qres.begin(), qres.end(), |             std::sort(qres.begin(), qres.end(), | ||||||
|                       [qp](const SpatElement& e1, const SpatElement& e2){ |                       [qp](const PointIndexEl& e1, const PointIndexEl& e2){ | ||||||
|                 return distance(e1.first, qp) < distance(e2.first, qp); |                 return distance(e1.first, qp) < distance(e2.first, qp); | ||||||
|             }); |             }); | ||||||
| 
 | 
 | ||||||
|             for(auto& re : qres) { |             for(auto& re : qres) { // process the queried neighbors
 | ||||||
| 
 | 
 | ||||||
|                 if(re.second == el.second) continue; |                 if(re.second == el.second) continue; // Skip self
 | ||||||
| 
 | 
 | ||||||
|                 auto a = el.second, b = re.second; |                 auto a = el.second, b = re.second; | ||||||
| 
 | 
 | ||||||
|                 // I hope that the area of a square is never equal to its
 |                 // Get unique hash for the given pair (order doesn't matter)
 | ||||||
|                 // circumference
 |                 auto hashval = pairhash(a, b); | ||||||
|                 auto hashval = 2 * (a + b) + a * b; |  | ||||||
|                  |                  | ||||||
|  |                 // Search for the pair amongst the remembered pairs
 | ||||||
|                 if(pairs.find(hashval) != pairs.end()) continue; |                 if(pairs.find(hashval) != pairs.end()) continue; | ||||||
| 
 | 
 | ||||||
|                 const Pillar& neighborpillar = m_result.pillars()[re.second]; |                 const Pillar& neighborpillar = m_result.pillars()[re.second]; | ||||||
| 
 | 
 | ||||||
|                 // this neighbor is occupied
 |                 // this neighbor is occupied, skip
 | ||||||
|                 if(neighborpillar.links >= neighbors) continue; |                 if(neighborpillar.links >= neighbors) continue; | ||||||
| 
 | 
 | ||||||
|                 if(interconnect(pillar, neighborpillar)) { |                 if(interconnect(pillar, neighborpillar)) { | ||||||
|  | @ -1961,46 +2191,78 @@ public: | ||||||
|             } |             } | ||||||
|         }; |         }; | ||||||
|          |          | ||||||
|  |         // Run the cascade for the pillars in the index
 | ||||||
|         m_pillar_index.foreach(cascadefn); |         m_pillar_index.foreach(cascadefn); | ||||||
|         |         | ||||||
|  |         // We would be done here if we could allow some pillars to not be
 | ||||||
|  |         // connected with any neighbors. But this might leave the support tree
 | ||||||
|  |         // unprintable.
 | ||||||
|  |         //
 | ||||||
|  |         // The current solution is to insert additional pillars next to these
 | ||||||
|  |         // lonely pillars. One or even two additional pillar might get inserted
 | ||||||
|  |         // depending on the length of the lonely pillar.
 | ||||||
|  |          | ||||||
|         size_t pillarcount = m_result.pillars().size(); |         size_t pillarcount = m_result.pillars().size(); | ||||||
|          |          | ||||||
|  |         // Again, go through all pillars, this time in the whole support tree
 | ||||||
|  |         // not just the index.
 | ||||||
|         for(size_t pid = 0; pid < pillarcount; pid++) { |         for(size_t pid = 0; pid < pillarcount; pid++) { | ||||||
|             auto pillar = [this, pid]() { return m_result.pillar(pid); }; |             auto pillar = [this, pid]() { return m_result.pillar(pid); }; | ||||||
|             |             | ||||||
|  |             // Decide how many additional pillars will be needed:
 | ||||||
|  |              | ||||||
|             unsigned needpillars = 0; |             unsigned needpillars = 0; | ||||||
|             if(pillar().bridges > m_cfg.max_bridges_on_pillar) needpillars = 3; |             if (pillar().bridges > m_cfg.max_bridges_on_pillar) | ||||||
|  |                 needpillars = 3; | ||||||
|             else if (pillar().links < 2 && pillar().height > H2) { |             else if (pillar().links < 2 && pillar().height > H2) { | ||||||
|                 // Not enough neighbors to support this pillar
 |                 // Not enough neighbors to support this pillar
 | ||||||
|                 needpillars = 2 - pillar().links; |                 needpillars = 2 - pillar().links; | ||||||
|             } |             } else if (pillar().links < 1 && pillar().height > H1) { | ||||||
|             else if(pillar().links < 1 && pillar().height > H1) { |  | ||||||
|                 // No neighbors could be found and the pillar is too long.
 |                 // No neighbors could be found and the pillar is too long.
 | ||||||
|                 needpillars = 1; |                 needpillars = 1; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             // Search for new pillar locations
 |             // Search for new pillar locations:
 | ||||||
|  | 
 | ||||||
|             bool   found    = false; |             bool   found    = false; | ||||||
|             double alpha    = 0; // goes to 2Pi
 |             double alpha    = 0; // goes to 2Pi
 | ||||||
|             double r        = 2 * m_cfg.base_radius_mm; |             double r        = 2 * m_cfg.base_radius_mm; | ||||||
|             Vec3d  pillarsp = pillar().startpoint(); |             Vec3d  pillarsp = pillar().startpoint(); | ||||||
|  | 
 | ||||||
|  |             // temp value for starting point detection
 | ||||||
|             Vec3d sp(pillarsp(X), pillarsp(Y), pillarsp(Z) - r); |             Vec3d sp(pillarsp(X), pillarsp(Y), pillarsp(Z) - r); | ||||||
|             std::vector<bool> tv(needpillars, false); | 
 | ||||||
|             std::vector<Vec3d> spts(needpillars); |             // A vector of bool for placement feasbility
 | ||||||
|  |             std::vector<bool>  canplace(needpillars, false); | ||||||
|  |             std::vector<Vec3d> spts(needpillars); // vector of starting points
 | ||||||
|  | 
 | ||||||
|  |             double gnd      = m_result.ground_level; | ||||||
|  |             double min_dist = m_cfg.pillar_base_safety_distance_mm + | ||||||
|  |                               m_cfg.base_radius_mm + EPSILON; | ||||||
|              |              | ||||||
|             while(!found && alpha < 2*PI) { |             while(!found && alpha < 2*PI) { | ||||||
| 
 |                 for (unsigned n = 0; | ||||||
|                 for(unsigned n = 0; n < needpillars; n++) { |                      n < needpillars && (!n || canplace[n - 1]); | ||||||
|  |                      n++) | ||||||
|  |                 { | ||||||
|                     double a = alpha + n * PI / 3; |                     double a = alpha + n * PI / 3; | ||||||
|                     Vec3d  s = sp; |                     Vec3d  s = sp; | ||||||
|                     s(X) += std::cos(a) * r; |                     s(X) += std::cos(a) * r; | ||||||
|                     s(Y) += std::sin(a) * r; |                     s(Y) += std::sin(a) * r; | ||||||
|                     spts[n] = s; |                     spts[n] = s; | ||||||
|  |                      | ||||||
|  |                     // Check the path vertically down                    
 | ||||||
|                     auto hr = bridge_mesh_intersect(s, {0, 0, -1}, pillar().r); |                     auto hr = bridge_mesh_intersect(s, {0, 0, -1}, pillar().r); | ||||||
|                     tv[n] = std::isinf(hr.distance()); |                     Vec3d gndsp{s(X), s(Y), gnd}; | ||||||
|  |                      | ||||||
|  |                     // If the path is clear, check for pillar base collisions
 | ||||||
|  |                     canplace[n] = std::isinf(hr.distance()) && | ||||||
|  |                                   std::sqrt(m_mesh.squared_distance(gndsp)) > | ||||||
|  |                                       min_dist; | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|                 found = std::all_of(tv.begin(), tv.end(), [](bool v){return v;}); |                 found = std::all_of(canplace.begin(), canplace.end(), | ||||||
|  |                                     [](bool v) { return v; }); | ||||||
| 
 | 
 | ||||||
|                 // 20 angles will be tried...
 |                 // 20 angles will be tried...
 | ||||||
|                 alpha += 0.1 * PI; |                 alpha += 0.1 * PI; | ||||||
|  | @ -2010,7 +2272,7 @@ public: | ||||||
|             newpills.reserve(needpillars); |             newpills.reserve(needpillars); | ||||||
| 
 | 
 | ||||||
|             if(found) for(unsigned n = 0; n < needpillars; n++) { |             if(found) for(unsigned n = 0; n < needpillars; n++) { | ||||||
|                 Vec3d s = spts[n]; double gnd = m_result.ground_level; |                 Vec3d s = spts[n];  | ||||||
|                 Pillar p(s, Vec3d(s(X), s(Y), gnd), pillar().r); |                 Pillar p(s, Vec3d(s(X), s(Y), gnd), pillar().r); | ||||||
|                 p.add_base(m_cfg.base_height_mm, m_cfg.base_radius_mm); |                 p.add_base(m_cfg.base_height_mm, m_cfg.base_radius_mm); | ||||||
| 
 | 
 | ||||||
|  | @ -2075,9 +2337,13 @@ public: | ||||||
|             // This is only for checking
 |             // This is only for checking
 | ||||||
|             double idist = bridge_mesh_intersect(sph, dir, R, true); |             double idist = bridge_mesh_intersect(sph, dir, R, true); | ||||||
|             double dist = ray_mesh_intersect(sj, dir); |             double dist = ray_mesh_intersect(sj, dir); | ||||||
|  |             if (std::isinf(dist)) | ||||||
|  |                 dist = sph(Z) - m_mesh.ground_level() | ||||||
|  |                        + m_mesh.ground_level_offset(); | ||||||
| 
 | 
 | ||||||
|             if(std::isinf(idist) || std::isnan(idist) || idist < 2*R || |             if(std::isnan(idist) || idist < 2*R || | ||||||
|                std::isinf(dist)  || std::isnan(dist)   || dist < 2*R) { |                std::isnan(dist)  || dist  < 2*R) | ||||||
|  |             { | ||||||
|                 BOOST_LOG_TRIVIAL(warning) << "Can not find route for headless" |                 BOOST_LOG_TRIVIAL(warning) << "Can not find route for headless" | ||||||
|                                            << " support stick at: " |                                            << " support stick at: " | ||||||
|                                            << sj.transpose(); |                                            << sj.transpose(); | ||||||
|  | @ -2085,7 +2351,7 @@ public: | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             Vec3d ej = sj + (dist + HWIDTH_MM)* dir; |             Vec3d ej = sj + (dist + HWIDTH_MM)* dir; | ||||||
|             m_result.add_compact_bridge(sp, ej, n, R); |             m_result.add_compact_bridge(sp, ej, n, R, !std::isinf(dist)); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| }; | }; | ||||||
|  | @ -2214,7 +2480,9 @@ bool SLASupportTree::generate(const std::vector<SupportPoint> &support_points, | ||||||
|     return pc == ABORT; |     return pc == ABORT; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| SLASupportTree::SLASupportTree(): m_impl(new Impl()) {} | SLASupportTree::SLASupportTree(double gnd_lvl): m_impl(new Impl()) { | ||||||
|  |     m_impl->ground_level = gnd_lvl; | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| const TriangleMesh &SLASupportTree::merged_mesh() const | const TriangleMesh &SLASupportTree::merged_mesh() const | ||||||
| { | { | ||||||
|  | @ -2226,7 +2494,7 @@ void SLASupportTree::merged_mesh_with_pad(TriangleMesh &outmesh) const { | ||||||
|     outmesh.merge(get_pad()); |     outmesh.merge(get_pad()); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| SlicedSupports SLASupportTree::slice(float layerh, float init_layerh) const | std::vector<ExPolygons> SLASupportTree::slice(float layerh, float init_layerh) const | ||||||
| { | { | ||||||
|     if(init_layerh < 0) init_layerh = layerh; |     if(init_layerh < 0) init_layerh = layerh; | ||||||
|     auto& stree = get(); |     auto& stree = get(); | ||||||
|  | @ -2245,36 +2513,31 @@ SlicedSupports SLASupportTree::slice(float layerh, float init_layerh) const | ||||||
| 
 | 
 | ||||||
|     TriangleMesh fullmesh = m_impl->merged_mesh(); |     TriangleMesh fullmesh = m_impl->merged_mesh(); | ||||||
|     fullmesh.merge(get_pad()); |     fullmesh.merge(get_pad()); | ||||||
|     fullmesh.require_shared_vertices(); // TriangleMeshSlicer needs this
 |     if (!fullmesh.empty()) fullmesh.require_shared_vertices(); | ||||||
|     TriangleMeshSlicer slicer(&fullmesh); |     TriangleMeshSlicer slicer(&fullmesh); | ||||||
|     SlicedSupports ret; |     std::vector<ExPolygons> ret; | ||||||
|     slicer.slice(heights, 0.f, &ret, get().ctl().cancelfn); |     slicer.slice(heights, 0.f, &ret, get().ctl().cancelfn); | ||||||
| 
 | 
 | ||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| SlicedSupports SLASupportTree::slice(const std::vector<float> &heights, | std::vector<ExPolygons> SLASupportTree::slice(const std::vector<float> &heights, | ||||||
|                                      float cr) const |                                      float cr) const | ||||||
| { | { | ||||||
|     TriangleMesh fullmesh = m_impl->merged_mesh(); |     TriangleMesh fullmesh = m_impl->merged_mesh(); | ||||||
|     fullmesh.merge(get_pad()); |     fullmesh.merge(get_pad()); | ||||||
|     fullmesh.require_shared_vertices(); // TriangleMeshSlicer needs this
 |     if (!fullmesh.empty()) fullmesh.require_shared_vertices(); | ||||||
|     TriangleMeshSlicer slicer(&fullmesh); |     TriangleMeshSlicer slicer(&fullmesh); | ||||||
|     SlicedSupports ret; |     std::vector<ExPolygons> ret; | ||||||
|     slicer.slice(heights, cr, &ret, get().ctl().cancelfn); |     slicer.slice(heights, cr, &ret, get().ctl().cancelfn); | ||||||
| 
 | 
 | ||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const TriangleMesh &SLASupportTree::add_pad(const SliceLayer& baseplate, | const TriangleMesh &SLASupportTree::add_pad(const ExPolygons& modelbase, | ||||||
|                                             const PoolConfig& pcfg) const |                                             const PoolConfig& pcfg) const | ||||||
| { | { | ||||||
| //    PoolConfig pcfg;
 |     return m_impl->create_pad(merged_mesh(), modelbase, pcfg).tmesh; | ||||||
| //    pcfg.min_wall_thickness_mm = min_wall_thickness_mm;
 |  | ||||||
| //    pcfg.min_wall_height_mm    = min_wall_height_mm;
 |  | ||||||
| //    pcfg.max_merge_distance_mm = max_merge_distance_mm;
 |  | ||||||
| //    pcfg.edge_radius_mm        = edge_radius_mm;
 |  | ||||||
|     return m_impl->create_pad(merged_mesh(), baseplate, pcfg).tmesh; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const TriangleMesh &SLASupportTree::get_pad() const | const TriangleMesh &SLASupportTree::get_pad() const | ||||||
|  |  | ||||||
|  | @ -24,10 +24,11 @@ class TriangleMesh; | ||||||
| class Model; | class Model; | ||||||
| class ModelInstance; | class ModelInstance; | ||||||
| class ModelObject; | class ModelObject; | ||||||
|  | class Polygon; | ||||||
| class ExPolygon; | class ExPolygon; | ||||||
| 
 | 
 | ||||||
| using SliceLayer = std::vector<ExPolygon>; | using Polygons = std::vector<Polygon>; | ||||||
| using SlicedSupports = std::vector<SliceLayer>; | using ExPolygons = std::vector<ExPolygon>; | ||||||
| 
 | 
 | ||||||
| namespace sla { | namespace sla { | ||||||
| 
 | 
 | ||||||
|  | @ -81,6 +82,10 @@ struct SupportConfig { | ||||||
|     // and the model object's bounding box bottom.
 |     // and the model object's bounding box bottom.
 | ||||||
|     double object_elevation_mm = 10; |     double object_elevation_mm = 10; | ||||||
|      |      | ||||||
|  |     // The shortest distance between a pillar base perimeter from the model
 | ||||||
|  |     // body. This is only useful when elevation is set to zero.
 | ||||||
|  |     double pillar_base_safety_distance_mm = 0.5; | ||||||
|  | 
 | ||||||
|     // /////////////////////////////////////////////////////////////////////////
 |     // /////////////////////////////////////////////////////////////////////////
 | ||||||
|     // Compile time configuration values (candidates for runtime)
 |     // Compile time configuration values (candidates for runtime)
 | ||||||
|     // /////////////////////////////////////////////////////////////////////////
 |     // /////////////////////////////////////////////////////////////////////////
 | ||||||
|  | @ -160,7 +165,7 @@ class SLASupportTree { | ||||||
| 
 | 
 | ||||||
| public: | public: | ||||||
| 
 | 
 | ||||||
|     SLASupportTree(); |     SLASupportTree(double ground_level = 0.0); | ||||||
| 
 | 
 | ||||||
|     SLASupportTree(const std::vector<SupportPoint>& pts, |     SLASupportTree(const std::vector<SupportPoint>& pts, | ||||||
|                    const EigenMesh3D& em, |                    const EigenMesh3D& em, | ||||||
|  | @ -179,12 +184,17 @@ public: | ||||||
|     void merged_mesh_with_pad(TriangleMesh&) const; |     void merged_mesh_with_pad(TriangleMesh&) const; | ||||||
| 
 | 
 | ||||||
|     /// Get the sliced 2d layers of the support geometry.
 |     /// Get the sliced 2d layers of the support geometry.
 | ||||||
|     SlicedSupports slice(float layerh, float init_layerh = -1.0) const; |     std::vector<ExPolygons> slice(float layerh, float init_layerh = -1.0) const; | ||||||
| 
 | 
 | ||||||
|     SlicedSupports slice(const std::vector<float>&, float closing_radius) const; |     std::vector<ExPolygons> slice(const std::vector<float> &, | ||||||
|  |                                   float closing_radius) const; | ||||||
| 
 | 
 | ||||||
|     /// Adding the "pad" (base pool) under the supports
 |     /// Adding the "pad" (base pool) under the supports
 | ||||||
|     const TriangleMesh& add_pad(const SliceLayer& baseplate, |     /// modelbase will be used according to the embed_object flag in PoolConfig.
 | ||||||
|  |     /// If set, the plate will interpreted as the model's intrinsic pad. 
 | ||||||
|  |     /// Otherwise, the modelbase will be unified with the base plate calculated
 | ||||||
|  |     /// from the supports.
 | ||||||
|  |     const TriangleMesh& add_pad(const ExPolygons& modelbase, | ||||||
|                                 const PoolConfig& pcfg) const; |                                 const PoolConfig& pcfg) const; | ||||||
| 
 | 
 | ||||||
|     /// Get the pad geometry
 |     /// Get the pad geometry
 | ||||||
|  |  | ||||||
|  | @ -29,69 +29,137 @@ namespace sla { | ||||||
| using igl::PI; | using igl::PI; | ||||||
| 
 | 
 | ||||||
| /* **************************************************************************
 | /* **************************************************************************
 | ||||||
|  * SpatIndex implementation |  * PointIndex implementation | ||||||
|  * ************************************************************************** */ |  * ************************************************************************** */ | ||||||
| 
 | 
 | ||||||
| class SpatIndex::Impl { | class PointIndex::Impl { | ||||||
| public: | public: | ||||||
|     using BoostIndex = boost::geometry::index::rtree< SpatElement, |     using BoostIndex = boost::geometry::index::rtree< PointIndexEl, | ||||||
|                        boost::geometry::index::rstar<16, 4> /* ? */ >; |                        boost::geometry::index::rstar<16, 4> /* ? */ >; | ||||||
| 
 | 
 | ||||||
|     BoostIndex m_store; |     BoostIndex m_store; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| SpatIndex::SpatIndex(): m_impl(new Impl()) {} | PointIndex::PointIndex(): m_impl(new Impl()) {} | ||||||
| SpatIndex::~SpatIndex() {} | PointIndex::~PointIndex() {} | ||||||
| 
 | 
 | ||||||
| SpatIndex::SpatIndex(const SpatIndex &cpy): m_impl(new Impl(*cpy.m_impl)) {} | PointIndex::PointIndex(const PointIndex &cpy): m_impl(new Impl(*cpy.m_impl)) {} | ||||||
| SpatIndex::SpatIndex(SpatIndex&& cpy): m_impl(std::move(cpy.m_impl)) {} | PointIndex::PointIndex(PointIndex&& cpy): m_impl(std::move(cpy.m_impl)) {} | ||||||
| 
 | 
 | ||||||
| SpatIndex& SpatIndex::operator=(const SpatIndex &cpy) | PointIndex& PointIndex::operator=(const PointIndex &cpy) | ||||||
| { | { | ||||||
|     m_impl.reset(new Impl(*cpy.m_impl)); |     m_impl.reset(new Impl(*cpy.m_impl)); | ||||||
|     return *this; |     return *this; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| SpatIndex& SpatIndex::operator=(SpatIndex &&cpy) | PointIndex& PointIndex::operator=(PointIndex &&cpy) | ||||||
| { | { | ||||||
|     m_impl.swap(cpy.m_impl); |     m_impl.swap(cpy.m_impl); | ||||||
|     return *this; |     return *this; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void SpatIndex::insert(const SpatElement &el) | void PointIndex::insert(const PointIndexEl &el) | ||||||
| { | { | ||||||
|     m_impl->m_store.insert(el); |     m_impl->m_store.insert(el); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool SpatIndex::remove(const SpatElement& el) | bool PointIndex::remove(const PointIndexEl& el) | ||||||
| { | { | ||||||
|     return m_impl->m_store.remove(el) == 1; |     return m_impl->m_store.remove(el) == 1; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| std::vector<SpatElement> | std::vector<PointIndexEl> | ||||||
| SpatIndex::query(std::function<bool(const SpatElement &)> fn) | PointIndex::query(std::function<bool(const PointIndexEl &)> fn) | ||||||
| { | { | ||||||
|     namespace bgi = boost::geometry::index; |     namespace bgi = boost::geometry::index; | ||||||
| 
 | 
 | ||||||
|     std::vector<SpatElement> ret; |     std::vector<PointIndexEl> ret; | ||||||
|     m_impl->m_store.query(bgi::satisfies(fn), std::back_inserter(ret)); |     m_impl->m_store.query(bgi::satisfies(fn), std::back_inserter(ret)); | ||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| std::vector<SpatElement> SpatIndex::nearest(const Vec3d &el, unsigned k = 1) | std::vector<PointIndexEl> PointIndex::nearest(const Vec3d &el, unsigned k = 1) | ||||||
| { | { | ||||||
|     namespace bgi = boost::geometry::index; |     namespace bgi = boost::geometry::index; | ||||||
|     std::vector<SpatElement> ret; ret.reserve(k); |     std::vector<PointIndexEl> ret; ret.reserve(k); | ||||||
|     m_impl->m_store.query(bgi::nearest(el, k), std::back_inserter(ret)); |     m_impl->m_store.query(bgi::nearest(el, k), std::back_inserter(ret)); | ||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| size_t SpatIndex::size() const | size_t PointIndex::size() const | ||||||
| { | { | ||||||
|     return m_impl->m_store.size(); |     return m_impl->m_store.size(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void SpatIndex::foreach(std::function<void (const SpatElement &)> fn) | void PointIndex::foreach(std::function<void (const PointIndexEl &)> fn) | ||||||
|  | { | ||||||
|  |     for(auto& el : m_impl->m_store) fn(el); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* **************************************************************************
 | ||||||
|  |  * BoxIndex implementation | ||||||
|  |  * ************************************************************************** */ | ||||||
|  | 
 | ||||||
|  | class BoxIndex::Impl { | ||||||
|  | public: | ||||||
|  |     using BoostIndex = boost::geometry::index:: | ||||||
|  |         rtree<BoxIndexEl, boost::geometry::index::rstar<16, 4> /* ? */>; | ||||||
|  | 
 | ||||||
|  |     BoostIndex m_store; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | BoxIndex::BoxIndex(): m_impl(new Impl()) {} | ||||||
|  | BoxIndex::~BoxIndex() {} | ||||||
|  | 
 | ||||||
|  | BoxIndex::BoxIndex(const BoxIndex &cpy): m_impl(new Impl(*cpy.m_impl)) {} | ||||||
|  | BoxIndex::BoxIndex(BoxIndex&& cpy): m_impl(std::move(cpy.m_impl)) {} | ||||||
|  | 
 | ||||||
|  | BoxIndex& BoxIndex::operator=(const BoxIndex &cpy) | ||||||
|  | { | ||||||
|  |     m_impl.reset(new Impl(*cpy.m_impl)); | ||||||
|  |     return *this; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | BoxIndex& BoxIndex::operator=(BoxIndex &&cpy) | ||||||
|  | { | ||||||
|  |     m_impl.swap(cpy.m_impl); | ||||||
|  |     return *this; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void BoxIndex::insert(const BoxIndexEl &el) | ||||||
|  | { | ||||||
|  |     m_impl->m_store.insert(el); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool BoxIndex::remove(const BoxIndexEl& el) | ||||||
|  | { | ||||||
|  |     return m_impl->m_store.remove(el) == 1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | std::vector<BoxIndexEl> BoxIndex::query(const BoundingBox &qrbb, | ||||||
|  |                                         BoxIndex::QueryType qt) | ||||||
|  | { | ||||||
|  |     namespace bgi = boost::geometry::index; | ||||||
|  |      | ||||||
|  |     std::vector<BoxIndexEl> ret; ret.reserve(m_impl->m_store.size()); | ||||||
|  |      | ||||||
|  |     switch (qt) { | ||||||
|  |     case qtIntersects: | ||||||
|  |         m_impl->m_store.query(bgi::intersects(qrbb), std::back_inserter(ret)); | ||||||
|  |         break; | ||||||
|  |     case qtWithin: | ||||||
|  |         m_impl->m_store.query(bgi::within(qrbb), std::back_inserter(ret)); | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | size_t BoxIndex::size() const | ||||||
|  | { | ||||||
|  |     return m_impl->m_store.size(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void BoxIndex::foreach(std::function<void (const BoxIndexEl &)> fn) | ||||||
| { | { | ||||||
|     for(auto& el : m_impl->m_store) fn(el); |     for(auto& el : m_impl->m_store) fn(el); | ||||||
| } | } | ||||||
|  | @ -343,12 +411,14 @@ PointSet normals(const PointSet& points, | ||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
| namespace bgi = boost::geometry::index; | namespace bgi = boost::geometry::index; | ||||||
| using Index3D = bgi::rtree< SpatElement, bgi::rstar<16, 4> /* ? */ >; | using Index3D = bgi::rtree< PointIndexEl, bgi::rstar<16, 4> /* ? */ >; | ||||||
| 
 | 
 | ||||||
| ClusteredPoints cluster(Index3D& sindex, unsigned max_points, | ClusteredPoints cluster(Index3D &sindex, | ||||||
|                         std::function<std::vector<SpatElement>(const Index3D&, const SpatElement&)> qfn) |                         unsigned max_points, | ||||||
|  |                         std::function<std::vector<PointIndexEl>( | ||||||
|  |                             const Index3D &, const PointIndexEl &)> qfn) | ||||||
| { | { | ||||||
|     using Elems = std::vector<SpatElement>; |     using Elems = std::vector<PointIndexEl>; | ||||||
| 
 | 
 | ||||||
|     // Recursive function for visiting all the points in a given distance to
 |     // Recursive function for visiting all the points in a given distance to
 | ||||||
|     // each other
 |     // each other
 | ||||||
|  | @ -356,8 +426,8 @@ ClusteredPoints cluster(Index3D& sindex, unsigned max_points, | ||||||
|     [&sindex, &group, max_points, qfn](Elems& pts, Elems& cluster) |     [&sindex, &group, max_points, qfn](Elems& pts, Elems& cluster) | ||||||
|     { |     { | ||||||
|         for(auto& p : pts) { |         for(auto& p : pts) { | ||||||
|             std::vector<SpatElement> tmp = qfn(sindex, p); |             std::vector<PointIndexEl> tmp = qfn(sindex, p); | ||||||
|             auto cmp = [](const SpatElement& e1, const SpatElement& e2){ |             auto cmp = [](const PointIndexEl& e1, const PointIndexEl& e2){ | ||||||
|                 return e1.second < e2.second; |                 return e1.second < e2.second; | ||||||
|             }; |             }; | ||||||
| 
 | 
 | ||||||
|  | @ -401,12 +471,12 @@ ClusteredPoints cluster(Index3D& sindex, unsigned max_points, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| namespace { | namespace { | ||||||
| std::vector<SpatElement> distance_queryfn(const Index3D& sindex, | std::vector<PointIndexEl> distance_queryfn(const Index3D& sindex, | ||||||
|                                           const SpatElement& p, |                                           const PointIndexEl& p, | ||||||
|                                           double dist, |                                           double dist, | ||||||
|                                           unsigned max_points) |                                           unsigned max_points) | ||||||
| { | { | ||||||
|     std::vector<SpatElement> tmp; tmp.reserve(max_points); |     std::vector<PointIndexEl> tmp; tmp.reserve(max_points); | ||||||
|     sindex.query( |     sindex.query( | ||||||
|         bgi::nearest(p.first, max_points), |         bgi::nearest(p.first, max_points), | ||||||
|         std::back_inserter(tmp) |         std::back_inserter(tmp) | ||||||
|  | @ -433,7 +503,7 @@ ClusteredPoints cluster( | ||||||
|     for(auto idx : indices) sindex.insert( std::make_pair(pointfn(idx), idx)); |     for(auto idx : indices) sindex.insert( std::make_pair(pointfn(idx), idx)); | ||||||
| 
 | 
 | ||||||
|     return cluster(sindex, max_points, |     return cluster(sindex, max_points, | ||||||
|                    [dist, max_points](const Index3D& sidx, const SpatElement& p) |                    [dist, max_points](const Index3D& sidx, const PointIndexEl& p) | ||||||
|     { |     { | ||||||
|         return distance_queryfn(sidx, p, dist, max_points); |         return distance_queryfn(sidx, p, dist, max_points); | ||||||
|     }); |     }); | ||||||
|  | @ -443,7 +513,7 @@ ClusteredPoints cluster( | ||||||
| ClusteredPoints cluster( | ClusteredPoints cluster( | ||||||
|         const std::vector<unsigned>& indices, |         const std::vector<unsigned>& indices, | ||||||
|         std::function<Vec3d(unsigned)> pointfn, |         std::function<Vec3d(unsigned)> pointfn, | ||||||
|         std::function<bool(const SpatElement&, const SpatElement&)> predicate, |         std::function<bool(const PointIndexEl&, const PointIndexEl&)> predicate, | ||||||
|         unsigned max_points) |         unsigned max_points) | ||||||
| { | { | ||||||
|     // A spatial index for querying the nearest points
 |     // A spatial index for querying the nearest points
 | ||||||
|  | @ -453,10 +523,10 @@ ClusteredPoints cluster( | ||||||
|     for(auto idx : indices) sindex.insert( std::make_pair(pointfn(idx), idx)); |     for(auto idx : indices) sindex.insert( std::make_pair(pointfn(idx), idx)); | ||||||
| 
 | 
 | ||||||
|     return cluster(sindex, max_points, |     return cluster(sindex, max_points, | ||||||
|         [max_points, predicate](const Index3D& sidx, const SpatElement& p) |         [max_points, predicate](const Index3D& sidx, const PointIndexEl& p) | ||||||
|     { |     { | ||||||
|         std::vector<SpatElement> tmp; tmp.reserve(max_points); |         std::vector<PointIndexEl> tmp; tmp.reserve(max_points); | ||||||
|         sidx.query(bgi::satisfies([p, predicate](const SpatElement& e){ |         sidx.query(bgi::satisfies([p, predicate](const PointIndexEl& e){ | ||||||
|             return predicate(p, e); |             return predicate(p, e); | ||||||
|         }), std::back_inserter(tmp)); |         }), std::back_inserter(tmp)); | ||||||
|         return tmp; |         return tmp; | ||||||
|  | @ -473,7 +543,7 @@ ClusteredPoints cluster(const PointSet& pts, double dist, unsigned max_points) | ||||||
|         sindex.insert(std::make_pair(Vec3d(pts.row(i)), unsigned(i))); |         sindex.insert(std::make_pair(Vec3d(pts.row(i)), unsigned(i))); | ||||||
| 
 | 
 | ||||||
|     return cluster(sindex, max_points, |     return cluster(sindex, max_points, | ||||||
|                    [dist, max_points](const Index3D& sidx, const SpatElement& p) |                    [dist, max_points](const Index3D& sidx, const PointIndexEl& p) | ||||||
|     { |     { | ||||||
|         return distance_queryfn(sidx, p, dist, max_points); |         return distance_queryfn(sidx, p, dist, max_points); | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|  | @ -32,10 +32,9 @@ class SLAPrintObject::SupportData | ||||||
| { | { | ||||||
| public: | public: | ||||||
|     sla::EigenMesh3D emesh;              // index-triangle representation
 |     sla::EigenMesh3D emesh;              // index-triangle representation
 | ||||||
|     std::vector<sla::SupportPoint> |     std::vector<sla::SupportPoint> support_points;     // all the support points (manual/auto)
 | ||||||
|                    support_points;      // all the support points (manual/auto)
 |  | ||||||
|     SupportTreePtr                 support_tree_ptr;   // the supports
 |     SupportTreePtr                 support_tree_ptr;   // the supports
 | ||||||
|     SlicedSupports support_slices;      // sliced supports
 |     std::vector<ExPolygons>        support_slices;     // sliced supports
 | ||||||
| 
 | 
 | ||||||
|     inline SupportData(const TriangleMesh &trmesh) : emesh(trmesh) {} |     inline SupportData(const TriangleMesh &trmesh) : emesh(trmesh) {} | ||||||
| }; | }; | ||||||
|  | @ -441,12 +440,10 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, const DynamicPrintConf | ||||||
|         update_apply_status(this->invalidate_all_steps()); |         update_apply_status(this->invalidate_all_steps()); | ||||||
|         m_objects = print_objects_new; |         m_objects = print_objects_new; | ||||||
|         // Delete the PrintObjects marked as Unknown or Deleted.
 |         // Delete the PrintObjects marked as Unknown or Deleted.
 | ||||||
|         bool deleted_objects = false; |  | ||||||
|         for (auto &pos : print_object_status) |         for (auto &pos : print_object_status) | ||||||
|             if (pos.status == PrintObjectStatus::Unknown || pos.status == PrintObjectStatus::Deleted) { |             if (pos.status == PrintObjectStatus::Unknown || pos.status == PrintObjectStatus::Deleted) { | ||||||
|                 update_apply_status(pos.print_object->invalidate_all_steps()); |                 update_apply_status(pos.print_object->invalidate_all_steps()); | ||||||
|                 delete pos.print_object; |                 delete pos.print_object; | ||||||
|                 deleted_objects = true; |  | ||||||
|             } |             } | ||||||
|         if (new_objects) |         if (new_objects) | ||||||
|             update_apply_status(false); |             update_apply_status(false); | ||||||
|  | @ -473,7 +470,7 @@ void SLAPrint::set_task(const TaskParams ¶ms) | ||||||
| 
 | 
 | ||||||
|     int n_object_steps = int(params.to_object_step) + 1; |     int n_object_steps = int(params.to_object_step) + 1; | ||||||
|     if (n_object_steps == 0) |     if (n_object_steps == 0) | ||||||
|         n_object_steps = (int)slaposCount; |         n_object_steps = int(slaposCount); | ||||||
| 
 | 
 | ||||||
|     if (params.single_model_object.valid()) { |     if (params.single_model_object.valid()) { | ||||||
|         // Find the print object to be processed with priority.
 |         // Find the print object to be processed with priority.
 | ||||||
|  | @ -488,7 +485,7 @@ void SLAPrint::set_task(const TaskParams ¶ms) | ||||||
|         // Find out whether the priority print object is being currently processed.
 |         // Find out whether the priority print object is being currently processed.
 | ||||||
|         bool running = false; |         bool running = false; | ||||||
|         for (int istep = 0; istep < n_object_steps; ++ istep) { |         for (int istep = 0; istep < n_object_steps; ++ istep) { | ||||||
|             if (! print_object->m_stepmask[istep]) |             if (! print_object->m_stepmask[size_t(istep)]) | ||||||
|                 // Step was skipped, cancel.
 |                 // Step was skipped, cancel.
 | ||||||
|                 break; |                 break; | ||||||
|             if (print_object->is_step_started_unguarded(SLAPrintObjectStep(istep))) { |             if (print_object->is_step_started_unguarded(SLAPrintObjectStep(istep))) { | ||||||
|  | @ -504,7 +501,7 @@ void SLAPrint::set_task(const TaskParams ¶ms) | ||||||
|         if (params.single_model_instance_only) { |         if (params.single_model_instance_only) { | ||||||
|             // Suppress all the steps of other instances.
 |             // Suppress all the steps of other instances.
 | ||||||
|             for (SLAPrintObject *po : m_objects) |             for (SLAPrintObject *po : m_objects) | ||||||
|                 for (int istep = 0; istep < (int)slaposCount; ++ istep) |                 for (size_t istep = 0; istep < slaposCount; ++ istep) | ||||||
|                     po->m_stepmask[istep] = false; |                     po->m_stepmask[istep] = false; | ||||||
|         } else if (! running) { |         } else if (! running) { | ||||||
|             // Swap the print objects, so that the selected print_object is first in the row.
 |             // Swap the print objects, so that the selected print_object is first in the row.
 | ||||||
|  | @ -514,15 +511,15 @@ void SLAPrint::set_task(const TaskParams ¶ms) | ||||||
|         } |         } | ||||||
|         // and set the steps for the current object.
 |         // and set the steps for the current object.
 | ||||||
|         for (int istep = 0; istep < n_object_steps; ++ istep) |         for (int istep = 0; istep < n_object_steps; ++ istep) | ||||||
|             print_object->m_stepmask[istep] = true; |             print_object->m_stepmask[size_t(istep)] = true; | ||||||
|         for (int istep = n_object_steps; istep < (int)slaposCount; ++ istep) |         for (int istep = n_object_steps; istep < int(slaposCount); ++ istep) | ||||||
|             print_object->m_stepmask[istep] = false; |             print_object->m_stepmask[size_t(istep)] = false; | ||||||
|     } else { |     } else { | ||||||
|         // Slicing all objects.
 |         // Slicing all objects.
 | ||||||
|         bool running = false; |         bool running = false; | ||||||
|         for (SLAPrintObject *print_object : m_objects) |         for (SLAPrintObject *print_object : m_objects) | ||||||
|             for (int istep = 0; istep < n_object_steps; ++ istep) { |             for (int istep = 0; istep < n_object_steps; ++ istep) { | ||||||
|                 if (! print_object->m_stepmask[istep]) { |                 if (! print_object->m_stepmask[size_t(istep)]) { | ||||||
|                     // Step may have been skipped. Restart.
 |                     // Step may have been skipped. Restart.
 | ||||||
|                     goto loop_end; |                     goto loop_end; | ||||||
|                 } |                 } | ||||||
|  | @ -538,8 +535,8 @@ void SLAPrint::set_task(const TaskParams ¶ms) | ||||||
|             this->call_cancel_callback(); |             this->call_cancel_callback(); | ||||||
|         for (SLAPrintObject *po : m_objects) { |         for (SLAPrintObject *po : m_objects) { | ||||||
|             for (int istep = 0; istep < n_object_steps; ++ istep) |             for (int istep = 0; istep < n_object_steps; ++ istep) | ||||||
|                 po->m_stepmask[istep] = true; |                 po->m_stepmask[size_t(istep)] = true; | ||||||
|             for (int istep = n_object_steps; istep < (int)slaposCount; ++ istep) |             for (auto istep = size_t(n_object_steps); istep < slaposCount; ++ istep) | ||||||
|                 po->m_stepmask[istep] = false; |                 po->m_stepmask[istep] = false; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | @ -557,9 +554,9 @@ void SLAPrint::set_task(const TaskParams ¶ms) | ||||||
| void SLAPrint::finalize() | void SLAPrint::finalize() | ||||||
| { | { | ||||||
|     for (SLAPrintObject *po : m_objects) |     for (SLAPrintObject *po : m_objects) | ||||||
|         for (int istep = 0; istep < (int)slaposCount; ++ istep) |         for (size_t istep = 0; istep < slaposCount; ++ istep) | ||||||
|             po->m_stepmask[istep] = true; |             po->m_stepmask[istep] = true; | ||||||
|     for (int istep = 0; istep < (int)slapsCount; ++ istep) |     for (size_t istep = 0; istep < slapsCount; ++ istep) | ||||||
|         m_stepmask[istep] = true; |         m_stepmask[istep] = true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -597,21 +594,48 @@ sla::SupportConfig make_support_cfg(const SLAPrintObjectConfig& c) { | ||||||
|     scfg.pillar_widening_factor = c.support_pillar_widening_factor.getFloat(); |     scfg.pillar_widening_factor = c.support_pillar_widening_factor.getFloat(); | ||||||
|     scfg.base_radius_mm = 0.5*c.support_base_diameter.getFloat(); |     scfg.base_radius_mm = 0.5*c.support_base_diameter.getFloat(); | ||||||
|     scfg.base_height_mm = c.support_base_height.getFloat(); |     scfg.base_height_mm = c.support_base_height.getFloat(); | ||||||
|  |     scfg.pillar_base_safety_distance_mm = | ||||||
|  |         c.support_base_safety_distance.getFloat() < EPSILON ? | ||||||
|  |         scfg.safety_distance_mm : c.support_base_safety_distance.getFloat(); | ||||||
|      |      | ||||||
|     return scfg; |     return scfg; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | sla::PoolConfig::EmbedObject builtin_pad_cfg(const SLAPrintObjectConfig& c) { | ||||||
|  |     sla::PoolConfig::EmbedObject ret; | ||||||
|  |      | ||||||
|  |     ret.enabled = c.support_object_elevation.getFloat() <= EPSILON && | ||||||
|  |                   c.pad_enable.getBool() && c.supports_enable.getBool(); | ||||||
|  |      | ||||||
|  |     if(ret.enabled) { | ||||||
|  |         ret.object_gap_mm        = c.pad_object_gap.getFloat(); | ||||||
|  |         ret.stick_width_mm       = c.pad_object_connector_width.getFloat(); | ||||||
|  |         ret.stick_stride_mm      = c.pad_object_connector_stride.getFloat(); | ||||||
|  |         ret.stick_penetration_mm = c.pad_object_connector_penetration | ||||||
|  |                                     .getFloat(); | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| sla::PoolConfig make_pool_config(const SLAPrintObjectConfig& c) { | sla::PoolConfig make_pool_config(const SLAPrintObjectConfig& c) { | ||||||
|     sla::PoolConfig pcfg; |     sla::PoolConfig pcfg; | ||||||
| 
 | 
 | ||||||
|     pcfg.min_wall_thickness_mm = c.pad_wall_thickness.getFloat(); |     pcfg.min_wall_thickness_mm = c.pad_wall_thickness.getFloat(); | ||||||
|     pcfg.wall_slope = c.pad_wall_slope.getFloat(); |     pcfg.wall_slope = c.pad_wall_slope.getFloat() * PI / 180.0; | ||||||
|     pcfg.edge_radius_mm = c.pad_edge_radius.getFloat(); |      | ||||||
|  |     // We do not support radius for now
 | ||||||
|  |     pcfg.edge_radius_mm = 0.0; //c.pad_edge_radius.getFloat();
 | ||||||
|  |      | ||||||
|     pcfg.max_merge_distance_mm = c.pad_max_merge_distance.getFloat(); |     pcfg.max_merge_distance_mm = c.pad_max_merge_distance.getFloat(); | ||||||
|     pcfg.min_wall_height_mm = c.pad_wall_height.getFloat(); |     pcfg.min_wall_height_mm = c.pad_wall_height.getFloat(); | ||||||
| 
 | 
 | ||||||
|  |     // set builtin pad implicitly ON
 | ||||||
|  |     pcfg.embed_object = builtin_pad_cfg(c); | ||||||
|  |      | ||||||
|     return pcfg; |     return pcfg; | ||||||
| } | } | ||||||
|  | 
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| std::string SLAPrint::validate() const | std::string SLAPrint::validate() const | ||||||
|  | @ -635,8 +659,20 @@ std::string SLAPrint::validate() const | ||||||
|                 2 * cfg.head_back_radius_mm - |                 2 * cfg.head_back_radius_mm - | ||||||
|                 cfg.head_penetration_mm; |                 cfg.head_penetration_mm; | ||||||
|          |          | ||||||
|         if(supports_en && pinhead_width > cfg.object_elevation_mm) |         double elv = cfg.object_elevation_mm; | ||||||
|  | 
 | ||||||
|  |         if(supports_en && elv > EPSILON && elv < pinhead_width ) | ||||||
|             return L("Elevation is too low for object."); |             return L("Elevation is too low for object."); | ||||||
|  |          | ||||||
|  |         sla::PoolConfig::EmbedObject builtinpad = builtin_pad_cfg(po->config()); | ||||||
|  |         if(supports_en && builtinpad.enabled && | ||||||
|  |            cfg.pillar_base_safety_distance_mm < builtinpad.object_gap_mm) { | ||||||
|  |             return L( | ||||||
|  |                 "The endings of the support pillars will be deployed on the " | ||||||
|  |                 "gap between the object and the pad. 'Support base safety " | ||||||
|  |                 "distance' has to be greater than the 'Pad object gap' " | ||||||
|  |                 "parameter to avoid this."); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return ""; |     return ""; | ||||||
|  | @ -751,17 +787,26 @@ void SLAPrint::process() | ||||||
|              |              | ||||||
|             mit->set_model_slice_idx(po, id); ++mit; |             mit->set_model_slice_idx(po, id); ++mit; | ||||||
|         } |         } | ||||||
|  | 
 | ||||||
|  |         if(po.m_config.supports_enable.getBool() || | ||||||
|  |            po.m_config.pad_enable.getBool()) | ||||||
|  |         { | ||||||
|  |             po.m_supportdata.reset( | ||||||
|  |                 new SLAPrintObject::SupportData(po.transformed_mesh()) ); | ||||||
|  |         } | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     // In this step we check the slices, identify island and cover them with
 |     // In this step we check the slices, identify island and cover them with
 | ||||||
|     // support points. Then we sprinkle the rest of the mesh.
 |     // support points. Then we sprinkle the rest of the mesh.
 | ||||||
|     auto support_points = [this, ostepd](SLAPrintObject& po) { |     auto support_points = [this, ostepd](SLAPrintObject& po) { | ||||||
|         const ModelObject& mo = *po.m_model_object; |         // If supports are disabled, we can skip the model scan.
 | ||||||
|  |         if(!po.m_config.supports_enable.getBool()) return; | ||||||
|  | 
 | ||||||
|  |         if (!po.m_supportdata) | ||||||
|             po.m_supportdata.reset( |             po.m_supportdata.reset( | ||||||
|                 new SLAPrintObject::SupportData(po.transformed_mesh())); |                 new SLAPrintObject::SupportData(po.transformed_mesh())); | ||||||
| 
 | 
 | ||||||
|         // If supports are disabled, we can skip the model scan.
 |         const ModelObject& mo = *po.m_model_object; | ||||||
|         if(!po.m_config.supports_enable.getBool()) return; |  | ||||||
| 
 | 
 | ||||||
|         BOOST_LOG_TRIVIAL(debug) << "Support point count " |         BOOST_LOG_TRIVIAL(debug) << "Support point count " | ||||||
|                                  << mo.sla_support_points.size(); |                                  << mo.sla_support_points.size(); | ||||||
|  | @ -771,7 +816,7 @@ void SLAPrint::process() | ||||||
|         // into the backend cache.
 |         // into the backend cache.
 | ||||||
|         if (mo.sla_points_status != sla::PointsStatus::UserModified) { |         if (mo.sla_points_status != sla::PointsStatus::UserModified) { | ||||||
| 
 | 
 | ||||||
|             // Hypotetical use of the slice index:
 |             // Hypothetical use of the slice index:
 | ||||||
|             // auto bb = po.transformed_mesh().bounding_box();
 |             // auto bb = po.transformed_mesh().bounding_box();
 | ||||||
|             // auto range = po.get_slice_records(bb.min(Z));
 |             // auto range = po.get_slice_records(bb.min(Z));
 | ||||||
|             // std::vector<float> heights; heights.reserve(range.size());
 |             // std::vector<float> heights; heights.reserve(range.size());
 | ||||||
|  | @ -820,13 +865,40 @@ void SLAPrint::process() | ||||||
|             BOOST_LOG_TRIVIAL(debug) << "Automatic support points: " |             BOOST_LOG_TRIVIAL(debug) << "Automatic support points: " | ||||||
|                                      << po.m_supportdata->support_points.size(); |                                      << po.m_supportdata->support_points.size(); | ||||||
| 
 | 
 | ||||||
|             // Using RELOAD_SLA_SUPPORT_POINTS to tell the Plater to pass the update status to GLGizmoSlaSupports
 |             // Using RELOAD_SLA_SUPPORT_POINTS to tell the Plater to pass
 | ||||||
|             m_report_status(*this, -1, L("Generating support points"), SlicingStatus::RELOAD_SLA_SUPPORT_POINTS); |             // the update status to GLGizmoSlaSupports
 | ||||||
|  |             m_report_status(*this, | ||||||
|  |                             -1, | ||||||
|  |                             L("Generating support points"), | ||||||
|  |                             SlicingStatus::RELOAD_SLA_SUPPORT_POINTS); | ||||||
|         } |         } | ||||||
|         else { |         else { | ||||||
|             // There are either some points on the front-end, or the user removed them on purpose. No calculation will be done.
 |             // There are either some points on the front-end, or the user
 | ||||||
|  |             // removed them on purpose. No calculation will be done.
 | ||||||
|             po.m_supportdata->support_points = po.transformed_support_points(); |             po.m_supportdata->support_points = po.transformed_support_points(); | ||||||
|         } |         } | ||||||
|  |          | ||||||
|  |         // If the zero elevation mode is engaged, we have to filter out all the
 | ||||||
|  |         // points that are on the bottom of the object
 | ||||||
|  |         if (po.config().support_object_elevation.getFloat() <= EPSILON) { | ||||||
|  |             double gnd       = po.m_supportdata->emesh.ground_level(); | ||||||
|  |             auto & pts       = po.m_supportdata->support_points; | ||||||
|  |             double tolerance = po.config().pad_enable.getBool() | ||||||
|  |                                    ? po.m_config.pad_wall_thickness.getFloat() | ||||||
|  |                                    : po.m_config.support_base_height.getFloat(); | ||||||
|  | 
 | ||||||
|  |             // get iterator to the reorganized vector end
 | ||||||
|  |             auto endit = std::remove_if( | ||||||
|  |                 pts.begin(), | ||||||
|  |                 pts.end(), | ||||||
|  |                 [tolerance, gnd](const sla::SupportPoint &sp) { | ||||||
|  |                     double diff = std::abs(gnd - double(sp.pos(Z))); | ||||||
|  |                     return diff <= tolerance; | ||||||
|  |                 }); | ||||||
|  |              | ||||||
|  |             // erase all elements after the new end
 | ||||||
|  |             pts.erase(endit, pts.end()); | ||||||
|  |         } | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     // In this step we create the supports
 |     // In this step we create the supports
 | ||||||
|  | @ -834,9 +906,18 @@ void SLAPrint::process() | ||||||
|     { |     { | ||||||
|         if(!po.m_supportdata) return; |         if(!po.m_supportdata) return; | ||||||
|          |          | ||||||
|  |         sla::PoolConfig pcfg = make_pool_config(po.m_config); | ||||||
|  | 
 | ||||||
|  |         if (pcfg.embed_object) | ||||||
|  |             po.m_supportdata->emesh.ground_level_offset( | ||||||
|  |                 pcfg.min_wall_thickness_mm); | ||||||
|  | 
 | ||||||
|         if(!po.m_config.supports_enable.getBool()) { |         if(!po.m_config.supports_enable.getBool()) { | ||||||
|  |              | ||||||
|             // Generate empty support tree. It can still host a pad
 |             // Generate empty support tree. It can still host a pad
 | ||||||
|             po.m_supportdata->support_tree_ptr.reset(new SLASupportTree()); |             po.m_supportdata->support_tree_ptr.reset( | ||||||
|  |                     new SLASupportTree(po.m_supportdata->emesh.ground_level())); | ||||||
|  |              | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  | @ -888,40 +969,33 @@ void SLAPrint::process() | ||||||
|         // and before the supports had been sliced. (or the slicing has to be
 |         // and before the supports had been sliced. (or the slicing has to be
 | ||||||
|         // repeated)
 |         // repeated)
 | ||||||
| 
 | 
 | ||||||
|         if(!po.m_supportdata || !po.m_supportdata->support_tree_ptr) { |  | ||||||
|             BOOST_LOG_TRIVIAL(error) << "Uninitialized support data at " |  | ||||||
|                                      << "pad creation."; |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if(po.m_config.pad_enable.getBool()) |         if(po.m_config.pad_enable.getBool()) | ||||||
|         { |         { | ||||||
|             double wt = po.m_config.pad_wall_thickness.getFloat(); |             // Get the distilled pad configuration from the config
 | ||||||
|             double h =  po.m_config.pad_wall_height.getFloat(); |             sla::PoolConfig pcfg = make_pool_config(po.m_config); | ||||||
|             double md = po.m_config.pad_max_merge_distance.getFloat(); |  | ||||||
|             // Radius is disabled for now...
 |  | ||||||
|             double er = 0; // po.m_config.pad_edge_radius.getFloat();
 |  | ||||||
|             double tilt = po.m_config.pad_wall_slope.getFloat()  * PI / 180.0; |  | ||||||
|             double lh = po.m_config.layer_height.getFloat(); |  | ||||||
|             double elevation = po.m_config.support_object_elevation.getFloat(); |  | ||||||
|             if(!po.m_config.supports_enable.getBool()) elevation = 0; |  | ||||||
|             sla::PoolConfig pcfg(wt, h, md, er, tilt); |  | ||||||
| 
 | 
 | ||||||
|             ExPolygons bp; |             ExPolygons bp; // This will store the base plate of the pad.
 | ||||||
|             double   pad_h             = sla::get_pad_fullheight(pcfg); |             double   pad_h             = sla::get_pad_fullheight(pcfg); | ||||||
|             auto&& trmesh = po.transformed_mesh(); |             const TriangleMesh &trmesh = po.transformed_mesh(); | ||||||
| 
 | 
 | ||||||
|             // This call can get pretty time consuming
 |             // This call can get pretty time consuming
 | ||||||
|             auto thrfn = [this](){ throw_if_canceled(); }; |             auto thrfn = [this](){ throw_if_canceled(); }; | ||||||
| 
 | 
 | ||||||
|             if(elevation < pad_h) { |             if (!po.m_config.supports_enable.getBool() || pcfg.embed_object) { | ||||||
|                 // we have to count with the model geometry for the base plate
 |                 // No support (thus no elevation) or zero elevation mode
 | ||||||
|                 sla::base_plate(trmesh, bp, float(pad_h), float(lh), thrfn); |                 // we sometimes call it "builtin pad" is enabled so we will
 | ||||||
|  |                 // get a sample from the bottom of the mesh and use it for pad
 | ||||||
|  |                 // creation.
 | ||||||
|  |                 sla::base_plate(trmesh, | ||||||
|  |                                 bp, | ||||||
|  |                                 float(pad_h), | ||||||
|  |                                 float(po.m_config.layer_height.getFloat()), | ||||||
|  |                                 thrfn); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             pcfg.throw_on_cancel = thrfn; |             pcfg.throw_on_cancel = thrfn; | ||||||
|             po.m_supportdata->support_tree_ptr->add_pad(bp, pcfg); |             po.m_supportdata->support_tree_ptr->add_pad(bp, pcfg); | ||||||
|         } else { |         } else if(po.m_supportdata && po.m_supportdata->support_tree_ptr) { | ||||||
|             po.m_supportdata->support_tree_ptr->remove_pad(); |             po.m_supportdata->support_tree_ptr->remove_pad(); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  | @ -938,6 +1012,11 @@ void SLAPrint::process() | ||||||
| 
 | 
 | ||||||
|         if(sd) sd->support_slices.clear(); |         if(sd) sd->support_slices.clear(); | ||||||
| 
 | 
 | ||||||
|  |         // Don't bother if no supports and no pad is present.
 | ||||||
|  |         if (!po.m_config.supports_enable.getBool() && | ||||||
|  |             !po.m_config.pad_enable.getBool()) | ||||||
|  |             return; | ||||||
|  | 
 | ||||||
|         if(sd && sd->support_tree_ptr) { |         if(sd && sd->support_tree_ptr) { | ||||||
| 
 | 
 | ||||||
|             std::vector<float> heights; heights.reserve(po.m_slice_index.size()); |             std::vector<float> heights; heights.reserve(po.m_slice_index.size()); | ||||||
|  | @ -964,7 +1043,8 @@ void SLAPrint::process() | ||||||
|             po.m_slice_index[i].set_support_slice_idx(po, i); |             po.m_slice_index[i].set_support_slice_idx(po, i); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         // Using RELOAD_SLA_PREVIEW to tell the Plater to pass the update status to the 3D preview to load the SLA slices.
 |         // Using RELOAD_SLA_PREVIEW to tell the Plater to pass the update
 | ||||||
|  |         // status to the 3D preview to load the SLA slices.
 | ||||||
|         m_report_status(*this, -2, "", SlicingStatus::RELOAD_SLA_PREVIEW); |         m_report_status(*this, -2, "", SlicingStatus::RELOAD_SLA_PREVIEW); | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|  | @ -1536,14 +1616,17 @@ bool SLAPrint::is_step_done(SLAPrintObjectStep step) const | ||||||
|     return true; |     return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| SLAPrintObject::SLAPrintObject(SLAPrint *print, ModelObject *model_object): | SLAPrintObject::SLAPrintObject(SLAPrint *print, ModelObject *model_object) | ||||||
|     Inherited(print, model_object), |     : Inherited(print, model_object) | ||||||
|     m_stepmask(slaposCount, true), |     , m_stepmask(slaposCount, true) | ||||||
|     m_transformed_rmesh( [this](TriangleMesh& obj){ |     , m_transformed_rmesh([this](TriangleMesh &obj) { | ||||||
|             obj = m_model_object->raw_mesh(); obj.transform(m_trafo); obj.require_shared_vertices(); |         obj = m_model_object->raw_mesh(); | ||||||
|         }) |         if (!obj.empty()) { | ||||||
| { |             obj.transform(m_trafo); | ||||||
|  |             obj.require_shared_vertices(); | ||||||
|         } |         } | ||||||
|  |     }) | ||||||
|  | {} | ||||||
| 
 | 
 | ||||||
| SLAPrintObject::~SLAPrintObject() {} | SLAPrintObject::~SLAPrintObject() {} | ||||||
| 
 | 
 | ||||||
|  | @ -1582,13 +1665,19 @@ bool SLAPrintObject::invalidate_state_by_config_options(const std::vector<t_conf | ||||||
|             || opt_key == "support_critical_angle" |             || opt_key == "support_critical_angle" | ||||||
|             || opt_key == "support_max_bridge_length" |             || opt_key == "support_max_bridge_length" | ||||||
|             || opt_key == "support_max_pillar_link_distance" |             || opt_key == "support_max_pillar_link_distance" | ||||||
|  |             || opt_key == "support_base_safety_distance" | ||||||
|             ) { |             ) { | ||||||
|             steps.emplace_back(slaposSupportTree); |             steps.emplace_back(slaposSupportTree); | ||||||
|         } else if ( |         } else if ( | ||||||
|                opt_key == "pad_wall_height" |                opt_key == "pad_wall_height" | ||||||
|             || opt_key == "pad_max_merge_distance" |             || opt_key == "pad_max_merge_distance" | ||||||
|             || opt_key == "pad_wall_slope" |             || opt_key == "pad_wall_slope" | ||||||
|             || opt_key == "pad_edge_radius") { |             || opt_key == "pad_edge_radius" | ||||||
|  |             || opt_key == "pad_object_gap" | ||||||
|  |             || opt_key == "pad_object_connector_stride" | ||||||
|  |             || opt_key == "pad_object_connector_width" | ||||||
|  |             || opt_key == "pad_object_connector_penetration" | ||||||
|  |             ) { | ||||||
|             steps.emplace_back(slaposBasePool); |             steps.emplace_back(slaposBasePool); | ||||||
|         } else { |         } else { | ||||||
|             // All keys should be covered.
 |             // All keys should be covered.
 | ||||||
|  | @ -1629,17 +1718,16 @@ bool SLAPrintObject::invalidate_all_steps() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| double SLAPrintObject::get_elevation() const { | double SLAPrintObject::get_elevation() const { | ||||||
|     bool se = m_config.supports_enable.getBool(); |     bool   en  = m_config.supports_enable.getBool(); | ||||||
|     double ret = se? m_config.support_object_elevation.getFloat() : 0; |     double ret = en ? m_config.support_object_elevation.getFloat() : 0.; | ||||||
| 
 | 
 | ||||||
|     // if the pad is enabled, then half of the pad height is its base plate
 |  | ||||||
|     if(m_config.pad_enable.getBool()) { |     if(m_config.pad_enable.getBool()) { | ||||||
|         // Normally the elevation for the pad itself would be the thickness of
 |         // Normally the elevation for the pad itself would be the thickness of
 | ||||||
|         // its walls but currently it is half of its thickness. Whatever it
 |         // its walls but currently it is half of its thickness. Whatever it
 | ||||||
|         // will be in the future, we provide the config to the get_pad_elevation
 |         // will be in the future, we provide the config to the get_pad_elevation
 | ||||||
|         // method and we will have the correct value
 |         // method and we will have the correct value
 | ||||||
|         sla::PoolConfig pcfg = make_pool_config(m_config); |         sla::PoolConfig pcfg = make_pool_config(m_config); | ||||||
|         ret += sla::get_pad_elevation(pcfg); |         if(!pcfg.embed_object) ret += sla::get_pad_elevation(pcfg); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return ret; |     return ret; | ||||||
|  | @ -1647,14 +1735,14 @@ double SLAPrintObject::get_elevation() const { | ||||||
| 
 | 
 | ||||||
| double SLAPrintObject::get_current_elevation() const | double SLAPrintObject::get_current_elevation() const | ||||||
| { | { | ||||||
|     bool se = m_config.supports_enable.getBool(); |  | ||||||
|     bool has_supports = is_step_done(slaposSupportTree); |     bool has_supports = is_step_done(slaposSupportTree); | ||||||
|     bool has_pad = is_step_done(slaposBasePool); |     bool has_pad = is_step_done(slaposBasePool); | ||||||
| 
 | 
 | ||||||
|     if(!has_supports && !has_pad) |     if(!has_supports && !has_pad) | ||||||
|         return 0; |         return 0; | ||||||
|     else if(has_supports && !has_pad) |     else if(has_supports && !has_pad) { | ||||||
|         return se ? m_config.support_object_elevation.getFloat() : 0; |         return m_config.support_object_elevation.getFloat(); | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     return get_elevation(); |     return get_elevation(); | ||||||
| } | } | ||||||
|  | @ -1682,13 +1770,14 @@ namespace { // dummy empty static containers for return values in some methods | ||||||
| const std::vector<ExPolygons> EMPTY_SLICES; | const std::vector<ExPolygons> EMPTY_SLICES; | ||||||
| const TriangleMesh EMPTY_MESH; | const TriangleMesh EMPTY_MESH; | ||||||
| const ExPolygons EMPTY_SLICE; | const ExPolygons EMPTY_SLICE; | ||||||
|  | const std::vector<sla::SupportPoint> EMPTY_SUPPORT_POINTS; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const SliceRecord SliceRecord::EMPTY(0, std::nanf(""), 0.f); | const SliceRecord SliceRecord::EMPTY(0, std::nanf(""), 0.f); | ||||||
| 
 | 
 | ||||||
| const std::vector<sla::SupportPoint>& SLAPrintObject::get_support_points() const | const std::vector<sla::SupportPoint>& SLAPrintObject::get_support_points() const | ||||||
| { | { | ||||||
|     return m_supportdata->support_points; |     return m_supportdata? m_supportdata->support_points : EMPTY_SUPPORT_POINTS; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const std::vector<ExPolygons> &SLAPrintObject::get_support_slices() const | const std::vector<ExPolygons> &SLAPrintObject::get_support_slices() const | ||||||
|  |  | ||||||
|  | @ -41,13 +41,4 @@ | ||||||
| #define ENABLE_TEXTURES_FROM_SVG (1 && ENABLE_1_42_0_ALPHA7) | #define ENABLE_TEXTURES_FROM_SVG (1 && ENABLE_1_42_0_ALPHA7) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| //====================
 |  | ||||||
| // 1.42.0.alpha8 techs
 |  | ||||||
| //====================
 |  | ||||||
| #define ENABLE_1_42_0_ALPHA8 1 |  | ||||||
| 
 |  | ||||||
| // Toolbars and Gizmos use icons imported from svg files
 |  | ||||||
| #define ENABLE_SVG_ICONS (1 && ENABLE_1_42_0_ALPHA8 && ENABLE_TEXTURES_FROM_SVG) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| #endif // _technologies_h_
 | #endif // _technologies_h_
 | ||||||
|  |  | ||||||
|  | @ -410,6 +410,8 @@ public: | ||||||
|     BoundingBoxf3        transformed_convex_hull_bounding_box(const Transform3d &trafo) const; |     BoundingBoxf3        transformed_convex_hull_bounding_box(const Transform3d &trafo) const; | ||||||
|     // caching variant
 |     // caching variant
 | ||||||
|     const BoundingBoxf3& transformed_convex_hull_bounding_box() const; |     const BoundingBoxf3& transformed_convex_hull_bounding_box() const; | ||||||
|  |     // convex hull
 | ||||||
|  |     const TriangleMesh*  convex_hull() const { return m_convex_hull.get(); } | ||||||
| 
 | 
 | ||||||
|     bool                empty() const { return this->indexed_vertex_array.empty(); } |     bool                empty() const { return this->indexed_vertex_array.empty(); } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -229,6 +229,33 @@ std::string AppConfig::get_last_dir() const | ||||||
|     return std::string(); |     return std::string(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | std::vector<std::string> AppConfig::get_recent_projects() const | ||||||
|  | { | ||||||
|  |     std::vector<std::string> ret; | ||||||
|  |     const auto it = m_storage.find("recent_projects"); | ||||||
|  |     if (it != m_storage.end()) | ||||||
|  |     { | ||||||
|  |         for (const std::map<std::string, std::string>::value_type& item : it->second) | ||||||
|  |         { | ||||||
|  |             ret.push_back(item.second); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void AppConfig::set_recent_projects(const std::vector<std::string>& recent_projects) | ||||||
|  | { | ||||||
|  |     auto it = m_storage.find("recent_projects"); | ||||||
|  |     if (it == m_storage.end()) | ||||||
|  |         it = m_storage.insert(std::map<std::string, std::map<std::string, std::string>>::value_type("recent_projects", std::map<std::string, std::string>())).first; | ||||||
|  | 
 | ||||||
|  |     it->second.clear(); | ||||||
|  |     for (unsigned int i = 0; i < (unsigned int)recent_projects.size(); ++i) | ||||||
|  |     { | ||||||
|  |         it->second[std::to_string(i + 1)] = recent_projects[i]; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void AppConfig::update_config_dir(const std::string &dir) | void AppConfig::update_config_dir(const std::string &dir) | ||||||
| { | { | ||||||
|     this->set("recent", "config_directory", dir); |     this->set("recent", "config_directory", dir); | ||||||
|  |  | ||||||
|  | @ -122,6 +122,9 @@ public: | ||||||
| 	// Does the config file exist?
 | 	// Does the config file exist?
 | ||||||
| 	static bool 		exists(); | 	static bool 		exists(); | ||||||
| 
 | 
 | ||||||
|  |     std::vector<std::string> get_recent_projects() const; | ||||||
|  |     void set_recent_projects(const std::vector<std::string>& recent_projects); | ||||||
|  | 
 | ||||||
| private: | private: | ||||||
| 	// Map of section, name -> value
 | 	// Map of section, name -> value
 | ||||||
| 	std::map<std::string, std::map<std::string, std::string>> 	m_storage; | 	std::map<std::string, std::map<std::string, std::string>> 	m_storage; | ||||||
|  |  | ||||||
|  | @ -1199,11 +1199,7 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, Bed3D& bed, Camera& camera, GLToolbar | ||||||
|     , m_bed(bed) |     , m_bed(bed) | ||||||
|     , m_camera(camera) |     , m_camera(camera) | ||||||
|     , m_view_toolbar(view_toolbar) |     , m_view_toolbar(view_toolbar) | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     , m_toolbar(GLToolbar::Normal, "Top") |     , m_toolbar(GLToolbar::Normal, "Top") | ||||||
| #else |  | ||||||
|     , m_toolbar(GLToolbar::Normal) |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
|     , m_use_clipping_planes(false) |     , m_use_clipping_planes(false) | ||||||
|     , m_sidebar_field("") |     , m_sidebar_field("") | ||||||
|     , m_keep_dirty(false) |     , m_keep_dirty(false) | ||||||
|  | @ -3422,12 +3418,6 @@ bool GLCanvas3D::_init_toolbar() | ||||||
|     if (!m_toolbar.is_enabled()) |     if (!m_toolbar.is_enabled()) | ||||||
|         return true; |         return true; | ||||||
| 
 | 
 | ||||||
| #if !ENABLE_SVG_ICONS |  | ||||||
|     ItemsIconsTexture::Metadata icons_data; |  | ||||||
|     icons_data.filename = "toolbar.png"; |  | ||||||
|     icons_data.icon_size = 37; |  | ||||||
| #endif // !ENABLE_SVG_ICONS
 |  | ||||||
| 
 |  | ||||||
|     BackgroundTexture::Metadata background_data; |     BackgroundTexture::Metadata background_data; | ||||||
|     background_data.filename = "toolbar_background.png"; |     background_data.filename = "toolbar_background.png"; | ||||||
|     background_data.left = 16; |     background_data.left = 16; | ||||||
|  | @ -3435,11 +3425,7 @@ bool GLCanvas3D::_init_toolbar() | ||||||
|     background_data.right = 16; |     background_data.right = 16; | ||||||
|     background_data.bottom = 16; |     background_data.bottom = 16; | ||||||
| 
 | 
 | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     if (!m_toolbar.init(background_data)) |     if (!m_toolbar.init(background_data)) | ||||||
| #else |  | ||||||
|     if (!m_toolbar.init(icons_data, background_data)) |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
|     { |     { | ||||||
|         // unable to init the toolbar texture, disable it
 |         // unable to init the toolbar texture, disable it
 | ||||||
|         m_toolbar.set_enabled(false); |         m_toolbar.set_enabled(false); | ||||||
|  | @ -3456,9 +3442,7 @@ bool GLCanvas3D::_init_toolbar() | ||||||
|     GLToolbarItem::Data item; |     GLToolbarItem::Data item; | ||||||
| 
 | 
 | ||||||
|     item.name = "add"; |     item.name = "add"; | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     item.icon_filename = "add.svg"; |     item.icon_filename = "add.svg"; | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
|     item.tooltip = _utf8(L("Add...")) + " [" + GUI::shortkey_ctrl_prefix() + "I]"; |     item.tooltip = _utf8(L("Add...")) + " [" + GUI::shortkey_ctrl_prefix() + "I]"; | ||||||
|     item.sprite_id = 0; |     item.sprite_id = 0; | ||||||
|     item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_ADD)); }; |     item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_ADD)); }; | ||||||
|  | @ -3466,9 +3450,7 @@ bool GLCanvas3D::_init_toolbar() | ||||||
|         return false; |         return false; | ||||||
| 
 | 
 | ||||||
|     item.name = "delete"; |     item.name = "delete"; | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     item.icon_filename = "remove.svg"; |     item.icon_filename = "remove.svg"; | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
|     item.tooltip = _utf8(L("Delete")) + " [Del]"; |     item.tooltip = _utf8(L("Delete")) + " [Del]"; | ||||||
|     item.sprite_id = 1; |     item.sprite_id = 1; | ||||||
|     item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_DELETE)); }; |     item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_DELETE)); }; | ||||||
|  | @ -3477,9 +3459,7 @@ bool GLCanvas3D::_init_toolbar() | ||||||
|         return false; |         return false; | ||||||
| 
 | 
 | ||||||
|     item.name = "deleteall"; |     item.name = "deleteall"; | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     item.icon_filename = "delete_all.svg"; |     item.icon_filename = "delete_all.svg"; | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
|     item.tooltip = _utf8(L("Delete all")) + " [" + GUI::shortkey_ctrl_prefix() + "Del]"; |     item.tooltip = _utf8(L("Delete all")) + " [" + GUI::shortkey_ctrl_prefix() + "Del]"; | ||||||
|     item.sprite_id = 2; |     item.sprite_id = 2; | ||||||
|     item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_DELETE_ALL)); }; |     item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_DELETE_ALL)); }; | ||||||
|  | @ -3488,9 +3468,7 @@ bool GLCanvas3D::_init_toolbar() | ||||||
|         return false; |         return false; | ||||||
| 
 | 
 | ||||||
|     item.name = "arrange"; |     item.name = "arrange"; | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     item.icon_filename = "arrange.svg"; |     item.icon_filename = "arrange.svg"; | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
|     item.tooltip = _utf8(L("Arrange")) + " [A]"; |     item.tooltip = _utf8(L("Arrange")) + " [A]"; | ||||||
|     item.sprite_id = 3; |     item.sprite_id = 3; | ||||||
|     item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_ARRANGE)); }; |     item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_ARRANGE)); }; | ||||||
|  | @ -3502,9 +3480,7 @@ bool GLCanvas3D::_init_toolbar() | ||||||
|         return false; |         return false; | ||||||
| 
 | 
 | ||||||
|     item.name = "copy"; |     item.name = "copy"; | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     item.icon_filename = "copy.svg"; |     item.icon_filename = "copy.svg"; | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
|     item.tooltip = _utf8(L("Copy")) + " [" + GUI::shortkey_ctrl_prefix() + "C]"; |     item.tooltip = _utf8(L("Copy")) + " [" + GUI::shortkey_ctrl_prefix() + "C]"; | ||||||
|     item.sprite_id = 4; |     item.sprite_id = 4; | ||||||
|     item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_COPY)); }; |     item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_COPY)); }; | ||||||
|  | @ -3513,9 +3489,7 @@ bool GLCanvas3D::_init_toolbar() | ||||||
|         return false; |         return false; | ||||||
| 
 | 
 | ||||||
|     item.name = "paste"; |     item.name = "paste"; | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     item.icon_filename = "paste.svg"; |     item.icon_filename = "paste.svg"; | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
|     item.tooltip = _utf8(L("Paste")) + " [" + GUI::shortkey_ctrl_prefix() + "V]"; |     item.tooltip = _utf8(L("Paste")) + " [" + GUI::shortkey_ctrl_prefix() + "V]"; | ||||||
|     item.sprite_id = 5; |     item.sprite_id = 5; | ||||||
|     item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_PASTE)); }; |     item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_PASTE)); }; | ||||||
|  | @ -3527,9 +3501,7 @@ bool GLCanvas3D::_init_toolbar() | ||||||
|         return false; |         return false; | ||||||
| 
 | 
 | ||||||
|     item.name = "more"; |     item.name = "more"; | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     item.icon_filename = "instance_add.svg"; |     item.icon_filename = "instance_add.svg"; | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
|     item.tooltip = _utf8(L("Add instance")) + " [+]"; |     item.tooltip = _utf8(L("Add instance")) + " [+]"; | ||||||
|     item.sprite_id = 6; |     item.sprite_id = 6; | ||||||
|     item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_MORE)); }; |     item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_MORE)); }; | ||||||
|  | @ -3539,9 +3511,7 @@ bool GLCanvas3D::_init_toolbar() | ||||||
|         return false; |         return false; | ||||||
| 
 | 
 | ||||||
|     item.name = "fewer"; |     item.name = "fewer"; | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     item.icon_filename = "instance_remove.svg"; |     item.icon_filename = "instance_remove.svg"; | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
|     item.tooltip = _utf8(L("Remove instance")) + " [-]"; |     item.tooltip = _utf8(L("Remove instance")) + " [-]"; | ||||||
|     item.sprite_id = 7; |     item.sprite_id = 7; | ||||||
|     item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_FEWER)); }; |     item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_FEWER)); }; | ||||||
|  | @ -3554,9 +3524,7 @@ bool GLCanvas3D::_init_toolbar() | ||||||
|         return false; |         return false; | ||||||
| 
 | 
 | ||||||
|     item.name = "splitobjects"; |     item.name = "splitobjects"; | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     item.icon_filename = "split_objects.svg"; |     item.icon_filename = "split_objects.svg"; | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
|     item.tooltip = _utf8(L("Split to objects")); |     item.tooltip = _utf8(L("Split to objects")); | ||||||
|     item.sprite_id = 8; |     item.sprite_id = 8; | ||||||
|     item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_SPLIT_OBJECTS)); }; |     item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_SPLIT_OBJECTS)); }; | ||||||
|  | @ -3566,9 +3534,7 @@ bool GLCanvas3D::_init_toolbar() | ||||||
|         return false; |         return false; | ||||||
| 
 | 
 | ||||||
|     item.name = "splitvolumes"; |     item.name = "splitvolumes"; | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     item.icon_filename = "split_parts.svg"; |     item.icon_filename = "split_parts.svg"; | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
|     item.tooltip = _utf8(L("Split to parts")); |     item.tooltip = _utf8(L("Split to parts")); | ||||||
|     item.sprite_id = 9; |     item.sprite_id = 9; | ||||||
|     item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_SPLIT_VOLUMES)); }; |     item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_SPLIT_VOLUMES)); }; | ||||||
|  | @ -3581,9 +3547,7 @@ bool GLCanvas3D::_init_toolbar() | ||||||
|         return false; |         return false; | ||||||
| 
 | 
 | ||||||
|     item.name = "layersediting"; |     item.name = "layersediting"; | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     item.icon_filename = "layers_white.svg"; |     item.icon_filename = "layers_white.svg"; | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
|     item.tooltip = _utf8(L("Layers editing")); |     item.tooltip = _utf8(L("Layers editing")); | ||||||
|     item.sprite_id = 10; |     item.sprite_id = 10; | ||||||
|     item.is_toggable = true; |     item.is_toggable = true; | ||||||
|  | @ -3921,9 +3885,6 @@ void GLCanvas3D::_render_overlays() const | ||||||
|     _render_gizmos_overlay(); |     _render_gizmos_overlay(); | ||||||
|     _render_warning_texture(); |     _render_warning_texture(); | ||||||
|     _render_legend_texture(); |     _render_legend_texture(); | ||||||
| #if !ENABLE_SVG_ICONS |  | ||||||
|     _resize_toolbars(); |  | ||||||
| #endif // !ENABLE_SVG_ICONS
 |  | ||||||
|     _render_toolbar(); |     _render_toolbar(); | ||||||
|     _render_view_toolbar(); |     _render_view_toolbar(); | ||||||
| 
 | 
 | ||||||
|  | @ -4000,7 +3961,6 @@ void GLCanvas3D::_render_current_gizmo() const | ||||||
| 
 | 
 | ||||||
| void GLCanvas3D::_render_gizmos_overlay() const | void GLCanvas3D::_render_gizmos_overlay() const | ||||||
| { | { | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
| #if ENABLE_RETINA_GL | #if ENABLE_RETINA_GL | ||||||
| //     m_gizmos.set_overlay_scale(m_retina_helper->get_scale_factor());
 | //     m_gizmos.set_overlay_scale(m_retina_helper->get_scale_factor());
 | ||||||
|     const float scale = m_retina_helper->get_scale_factor()*wxGetApp().toolbar_icon_scale(); |     const float scale = m_retina_helper->get_scale_factor()*wxGetApp().toolbar_icon_scale(); | ||||||
|  | @ -4011,14 +3971,12 @@ void GLCanvas3D::_render_gizmos_overlay() const | ||||||
|     const float size = int(GLGizmosManager::Default_Icons_Size*wxGetApp().toolbar_icon_scale()); |     const float size = int(GLGizmosManager::Default_Icons_Size*wxGetApp().toolbar_icon_scale()); | ||||||
|     m_gizmos.set_overlay_icon_size(size); //! #ys_FIXME_experiment
 |     m_gizmos.set_overlay_icon_size(size); //! #ys_FIXME_experiment
 | ||||||
| #endif /* __WXMSW__ */ | #endif /* __WXMSW__ */ | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
| 
 | 
 | ||||||
|     m_gizmos.render_overlay(*this, m_selection); |     m_gizmos.render_overlay(*this, m_selection); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GLCanvas3D::_render_toolbar() const | void GLCanvas3D::_render_toolbar() const | ||||||
| { | { | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
| #if ENABLE_RETINA_GL | #if ENABLE_RETINA_GL | ||||||
| //     m_toolbar.set_scale(m_retina_helper->get_scale_factor());
 | //     m_toolbar.set_scale(m_retina_helper->get_scale_factor());
 | ||||||
|     const float scale = m_retina_helper->get_scale_factor() * wxGetApp().toolbar_icon_scale(true); |     const float scale = m_retina_helper->get_scale_factor() * wxGetApp().toolbar_icon_scale(true); | ||||||
|  | @ -4073,20 +4031,12 @@ void GLCanvas3D::_render_toolbar() const | ||||||
|     } |     } | ||||||
|     } |     } | ||||||
|     m_toolbar.set_position(top, left); |     m_toolbar.set_position(top, left); | ||||||
| #else |  | ||||||
| #if ENABLE_RETINA_GL |  | ||||||
|     m_toolbar.set_icons_scale(m_retina_helper->get_scale_factor()); |  | ||||||
| #else |  | ||||||
|     m_toolbar.set_icons_scale(m_canvas->GetContentScaleFactor()); |  | ||||||
| #endif /* __WXMSW__ */ |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
| 
 | 
 | ||||||
|     m_toolbar.render(*this); |     m_toolbar.render(*this); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GLCanvas3D::_render_view_toolbar() const | void GLCanvas3D::_render_view_toolbar() const | ||||||
| { | { | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
| #if ENABLE_RETINA_GL | #if ENABLE_RETINA_GL | ||||||
| //     m_view_toolbar.set_scale(m_retina_helper->get_scale_factor());
 | //     m_view_toolbar.set_scale(m_retina_helper->get_scale_factor());
 | ||||||
|     const float scale = m_retina_helper->get_scale_factor() * wxGetApp().toolbar_icon_scale(); |     const float scale = m_retina_helper->get_scale_factor() * wxGetApp().toolbar_icon_scale(); | ||||||
|  | @ -4106,13 +4056,6 @@ void GLCanvas3D::_render_view_toolbar() const | ||||||
|     float top = (-0.5f * (float)cnv_size.get_height() + m_view_toolbar.get_height()) * inv_zoom; |     float top = (-0.5f * (float)cnv_size.get_height() + m_view_toolbar.get_height()) * inv_zoom; | ||||||
|     float left = -0.5f * (float)cnv_size.get_width() * inv_zoom; |     float left = -0.5f * (float)cnv_size.get_width() * inv_zoom; | ||||||
|     m_view_toolbar.set_position(top, left); |     m_view_toolbar.set_position(top, left); | ||||||
| #else |  | ||||||
| #if ENABLE_RETINA_GL |  | ||||||
|     m_view_toolbar.set_icons_scale(m_retina_helper->get_scale_factor()); |  | ||||||
| #else |  | ||||||
|     m_view_toolbar.set_icons_scale(m_canvas->GetContentScaleFactor()); |  | ||||||
| #endif /* __WXMSW__ */ |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
|     m_view_toolbar.render(*this); |     m_view_toolbar.render(*this); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -5555,73 +5498,6 @@ bool GLCanvas3D::_is_any_volume_outside() const | ||||||
|     return false; |     return false; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #if !ENABLE_SVG_ICONS |  | ||||||
| void GLCanvas3D::_resize_toolbars() const |  | ||||||
| { |  | ||||||
|     Size cnv_size = get_canvas_size(); |  | ||||||
|     float zoom = (float)m_camera.get_zoom(); |  | ||||||
|     float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; |  | ||||||
| 
 |  | ||||||
| #if ENABLE_RETINA_GL |  | ||||||
|     m_toolbar.set_icons_scale(m_retina_helper->get_scale_factor()); |  | ||||||
| #else |  | ||||||
|     m_toolbar.set_icons_scale(m_canvas->GetContentScaleFactor()); |  | ||||||
| #endif /* __WXMSW__ */ |  | ||||||
| 
 |  | ||||||
|     GLToolbar::Layout::EOrientation orientation = m_toolbar.get_layout_orientation(); |  | ||||||
| 
 |  | ||||||
|     switch (m_toolbar.get_layout_type()) |  | ||||||
|     { |  | ||||||
|     default: |  | ||||||
|     case GLToolbar::Layout::Horizontal: |  | ||||||
|     { |  | ||||||
|         // centers the toolbar on the top edge of the 3d scene
 |  | ||||||
|         float top, left; |  | ||||||
|         if (orientation == GLToolbar::Layout::Top) |  | ||||||
|         { |  | ||||||
|             top = 0.5f * (float)cnv_size.get_height() * inv_zoom; |  | ||||||
|             left = -0.5f * m_toolbar.get_width() * inv_zoom; |  | ||||||
|         } |  | ||||||
|         else |  | ||||||
|         { |  | ||||||
|             top = (-0.5f * (float)cnv_size.get_height() + m_view_toolbar.get_height()) * inv_zoom; |  | ||||||
|             left = -0.5f * m_toolbar.get_width() * inv_zoom; |  | ||||||
|         } |  | ||||||
|         m_toolbar.set_position(top, left); |  | ||||||
|         break; |  | ||||||
|     } |  | ||||||
|     case GLToolbar::Layout::Vertical: |  | ||||||
|     { |  | ||||||
|         // centers the toolbar on the right edge of the 3d scene
 |  | ||||||
|         float top, left; |  | ||||||
|         if (orientation == GLToolbar::Layout::Left) |  | ||||||
|         { |  | ||||||
|             top = 0.5f * m_toolbar.get_height() * inv_zoom; |  | ||||||
|             left = (-0.5f * (float)cnv_size.get_width()) * inv_zoom; |  | ||||||
|         } |  | ||||||
|         else |  | ||||||
|         { |  | ||||||
|             top = 0.5f * m_toolbar.get_height() * inv_zoom; |  | ||||||
|             left = (0.5f * (float)cnv_size.get_width() - m_toolbar.get_width()) * inv_zoom; |  | ||||||
|         } |  | ||||||
|         m_toolbar.set_position(top, left); |  | ||||||
|         break; |  | ||||||
|     } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| #if ENABLE_RETINA_GL |  | ||||||
|     m_view_toolbar.set_icons_scale(m_retina_helper->get_scale_factor()); |  | ||||||
| #else |  | ||||||
|     m_view_toolbar.set_icons_scale(m_canvas->GetContentScaleFactor()); |  | ||||||
| #endif /* __WXMSW__ */ |  | ||||||
| 
 |  | ||||||
|     // places the toolbar on the bottom-left corner of the 3d scene
 |  | ||||||
|     float top = (-0.5f * (float)cnv_size.get_height() + m_view_toolbar.get_height()) * inv_zoom; |  | ||||||
|     float left = -0.5f * (float)cnv_size.get_width() * inv_zoom; |  | ||||||
|     m_view_toolbar.set_position(top, left); |  | ||||||
| } |  | ||||||
| #endif // !ENABLE_SVG_ICONS
 |  | ||||||
| 
 |  | ||||||
| void GLCanvas3D::_update_selection_from_hover() | void GLCanvas3D::_update_selection_from_hover() | ||||||
| { | { | ||||||
|     bool ctrl_pressed = wxGetKeyState(WXK_CONTROL); |     bool ctrl_pressed = wxGetKeyState(WXK_CONTROL); | ||||||
|  |  | ||||||
|  | @ -728,10 +728,6 @@ private: | ||||||
| 
 | 
 | ||||||
|     bool _is_any_volume_outside() const; |     bool _is_any_volume_outside() const; | ||||||
| 
 | 
 | ||||||
| #if !ENABLE_SVG_ICONS |  | ||||||
|     void _resize_toolbars() const; |  | ||||||
| #endif // !ENABLE_SVG_ICONS
 |  | ||||||
| 
 |  | ||||||
|     // updates the selection from the content of m_hover_volume_idxs
 |     // updates the selection from the content of m_hover_volume_idxs
 | ||||||
|     void _update_selection_from_hover(); |     void _update_selection_from_hover(); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -38,9 +38,7 @@ const GLToolbarItem::EnabledStateCallback GLToolbarItem::Default_Enabled_State_C | ||||||
| 
 | 
 | ||||||
| GLToolbarItem::Data::Data() | GLToolbarItem::Data::Data() | ||||||
|     : name("") |     : name("") | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     , icon_filename("") |     , icon_filename("") | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
|     , tooltip("") |     , tooltip("") | ||||||
|     , sprite_id(-1) |     , sprite_id(-1) | ||||||
|     , is_toggable(false) |     , is_toggable(false) | ||||||
|  | @ -105,14 +103,6 @@ GLTexture::Quad_UVs GLToolbarItem::get_uvs(unsigned int tex_width, unsigned int | ||||||
|     return uvs; |     return uvs; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #if !ENABLE_SVG_ICONS |  | ||||||
| ItemsIconsTexture::Metadata::Metadata() |  | ||||||
|     : filename("") |  | ||||||
|     , icon_size(0) |  | ||||||
| { |  | ||||||
| } |  | ||||||
| #endif // !ENABLE_SVG_ICONS
 |  | ||||||
| 
 |  | ||||||
| BackgroundTexture::Metadata::Metadata() | BackgroundTexture::Metadata::Metadata() | ||||||
|     : filename("") |     : filename("") | ||||||
|     , left(0) |     , left(0) | ||||||
|  | @ -122,9 +112,7 @@ BackgroundTexture::Metadata::Metadata() | ||||||
| { | { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
| const float GLToolbar::Default_Icons_Size = 40.0f; | const float GLToolbar::Default_Icons_Size = 40.0f; | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
| 
 | 
 | ||||||
| GLToolbar::Layout::Layout() | GLToolbar::Layout::Layout() | ||||||
|     : type(Horizontal) |     : type(Horizontal) | ||||||
|  | @ -134,31 +122,19 @@ GLToolbar::Layout::Layout() | ||||||
|     , border(0.0f) |     , border(0.0f) | ||||||
|     , separator_size(0.0f) |     , separator_size(0.0f) | ||||||
|     , gap_size(0.0f) |     , gap_size(0.0f) | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     , icons_size(Default_Icons_Size) |     , icons_size(Default_Icons_Size) | ||||||
|     , scale(1.0f) |     , scale(1.0f) | ||||||
| #else |  | ||||||
|     , icons_scale(1.0f) |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
|     , width(0.0f) |     , width(0.0f) | ||||||
|     , height(0.0f) |     , height(0.0f) | ||||||
|     , dirty(true) |     , dirty(true) | ||||||
| { | { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
| GLToolbar::GLToolbar(GLToolbar::EType type, const std::string& name) | GLToolbar::GLToolbar(GLToolbar::EType type, const std::string& name) | ||||||
| #else |  | ||||||
| GLToolbar::GLToolbar(GLToolbar::EType type) |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
|     : m_type(type) |     : m_type(type) | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     , m_name(name) |     , m_name(name) | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
|     , m_enabled(false) |     , m_enabled(false) | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     , m_icons_texture_dirty(true) |     , m_icons_texture_dirty(true) | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
|     , m_tooltip("") |     , m_tooltip("") | ||||||
| { | { | ||||||
| } | } | ||||||
|  | @ -171,27 +147,13 @@ GLToolbar::~GLToolbar() | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
| bool GLToolbar::init(const BackgroundTexture::Metadata& background_texture) | bool GLToolbar::init(const BackgroundTexture::Metadata& background_texture) | ||||||
| #else |  | ||||||
| bool GLToolbar::init(const ItemsIconsTexture::Metadata& icons_texture, const BackgroundTexture::Metadata& background_texture) |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
| { | { | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     if (m_background_texture.texture.get_id() != 0) |     if (m_background_texture.texture.get_id() != 0) | ||||||
|         return true; |         return true; | ||||||
| 
 | 
 | ||||||
|     std::string path = resources_dir() + "/icons/"; |     std::string path = resources_dir() + "/icons/"; | ||||||
|     bool res = false; |     bool res = false; | ||||||
| #else |  | ||||||
|     if (m_icons_texture.texture.get_id() != 0) |  | ||||||
|         return true; |  | ||||||
| 
 |  | ||||||
|     std::string path = resources_dir() + "/icons/"; |  | ||||||
|     bool res = !icons_texture.filename.empty() && m_icons_texture.texture.load_from_file(path + icons_texture.filename, false, true); |  | ||||||
|     if (res) |  | ||||||
|         m_icons_texture.metadata = icons_texture; |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
| 
 | 
 | ||||||
|     if (!background_texture.filename.empty()) |     if (!background_texture.filename.empty()) | ||||||
|         res = m_background_texture.texture.load_from_file(path + background_texture.filename, false, true); |         res = m_background_texture.texture.load_from_file(path + background_texture.filename, false, true); | ||||||
|  | @ -247,7 +209,6 @@ void GLToolbar::set_gap_size(float size) | ||||||
|     m_layout.dirty = true; |     m_layout.dirty = true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
| void GLToolbar::set_icons_size(float size) | void GLToolbar::set_icons_size(float size) | ||||||
| { | { | ||||||
|     if (m_layout.icons_size != size) |     if (m_layout.icons_size != size) | ||||||
|  | @ -267,13 +228,6 @@ void GLToolbar::set_scale(float scale) | ||||||
|         m_icons_texture_dirty = true; |         m_icons_texture_dirty = true; | ||||||
|     } |     } | ||||||
| } | } | ||||||
| #else |  | ||||||
| void GLToolbar::set_icons_scale(float scale) |  | ||||||
| { |  | ||||||
|     m_layout.icons_scale = scale; |  | ||||||
|     m_layout.dirty = true; |  | ||||||
| } |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
| 
 | 
 | ||||||
| bool GLToolbar::is_enabled() const | bool GLToolbar::is_enabled() const | ||||||
| { | { | ||||||
|  | @ -385,10 +339,8 @@ void GLToolbar::render(const GLCanvas3D& parent) const | ||||||
|     if (!m_enabled || m_items.empty()) |     if (!m_enabled || m_items.empty()) | ||||||
|         return; |         return; | ||||||
| 
 | 
 | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     if (m_icons_texture_dirty) |     if (m_icons_texture_dirty) | ||||||
|         generate_icons_texture(); |         generate_icons_texture(); | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
| 
 | 
 | ||||||
|     switch (m_layout.type) |     switch (m_layout.type) | ||||||
|     { |     { | ||||||
|  | @ -492,20 +444,12 @@ float GLToolbar::get_width_horizontal() const | ||||||
| 
 | 
 | ||||||
| float GLToolbar::get_width_vertical() const | float GLToolbar::get_width_vertical() const | ||||||
| { | { | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     return (2.0f * m_layout.border + m_layout.icons_size) * m_layout.scale; |     return (2.0f * m_layout.border + m_layout.icons_size) * m_layout.scale; | ||||||
| #else |  | ||||||
|     return 2.0f * m_layout.border * m_layout.icons_scale + m_icons_texture.metadata.icon_size * m_layout.icons_scale; |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| float GLToolbar::get_height_horizontal() const | float GLToolbar::get_height_horizontal() const | ||||||
| { | { | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     return (2.0f * m_layout.border + m_layout.icons_size) * m_layout.scale; |     return (2.0f * m_layout.border + m_layout.icons_size) * m_layout.scale; | ||||||
| #else |  | ||||||
|     return 2.0f * m_layout.border * m_layout.icons_scale + m_icons_texture.metadata.icon_size * m_layout.icons_scale; |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| float GLToolbar::get_height_vertical() const | float GLToolbar::get_height_vertical() const | ||||||
|  | @ -515,7 +459,6 @@ float GLToolbar::get_height_vertical() const | ||||||
| 
 | 
 | ||||||
| float GLToolbar::get_main_size() const | float GLToolbar::get_main_size() const | ||||||
| { | { | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     float size = 2.0f * m_layout.border; |     float size = 2.0f * m_layout.border; | ||||||
|     for (unsigned int i = 0; i < (unsigned int)m_items.size(); ++i) |     for (unsigned int i = 0; i < (unsigned int)m_items.size(); ++i) | ||||||
|     { |     { | ||||||
|  | @ -531,25 +474,7 @@ float GLToolbar::get_main_size() const | ||||||
|     if (m_items.size() > 1) |     if (m_items.size() > 1) | ||||||
|         size += ((float)m_items.size() - 1.0f) * m_layout.gap_size; |         size += ((float)m_items.size() - 1.0f) * m_layout.gap_size; | ||||||
| 
 | 
 | ||||||
|     size *= m_layout.scale; |     return size * m_layout.scale; | ||||||
| #else |  | ||||||
|     float size = 2.0f * m_layout.border * m_layout.icons_scale; |  | ||||||
|     for (unsigned int i = 0; i < (unsigned int)m_items.size(); ++i) |  | ||||||
|     { |  | ||||||
|         if (!m_items[i]->is_visible()) |  | ||||||
|             continue; |  | ||||||
| 
 |  | ||||||
|         if (m_items[i]->is_separator()) |  | ||||||
|             size += m_layout.separator_size * m_layout.icons_scale; |  | ||||||
|         else |  | ||||||
|             size += (float)m_icons_texture.metadata.icon_size * m_layout.icons_scale; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if (m_items.size() > 1) |  | ||||||
|         size += ((float)m_items.size() - 1.0f) * m_layout.gap_size * m_layout.icons_scale; |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
| 
 |  | ||||||
|     return size; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GLToolbar::do_action(unsigned int item_id, GLCanvas3D& parent) | void GLToolbar::do_action(unsigned int item_id, GLCanvas3D& parent) | ||||||
|  | @ -609,20 +534,12 @@ std::string GLToolbar::update_hover_state_horizontal(const Vec2d& mouse_pos, GLC | ||||||
| 
 | 
 | ||||||
|     float zoom = (float)parent.get_camera().get_zoom(); |     float zoom = (float)parent.get_camera().get_zoom(); | ||||||
|     float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; |     float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     float factor = m_layout.scale * inv_zoom; |     float factor = m_layout.scale * inv_zoom; | ||||||
| #else |  | ||||||
|     float factor = m_layout.icons_scale * inv_zoom; |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
| 
 | 
 | ||||||
|     Size cnv_size = parent.get_canvas_size(); |     Size cnv_size = parent.get_canvas_size(); | ||||||
|     Vec2d scaled_mouse_pos((mouse_pos(0) - 0.5 * (double)cnv_size.get_width()) * inv_zoom, (0.5 * (double)cnv_size.get_height() - mouse_pos(1)) * inv_zoom); |     Vec2d scaled_mouse_pos((mouse_pos(0) - 0.5 * (double)cnv_size.get_width()) * inv_zoom, (0.5 * (double)cnv_size.get_height() - mouse_pos(1)) * inv_zoom); | ||||||
| 
 | 
 | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     float scaled_icons_size = m_layout.icons_size * factor; |     float scaled_icons_size = m_layout.icons_size * factor; | ||||||
| #else |  | ||||||
|     float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * factor; |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
|     float scaled_separator_size = m_layout.separator_size * factor; |     float scaled_separator_size = m_layout.separator_size * factor; | ||||||
|     float scaled_gap_size = m_layout.gap_size * factor; |     float scaled_gap_size = m_layout.gap_size * factor; | ||||||
|     float scaled_border = m_layout.border * factor; |     float scaled_border = m_layout.border * factor; | ||||||
|  | @ -714,20 +631,12 @@ std::string GLToolbar::update_hover_state_vertical(const Vec2d& mouse_pos, GLCan | ||||||
| 
 | 
 | ||||||
|     float zoom = (float)parent.get_camera().get_zoom(); |     float zoom = (float)parent.get_camera().get_zoom(); | ||||||
|     float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; |     float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     float factor = m_layout.scale * inv_zoom; |     float factor = m_layout.scale * inv_zoom; | ||||||
| #else |  | ||||||
|     float factor = m_layout.icons_scale * inv_zoom; |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
| 
 | 
 | ||||||
|     Size cnv_size = parent.get_canvas_size(); |     Size cnv_size = parent.get_canvas_size(); | ||||||
|     Vec2d scaled_mouse_pos((mouse_pos(0) - 0.5 * (double)cnv_size.get_width()) * inv_zoom, (0.5 * (double)cnv_size.get_height() - mouse_pos(1)) * inv_zoom); |     Vec2d scaled_mouse_pos((mouse_pos(0) - 0.5 * (double)cnv_size.get_width()) * inv_zoom, (0.5 * (double)cnv_size.get_height() - mouse_pos(1)) * inv_zoom); | ||||||
| 
 | 
 | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     float scaled_icons_size = m_layout.icons_size * factor; |     float scaled_icons_size = m_layout.icons_size * factor; | ||||||
| #else |  | ||||||
|     float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * factor; |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
|     float scaled_separator_size = m_layout.separator_size * factor; |     float scaled_separator_size = m_layout.separator_size * factor; | ||||||
|     float scaled_gap_size = m_layout.gap_size * factor; |     float scaled_gap_size = m_layout.gap_size * factor; | ||||||
|     float scaled_border = m_layout.border * factor; |     float scaled_border = m_layout.border * factor; | ||||||
|  | @ -831,20 +740,12 @@ int GLToolbar::contains_mouse_horizontal(const Vec2d& mouse_pos, const GLCanvas3 | ||||||
| 
 | 
 | ||||||
|     float zoom = (float)parent.get_camera().get_zoom(); |     float zoom = (float)parent.get_camera().get_zoom(); | ||||||
|     float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; |     float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     float factor = m_layout.scale * inv_zoom; |     float factor = m_layout.scale * inv_zoom; | ||||||
| #else |  | ||||||
|     float factor = m_layout.icons_scale * inv_zoom; |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
| 
 | 
 | ||||||
|     Size cnv_size = parent.get_canvas_size(); |     Size cnv_size = parent.get_canvas_size(); | ||||||
|     Vec2d scaled_mouse_pos((mouse_pos(0) - 0.5 * (double)cnv_size.get_width()) * inv_zoom, (0.5 * (double)cnv_size.get_height() - mouse_pos(1)) * inv_zoom); |     Vec2d scaled_mouse_pos((mouse_pos(0) - 0.5 * (double)cnv_size.get_width()) * inv_zoom, (0.5 * (double)cnv_size.get_height() - mouse_pos(1)) * inv_zoom); | ||||||
| 
 | 
 | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     float scaled_icons_size = m_layout.icons_size * factor; |     float scaled_icons_size = m_layout.icons_size * factor; | ||||||
| #else |  | ||||||
|     float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * factor; |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
|     float scaled_separator_size = m_layout.separator_size * factor; |     float scaled_separator_size = m_layout.separator_size * factor; | ||||||
|     float scaled_gap_size = m_layout.gap_size * factor; |     float scaled_gap_size = m_layout.gap_size * factor; | ||||||
|     float scaled_border = m_layout.border * factor; |     float scaled_border = m_layout.border * factor; | ||||||
|  | @ -914,20 +815,12 @@ int GLToolbar::contains_mouse_vertical(const Vec2d& mouse_pos, const GLCanvas3D& | ||||||
| 
 | 
 | ||||||
|     float zoom = (float)parent.get_camera().get_zoom(); |     float zoom = (float)parent.get_camera().get_zoom(); | ||||||
|     float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; |     float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     float factor = m_layout.scale * inv_zoom; |     float factor = m_layout.scale * inv_zoom; | ||||||
| #else |  | ||||||
|     float factor = m_layout.icons_scale * inv_zoom; |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
| 
 | 
 | ||||||
|     Size cnv_size = parent.get_canvas_size(); |     Size cnv_size = parent.get_canvas_size(); | ||||||
|     Vec2d scaled_mouse_pos((mouse_pos(0) - 0.5 * (double)cnv_size.get_width()) * inv_zoom, (0.5 * (double)cnv_size.get_height() - mouse_pos(1)) * inv_zoom); |     Vec2d scaled_mouse_pos((mouse_pos(0) - 0.5 * (double)cnv_size.get_width()) * inv_zoom, (0.5 * (double)cnv_size.get_height() - mouse_pos(1)) * inv_zoom); | ||||||
| 
 | 
 | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     float scaled_icons_size = m_layout.icons_size * factor; |     float scaled_icons_size = m_layout.icons_size * factor; | ||||||
| #else |  | ||||||
|     float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * factor; |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
|     float scaled_separator_size = m_layout.separator_size * factor; |     float scaled_separator_size = m_layout.separator_size * factor; | ||||||
|     float scaled_gap_size = m_layout.gap_size * factor; |     float scaled_gap_size = m_layout.gap_size * factor; | ||||||
|     float scaled_border = m_layout.border * factor; |     float scaled_border = m_layout.border * factor; | ||||||
|  | @ -993,34 +886,15 @@ int GLToolbar::contains_mouse_vertical(const Vec2d& mouse_pos, const GLCanvas3D& | ||||||
| 
 | 
 | ||||||
| void GLToolbar::render_horizontal(const GLCanvas3D& parent) const | void GLToolbar::render_horizontal(const GLCanvas3D& parent) const | ||||||
| { | { | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     unsigned int tex_id = m_icons_texture.get_id(); |     unsigned int tex_id = m_icons_texture.get_id(); | ||||||
|     int tex_width = m_icons_texture.get_width(); |     int tex_width = m_icons_texture.get_width(); | ||||||
|     int tex_height = m_icons_texture.get_height(); |     int tex_height = m_icons_texture.get_height(); | ||||||
| #else |  | ||||||
|     unsigned int tex_id = m_icons_texture.texture.get_id(); |  | ||||||
|     int tex_width = m_icons_texture.texture.get_width(); |  | ||||||
|     int tex_height = m_icons_texture.texture.get_height(); |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
| 
 |  | ||||||
| #if !ENABLE_SVG_ICONS |  | ||||||
|     if ((tex_id == 0) || (tex_width <= 0) || (tex_height <= 0)) |  | ||||||
|         return; |  | ||||||
| #endif // !ENABLE_SVG_ICONS
 |  | ||||||
| 
 | 
 | ||||||
|     float zoom = (float)parent.get_camera().get_zoom(); |     float zoom = (float)parent.get_camera().get_zoom(); | ||||||
|     float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; |     float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     float factor = inv_zoom * m_layout.scale; |     float factor = inv_zoom * m_layout.scale; | ||||||
| #else |  | ||||||
|     float factor = inv_zoom * m_layout.icons_scale; |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
| 
 | 
 | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     float scaled_icons_size = m_layout.icons_size * factor; |     float scaled_icons_size = m_layout.icons_size * factor; | ||||||
| #else |  | ||||||
|     float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * factor; |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
|     float scaled_separator_size = m_layout.separator_size * factor; |     float scaled_separator_size = m_layout.separator_size * factor; | ||||||
|     float scaled_gap_size = m_layout.gap_size * factor; |     float scaled_gap_size = m_layout.gap_size * factor; | ||||||
|     float scaled_border = m_layout.border * factor; |     float scaled_border = m_layout.border * factor; | ||||||
|  | @ -1121,10 +995,8 @@ void GLToolbar::render_horizontal(const GLCanvas3D& parent) const | ||||||
|     left += scaled_border; |     left += scaled_border; | ||||||
|     top -= scaled_border; |     top -= scaled_border; | ||||||
| 
 | 
 | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     if ((tex_id == 0) || (tex_width <= 0) || (tex_height <= 0)) |     if ((tex_id == 0) || (tex_width <= 0) || (tex_height <= 0)) | ||||||
|         return; |         return; | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
| 
 | 
 | ||||||
|     // renders icons
 |     // renders icons
 | ||||||
|     for (const GLToolbarItem* item : m_items) |     for (const GLToolbarItem* item : m_items) | ||||||
|  | @ -1136,11 +1008,7 @@ void GLToolbar::render_horizontal(const GLCanvas3D& parent) const | ||||||
|             left += separator_stride; |             left += separator_stride; | ||||||
|         else |         else | ||||||
|         { |         { | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|             item->render(tex_id, left, left + scaled_icons_size, top - scaled_icons_size, top, (unsigned int)tex_width, (unsigned int)tex_height, (unsigned int)(m_layout.icons_size * m_layout.scale)); |             item->render(tex_id, left, left + scaled_icons_size, top - scaled_icons_size, top, (unsigned int)tex_width, (unsigned int)tex_height, (unsigned int)(m_layout.icons_size * m_layout.scale)); | ||||||
| #else |  | ||||||
|             item->render(tex_id, left, left + scaled_icons_size, top - scaled_icons_size, top, (unsigned int)tex_width, (unsigned int)tex_height, m_icons_texture.metadata.icon_size); |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
|             left += icon_stride; |             left += icon_stride; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | @ -1148,34 +1016,15 @@ void GLToolbar::render_horizontal(const GLCanvas3D& parent) const | ||||||
| 
 | 
 | ||||||
| void GLToolbar::render_vertical(const GLCanvas3D& parent) const | void GLToolbar::render_vertical(const GLCanvas3D& parent) const | ||||||
| { | { | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     unsigned int tex_id = m_icons_texture.get_id(); |     unsigned int tex_id = m_icons_texture.get_id(); | ||||||
|     int tex_width = m_icons_texture.get_width(); |     int tex_width = m_icons_texture.get_width(); | ||||||
|     int tex_height = m_icons_texture.get_height(); |     int tex_height = m_icons_texture.get_height(); | ||||||
| #else |  | ||||||
|     unsigned int tex_id = m_icons_texture.texture.get_id(); |  | ||||||
|     int tex_width = m_icons_texture.texture.get_width(); |  | ||||||
|     int tex_height = m_icons_texture.texture.get_height(); |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
| 
 |  | ||||||
| #if !ENABLE_SVG_ICONS |  | ||||||
|     if ((tex_id == 0) || (tex_width <= 0) || (tex_height <= 0)) |  | ||||||
|         return; |  | ||||||
| #endif // !ENABLE_SVG_ICONS
 |  | ||||||
| 
 | 
 | ||||||
|     float zoom = (float)parent.get_camera().get_zoom(); |     float zoom = (float)parent.get_camera().get_zoom(); | ||||||
|     float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; |     float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     float factor = inv_zoom * m_layout.scale; |     float factor = inv_zoom * m_layout.scale; | ||||||
| #else |  | ||||||
|     float factor = inv_zoom * m_layout.icons_scale; |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
| 
 | 
 | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     float scaled_icons_size = m_layout.icons_size * factor; |     float scaled_icons_size = m_layout.icons_size * factor; | ||||||
| #else |  | ||||||
|     float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * m_layout.icons_scale * factor; |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
|     float scaled_separator_size = m_layout.separator_size * factor; |     float scaled_separator_size = m_layout.separator_size * factor; | ||||||
|     float scaled_gap_size = m_layout.gap_size * factor; |     float scaled_gap_size = m_layout.gap_size * factor; | ||||||
|     float scaled_border = m_layout.border * factor; |     float scaled_border = m_layout.border * factor; | ||||||
|  | @ -1276,10 +1125,8 @@ void GLToolbar::render_vertical(const GLCanvas3D& parent) const | ||||||
|     left += scaled_border; |     left += scaled_border; | ||||||
|     top -= scaled_border; |     top -= scaled_border; | ||||||
| 
 | 
 | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     if ((tex_id == 0) || (tex_width <= 0) || (tex_height <= 0)) |     if ((tex_id == 0) || (tex_width <= 0) || (tex_height <= 0)) | ||||||
|         return; |         return; | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
| 
 | 
 | ||||||
|     // renders icons
 |     // renders icons
 | ||||||
|     for (const GLToolbarItem* item : m_items) |     for (const GLToolbarItem* item : m_items) | ||||||
|  | @ -1291,17 +1138,12 @@ void GLToolbar::render_vertical(const GLCanvas3D& parent) const | ||||||
|             top -= separator_stride; |             top -= separator_stride; | ||||||
|         else |         else | ||||||
|         { |         { | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|             item->render(tex_id, left, left + scaled_icons_size, top - scaled_icons_size, top, (unsigned int)tex_width, (unsigned int)tex_height, (unsigned int)(m_layout.icons_size * m_layout.scale)); |             item->render(tex_id, left, left + scaled_icons_size, top - scaled_icons_size, top, (unsigned int)tex_width, (unsigned int)tex_height, (unsigned int)(m_layout.icons_size * m_layout.scale)); | ||||||
| #else |  | ||||||
|             item->render(tex_id, left, left + scaled_icons_size, top - scaled_icons_size, top, (unsigned int)tex_width, (unsigned int)tex_height, m_icons_texture.metadata.icon_size); |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
|             top -= icon_stride; |             top -= icon_stride; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
| bool GLToolbar::generate_icons_texture() const | bool GLToolbar::generate_icons_texture() const | ||||||
| { | { | ||||||
|     std::string path = resources_dir() + "/icons/"; |     std::string path = resources_dir() + "/icons/"; | ||||||
|  | @ -1337,7 +1179,6 @@ bool GLToolbar::generate_icons_texture() const | ||||||
| 
 | 
 | ||||||
|     return res; |     return res; | ||||||
| } | } | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
| 
 | 
 | ||||||
| bool GLToolbar::update_items_visibility() | bool GLToolbar::update_items_visibility() | ||||||
| { | { | ||||||
|  |  | ||||||
|  | @ -58,9 +58,7 @@ public: | ||||||
|     struct Data |     struct Data | ||||||
|     { |     { | ||||||
|         std::string name; |         std::string name; | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|         std::string icon_filename; |         std::string icon_filename; | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
|         std::string tooltip; |         std::string tooltip; | ||||||
|         unsigned int sprite_id; |         unsigned int sprite_id; | ||||||
|         bool is_toggable; |         bool is_toggable; | ||||||
|  | @ -88,9 +86,7 @@ public: | ||||||
|     void set_state(EState state) { m_state = state; } |     void set_state(EState state) { m_state = state; } | ||||||
| 
 | 
 | ||||||
|     const std::string& get_name() const { return m_data.name; } |     const std::string& get_name() const { return m_data.name; } | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     const std::string& get_icon_filename() const { return m_data.icon_filename; } |     const std::string& get_icon_filename() const { return m_data.icon_filename; } | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
|     const std::string& get_tooltip() const { return m_data.tooltip; } |     const std::string& get_tooltip() const { return m_data.tooltip; } | ||||||
| 
 | 
 | ||||||
|     void do_action() { m_data.action_callback(); } |     void do_action() { m_data.action_callback(); } | ||||||
|  | @ -118,27 +114,6 @@ private: | ||||||
|     friend class GLToolbar; |     friend class GLToolbar; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| #if !ENABLE_SVG_ICONS |  | ||||||
| // items icon textures are assumed to be square and all with the same size in pixels, no internal check is done
 |  | ||||||
| // icons are layed-out into the texture starting from the top-left corner in the same order as enum GLToolbarItem::EState
 |  | ||||||
| // from left to right
 |  | ||||||
| struct ItemsIconsTexture |  | ||||||
| { |  | ||||||
|     struct Metadata |  | ||||||
|     { |  | ||||||
|         // path of the file containing the icons' texture
 |  | ||||||
|         std::string filename; |  | ||||||
|         // size of the square icons, in pixels
 |  | ||||||
|         unsigned int icon_size; |  | ||||||
| 
 |  | ||||||
|         Metadata(); |  | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
|     GLTexture texture; |  | ||||||
|     Metadata metadata; |  | ||||||
| }; |  | ||||||
| #endif // !ENABLE_SVG_ICONS
 |  | ||||||
| 
 |  | ||||||
| struct BackgroundTexture | struct BackgroundTexture | ||||||
| { | { | ||||||
|     struct Metadata |     struct Metadata | ||||||
|  | @ -164,9 +139,7 @@ struct BackgroundTexture | ||||||
| class GLToolbar | class GLToolbar | ||||||
| { | { | ||||||
| public: | public: | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     static const float Default_Icons_Size; |     static const float Default_Icons_Size; | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
| 
 | 
 | ||||||
|     enum EType : unsigned char |     enum EType : unsigned char | ||||||
|     { |     { | ||||||
|  | @ -201,12 +174,8 @@ public: | ||||||
|         float border; |         float border; | ||||||
|         float separator_size; |         float separator_size; | ||||||
|         float gap_size; |         float gap_size; | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|         float icons_size; |         float icons_size; | ||||||
|         float scale; |         float scale; | ||||||
| #else |  | ||||||
|         float icons_scale; |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
| 
 | 
 | ||||||
|         float width; |         float width; | ||||||
|         float height; |         float height; | ||||||
|  | @ -219,16 +188,10 @@ private: | ||||||
|     typedef std::vector<GLToolbarItem*> ItemsList; |     typedef std::vector<GLToolbarItem*> ItemsList; | ||||||
| 
 | 
 | ||||||
|     EType m_type; |     EType m_type; | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     std::string m_name; |     std::string m_name; | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
|     bool m_enabled; |     bool m_enabled; | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     mutable GLTexture m_icons_texture; |     mutable GLTexture m_icons_texture; | ||||||
|     mutable bool m_icons_texture_dirty; |     mutable bool m_icons_texture_dirty; | ||||||
| #else |  | ||||||
|     ItemsIconsTexture m_icons_texture; |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
|     BackgroundTexture m_background_texture; |     BackgroundTexture m_background_texture; | ||||||
|     mutable Layout m_layout; |     mutable Layout m_layout; | ||||||
| 
 | 
 | ||||||
|  | @ -251,18 +214,10 @@ private: | ||||||
|     std::string m_tooltip; |     std::string m_tooltip; | ||||||
| 
 | 
 | ||||||
| public: | public: | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     GLToolbar(EType type, const std::string& name); |     GLToolbar(EType type, const std::string& name); | ||||||
| #else |  | ||||||
|     explicit GLToolbar(EType type); |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
|     ~GLToolbar(); |     ~GLToolbar(); | ||||||
| 
 | 
 | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     bool init(const BackgroundTexture::Metadata& background_texture); |     bool init(const BackgroundTexture::Metadata& background_texture); | ||||||
| #else |  | ||||||
|     bool init(const ItemsIconsTexture::Metadata& icons_texture, const BackgroundTexture::Metadata& background_texture); |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
| 
 | 
 | ||||||
|     Layout::EType get_layout_type() const; |     Layout::EType get_layout_type() const; | ||||||
|     void set_layout_type(Layout::EType type); |     void set_layout_type(Layout::EType type); | ||||||
|  | @ -273,12 +228,8 @@ public: | ||||||
|     void set_border(float border); |     void set_border(float border); | ||||||
|     void set_separator_size(float size); |     void set_separator_size(float size); | ||||||
|     void set_gap_size(float size); |     void set_gap_size(float size); | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     void set_icons_size(float size); |     void set_icons_size(float size); | ||||||
|     void set_scale(float scale); |     void set_scale(float scale); | ||||||
| #else |  | ||||||
|     void set_icons_scale(float scale); |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
| 
 | 
 | ||||||
|     bool is_enabled() const; |     bool is_enabled() const; | ||||||
|     void set_enabled(bool enable); |     void set_enabled(bool enable); | ||||||
|  | @ -297,7 +248,6 @@ public: | ||||||
| 
 | 
 | ||||||
|     const std::string& get_tooltip() const { return m_tooltip; } |     const std::string& get_tooltip() const { return m_tooltip; } | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|     // returns true if any item changed its state
 |     // returns true if any item changed its state
 | ||||||
|     bool update_items_state(); |     bool update_items_state(); | ||||||
| 
 | 
 | ||||||
|  | @ -324,9 +274,7 @@ private: | ||||||
|     void render_horizontal(const GLCanvas3D& parent) const; |     void render_horizontal(const GLCanvas3D& parent) const; | ||||||
|     void render_vertical(const GLCanvas3D& parent) const; |     void render_vertical(const GLCanvas3D& parent) const; | ||||||
| 
 | 
 | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     bool generate_icons_texture() const; |     bool generate_icons_texture() const; | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
| 
 | 
 | ||||||
|     // returns true if any item changed its state
 |     // returns true if any item changed its state
 | ||||||
|     bool update_items_visibility(); |     bool update_items_visibility(); | ||||||
|  |  | ||||||
|  | @ -17,6 +17,28 @@ namespace Slic3r | ||||||
| namespace GUI | namespace GUI | ||||||
| { | { | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  | // Helper function to be used by drop to bed button. Returns lowest point of this
 | ||||||
|  | // volume in world coordinate system.
 | ||||||
|  | static double get_volume_min_z(const GLVolume* volume) | ||||||
|  | { | ||||||
|  |     const Transform3f& world_matrix = volume->world_matrix().cast<float>(); | ||||||
|  | 
 | ||||||
|  |     // need to get the ModelVolume pointer
 | ||||||
|  |     const ModelObject* mo = wxGetApp().model_objects()->at(volume->composite_id.object_id); | ||||||
|  |     const ModelVolume* mv = mo->volumes[volume->composite_id.volume_id]; | ||||||
|  |     const TriangleMesh& hull = mv->get_convex_hull(); | ||||||
|  | 
 | ||||||
|  |     float min_z = std::numeric_limits<float>::max(); | ||||||
|  |     for (const stl_facet& facet : hull.stl.facet_start) { | ||||||
|  |         for (int i = 0; i < 3; ++ i) | ||||||
|  |             min_z = std::min(min_z, Vec3f::UnitZ().dot(world_matrix * facet.vertex[i])); | ||||||
|  |     } | ||||||
|  |     return min_z; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| static wxBitmapComboBox* create_word_local_combo(wxWindow *parent) | static wxBitmapComboBox* create_word_local_combo(wxWindow *parent) | ||||||
| { | { | ||||||
|     wxSize size(15 * wxGetApp().em_unit(), -1); |     wxSize size(15 * wxGetApp().em_unit(), -1); | ||||||
|  | @ -185,7 +207,7 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : | ||||||
|         unsigned int axis_idx = (axis[0] - 'x'); // 0, 1 or 2
 |         unsigned int axis_idx = (axis[0] - 'x'); // 0, 1 or 2
 | ||||||
| 
 | 
 | ||||||
|         // We will add a button to toggle mirroring to each axis:
 |         // We will add a button to toggle mirroring to each axis:
 | ||||||
|         auto mirror_button = [=](wxWindow* parent) { |         auto mirror_button = [this, mirror_btn_width, axis_idx, &label](wxWindow* parent) { | ||||||
|             wxSize btn_size(em_unit(parent) * mirror_btn_width, em_unit(parent) * mirror_btn_width); |             wxSize btn_size(em_unit(parent) * mirror_btn_width, em_unit(parent) * mirror_btn_width); | ||||||
|             auto btn = new ScalableButton(parent, wxID_ANY, "mirroring_off.png", wxEmptyString, btn_size, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER | wxTRANSPARENT_WINDOW); |             auto btn = new ScalableButton(parent, wxID_ANY, "mirroring_off.png", wxEmptyString, btn_size, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER | wxTRANSPARENT_WINDOW); | ||||||
|             btn->SetToolTip(wxString::Format(_(L("Toggle %s axis mirroring")), label)); |             btn->SetToolTip(wxString::Format(_(L("Toggle %s axis mirroring")), label)); | ||||||
|  | @ -195,7 +217,7 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : | ||||||
|             auto sizer = new wxBoxSizer(wxHORIZONTAL); |             auto sizer = new wxBoxSizer(wxHORIZONTAL); | ||||||
|             sizer->Add(btn); |             sizer->Add(btn); | ||||||
| 
 | 
 | ||||||
|             btn->Bind(wxEVT_BUTTON, [=](wxCommandEvent &e) { |             btn->Bind(wxEVT_BUTTON, [this, axis_idx](wxCommandEvent &e) { | ||||||
|                 Axis axis = (Axis)(axis_idx + X); |                 Axis axis = (Axis)(axis_idx + X); | ||||||
|                 if (m_mirror_buttons[axis_idx].second == mbHidden) |                 if (m_mirror_buttons[axis_idx].second == mbHidden) | ||||||
|                     return; |                     return; | ||||||
|  | @ -258,13 +280,13 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : | ||||||
|                 return btn; |                 return btn; | ||||||
|             }; |             }; | ||||||
|             // Add reset scale button
 |             // Add reset scale button
 | ||||||
|             auto reset_scale_button = [=](wxWindow* parent) { |             auto reset_scale_button = [this](wxWindow* parent) { | ||||||
|                 auto btn = new ScalableButton(parent, wxID_ANY, ScalableBitmap(parent, "undo")); |                 auto btn = new ScalableButton(parent, wxID_ANY, ScalableBitmap(parent, "undo")); | ||||||
|                 btn->SetToolTip(_(L("Reset scale"))); |                 btn->SetToolTip(_(L("Reset scale"))); | ||||||
|                 m_reset_scale_button = btn; |                 m_reset_scale_button = btn; | ||||||
|                 auto sizer = new wxBoxSizer(wxHORIZONTAL); |                 auto sizer = new wxBoxSizer(wxHORIZONTAL); | ||||||
|                 sizer->Add(btn, wxBU_EXACTFIT); |                 sizer->Add(btn, wxBU_EXACTFIT); | ||||||
|                 btn->Bind(wxEVT_BUTTON, [=](wxCommandEvent &e) { |                 btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent &e) { | ||||||
|                     change_scale_value(0, 100.); |                     change_scale_value(0, 100.); | ||||||
|                     change_scale_value(1, 100.); |                     change_scale_value(1, 100.); | ||||||
|                     change_scale_value(2, 100.); |                     change_scale_value(2, 100.); | ||||||
|  | @ -275,13 +297,13 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : | ||||||
|         } |         } | ||||||
|         else if (option_name == "Rotation") { |         else if (option_name == "Rotation") { | ||||||
|             // Add reset rotation button
 |             // Add reset rotation button
 | ||||||
|             auto reset_rotation_button = [=](wxWindow* parent) { |             auto reset_rotation_button = [this](wxWindow* parent) { | ||||||
|                 auto btn = new ScalableButton(parent, wxID_ANY, ScalableBitmap(parent, "undo")); |                 auto btn = new ScalableButton(parent, wxID_ANY, ScalableBitmap(parent, "undo")); | ||||||
|                 btn->SetToolTip(_(L("Reset rotation"))); |                 btn->SetToolTip(_(L("Reset rotation"))); | ||||||
|                 m_reset_rotation_button = btn; |                 m_reset_rotation_button = btn; | ||||||
|                 auto sizer = new wxBoxSizer(wxHORIZONTAL); |                 auto sizer = new wxBoxSizer(wxHORIZONTAL); | ||||||
|                 sizer->Add(btn, wxBU_EXACTFIT); |                 sizer->Add(btn, wxBU_EXACTFIT); | ||||||
|                 btn->Bind(wxEVT_BUTTON, [=](wxCommandEvent &e) { |                 btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent &e) { | ||||||
|                     GLCanvas3D* canvas = wxGetApp().plater()->canvas3D(); |                     GLCanvas3D* canvas = wxGetApp().plater()->canvas3D(); | ||||||
|                     Selection& selection = canvas->get_selection(); |                     Selection& selection = canvas->get_selection(); | ||||||
| 
 | 
 | ||||||
|  | @ -310,6 +332,34 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : | ||||||
|             }; |             }; | ||||||
|             line.append_widget(reset_rotation_button); |             line.append_widget(reset_rotation_button); | ||||||
|         } |         } | ||||||
|  |         else if (option_name == "Position") { | ||||||
|  |             // Add drop to bed button
 | ||||||
|  |             auto drop_to_bed_button = [=](wxWindow* parent) { | ||||||
|  |                 auto btn = new ScalableButton(parent, wxID_ANY, ScalableBitmap(parent, "drop_to_bed.png")); | ||||||
|  |                 btn->SetToolTip(_(L("Drop to bed"))); | ||||||
|  |                 m_drop_to_bed_button = btn; | ||||||
|  |                 auto sizer = new wxBoxSizer(wxHORIZONTAL); | ||||||
|  |                 sizer->Add(btn, wxBU_EXACTFIT); | ||||||
|  |                 btn->Bind(wxEVT_BUTTON, [=](wxCommandEvent &e) { | ||||||
|  |                     // ???
 | ||||||
|  |                     GLCanvas3D* canvas = wxGetApp().plater()->canvas3D(); | ||||||
|  |                     Selection& selection = canvas->get_selection(); | ||||||
|  | 
 | ||||||
|  |                     if (selection.is_single_volume() || selection.is_single_modifier()) { | ||||||
|  |                         const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); | ||||||
|  | 
 | ||||||
|  |                         const Geometry::Transformation& instance_trafo = volume->get_instance_transformation(); | ||||||
|  |                         Vec3d diff = m_cache.position - instance_trafo.get_matrix(true).inverse() * Vec3d(0., 0., get_volume_min_z(volume)); | ||||||
|  | 
 | ||||||
|  |                         change_position_value(0, diff.x()); | ||||||
|  |                         change_position_value(1, diff.y()); | ||||||
|  |                         change_position_value(2, diff.z()); | ||||||
|  |                     } | ||||||
|  |                 }); | ||||||
|  |             return sizer; | ||||||
|  |             }; | ||||||
|  |             line.append_widget(drop_to_bed_button); | ||||||
|  |         } | ||||||
|         // Add empty bmp (Its size have to be equal to PrusaLockButton) in front of "Size" option to label alignment
 |         // Add empty bmp (Its size have to be equal to PrusaLockButton) in front of "Size" option to label alignment
 | ||||||
|         else if (option_name == "Size") { |         else if (option_name == "Size") { | ||||||
|             line.near_label_widget = [this](wxWindow* parent) { |             line.near_label_widget = [this](wxWindow* parent) { | ||||||
|  | @ -536,11 +586,13 @@ void ObjectManipulation::update_reset_buttons_visibility() | ||||||
| 
 | 
 | ||||||
|     bool show_rotation = false; |     bool show_rotation = false; | ||||||
|     bool show_scale = false; |     bool show_scale = false; | ||||||
|  |     bool show_drop_to_bed = false; | ||||||
| 
 | 
 | ||||||
|     if (selection.is_single_full_instance() || selection.is_single_modifier() || selection.is_single_volume()) { |     if (selection.is_single_full_instance() || selection.is_single_modifier() || selection.is_single_volume()) { | ||||||
|         const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); |         const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); | ||||||
|         Vec3d rotation; |         Vec3d rotation; | ||||||
|         Vec3d scale; |         Vec3d scale; | ||||||
|  |         double min_z = 0.; | ||||||
| 
 | 
 | ||||||
|         if (selection.is_single_full_instance()) { |         if (selection.is_single_full_instance()) { | ||||||
|             rotation = volume->get_instance_rotation(); |             rotation = volume->get_instance_rotation(); | ||||||
|  | @ -549,14 +601,17 @@ void ObjectManipulation::update_reset_buttons_visibility() | ||||||
|         else { |         else { | ||||||
|             rotation = volume->get_volume_rotation(); |             rotation = volume->get_volume_rotation(); | ||||||
|             scale = volume->get_volume_scaling_factor(); |             scale = volume->get_volume_scaling_factor(); | ||||||
|  |             min_z = get_volume_min_z(volume); | ||||||
|         } |         } | ||||||
|         show_rotation = !rotation.isApprox(Vec3d::Zero()); |         show_rotation = !rotation.isApprox(Vec3d::Zero()); | ||||||
|         show_scale = !scale.isApprox(Vec3d::Ones()); |         show_scale = !scale.isApprox(Vec3d::Ones()); | ||||||
|  |         show_drop_to_bed = (std::abs(min_z) > EPSILON); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     wxGetApp().CallAfter([this, show_rotation, show_scale]{ |     wxGetApp().CallAfter([this, show_rotation, show_scale, show_drop_to_bed]{ | ||||||
|         m_reset_rotation_button->Show(show_rotation); |         m_reset_rotation_button->Show(show_rotation); | ||||||
|         m_reset_scale_button->Show(show_scale); |         m_reset_scale_button->Show(show_scale); | ||||||
|  |         m_drop_to_bed_button->Show(show_drop_to_bed); | ||||||
|     }); |     }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -869,6 +924,7 @@ void ObjectManipulation::msw_rescale() | ||||||
|     m_mirror_bitmap_hidden.msw_rescale(); |     m_mirror_bitmap_hidden.msw_rescale(); | ||||||
|     m_reset_scale_button->msw_rescale(); |     m_reset_scale_button->msw_rescale(); | ||||||
|     m_reset_rotation_button->msw_rescale(); |     m_reset_rotation_button->msw_rescale(); | ||||||
|  |     m_drop_to_bed_button->msw_rescale(); | ||||||
| 
 | 
 | ||||||
|     get_og()->msw_rescale(); |     get_og()->msw_rescale(); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -56,6 +56,7 @@ class ObjectManipulation : public OG_Settings | ||||||
|     // Non-owning pointers to the reset buttons, so we can hide and show them.
 |     // Non-owning pointers to the reset buttons, so we can hide and show them.
 | ||||||
|     ScalableButton* m_reset_scale_button = nullptr; |     ScalableButton* m_reset_scale_button = nullptr; | ||||||
|     ScalableButton* m_reset_rotation_button = nullptr; |     ScalableButton* m_reset_rotation_button = nullptr; | ||||||
|  |     ScalableButton* m_drop_to_bed_button = nullptr; | ||||||
| 
 | 
 | ||||||
|     // Mirroring buttons and their current state
 |     // Mirroring buttons and their current state
 | ||||||
|     enum MirrorButtonState { |     enum MirrorButtonState { | ||||||
|  |  | ||||||
|  | @ -132,18 +132,12 @@ void GLGizmoBase::Grabber::render_face(float half_size) const | ||||||
|     glsafe(::glEnd()); |     glsafe(::glEnd()); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
| GLGizmoBase::GLGizmoBase(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) | GLGizmoBase::GLGizmoBase(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) | ||||||
| #else |  | ||||||
| GLGizmoBase::GLGizmoBase(GLCanvas3D& parent, unsigned int sprite_id) |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
|     : m_parent(parent) |     : m_parent(parent) | ||||||
|     , m_group_id(-1) |     , m_group_id(-1) | ||||||
|     , m_state(Off) |     , m_state(Off) | ||||||
|     , m_shortcut_key(0) |     , m_shortcut_key(0) | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     , m_icon_filename(icon_filename) |     , m_icon_filename(icon_filename) | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
|     , m_sprite_id(sprite_id) |     , m_sprite_id(sprite_id) | ||||||
|     , m_hover_id(-1) |     , m_hover_id(-1) | ||||||
|     , m_dragging(false) |     , m_dragging(false) | ||||||
|  |  | ||||||
|  | @ -89,9 +89,7 @@ protected: | ||||||
|     int m_group_id; |     int m_group_id; | ||||||
|     EState m_state; |     EState m_state; | ||||||
|     int m_shortcut_key; |     int m_shortcut_key; | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     std::string m_icon_filename; |     std::string m_icon_filename; | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
|     unsigned int m_sprite_id; |     unsigned int m_sprite_id; | ||||||
|     int m_hover_id; |     int m_hover_id; | ||||||
|     bool m_dragging; |     bool m_dragging; | ||||||
|  | @ -102,11 +100,7 @@ protected: | ||||||
|     ImGuiWrapper* m_imgui; |     ImGuiWrapper* m_imgui; | ||||||
| 
 | 
 | ||||||
| public: | public: | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     GLGizmoBase(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); |     GLGizmoBase(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); | ||||||
| #else |  | ||||||
|     GLGizmoBase(GLCanvas3D& parent, unsigned int sprite_id); |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
|     virtual ~GLGizmoBase() {} |     virtual ~GLGizmoBase() {} | ||||||
| 
 | 
 | ||||||
|     bool init() { return on_init(); } |     bool init() { return on_init(); } | ||||||
|  | @ -122,9 +116,7 @@ public: | ||||||
|     int get_shortcut_key() const { return m_shortcut_key; } |     int get_shortcut_key() const { return m_shortcut_key; } | ||||||
|     void set_shortcut_key(int key) { m_shortcut_key = key; } |     void set_shortcut_key(int key) { m_shortcut_key = key; } | ||||||
| 
 | 
 | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     const std::string& get_icon_filename() const { return m_icon_filename; } |     const std::string& get_icon_filename() const { return m_icon_filename; } | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
| 
 | 
 | ||||||
|     bool is_activable(const Selection& selection) const { return on_is_activable(selection); } |     bool is_activable(const Selection& selection) const { return on_is_activable(selection); } | ||||||
|     bool is_selectable() const { return on_is_selectable(); } |     bool is_selectable() const { return on_is_selectable(); } | ||||||
|  |  | ||||||
|  | @ -19,13 +19,8 @@ const double GLGizmoCut::Offset = 10.0; | ||||||
| const double GLGizmoCut::Margin = 20.0; | const double GLGizmoCut::Margin = 20.0; | ||||||
| const std::array<float, 3> GLGizmoCut::GrabberColor = { 1.0, 0.5, 0.0 }; | const std::array<float, 3> GLGizmoCut::GrabberColor = { 1.0, 0.5, 0.0 }; | ||||||
| 
 | 
 | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
| GLGizmoCut::GLGizmoCut(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) | GLGizmoCut::GLGizmoCut(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) | ||||||
|     : GLGizmoBase(parent, icon_filename, sprite_id) |     : GLGizmoBase(parent, icon_filename, sprite_id) | ||||||
| #else |  | ||||||
| GLGizmoCut::GLGizmoCut(GLCanvas3D& parent, unsigned int sprite_id) |  | ||||||
|     : GLGizmoBase(parent, sprite_id) |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
|     , m_cut_z(0.0) |     , m_cut_z(0.0) | ||||||
|     , m_max_z(0.0) |     , m_max_z(0.0) | ||||||
|     , m_keep_upper(true) |     , m_keep_upper(true) | ||||||
|  |  | ||||||
|  | @ -23,11 +23,7 @@ class GLGizmoCut : public GLGizmoBase | ||||||
|     bool m_rotate_lower; |     bool m_rotate_lower; | ||||||
| 
 | 
 | ||||||
| public: | public: | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     GLGizmoCut(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); |     GLGizmoCut(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); | ||||||
| #else |  | ||||||
|     GLGizmoCut(GLCanvas3D& parent, unsigned int sprite_id); |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
| 
 | 
 | ||||||
| protected: | protected: | ||||||
|     virtual bool on_init(); |     virtual bool on_init(); | ||||||
|  |  | ||||||
|  | @ -9,13 +9,8 @@ namespace Slic3r { | ||||||
| namespace GUI { | namespace GUI { | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
| GLGizmoFlatten::GLGizmoFlatten(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) | GLGizmoFlatten::GLGizmoFlatten(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) | ||||||
|     : GLGizmoBase(parent, icon_filename, sprite_id) |     : GLGizmoBase(parent, icon_filename, sprite_id) | ||||||
| #else |  | ||||||
| GLGizmoFlatten::GLGizmoFlatten(GLCanvas3D& parent, unsigned int sprite_id) |  | ||||||
|     : GLGizmoBase(parent, sprite_id) |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
|     , m_normal(Vec3d::Zero()) |     , m_normal(Vec3d::Zero()) | ||||||
|     , m_starting_center(Vec3d::Zero()) |     , m_starting_center(Vec3d::Zero()) | ||||||
| { | { | ||||||
|  |  | ||||||
|  | @ -37,11 +37,7 @@ private: | ||||||
|     bool is_plane_update_necessary() const; |     bool is_plane_update_necessary() const; | ||||||
| 
 | 
 | ||||||
| public: | public: | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     GLGizmoFlatten(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); |     GLGizmoFlatten(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); | ||||||
| #else |  | ||||||
|     GLGizmoFlatten(GLCanvas3D& parent, unsigned int sprite_id); |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
| 
 | 
 | ||||||
|     void set_flattening_data(const ModelObject* model_object); |     void set_flattening_data(const ModelObject* model_object); | ||||||
|     Vec3d get_flattening_normal() const; |     Vec3d get_flattening_normal() const; | ||||||
|  |  | ||||||
|  | @ -10,13 +10,8 @@ namespace GUI { | ||||||
| 
 | 
 | ||||||
| const double GLGizmoMove3D::Offset = 10.0; | const double GLGizmoMove3D::Offset = 10.0; | ||||||
| 
 | 
 | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
| GLGizmoMove3D::GLGizmoMove3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) | GLGizmoMove3D::GLGizmoMove3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) | ||||||
|     : GLGizmoBase(parent, icon_filename, sprite_id) |     : GLGizmoBase(parent, icon_filename, sprite_id) | ||||||
| #else |  | ||||||
| GLGizmoMove3D::GLGizmoMove3D(GLCanvas3D& parent, unsigned int sprite_id) |  | ||||||
|     : GLGizmoBase(parent, sprite_id) |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
|     , m_displacement(Vec3d::Zero()) |     , m_displacement(Vec3d::Zero()) | ||||||
|     , m_snap_step(1.0) |     , m_snap_step(1.0) | ||||||
|     , m_starting_drag_position(Vec3d::Zero()) |     , m_starting_drag_position(Vec3d::Zero()) | ||||||
|  |  | ||||||
|  | @ -22,11 +22,7 @@ class GLGizmoMove3D : public GLGizmoBase | ||||||
|     GLUquadricObj* m_quadric; |     GLUquadricObj* m_quadric; | ||||||
| 
 | 
 | ||||||
| public: | public: | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     GLGizmoMove3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); |     GLGizmoMove3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); | ||||||
| #else |  | ||||||
|     GLGizmoMove3D(GLCanvas3D& parent, unsigned int sprite_id); |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
|     virtual ~GLGizmoMove3D(); |     virtual ~GLGizmoMove3D(); | ||||||
| 
 | 
 | ||||||
|     double get_snap_step(double step) const { return m_snap_step; } |     double get_snap_step(double step) const { return m_snap_step; } | ||||||
|  |  | ||||||
|  | @ -19,11 +19,7 @@ const unsigned int GLGizmoRotate::SnapRegionsCount = 8; | ||||||
| const float GLGizmoRotate::GrabberOffset = 0.15f; // in percent of radius
 | const float GLGizmoRotate::GrabberOffset = 0.15f; // in percent of radius
 | ||||||
| 
 | 
 | ||||||
| GLGizmoRotate::GLGizmoRotate(GLCanvas3D& parent, GLGizmoRotate::Axis axis) | GLGizmoRotate::GLGizmoRotate(GLCanvas3D& parent, GLGizmoRotate::Axis axis) | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     : GLGizmoBase(parent, "", -1) |     : GLGizmoBase(parent, "", -1) | ||||||
| #else |  | ||||||
|     : GLGizmoBase(parent, -1) |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
|     , m_axis(axis) |     , m_axis(axis) | ||||||
|     , m_angle(0.0) |     , m_angle(0.0) | ||||||
|     , m_quadric(nullptr) |     , m_quadric(nullptr) | ||||||
|  | @ -40,11 +36,7 @@ GLGizmoRotate::GLGizmoRotate(GLCanvas3D& parent, GLGizmoRotate::Axis axis) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| GLGizmoRotate::GLGizmoRotate(const GLGizmoRotate& other) | GLGizmoRotate::GLGizmoRotate(const GLGizmoRotate& other) | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     : GLGizmoBase(other.m_parent, other.m_icon_filename, other.m_sprite_id) |     : GLGizmoBase(other.m_parent, other.m_icon_filename, other.m_sprite_id) | ||||||
| #else |  | ||||||
|     : GLGizmoBase(other.m_parent, other.m_sprite_id) |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
|     , m_axis(other.m_axis) |     , m_axis(other.m_axis) | ||||||
|     , m_angle(other.m_angle) |     , m_angle(other.m_angle) | ||||||
|     , m_quadric(nullptr) |     , m_quadric(nullptr) | ||||||
|  | @ -417,13 +409,8 @@ Vec3d GLGizmoRotate::mouse_position_in_local_plane(const Linef3& mouse_ray, cons | ||||||
|     return transform(mouse_ray, m).intersect_plane(0.0); |     return transform(mouse_ray, m).intersect_plane(0.0); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
| GLGizmoRotate3D::GLGizmoRotate3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) | GLGizmoRotate3D::GLGizmoRotate3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) | ||||||
|     : GLGizmoBase(parent, icon_filename, sprite_id) |     : GLGizmoBase(parent, icon_filename, sprite_id) | ||||||
| #else |  | ||||||
| GLGizmoRotate3D::GLGizmoRotate3D(GLCanvas3D& parent, unsigned int sprite_id) |  | ||||||
|     : GLGizmoBase(parent, sprite_id) |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
| { | { | ||||||
|     m_gizmos.emplace_back(parent, GLGizmoRotate::X); |     m_gizmos.emplace_back(parent, GLGizmoRotate::X); | ||||||
|     m_gizmos.emplace_back(parent, GLGizmoRotate::Y); |     m_gizmos.emplace_back(parent, GLGizmoRotate::Y); | ||||||
|  |  | ||||||
|  | @ -76,11 +76,7 @@ class GLGizmoRotate3D : public GLGizmoBase | ||||||
|     std::vector<GLGizmoRotate> m_gizmos; |     std::vector<GLGizmoRotate> m_gizmos; | ||||||
| 
 | 
 | ||||||
| public: | public: | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     GLGizmoRotate3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); |     GLGizmoRotate3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); | ||||||
| #else |  | ||||||
|     GLGizmoRotate3D(GLCanvas3D& parent, unsigned int sprite_id); |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
| 
 | 
 | ||||||
|     Vec3d get_rotation() const { return Vec3d(m_gizmos[X].get_angle(), m_gizmos[Y].get_angle(), m_gizmos[Z].get_angle()); } |     Vec3d get_rotation() const { return Vec3d(m_gizmos[X].get_angle(), m_gizmos[Y].get_angle(), m_gizmos[Z].get_angle()); } | ||||||
|     void set_rotation(const Vec3d& rotation) { m_gizmos[X].set_angle(rotation(0)); m_gizmos[Y].set_angle(rotation(1)); m_gizmos[Z].set_angle(rotation(2)); } |     void set_rotation(const Vec3d& rotation) { m_gizmos[X].set_angle(rotation(0)); m_gizmos[Y].set_angle(rotation(1)); m_gizmos[Z].set_angle(rotation(2)); } | ||||||
|  |  | ||||||
|  | @ -13,13 +13,8 @@ namespace GUI { | ||||||
| 
 | 
 | ||||||
| const float GLGizmoScale3D::Offset = 5.0f; | const float GLGizmoScale3D::Offset = 5.0f; | ||||||
| 
 | 
 | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
| GLGizmoScale3D::GLGizmoScale3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) | GLGizmoScale3D::GLGizmoScale3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) | ||||||
|     : GLGizmoBase(parent, icon_filename, sprite_id) |     : GLGizmoBase(parent, icon_filename, sprite_id) | ||||||
| #else |  | ||||||
| GLGizmoScale3D::GLGizmoScale3D(GLCanvas3D& parent, unsigned int sprite_id) |  | ||||||
|     : GLGizmoBase(parent, sprite_id) |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
|     , m_scale(Vec3d::Ones()) |     , m_scale(Vec3d::Ones()) | ||||||
|     , m_offset(Vec3d::Zero()) |     , m_offset(Vec3d::Zero()) | ||||||
|     , m_snap_step(0.05) |     , m_snap_step(0.05) | ||||||
|  |  | ||||||
|  | @ -32,11 +32,7 @@ class GLGizmoScale3D : public GLGizmoBase | ||||||
|     StartingData m_starting; |     StartingData m_starting; | ||||||
| 
 | 
 | ||||||
| public: | public: | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     GLGizmoScale3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); |     GLGizmoScale3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); | ||||||
| #else |  | ||||||
|     GLGizmoScale3D(GLCanvas3D& parent, unsigned int sprite_id); |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
| 
 | 
 | ||||||
|     double get_snap_step(double step) const { return m_snap_step; } |     double get_snap_step(double step) const { return m_snap_step; } | ||||||
|     void set_snap_step(double step) { m_snap_step = step; } |     void set_snap_step(double step) { m_snap_step = step; } | ||||||
|  |  | ||||||
|  | @ -19,13 +19,8 @@ | ||||||
| namespace Slic3r { | namespace Slic3r { | ||||||
| namespace GUI { | namespace GUI { | ||||||
| 
 | 
 | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
| GLGizmoSlaSupports::GLGizmoSlaSupports(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) | GLGizmoSlaSupports::GLGizmoSlaSupports(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) | ||||||
|     : GLGizmoBase(parent, icon_filename, sprite_id) |     : GLGizmoBase(parent, icon_filename, sprite_id) | ||||||
| #else |  | ||||||
| GLGizmoSlaSupports::GLGizmoSlaSupports(GLCanvas3D& parent, unsigned int sprite_id) |  | ||||||
|     : GLGizmoBase(parent, sprite_id) |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
|     , m_quadric(nullptr) |     , m_quadric(nullptr) | ||||||
|     , m_its(nullptr) |     , m_its(nullptr) | ||||||
| { | { | ||||||
|  |  | ||||||
|  | @ -58,11 +58,7 @@ private: | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
| public: | public: | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     GLGizmoSlaSupports(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); |     GLGizmoSlaSupports(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); | ||||||
| #else |  | ||||||
|     GLGizmoSlaSupports(GLCanvas3D& parent, unsigned int sprite_id); |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
|     virtual ~GLGizmoSlaSupports(); |     virtual ~GLGizmoSlaSupports(); | ||||||
|     void set_sla_support_data(ModelObject* model_object, const Selection& selection); |     void set_sla_support_data(ModelObject* model_object, const Selection& selection); | ||||||
|     bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down); |     bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down); | ||||||
|  |  | ||||||
|  | @ -12,17 +12,12 @@ | ||||||
| namespace Slic3r { | namespace Slic3r { | ||||||
| namespace GUI { | namespace GUI { | ||||||
| 
 | 
 | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
| const float GLGizmosManager::Default_Icons_Size = 64; | const float GLGizmosManager::Default_Icons_Size = 64; | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
| 
 | 
 | ||||||
| GLGizmosManager::GLGizmosManager() | GLGizmosManager::GLGizmosManager() | ||||||
|     : m_enabled(false) |     : m_enabled(false) | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     , m_icons_texture_dirty(true) |     , m_icons_texture_dirty(true) | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
|     , m_current(Undefined) |     , m_current(Undefined) | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     , m_overlay_icons_size(Default_Icons_Size) |     , m_overlay_icons_size(Default_Icons_Size) | ||||||
|     , m_overlay_scale(1.0f) |     , m_overlay_scale(1.0f) | ||||||
|     , m_overlay_border(5.0f) |     , m_overlay_border(5.0f) | ||||||
|  | @ -30,11 +25,6 @@ GLGizmosManager::GLGizmosManager() | ||||||
|     , m_tooltip("") |     , m_tooltip("") | ||||||
| { | { | ||||||
| } | } | ||||||
| #else |  | ||||||
| { |  | ||||||
|     set_overlay_scale(1.0); |  | ||||||
| } |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
| 
 | 
 | ||||||
| GLGizmosManager::~GLGizmosManager() | GLGizmosManager::~GLGizmosManager() | ||||||
| { | { | ||||||
|  | @ -43,20 +33,6 @@ GLGizmosManager::~GLGizmosManager() | ||||||
| 
 | 
 | ||||||
| bool GLGizmosManager::init(GLCanvas3D& parent) | bool GLGizmosManager::init(GLCanvas3D& parent) | ||||||
| { | { | ||||||
| #if !ENABLE_SVG_ICONS |  | ||||||
|     m_icons_texture.metadata.filename = "gizmos.png"; |  | ||||||
|     m_icons_texture.metadata.icon_size = 64; |  | ||||||
| 
 |  | ||||||
|     if (!m_icons_texture.metadata.filename.empty()) |  | ||||||
|     { |  | ||||||
|         if (!m_icons_texture.texture.load_from_file(resources_dir() + "/icons/" + m_icons_texture.metadata.filename, false, true)) |  | ||||||
|         { |  | ||||||
|             reset(); |  | ||||||
|             return false; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| #endif // !ENABLE_SVG_ICONS
 |  | ||||||
| 
 |  | ||||||
|     m_background_texture.metadata.filename = "toolbar_background.png"; |     m_background_texture.metadata.filename = "toolbar_background.png"; | ||||||
|     m_background_texture.metadata.left = 16; |     m_background_texture.metadata.left = 16; | ||||||
|     m_background_texture.metadata.top = 16; |     m_background_texture.metadata.top = 16; | ||||||
|  | @ -72,11 +48,7 @@ bool GLGizmosManager::init(GLCanvas3D& parent) | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     GLGizmoBase* gizmo = new GLGizmoMove3D(parent, "move.svg", 0); |     GLGizmoBase* gizmo = new GLGizmoMove3D(parent, "move.svg", 0); | ||||||
| #else |  | ||||||
|     GLGizmoBase* gizmo = new GLGizmoMove3D(parent, 0); |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
|     if (gizmo == nullptr) |     if (gizmo == nullptr) | ||||||
|         return false; |         return false; | ||||||
| 
 | 
 | ||||||
|  | @ -85,11 +57,7 @@ bool GLGizmosManager::init(GLCanvas3D& parent) | ||||||
| 
 | 
 | ||||||
|     m_gizmos.insert(GizmosMap::value_type(Move, gizmo)); |     m_gizmos.insert(GizmosMap::value_type(Move, gizmo)); | ||||||
| 
 | 
 | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     gizmo = new GLGizmoScale3D(parent, "scale.svg", 1); |     gizmo = new GLGizmoScale3D(parent, "scale.svg", 1); | ||||||
| #else |  | ||||||
|     gizmo = new GLGizmoScale3D(parent, 1); |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
|     if (gizmo == nullptr) |     if (gizmo == nullptr) | ||||||
|         return false; |         return false; | ||||||
| 
 | 
 | ||||||
|  | @ -98,11 +66,7 @@ bool GLGizmosManager::init(GLCanvas3D& parent) | ||||||
| 
 | 
 | ||||||
|     m_gizmos.insert(GizmosMap::value_type(Scale, gizmo)); |     m_gizmos.insert(GizmosMap::value_type(Scale, gizmo)); | ||||||
| 
 | 
 | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     gizmo = new GLGizmoRotate3D(parent, "rotate.svg", 2); |     gizmo = new GLGizmoRotate3D(parent, "rotate.svg", 2); | ||||||
| #else |  | ||||||
|     gizmo = new GLGizmoRotate3D(parent, 2); |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
|     if (gizmo == nullptr) |     if (gizmo == nullptr) | ||||||
|     { |     { | ||||||
|         reset(); |         reset(); | ||||||
|  | @ -117,11 +81,7 @@ bool GLGizmosManager::init(GLCanvas3D& parent) | ||||||
| 
 | 
 | ||||||
|     m_gizmos.insert(GizmosMap::value_type(Rotate, gizmo)); |     m_gizmos.insert(GizmosMap::value_type(Rotate, gizmo)); | ||||||
| 
 | 
 | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     gizmo = new GLGizmoFlatten(parent, "place.svg", 3); |     gizmo = new GLGizmoFlatten(parent, "place.svg", 3); | ||||||
| #else |  | ||||||
|     gizmo = new GLGizmoFlatten(parent, 3); |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
|     if (gizmo == nullptr) |     if (gizmo == nullptr) | ||||||
|         return false; |         return false; | ||||||
| 
 | 
 | ||||||
|  | @ -132,11 +92,7 @@ bool GLGizmosManager::init(GLCanvas3D& parent) | ||||||
| 
 | 
 | ||||||
|     m_gizmos.insert(GizmosMap::value_type(Flatten, gizmo)); |     m_gizmos.insert(GizmosMap::value_type(Flatten, gizmo)); | ||||||
| 
 | 
 | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     gizmo = new GLGizmoCut(parent, "cut.svg", 4); |     gizmo = new GLGizmoCut(parent, "cut.svg", 4); | ||||||
| #else |  | ||||||
|     gizmo = new GLGizmoCut(parent, 4); |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
|     if (gizmo == nullptr) |     if (gizmo == nullptr) | ||||||
|         return false; |         return false; | ||||||
| 
 | 
 | ||||||
|  | @ -147,11 +103,7 @@ bool GLGizmosManager::init(GLCanvas3D& parent) | ||||||
| 
 | 
 | ||||||
|     m_gizmos.insert(GizmosMap::value_type(Cut, gizmo)); |     m_gizmos.insert(GizmosMap::value_type(Cut, gizmo)); | ||||||
| 
 | 
 | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     gizmo = new GLGizmoSlaSupports(parent, "sla_supports.svg", 5); |     gizmo = new GLGizmoSlaSupports(parent, "sla_supports.svg", 5); | ||||||
| #else |  | ||||||
|     gizmo = new GLGizmoSlaSupports(parent, 5); |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
|     if (gizmo == nullptr) |     if (gizmo == nullptr) | ||||||
|         return false; |         return false; | ||||||
| 
 | 
 | ||||||
|  | @ -165,7 +117,6 @@ bool GLGizmosManager::init(GLCanvas3D& parent) | ||||||
|     return true; |     return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
| void GLGizmosManager::set_overlay_icon_size(float size) | void GLGizmosManager::set_overlay_icon_size(float size) | ||||||
| { | { | ||||||
|     if (m_overlay_icons_size != size) |     if (m_overlay_icons_size != size) | ||||||
|  | @ -174,21 +125,14 @@ void GLGizmosManager::set_overlay_icon_size(float size) | ||||||
|         m_icons_texture_dirty = true; |         m_icons_texture_dirty = true; | ||||||
|     } |     } | ||||||
| } | } | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
| 
 | 
 | ||||||
| void GLGizmosManager::set_overlay_scale(float scale) | void GLGizmosManager::set_overlay_scale(float scale) | ||||||
| { | { | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     if (m_overlay_scale != scale) |     if (m_overlay_scale != scale) | ||||||
|     { |     { | ||||||
|         m_overlay_scale = scale; |         m_overlay_scale = scale; | ||||||
|         m_icons_texture_dirty = true; |         m_icons_texture_dirty = true; | ||||||
|     } |     } | ||||||
| #else |  | ||||||
|     m_overlay_icons_scale = scale; |  | ||||||
|     m_overlay_border = 5.0f * scale; |  | ||||||
|     m_overlay_gap_y = 5.0f * scale; |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GLGizmosManager::refresh_on_off_state(const Selection& selection) | void GLGizmosManager::refresh_on_off_state(const Selection& selection) | ||||||
|  | @ -526,10 +470,8 @@ void GLGizmosManager::render_overlay(const GLCanvas3D& canvas, const Selection& | ||||||
|     if (!m_enabled) |     if (!m_enabled) | ||||||
|         return; |         return; | ||||||
| 
 | 
 | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     if (m_icons_texture_dirty) |     if (m_icons_texture_dirty) | ||||||
|         generate_icons_texture(); |         generate_icons_texture(); | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
| 
 | 
 | ||||||
|     do_render_overlay(canvas, selection); |     do_render_overlay(canvas, selection); | ||||||
| } | } | ||||||
|  | @ -935,11 +877,7 @@ void GLGizmosManager::do_render_overlay(const GLCanvas3D& canvas, const Selectio | ||||||
| 
 | 
 | ||||||
|     float height = get_total_overlay_height(); |     float height = get_total_overlay_height(); | ||||||
|     float width = get_total_overlay_width(); |     float width = get_total_overlay_width(); | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     float scaled_border = m_overlay_border * m_overlay_scale * inv_zoom; |     float scaled_border = m_overlay_border * m_overlay_scale * inv_zoom; | ||||||
| #else |  | ||||||
|     float scaled_border = m_overlay_border * inv_zoom; |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
| 
 | 
 | ||||||
|     float top_x = (-0.5f * cnv_w) * inv_zoom; |     float top_x = (-0.5f * cnv_w) * inv_zoom; | ||||||
|     float top_y = (0.5f * height) * inv_zoom; |     float top_y = (0.5f * height) * inv_zoom; | ||||||
|  | @ -1015,7 +953,6 @@ void GLGizmosManager::do_render_overlay(const GLCanvas3D& canvas, const Selectio | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     top_x += scaled_border; |     top_x += scaled_border; | ||||||
|     top_y -= scaled_border; |     top_y -= scaled_border; | ||||||
|     float scaled_gap_y = m_overlay_gap_y * m_overlay_scale * inv_zoom; |     float scaled_gap_y = m_overlay_gap_y * m_overlay_scale * inv_zoom; | ||||||
|  | @ -1027,21 +964,9 @@ void GLGizmosManager::do_render_overlay(const GLCanvas3D& canvas, const Selectio | ||||||
|     unsigned int tex_height = m_icons_texture.get_height(); |     unsigned int tex_height = m_icons_texture.get_height(); | ||||||
|     float inv_tex_width = (tex_width != 0) ? 1.0f / (float)tex_width : 0.0f; |     float inv_tex_width = (tex_width != 0) ? 1.0f / (float)tex_width : 0.0f; | ||||||
|     float inv_tex_height = (tex_height != 0) ? 1.0f / (float)tex_height : 0.0f; |     float inv_tex_height = (tex_height != 0) ? 1.0f / (float)tex_height : 0.0f; | ||||||
| #else |  | ||||||
|     top_x += m_overlay_border * inv_zoom; |  | ||||||
|     top_y -= m_overlay_border * inv_zoom; |  | ||||||
|     float scaled_gap_y = m_overlay_gap_y * inv_zoom; |  | ||||||
| 
 | 
 | ||||||
|     float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * m_overlay_icons_scale * inv_zoom; |  | ||||||
|     unsigned int icons_texture_id = m_icons_texture.texture.get_id(); |  | ||||||
|     unsigned int texture_size = m_icons_texture.texture.get_width(); |  | ||||||
|     float inv_texture_size = (texture_size != 0) ? 1.0f / (float)texture_size : 0.0f; |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
| 
 |  | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     if ((icons_texture_id == 0) || (tex_width <= 0) || (tex_height <= 0)) |     if ((icons_texture_id == 0) || (tex_width <= 0) || (tex_height <= 0)) | ||||||
|         return; |         return; | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
| 
 | 
 | ||||||
|     for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) |     for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) | ||||||
|     { |     { | ||||||
|  | @ -1051,78 +976,44 @@ void GLGizmosManager::do_render_overlay(const GLCanvas3D& canvas, const Selectio | ||||||
|         unsigned int sprite_id = it->second->get_sprite_id(); |         unsigned int sprite_id = it->second->get_sprite_id(); | ||||||
|         GLGizmoBase::EState state = it->second->get_state(); |         GLGizmoBase::EState state = it->second->get_state(); | ||||||
| 
 | 
 | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|         float u_icon_size = m_overlay_icons_size * m_overlay_scale * inv_tex_width; |         float u_icon_size = m_overlay_icons_size * m_overlay_scale * inv_tex_width; | ||||||
|         float v_icon_size = m_overlay_icons_size * m_overlay_scale * inv_tex_height; |         float v_icon_size = m_overlay_icons_size * m_overlay_scale * inv_tex_height; | ||||||
|         float v_top = sprite_id * v_icon_size; |         float v_top = sprite_id * v_icon_size; | ||||||
|         float u_left = state * u_icon_size; |         float u_left = state * u_icon_size; | ||||||
|         float v_bottom = v_top + v_icon_size; |         float v_bottom = v_top + v_icon_size; | ||||||
|         float u_right = u_left + u_icon_size; |         float u_right = u_left + u_icon_size; | ||||||
| #else |  | ||||||
|         float uv_icon_size = (float)m_icons_texture.metadata.icon_size * inv_texture_size; |  | ||||||
|         float v_top = sprite_id * uv_icon_size; |  | ||||||
|         float u_left = state * uv_icon_size; |  | ||||||
|         float v_bottom = v_top + uv_icon_size; |  | ||||||
|         float u_right = u_left + uv_icon_size; |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
| 
 | 
 | ||||||
|         GLTexture::render_sub_texture(icons_texture_id, top_x, top_x + scaled_icons_size, top_y - scaled_icons_size, top_y, { { u_left, v_bottom }, { u_right, v_bottom }, { u_right, v_top }, { u_left, v_top } }); |         GLTexture::render_sub_texture(icons_texture_id, top_x, top_x + scaled_icons_size, top_y - scaled_icons_size, top_y, { { u_left, v_bottom }, { u_right, v_bottom }, { u_right, v_top }, { u_left, v_top } }); | ||||||
|         if (it->second->get_state() == GLGizmoBase::On) { |         if (it->second->get_state() == GLGizmoBase::On) { | ||||||
|             float toolbar_top = (float)cnv_h - canvas.get_view_toolbar_height(); |             float toolbar_top = (float)cnv_h - canvas.get_view_toolbar_height(); | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|             it->second->render_input_window(width, 0.5f * cnv_h - top_y * zoom, toolbar_top, selection); |             it->second->render_input_window(width, 0.5f * cnv_h - top_y * zoom, toolbar_top, selection); | ||||||
| #else |  | ||||||
|             it->second->render_input_window(2.0f * m_overlay_border + scaled_icons_size * zoom, 0.5f * cnv_h - top_y * zoom, toolbar_top, selection); |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
|         } |         } | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|         top_y -= scaled_stride_y; |         top_y -= scaled_stride_y; | ||||||
| #else |  | ||||||
|         top_y -= (scaled_icons_size + scaled_gap_y); |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| float GLGizmosManager::get_total_overlay_height() const | float GLGizmosManager::get_total_overlay_height() const | ||||||
| { | { | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     float scaled_icons_size = m_overlay_icons_size * m_overlay_scale; |     float scaled_icons_size = m_overlay_icons_size * m_overlay_scale; | ||||||
|     float scaled_border = m_overlay_border * m_overlay_scale; |     float scaled_border = m_overlay_border * m_overlay_scale; | ||||||
|     float scaled_gap_y = m_overlay_gap_y * m_overlay_scale; |     float scaled_gap_y = m_overlay_gap_y * m_overlay_scale; | ||||||
|     float scaled_stride_y = scaled_icons_size + scaled_gap_y; |     float scaled_stride_y = scaled_icons_size + scaled_gap_y; | ||||||
|     float height = 2.0f * scaled_border; |     float height = 2.0f * scaled_border; | ||||||
| #else |  | ||||||
|     float height = 2.0f * m_overlay_border; |  | ||||||
| 
 |  | ||||||
|     float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * m_overlay_icons_scale; |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
| 
 | 
 | ||||||
|     for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) |     for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) | ||||||
|     { |     { | ||||||
|         if ((it->second == nullptr) || !it->second->is_selectable()) |         if ((it->second == nullptr) || !it->second->is_selectable()) | ||||||
|             continue; |             continue; | ||||||
| 
 | 
 | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|         height += scaled_stride_y; |         height += scaled_stride_y; | ||||||
| #else |  | ||||||
|         height += (scaled_icons_size + m_overlay_gap_y); |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     return height - scaled_gap_y; |     return height - scaled_gap_y; | ||||||
| #else |  | ||||||
|     return height - m_overlay_gap_y; |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| float GLGizmosManager::get_total_overlay_width() const | float GLGizmosManager::get_total_overlay_width() const | ||||||
| { | { | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     return (2.0f * m_overlay_border + m_overlay_icons_size) * m_overlay_scale; |     return (2.0f * m_overlay_border + m_overlay_icons_size) * m_overlay_scale; | ||||||
| #else |  | ||||||
|     return (float)m_icons_texture.metadata.icon_size * m_overlay_icons_scale + 2.0f * m_overlay_border; |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| GLGizmoBase* GLGizmosManager::get_current() const | GLGizmoBase* GLGizmosManager::get_current() const | ||||||
|  | @ -1131,7 +1022,6 @@ GLGizmoBase* GLGizmosManager::get_current() const | ||||||
|     return (it != m_gizmos.end()) ? it->second : nullptr; |     return (it != m_gizmos.end()) ? it->second : nullptr; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
| bool GLGizmosManager::generate_icons_texture() const | bool GLGizmosManager::generate_icons_texture() const | ||||||
| { | { | ||||||
|     std::string path = resources_dir() + "/icons/"; |     std::string path = resources_dir() + "/icons/"; | ||||||
|  | @ -1157,7 +1047,6 @@ bool GLGizmosManager::generate_icons_texture() const | ||||||
| 
 | 
 | ||||||
|     return res; |     return res; | ||||||
| } | } | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
| 
 | 
 | ||||||
| void GLGizmosManager::update_on_off_state(const GLCanvas3D& canvas, const Vec2d& mouse_pos, const Selection& selection) | void GLGizmosManager::update_on_off_state(const GLCanvas3D& canvas, const Vec2d& mouse_pos, const Selection& selection) | ||||||
| { | { | ||||||
|  | @ -1167,27 +1056,18 @@ void GLGizmosManager::update_on_off_state(const GLCanvas3D& canvas, const Vec2d& | ||||||
|     float cnv_h = (float)canvas.get_canvas_size().get_height(); |     float cnv_h = (float)canvas.get_canvas_size().get_height(); | ||||||
|     float height = get_total_overlay_height(); |     float height = get_total_overlay_height(); | ||||||
| 
 | 
 | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     float scaled_icons_size = m_overlay_icons_size * m_overlay_scale; |     float scaled_icons_size = m_overlay_icons_size * m_overlay_scale; | ||||||
|     float scaled_border = m_overlay_border * m_overlay_scale; |     float scaled_border = m_overlay_border * m_overlay_scale; | ||||||
|     float scaled_gap_y = m_overlay_gap_y * m_overlay_scale; |     float scaled_gap_y = m_overlay_gap_y * m_overlay_scale; | ||||||
|     float scaled_stride_y = scaled_icons_size + scaled_gap_y; |     float scaled_stride_y = scaled_icons_size + scaled_gap_y; | ||||||
|     float top_y = 0.5f * (cnv_h - height) + scaled_border; |     float top_y = 0.5f * (cnv_h - height) + scaled_border; | ||||||
| #else |  | ||||||
|     float top_y = 0.5f * (cnv_h - height) + m_overlay_border; |  | ||||||
|     float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * m_overlay_icons_scale; |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
| 
 | 
 | ||||||
|     for (GizmosMap::iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) |     for (GizmosMap::iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) | ||||||
|     { |     { | ||||||
|         if ((it->second == nullptr) || !it->second->is_selectable()) |         if ((it->second == nullptr) || !it->second->is_selectable()) | ||||||
|             continue; |             continue; | ||||||
| 
 | 
 | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|         bool inside = (scaled_border <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= scaled_border + scaled_icons_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + scaled_icons_size); |         bool inside = (scaled_border <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= scaled_border + scaled_icons_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + scaled_icons_size); | ||||||
| #else |  | ||||||
|         bool inside = (m_overlay_border <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= m_overlay_border + scaled_icons_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + scaled_icons_size); |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
|         if (it->second->is_activable(selection) && inside) |         if (it->second->is_activable(selection) && inside) | ||||||
|         { |         { | ||||||
|             if ((it->second->get_state() == GLGizmoBase::On)) |             if ((it->second->get_state() == GLGizmoBase::On)) | ||||||
|  | @ -1204,11 +1084,7 @@ void GLGizmosManager::update_on_off_state(const GLCanvas3D& canvas, const Vec2d& | ||||||
|         else |         else | ||||||
|             it->second->set_state(GLGizmoBase::Off); |             it->second->set_state(GLGizmoBase::Off); | ||||||
| 
 | 
 | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|         top_y += scaled_stride_y; |         top_y += scaled_stride_y; | ||||||
| #else |  | ||||||
|         top_y += (scaled_icons_size + m_overlay_gap_y); |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     GizmosMap::iterator it = m_gizmos.find(m_current); |     GizmosMap::iterator it = m_gizmos.find(m_current); | ||||||
|  | @ -1227,38 +1103,25 @@ std::string GLGizmosManager::update_hover_state(const GLCanvas3D& canvas, const | ||||||
| 
 | 
 | ||||||
|     float cnv_h = (float)canvas.get_canvas_size().get_height(); |     float cnv_h = (float)canvas.get_canvas_size().get_height(); | ||||||
|     float height = get_total_overlay_height(); |     float height = get_total_overlay_height(); | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     float scaled_icons_size = m_overlay_icons_size * m_overlay_scale; |     float scaled_icons_size = m_overlay_icons_size * m_overlay_scale; | ||||||
|     float scaled_border = m_overlay_border * m_overlay_scale; |     float scaled_border = m_overlay_border * m_overlay_scale; | ||||||
|     float scaled_gap_y = m_overlay_gap_y * m_overlay_scale; |     float scaled_gap_y = m_overlay_gap_y * m_overlay_scale; | ||||||
|     float scaled_stride_y = scaled_icons_size + scaled_gap_y; |     float scaled_stride_y = scaled_icons_size + scaled_gap_y; | ||||||
|     float top_y = 0.5f * (cnv_h - height) + scaled_border; |     float top_y = 0.5f * (cnv_h - height) + scaled_border; | ||||||
| #else |  | ||||||
|     float top_y = 0.5f * (cnv_h - height) + m_overlay_border; |  | ||||||
|     float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * m_overlay_icons_scale; |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
| 
 | 
 | ||||||
|     for (GizmosMap::iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) |     for (GizmosMap::iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) | ||||||
|     { |     { | ||||||
|         if ((it->second == nullptr) || !it->second->is_selectable()) |         if ((it->second == nullptr) || !it->second->is_selectable()) | ||||||
|             continue; |             continue; | ||||||
| 
 | 
 | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|         bool inside = (scaled_border <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= scaled_border + scaled_icons_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + scaled_icons_size); |         bool inside = (scaled_border <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= scaled_border + scaled_icons_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + scaled_icons_size); | ||||||
| #else |  | ||||||
|         bool inside = (m_overlay_border <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= m_overlay_border + scaled_icons_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + scaled_icons_size); |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
|         if (inside) |         if (inside) | ||||||
|             name = it->second->get_name(); |             name = it->second->get_name(); | ||||||
| 
 | 
 | ||||||
|         if (it->second->is_activable(selection) && (it->second->get_state() != GLGizmoBase::On)) |         if (it->second->is_activable(selection) && (it->second->get_state() != GLGizmoBase::On)) | ||||||
|             it->second->set_state(inside ? GLGizmoBase::Hover : GLGizmoBase::Off); |             it->second->set_state(inside ? GLGizmoBase::Hover : GLGizmoBase::Off); | ||||||
| 
 | 
 | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|         top_y += scaled_stride_y; |         top_y += scaled_stride_y; | ||||||
| #else |  | ||||||
|         top_y += (scaled_icons_size + m_overlay_gap_y); |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return name; |     return name; | ||||||
|  | @ -1272,34 +1135,21 @@ bool GLGizmosManager::overlay_contains_mouse(const GLCanvas3D& canvas, const Vec | ||||||
|     float cnv_h = (float)canvas.get_canvas_size().get_height(); |     float cnv_h = (float)canvas.get_canvas_size().get_height(); | ||||||
|     float height = get_total_overlay_height(); |     float height = get_total_overlay_height(); | ||||||
| 
 | 
 | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     float scaled_icons_size = m_overlay_icons_size * m_overlay_scale; |     float scaled_icons_size = m_overlay_icons_size * m_overlay_scale; | ||||||
|     float scaled_border = m_overlay_border * m_overlay_scale; |     float scaled_border = m_overlay_border * m_overlay_scale; | ||||||
|     float scaled_gap_y = m_overlay_gap_y * m_overlay_scale; |     float scaled_gap_y = m_overlay_gap_y * m_overlay_scale; | ||||||
|     float scaled_stride_y = scaled_icons_size + scaled_gap_y; |     float scaled_stride_y = scaled_icons_size + scaled_gap_y; | ||||||
|     float top_y = 0.5f * (cnv_h - height) + scaled_border; |     float top_y = 0.5f * (cnv_h - height) + scaled_border; | ||||||
| #else |  | ||||||
|     float top_y = 0.5f * (cnv_h - height) + m_overlay_border; |  | ||||||
|     float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * m_overlay_icons_scale; |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
| 
 | 
 | ||||||
|     for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) |     for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) | ||||||
|     { |     { | ||||||
|         if ((it->second == nullptr) || !it->second->is_selectable()) |         if ((it->second == nullptr) || !it->second->is_selectable()) | ||||||
|             continue; |             continue; | ||||||
| 
 | 
 | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|         if ((scaled_border <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= scaled_border + scaled_icons_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + scaled_icons_size)) |         if ((scaled_border <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= scaled_border + scaled_icons_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + scaled_icons_size)) | ||||||
| #else |  | ||||||
|         if ((m_overlay_border <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= m_overlay_border + scaled_icons_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + scaled_icons_size)) |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
|             return true; |             return true; | ||||||
| 
 | 
 | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|         top_y += scaled_stride_y; |         top_y += scaled_stride_y; | ||||||
| #else |  | ||||||
|         top_y += (scaled_icons_size + m_overlay_gap_y); |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return false; |     return false; | ||||||
|  |  | ||||||
|  | @ -46,9 +46,7 @@ public: | ||||||
| class GLGizmosManager | class GLGizmosManager | ||||||
| { | { | ||||||
| public: | public: | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     static const float Default_Icons_Size; |     static const float Default_Icons_Size; | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
| 
 | 
 | ||||||
|     enum EType : unsigned char |     enum EType : unsigned char | ||||||
|     { |     { | ||||||
|  | @ -66,21 +64,13 @@ private: | ||||||
|     bool m_enabled; |     bool m_enabled; | ||||||
|     typedef std::map<EType, GLGizmoBase*> GizmosMap; |     typedef std::map<EType, GLGizmoBase*> GizmosMap; | ||||||
|     GizmosMap m_gizmos; |     GizmosMap m_gizmos; | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     mutable GLTexture m_icons_texture; |     mutable GLTexture m_icons_texture; | ||||||
|     mutable bool m_icons_texture_dirty; |     mutable bool m_icons_texture_dirty; | ||||||
| #else |  | ||||||
|     ItemsIconsTexture m_icons_texture; |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
|     BackgroundTexture m_background_texture; |     BackgroundTexture m_background_texture; | ||||||
|     EType m_current; |     EType m_current; | ||||||
| 
 | 
 | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     float m_overlay_icons_size; |     float m_overlay_icons_size; | ||||||
|     float m_overlay_scale; |     float m_overlay_scale; | ||||||
| #else |  | ||||||
|     float m_overlay_icons_scale; |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
|     float m_overlay_border; |     float m_overlay_border; | ||||||
|     float m_overlay_gap_y; |     float m_overlay_gap_y; | ||||||
| 
 | 
 | ||||||
|  | @ -109,9 +99,7 @@ public: | ||||||
|     bool is_enabled() const { return m_enabled; } |     bool is_enabled() const { return m_enabled; } | ||||||
|     void set_enabled(bool enable) { m_enabled = enable; } |     void set_enabled(bool enable) { m_enabled = enable; } | ||||||
| 
 | 
 | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     void set_overlay_icon_size(float size); |     void set_overlay_icon_size(float size); | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
|     void set_overlay_scale(float scale); |     void set_overlay_scale(float scale); | ||||||
| 
 | 
 | ||||||
|     void refresh_on_off_state(const Selection& selection); |     void refresh_on_off_state(const Selection& selection); | ||||||
|  | @ -173,9 +161,7 @@ private: | ||||||
| 
 | 
 | ||||||
|     GLGizmoBase* get_current() const; |     GLGizmoBase* get_current() const; | ||||||
| 
 | 
 | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     bool generate_icons_texture() const; |     bool generate_icons_texture() const; | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
| 
 | 
 | ||||||
|     void update_on_off_state(const GLCanvas3D& canvas, const Vec2d& mouse_pos, const Selection& selection); |     void update_on_off_state(const GLCanvas3D& canvas, const Vec2d& mouse_pos, const Selection& selection); | ||||||
|     std::string update_hover_state(const GLCanvas3D& canvas, const Vec2d& mouse_pos); |     std::string update_hover_state(const GLCanvas3D& canvas, const Vec2d& mouse_pos); | ||||||
|  |  | ||||||
|  | @ -35,6 +35,7 @@ namespace GUI { | ||||||
| MainFrame::MainFrame() : | MainFrame::MainFrame() : | ||||||
| DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE, "mainframe"), | DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE, "mainframe"), | ||||||
|     m_printhost_queue_dlg(new PrintHostQueueDialog(this)) |     m_printhost_queue_dlg(new PrintHostQueueDialog(this)) | ||||||
|  |     , m_recent_projects(9) | ||||||
| { | { | ||||||
|     // Fonts were created by the DPIFrame constructor for the monitor, on which the window opened.
 |     // Fonts were created by the DPIFrame constructor for the monitor, on which the window opened.
 | ||||||
|     wxGetApp().update_fonts(this); |     wxGetApp().update_fonts(this); | ||||||
|  | @ -383,6 +384,40 @@ void MainFrame::init_menubar() | ||||||
|         append_menu_item(fileMenu, wxID_ANY, _(L("&Open Project")) + dots + "\tCtrl+O", _(L("Open a project file")), |         append_menu_item(fileMenu, wxID_ANY, _(L("&Open Project")) + dots + "\tCtrl+O", _(L("Open a project file")), | ||||||
|             [this](wxCommandEvent&) { if (m_plater) m_plater->load_project(); }, menu_icon("open"), nullptr, |             [this](wxCommandEvent&) { if (m_plater) m_plater->load_project(); }, menu_icon("open"), nullptr, | ||||||
|             [this](){return m_plater != nullptr; }, this); |             [this](){return m_plater != nullptr; }, this); | ||||||
|  | 
 | ||||||
|  |         wxMenu* recent_projects_menu = new wxMenu(); | ||||||
|  |         wxMenuItem* recent_projects_submenu = append_submenu(fileMenu, recent_projects_menu, wxID_ANY, _(L("Recent projects")), ""); | ||||||
|  |         m_recent_projects.UseMenu(recent_projects_menu); | ||||||
|  |         Bind(wxEVT_MENU, [this](wxCommandEvent& evt) { | ||||||
|  |             size_t file_id = evt.GetId() - wxID_FILE1; | ||||||
|  |             wxString filename = m_recent_projects.GetHistoryFile(file_id); | ||||||
|  |             if (wxFileExists(filename)) | ||||||
|  |                 m_plater->load_project(filename); | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 wxMessageDialog msg(this, _(L("The selected project is no more available")), _(L("Error"))); | ||||||
|  |                 msg.ShowModal(); | ||||||
|  | 
 | ||||||
|  |                 m_recent_projects.RemoveFileFromHistory(file_id); | ||||||
|  |                 std::vector<std::string> recent_projects; | ||||||
|  |                 size_t count = m_recent_projects.GetCount(); | ||||||
|  |                 for (size_t i = 0; i < count; ++i) | ||||||
|  |                 { | ||||||
|  |                     recent_projects.push_back(into_u8(m_recent_projects.GetHistoryFile(i))); | ||||||
|  |                 } | ||||||
|  |                 wxGetApp().app_config->set_recent_projects(recent_projects); | ||||||
|  |                 wxGetApp().app_config->save(); | ||||||
|  |             } | ||||||
|  |             }, wxID_FILE1, wxID_FILE9); | ||||||
|  | 
 | ||||||
|  |         std::vector<std::string> recent_projects = wxGetApp().app_config->get_recent_projects(); | ||||||
|  |         for (const std::string& project : recent_projects) | ||||||
|  |         { | ||||||
|  |             m_recent_projects.AddFileToHistory(from_u8(project)); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(m_recent_projects.GetCount() > 0); }, recent_projects_submenu->GetId()); | ||||||
|  | 
 | ||||||
|         append_menu_item(fileMenu, wxID_ANY, _(L("&Save Project")) + "\tCtrl+S", _(L("Save current project file")), |         append_menu_item(fileMenu, wxID_ANY, _(L("&Save Project")) + "\tCtrl+S", _(L("Save current project file")), | ||||||
|             [this](wxCommandEvent&) { if (m_plater) m_plater->export_3mf(into_path(m_plater->get_project_filename(".3mf"))); }, menu_icon("save"), nullptr, |             [this](wxCommandEvent&) { if (m_plater) m_plater->export_3mf(into_path(m_plater->get_project_filename(".3mf"))); }, menu_icon("save"), nullptr, | ||||||
|             [this](){return m_plater != nullptr && can_save(); }, this); |             [this](){return m_plater != nullptr && can_save(); }, this); | ||||||
|  | @ -1046,6 +1081,23 @@ void MainFrame::on_config_changed(DynamicPrintConfig* config) const | ||||||
|         m_plater->on_config_change(*config); // propagate config change events to the plater
 |         m_plater->on_config_change(*config); // propagate config change events to the plater
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void MainFrame::add_to_recent_projects(const wxString& filename) | ||||||
|  | { | ||||||
|  |     if (wxFileExists(filename)) | ||||||
|  |     { | ||||||
|  |         m_recent_projects.AddFileToHistory(filename); | ||||||
|  |         std::vector<std::string> recent_projects; | ||||||
|  |         size_t count = m_recent_projects.GetCount(); | ||||||
|  |         for (size_t i = 0; i < count; ++i) | ||||||
|  |         { | ||||||
|  |             recent_projects.push_back(into_u8(m_recent_projects.GetHistoryFile(i))); | ||||||
|  |         } | ||||||
|  |         wxGetApp().app_config->set_recent_projects(recent_projects); | ||||||
|  |         wxGetApp().app_config->save(); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //
 | ||||||
| // Called after the Preferences dialog is closed and the program settings are saved.
 | // Called after the Preferences dialog is closed and the program settings are saved.
 | ||||||
| // Update the UI based on the current preferences.
 | // Update the UI based on the current preferences.
 | ||||||
| void MainFrame::update_ui_from_settings() | void MainFrame::update_ui_from_settings() | ||||||
|  |  | ||||||
|  | @ -6,6 +6,7 @@ | ||||||
| #include <wx/frame.h> | #include <wx/frame.h> | ||||||
| #include <wx/settings.h> | #include <wx/settings.h> | ||||||
| #include <wx/string.h> | #include <wx/string.h> | ||||||
|  | #include <wx/filehistory.h> | ||||||
| 
 | 
 | ||||||
| #include <string> | #include <string> | ||||||
| #include <map> | #include <map> | ||||||
|  | @ -84,6 +85,8 @@ class MainFrame : public DPIFrame | ||||||
|     // vector of a MenuBar items changeable in respect to printer technology 
 |     // vector of a MenuBar items changeable in respect to printer technology 
 | ||||||
|     std::vector<wxMenuItem*> m_changeable_menu_items; |     std::vector<wxMenuItem*> m_changeable_menu_items; | ||||||
| 
 | 
 | ||||||
|  |     wxFileHistory m_recent_projects; | ||||||
|  | 
 | ||||||
| protected: | protected: | ||||||
|     virtual void on_dpi_changed(const wxRect &suggested_rect); |     virtual void on_dpi_changed(const wxRect &suggested_rect); | ||||||
| 
 | 
 | ||||||
|  | @ -121,6 +124,8 @@ public: | ||||||
|     // Propagate changed configuration from the Tab to the Platter and save changes to the AppConfig
 |     // Propagate changed configuration from the Tab to the Platter and save changes to the AppConfig
 | ||||||
|     void        on_config_changed(DynamicPrintConfig* cfg) const ; |     void        on_config_changed(DynamicPrintConfig* cfg) const ; | ||||||
| 
 | 
 | ||||||
|  |     void        add_to_recent_projects(const wxString& filename); | ||||||
|  | 
 | ||||||
|     PrintHostQueueDialog* printhost_queue_dlg() { return m_printhost_queue_dlg; } |     PrintHostQueueDialog* printhost_queue_dlg() { return m_printhost_queue_dlg; } | ||||||
| 
 | 
 | ||||||
|     Plater*             m_plater { nullptr }; |     Plater*             m_plater { nullptr }; | ||||||
|  |  | ||||||
|  | @ -266,7 +266,7 @@ void OptionsGroup::append_line(const Line& line, wxStaticText**	full_Label/* = n | ||||||
|             is_sizer_field(field) ? |             is_sizer_field(field) ? | ||||||
|                 v_sizer->Add(field->getSizer(), 0, wxEXPAND) : |                 v_sizer->Add(field->getSizer(), 0, wxEXPAND) : | ||||||
|                 v_sizer->Add(field->getWindow(), 0, wxEXPAND); |                 v_sizer->Add(field->getWindow(), 0, wxEXPAND); | ||||||
|             return; |             break;//return;
 | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
| 		is_sizer_field(field) ?  | 		is_sizer_field(field) ?  | ||||||
|  | @ -300,7 +300,7 @@ void OptionsGroup::append_line(const Line& line, wxStaticText**	full_Label/* = n | ||||||
|         { |         { | ||||||
|             // extra widget for non-staticbox option group (like for the frequently used parameters on the sidebar) should be wxALIGN_RIGHT
 |             // extra widget for non-staticbox option group (like for the frequently used parameters on the sidebar) should be wxALIGN_RIGHT
 | ||||||
|             const auto v_sizer = new wxBoxSizer(wxVERTICAL); |             const auto v_sizer = new wxBoxSizer(wxVERTICAL); | ||||||
|             sizer->Add(v_sizer, 1, wxEXPAND); |             sizer->Add(v_sizer, option_set.size() == 1 ? 0 : 1, wxEXPAND); | ||||||
|             v_sizer->Add(extra_widget(this->ctrl_parent()), 0, wxALIGN_RIGHT); |             v_sizer->Add(extra_widget(this->ctrl_parent()), 0, wxALIGN_RIGHT); | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  | @ -162,6 +162,12 @@ public: | ||||||
| 
 | 
 | ||||||
|     void            clear_fields_except_of(const std::vector<std::string> left_fields); |     void            clear_fields_except_of(const std::vector<std::string> left_fields); | ||||||
| 
 | 
 | ||||||
|  |     void            hide_labels() { | ||||||
|  |         label_width = 0; | ||||||
|  |         m_grid_sizer->SetCols(m_grid_sizer->GetEffectiveColsCount()-1); | ||||||
|  |         static_cast<wxFlexGridSizer*>(m_grid_sizer)->AddGrowableCol(!extra_column ? 0 : 1); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
| 	OptionsGroup(	wxWindow* _parent, const wxString& title, bool is_tab_opt = false,  | 	OptionsGroup(	wxWindow* _parent, const wxString& title, bool is_tab_opt = false,  | ||||||
| 					column_t extra_clmn = nullptr) : | 					column_t extra_clmn = nullptr) : | ||||||
| 					m_parent(_parent), title(title),  | 					m_parent(_parent), title(title),  | ||||||
|  |  | ||||||
|  | @ -245,6 +245,7 @@ wxBitmapComboBox(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(15 * | ||||||
|     last_selected(wxNOT_FOUND), |     last_selected(wxNOT_FOUND), | ||||||
|     m_em_unit(wxGetApp().em_unit()) |     m_em_unit(wxGetApp().em_unit()) | ||||||
| { | { | ||||||
|  |     SetFont(wxGetApp().normal_font()); | ||||||
|     Bind(wxEVT_COMBOBOX, [this](wxCommandEvent &evt) { |     Bind(wxEVT_COMBOBOX, [this](wxCommandEvent &evt) { | ||||||
|         auto selected_item = this->GetSelection(); |         auto selected_item = this->GetSelection(); | ||||||
| 
 | 
 | ||||||
|  | @ -372,24 +373,36 @@ class FreqChangedParams : public OG_Settings | ||||||
|     wxSizer*        m_sizer {nullptr}; |     wxSizer*        m_sizer {nullptr}; | ||||||
| 
 | 
 | ||||||
|     std::shared_ptr<ConfigOptionsGroup> m_og_sla; |     std::shared_ptr<ConfigOptionsGroup> m_og_sla; | ||||||
|  |     std::vector<ScalableButton*>        m_empty_buttons; | ||||||
| public: | public: | ||||||
|     FreqChangedParams(wxWindow* parent, const int label_width); |     FreqChangedParams(wxWindow* parent); | ||||||
|     ~FreqChangedParams() {} |     ~FreqChangedParams() {} | ||||||
| 
 | 
 | ||||||
|     wxButton*       get_wiping_dialog_button() { return m_wiping_dialog_button; } |     wxButton*       get_wiping_dialog_button() { return m_wiping_dialog_button; } | ||||||
|     wxSizer*        get_sizer() override; |     wxSizer*        get_sizer() override; | ||||||
|     ConfigOptionsGroup* get_og(const bool is_fff); |     ConfigOptionsGroup* get_og(const bool is_fff); | ||||||
|     void            Show(const bool is_fff); |     void            Show(const bool is_fff); | ||||||
|  | 
 | ||||||
|  |     void            msw_rescale(); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| FreqChangedParams::FreqChangedParams(wxWindow* parent, const int label_width) : | void FreqChangedParams::msw_rescale() | ||||||
|  | { | ||||||
|  |     m_og->msw_rescale(); | ||||||
|  |     m_og_sla->msw_rescale(); | ||||||
|  | 
 | ||||||
|  |     for (auto btn: m_empty_buttons) | ||||||
|  |         btn->msw_rescale(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | FreqChangedParams::FreqChangedParams(wxWindow* parent) : | ||||||
|     OG_Settings(parent, false) |     OG_Settings(parent, false) | ||||||
| { | { | ||||||
|     DynamicPrintConfig*	config = &wxGetApp().preset_bundle->prints.get_edited_preset().config; |     DynamicPrintConfig*	config = &wxGetApp().preset_bundle->prints.get_edited_preset().config; | ||||||
| 
 | 
 | ||||||
|     // Frequently changed parameters for FFF_technology
 |     // Frequently changed parameters for FFF_technology
 | ||||||
|     m_og->set_config(config); |     m_og->set_config(config); | ||||||
|     m_og->label_width = label_width == 0 ? 1 : label_width; |     m_og->hide_labels(); | ||||||
| 
 | 
 | ||||||
|     m_og->m_on_change = [config, this](t_config_option_key opt_key, boost::any value) { |     m_og->m_on_change = [config, this](t_config_option_key opt_key, boost::any value) { | ||||||
|         Tab* tab_print = wxGetApp().get_tab(Preset::TYPE_PRINT); |         Tab* tab_print = wxGetApp().get_tab(Preset::TYPE_PRINT); | ||||||
|  | @ -461,6 +474,20 @@ FreqChangedParams::FreqChangedParams(wxWindow* parent, const int label_width) : | ||||||
|     Option option = Option(support_def, "support"); |     Option option = Option(support_def, "support"); | ||||||
|     option.opt.full_width = true; |     option.opt.full_width = true; | ||||||
|     line.append_option(option); |     line.append_option(option); | ||||||
|  | 
 | ||||||
|  |     /* Not a best solution, but
 | ||||||
|  |      * Temporary workaround for right border alignment | ||||||
|  |      */ | ||||||
|  |     auto empty_widget = [this] (wxWindow* parent) { | ||||||
|  |         auto sizer = new wxBoxSizer(wxHORIZONTAL); | ||||||
|  |         auto btn = new ScalableButton(parent, wxID_ANY, "mirroring_transparent.png", wxEmptyString, | ||||||
|  |             wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER | wxTRANSPARENT_WINDOW); | ||||||
|  |         sizer->Add(btn, 0, wxALIGN_CENTER_VERTICAL | wxLEFT | wxRIGHT, int(0.3 * wxGetApp().em_unit())); | ||||||
|  |         m_empty_buttons.push_back(btn); | ||||||
|  |         return sizer; | ||||||
|  |     }; | ||||||
|  |     line.append_widget(empty_widget); | ||||||
|  | 
 | ||||||
|     m_og->append_line(line); |     m_og->append_line(line); | ||||||
| 
 | 
 | ||||||
|      |      | ||||||
|  | @ -485,8 +512,9 @@ FreqChangedParams::FreqChangedParams(wxWindow* parent, const int label_width) : | ||||||
| 
 | 
 | ||||||
|     auto wiping_dialog_btn = [config, this](wxWindow* parent) { |     auto wiping_dialog_btn = [config, this](wxWindow* parent) { | ||||||
|         m_wiping_dialog_button = new wxButton(parent, wxID_ANY, _(L("Purging volumes")) + dots, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT); |         m_wiping_dialog_button = new wxButton(parent, wxID_ANY, _(L("Purging volumes")) + dots, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT); | ||||||
|  |         m_wiping_dialog_button->SetFont(wxGetApp().normal_font()); | ||||||
|         auto sizer = new wxBoxSizer(wxHORIZONTAL); |         auto sizer = new wxBoxSizer(wxHORIZONTAL); | ||||||
|         sizer->Add(m_wiping_dialog_button); |         sizer->Add(m_wiping_dialog_button, 0, wxALIGN_CENTER_VERTICAL); | ||||||
|         m_wiping_dialog_button->Bind(wxEVT_BUTTON, ([parent](wxCommandEvent& e) |         m_wiping_dialog_button->Bind(wxEVT_BUTTON, ([parent](wxCommandEvent& e) | ||||||
|         { |         { | ||||||
|             auto &config = wxGetApp().preset_bundle->project_config; |             auto &config = wxGetApp().preset_bundle->project_config; | ||||||
|  | @ -503,6 +531,13 @@ FreqChangedParams::FreqChangedParams(wxWindow* parent, const int label_width) : | ||||||
|                 wxPostEvent(parent, SimpleEvent(EVT_SCHEDULE_BACKGROUND_PROCESS, parent)); |                 wxPostEvent(parent, SimpleEvent(EVT_SCHEDULE_BACKGROUND_PROCESS, parent)); | ||||||
|             } |             } | ||||||
|         })); |         })); | ||||||
|  | 
 | ||||||
|  |         auto btn = new ScalableButton(parent, wxID_ANY, "mirroring_transparent.png", wxEmptyString,  | ||||||
|  |                                       wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER | wxTRANSPARENT_WINDOW); | ||||||
|  |         sizer->Add(btn , 0, wxALIGN_CENTER_VERTICAL | wxLEFT | wxRIGHT, | ||||||
|  |             int(0.3 * wxGetApp().em_unit())); | ||||||
|  |         m_empty_buttons.push_back(btn); | ||||||
|  | 
 | ||||||
|         return sizer; |         return sizer; | ||||||
|     }; |     }; | ||||||
|     line.append_widget(wiping_dialog_btn); |     line.append_widget(wiping_dialog_btn); | ||||||
|  | @ -512,9 +547,9 @@ FreqChangedParams::FreqChangedParams(wxWindow* parent, const int label_width) : | ||||||
| 
 | 
 | ||||||
|     // Frequently changed parameters for SLA_technology
 |     // Frequently changed parameters for SLA_technology
 | ||||||
|     m_og_sla = std::make_shared<ConfigOptionsGroup>(parent, ""); |     m_og_sla = std::make_shared<ConfigOptionsGroup>(parent, ""); | ||||||
|  |     m_og_sla->hide_labels(); | ||||||
|     DynamicPrintConfig*	config_sla = &wxGetApp().preset_bundle->sla_prints.get_edited_preset().config; |     DynamicPrintConfig*	config_sla = &wxGetApp().preset_bundle->sla_prints.get_edited_preset().config; | ||||||
|     m_og_sla->set_config(config_sla); |     m_og_sla->set_config(config_sla); | ||||||
|     m_og_sla->label_width = label_width == 0 ? 1 : label_width; |  | ||||||
| 
 | 
 | ||||||
|     m_og_sla->m_on_change = [config_sla, this](t_config_option_key opt_key, boost::any value) { |     m_og_sla->m_on_change = [config_sla, this](t_config_option_key opt_key, boost::any value) { | ||||||
|         Tab* tab = wxGetApp().get_tab(Preset::TYPE_SLA_PRINT); |         Tab* tab = wxGetApp().get_tab(Preset::TYPE_SLA_PRINT); | ||||||
|  | @ -553,6 +588,7 @@ FreqChangedParams::FreqChangedParams(wxWindow* parent, const int label_width) : | ||||||
|     option = Option(support_def_sla, "support"); |     option = Option(support_def_sla, "support"); | ||||||
|     option.opt.full_width = true; |     option.opt.full_width = true; | ||||||
|     line.append_option(option);  |     line.append_option(option);  | ||||||
|  |     line.append_widget(empty_widget); | ||||||
|     m_og_sla->append_line(line); |     m_og_sla->append_line(line); | ||||||
| 
 | 
 | ||||||
|     line = Line{ "", "" }; |     line = Line{ "", "" }; | ||||||
|  | @ -733,13 +769,12 @@ Sidebar::Sidebar(Plater *parent) | ||||||
|     init_combo(&p->combo_printer,       _(L("Printer")),            Preset::TYPE_PRINTER,       false); |     init_combo(&p->combo_printer,       _(L("Printer")),            Preset::TYPE_PRINTER,       false); | ||||||
| 
 | 
 | ||||||
|     const int margin_5  = int(0.5*wxGetApp().em_unit());// 5;
 |     const int margin_5  = int(0.5*wxGetApp().em_unit());// 5;
 | ||||||
|     const int margin_10 = 10;//int(1.5*wxGetApp().em_unit());// 15;
 |  | ||||||
| 
 | 
 | ||||||
|     p->sizer_params = new wxBoxSizer(wxVERTICAL); |     p->sizer_params = new wxBoxSizer(wxVERTICAL); | ||||||
| 
 | 
 | ||||||
|     // Frequently changed parameters
 |     // Frequently changed parameters
 | ||||||
|     p->frequently_changed_parameters = new FreqChangedParams(p->scrolled, 0/*label_width*/); |     p->frequently_changed_parameters = new FreqChangedParams(p->scrolled); | ||||||
|     p->sizer_params->Add(p->frequently_changed_parameters->get_sizer(), 0, wxEXPAND | wxTOP | wxBOTTOM, margin_10); |     p->sizer_params->Add(p->frequently_changed_parameters->get_sizer(), 0, wxEXPAND | wxTOP | wxBOTTOM, wxOSX ? 1 : margin_5); | ||||||
|      |      | ||||||
|     // Object List
 |     // Object List
 | ||||||
|     p->object_list = new ObjectList(p->scrolled); |     p->object_list = new ObjectList(p->scrolled); | ||||||
|  | @ -946,9 +981,7 @@ void Sidebar::msw_rescale() | ||||||
|     // ... then refill them and set min size to correct layout of the sidebar
 |     // ... then refill them and set min size to correct layout of the sidebar
 | ||||||
|     update_all_preset_comboboxes(); |     update_all_preset_comboboxes(); | ||||||
| 
 | 
 | ||||||
|     p->frequently_changed_parameters->get_og(true)->msw_rescale(); |     p->frequently_changed_parameters->msw_rescale(); | ||||||
|     p->frequently_changed_parameters->get_og(false)->msw_rescale(); |  | ||||||
| 
 |  | ||||||
|     p->object_list->msw_rescale(); |     p->object_list->msw_rescale(); | ||||||
|     p->object_manipulation->msw_rescale(); |     p->object_manipulation->msw_rescale(); | ||||||
|     p->object_settings->msw_rescale(); |     p->object_settings->msw_rescale(); | ||||||
|  | @ -1132,10 +1165,20 @@ void Sidebar::show_sliced_info_sizer(const bool show) | ||||||
|                 if (ps.estimated_normal_print_time != "N/A") { |                 if (ps.estimated_normal_print_time != "N/A") { | ||||||
|                     new_label += wxString::Format("\n    - %s", _(L("normal mode"))); |                     new_label += wxString::Format("\n    - %s", _(L("normal mode"))); | ||||||
|                     info_text += wxString::Format("\n%s", ps.estimated_normal_print_time); |                     info_text += wxString::Format("\n%s", ps.estimated_normal_print_time); | ||||||
|  |                     for (int i = (int)ps.estimated_normal_color_print_times.size() - 1; i >= 0; --i) | ||||||
|  |                     { | ||||||
|  |                         new_label += wxString::Format("\n      - %s%d", _(L("Color ")), i + 1); | ||||||
|  |                         info_text += wxString::Format("\n%s", ps.estimated_normal_color_print_times[i]); | ||||||
|  |                     } | ||||||
|                 } |                 } | ||||||
|                 if (ps.estimated_silent_print_time != "N/A") { |                 if (ps.estimated_silent_print_time != "N/A") { | ||||||
|                     new_label += wxString::Format("\n    - %s", _(L("stealth mode"))); |                     new_label += wxString::Format("\n    - %s", _(L("stealth mode"))); | ||||||
|                     info_text += wxString::Format("\n%s", ps.estimated_silent_print_time); |                     info_text += wxString::Format("\n%s", ps.estimated_silent_print_time); | ||||||
|  |                     for (int i = (int)ps.estimated_normal_color_print_times.size() - 1; i >= 0; --i) | ||||||
|  |                     { | ||||||
|  |                         new_label += wxString::Format("\n      - %s%d", _(L("Color ")), i + 1); | ||||||
|  |                         info_text += wxString::Format("\n%s", ps.estimated_normal_color_print_times[i]); | ||||||
|  |                     } | ||||||
|                 } |                 } | ||||||
|                 p->sliced_info->SetTextAndShow(siEstimatedTime,  info_text,      new_label); |                 p->sliced_info->SetTextAndShow(siEstimatedTime,  info_text,      new_label); | ||||||
|             } |             } | ||||||
|  | @ -1693,11 +1736,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) | ||||||
|         })) |         })) | ||||||
|     , sidebar(new Sidebar(q)) |     , sidebar(new Sidebar(q)) | ||||||
|     , delayed_scene_refresh(false) |     , delayed_scene_refresh(false) | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     , view_toolbar(GLToolbar::Radio, "View") |     , view_toolbar(GLToolbar::Radio, "View") | ||||||
| #else |  | ||||||
|     , view_toolbar(GLToolbar::Radio) |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
|     , m_project_filename(wxEmptyString) |     , m_project_filename(wxEmptyString) | ||||||
| { | { | ||||||
| 	this->q->SetFont(Slic3r::GUI::wxGetApp().normal_font()); | 	this->q->SetFont(Slic3r::GUI::wxGetApp().normal_font()); | ||||||
|  | @ -3238,6 +3277,9 @@ void Plater::priv::set_project_filename(const wxString& filename) | ||||||
| 
 | 
 | ||||||
|     m_project_filename = from_path(full_path); |     m_project_filename = from_path(full_path); | ||||||
|     wxGetApp().mainframe->update_title(); |     wxGetApp().mainframe->update_title(); | ||||||
|  | 
 | ||||||
|  |     if (!filename.empty()) | ||||||
|  |         wxGetApp().mainframe->add_to_recent_projects(filename); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool Plater::priv::init_common_menu(wxMenu* menu, const bool is_part/* = false*/) | bool Plater::priv::init_common_menu(wxMenu* menu, const bool is_part/* = false*/) | ||||||
|  | @ -3352,12 +3394,6 @@ bool Plater::priv::complit_init_part_menu() | ||||||
| 
 | 
 | ||||||
| void Plater::priv::init_view_toolbar() | void Plater::priv::init_view_toolbar() | ||||||
| { | { | ||||||
| #if !ENABLE_SVG_ICONS |  | ||||||
|     ItemsIconsTexture::Metadata icons_data; |  | ||||||
|     icons_data.filename = "view_toolbar.png"; |  | ||||||
|     icons_data.icon_size = 64; |  | ||||||
| #endif // !ENABLE_SVG_ICONS
 |  | ||||||
| 
 |  | ||||||
|     BackgroundTexture::Metadata background_data; |     BackgroundTexture::Metadata background_data; | ||||||
|     background_data.filename = "toolbar_background.png"; |     background_data.filename = "toolbar_background.png"; | ||||||
|     background_data.left = 16; |     background_data.left = 16; | ||||||
|  | @ -3365,11 +3401,7 @@ void Plater::priv::init_view_toolbar() | ||||||
|     background_data.right = 16; |     background_data.right = 16; | ||||||
|     background_data.bottom = 16; |     background_data.bottom = 16; | ||||||
| 
 | 
 | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     if (!view_toolbar.init(background_data)) |     if (!view_toolbar.init(background_data)) | ||||||
| #else |  | ||||||
|     if (!view_toolbar.init(icons_data, background_data)) |  | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
|         return; |         return; | ||||||
| 
 | 
 | ||||||
|     view_toolbar.set_layout_orientation(GLToolbar::Layout::Bottom); |     view_toolbar.set_layout_orientation(GLToolbar::Layout::Bottom); | ||||||
|  | @ -3379,9 +3411,7 @@ void Plater::priv::init_view_toolbar() | ||||||
|     GLToolbarItem::Data item; |     GLToolbarItem::Data item; | ||||||
| 
 | 
 | ||||||
|     item.name = "3D"; |     item.name = "3D"; | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     item.icon_filename = "editor.svg"; |     item.icon_filename = "editor.svg"; | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
|     item.tooltip = _utf8(L("3D editor view")) + " [" + GUI::shortkey_ctrl_prefix() + "5]"; |     item.tooltip = _utf8(L("3D editor view")) + " [" + GUI::shortkey_ctrl_prefix() + "5]"; | ||||||
|     item.sprite_id = 0; |     item.sprite_id = 0; | ||||||
|     item.action_callback = [this]() { if (this->q != nullptr) wxPostEvent(this->q, SimpleEvent(EVT_GLVIEWTOOLBAR_3D)); }; |     item.action_callback = [this]() { if (this->q != nullptr) wxPostEvent(this->q, SimpleEvent(EVT_GLVIEWTOOLBAR_3D)); }; | ||||||
|  | @ -3390,9 +3420,7 @@ void Plater::priv::init_view_toolbar() | ||||||
|         return; |         return; | ||||||
| 
 | 
 | ||||||
|     item.name = "Preview"; |     item.name = "Preview"; | ||||||
| #if ENABLE_SVG_ICONS |  | ||||||
|     item.icon_filename = "preview.svg"; |     item.icon_filename = "preview.svg"; | ||||||
| #endif // ENABLE_SVG_ICONS
 |  | ||||||
|     item.tooltip = _utf8(L("Preview")) + " [" + GUI::shortkey_ctrl_prefix() + "6]"; |     item.tooltip = _utf8(L("Preview")) + " [" + GUI::shortkey_ctrl_prefix() + "6]"; | ||||||
|     item.sprite_id = 1; |     item.sprite_id = 1; | ||||||
|     item.action_callback = [this]() { if (this->q != nullptr) wxPostEvent(this->q, SimpleEvent(EVT_GLVIEWTOOLBAR_PREVIEW)); }; |     item.action_callback = [this]() { if (this->q != nullptr) wxPostEvent(this->q, SimpleEvent(EVT_GLVIEWTOOLBAR_PREVIEW)); }; | ||||||
|  | @ -3565,15 +3593,19 @@ void Plater::load_project() | ||||||
| { | { | ||||||
|     wxString input_file; |     wxString input_file; | ||||||
|     wxGetApp().load_project(this, input_file); |     wxGetApp().load_project(this, input_file); | ||||||
|  |     load_project(input_file); | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
|     if (input_file.empty()) | void Plater::load_project(const wxString& filename) | ||||||
|  | { | ||||||
|  |     if (filename.empty()) | ||||||
|         return; |         return; | ||||||
| 
 | 
 | ||||||
|     p->reset(); |     p->reset(); | ||||||
|     p->set_project_filename(input_file); |     p->set_project_filename(filename); | ||||||
| 
 | 
 | ||||||
|     std::vector<fs::path> input_paths; |     std::vector<fs::path> input_paths; | ||||||
|     input_paths.push_back(into_path(input_file)); |     input_paths.push_back(into_path(filename)); | ||||||
|     load_files(input_paths); |     load_files(input_paths); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -138,6 +138,7 @@ public: | ||||||
| 
 | 
 | ||||||
|     void new_project(); |     void new_project(); | ||||||
|     void load_project(); |     void load_project(); | ||||||
|  |     void load_project(const wxString& filename); | ||||||
|     void add_model(); |     void add_model(); | ||||||
|     void extract_config_from_project(); |     void extract_config_from_project(); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -461,6 +461,7 @@ const std::vector<std::string>& Preset::sla_print_options() | ||||||
|             "support_pillar_widening_factor", |             "support_pillar_widening_factor", | ||||||
|             "support_base_diameter", |             "support_base_diameter", | ||||||
|             "support_base_height", |             "support_base_height", | ||||||
|  |             "support_base_safety_distance", | ||||||
|             "support_critical_angle", |             "support_critical_angle", | ||||||
|             "support_max_bridge_length", |             "support_max_bridge_length", | ||||||
|             "support_max_pillar_link_distance", |             "support_max_pillar_link_distance", | ||||||
|  | @ -474,6 +475,10 @@ const std::vector<std::string>& Preset::sla_print_options() | ||||||
|             "pad_max_merge_distance", |             "pad_max_merge_distance", | ||||||
|             "pad_edge_radius", |             "pad_edge_radius", | ||||||
|             "pad_wall_slope", |             "pad_wall_slope", | ||||||
|  |             "pad_object_gap", | ||||||
|  |             "pad_object_connector_stride", | ||||||
|  |             "pad_object_connector_width", | ||||||
|  |             "pad_object_connector_penetration", | ||||||
|             "output_filename_format",  |             "output_filename_format",  | ||||||
|             "default_sla_print_profile", |             "default_sla_print_profile", | ||||||
|             "compatible_printers", |             "compatible_printers", | ||||||
|  |  | ||||||
|  | @ -3515,6 +3515,7 @@ void TabSLAPrint::build() | ||||||
|     // optgroup->append_single_option_line("support_pillar_widening_factor");
 |     // optgroup->append_single_option_line("support_pillar_widening_factor");
 | ||||||
|     optgroup->append_single_option_line("support_base_diameter"); |     optgroup->append_single_option_line("support_base_diameter"); | ||||||
|     optgroup->append_single_option_line("support_base_height"); |     optgroup->append_single_option_line("support_base_height"); | ||||||
|  |     optgroup->append_single_option_line("support_base_safety_distance"); | ||||||
|     optgroup->append_single_option_line("support_object_elevation"); |     optgroup->append_single_option_line("support_object_elevation"); | ||||||
| 
 | 
 | ||||||
|     optgroup = page->new_optgroup(_(L("Connection of the support sticks and junctions"))); |     optgroup = page->new_optgroup(_(L("Connection of the support sticks and junctions"))); | ||||||
|  | @ -3536,6 +3537,11 @@ void TabSLAPrint::build() | ||||||
| //    optgroup->append_single_option_line("pad_edge_radius");
 | //    optgroup->append_single_option_line("pad_edge_radius");
 | ||||||
|     optgroup->append_single_option_line("pad_wall_slope"); |     optgroup->append_single_option_line("pad_wall_slope"); | ||||||
|      |      | ||||||
|  |     optgroup->append_single_option_line("pad_object_gap"); | ||||||
|  |     optgroup->append_single_option_line("pad_object_connector_stride"); | ||||||
|  |     optgroup->append_single_option_line("pad_object_connector_width"); | ||||||
|  |     optgroup->append_single_option_line("pad_object_connector_penetration"); | ||||||
|  |      | ||||||
| 	page = add_options_page(_(L("Advanced")), "wrench"); | 	page = add_options_page(_(L("Advanced")), "wrench"); | ||||||
| 	optgroup = page->new_optgroup(_(L("Slicing"))); | 	optgroup = page->new_optgroup(_(L("Slicing"))); | ||||||
| 	optgroup->append_single_option_line("slice_closing_radius"); | 	optgroup->append_single_option_line("slice_closing_radius"); | ||||||
|  | @ -3583,11 +3589,18 @@ void TabSLAPrint::update() | ||||||
|     double head_penetration = m_config->opt_float("support_head_penetration"); |     double head_penetration = m_config->opt_float("support_head_penetration"); | ||||||
|     double head_width       = m_config->opt_float("support_head_width"); |     double head_width       = m_config->opt_float("support_head_width"); | ||||||
|     if (head_penetration > head_width) { |     if (head_penetration > head_width) { | ||||||
|          wxString msg_text = _(L("Head penetration should not be greater than the head width.")); |         wxString msg_text = _( | ||||||
|          auto dialog = new wxMessageDialog(parent(), msg_text, _(L("Invalid Head penetration")), wxICON_WARNING | wxOK); |             L("Head penetration should not be greater than the head width.")); | ||||||
|  | 
 | ||||||
|  |         auto dialog = new wxMessageDialog(parent(), | ||||||
|  |                                           msg_text, | ||||||
|  |                                           _(L("Invalid Head penetration")), | ||||||
|  |                                           wxICON_WARNING | wxOK); | ||||||
|  | 
 | ||||||
|         DynamicPrintConfig new_conf = *m_config; |         DynamicPrintConfig new_conf = *m_config; | ||||||
|         if (dialog->ShowModal() == wxID_OK) { |         if (dialog->ShowModal() == wxID_OK) { | ||||||
|              new_conf.set_key_value("support_head_penetration", new ConfigOptionFloat(head_width)); |             new_conf.set_key_value("support_head_penetration", | ||||||
|  |                                    new ConfigOptionFloat(head_width)); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         load_config(new_conf); |         load_config(new_conf); | ||||||
|  | @ -3596,20 +3609,34 @@ void TabSLAPrint::update() | ||||||
|     double pinhead_d = m_config->opt_float("support_head_front_diameter"); |     double pinhead_d = m_config->opt_float("support_head_front_diameter"); | ||||||
|     double pillar_d  = m_config->opt_float("support_pillar_diameter"); |     double pillar_d  = m_config->opt_float("support_pillar_diameter"); | ||||||
|     if (pinhead_d > pillar_d) { |     if (pinhead_d > pillar_d) { | ||||||
|          wxString msg_text = _(L("Pinhead diameter should be smaller than the pillar diameter.")); |         wxString msg_text = _(L( | ||||||
|          auto dialog = new wxMessageDialog(parent(), msg_text, _(L("Invalid pinhead diameter")), wxICON_WARNING | wxOK); |             "Pinhead diameter should be smaller than the pillar diameter.")); | ||||||
|  | 
 | ||||||
|  |         auto dialog = new wxMessageDialog(parent(), | ||||||
|  |                                           msg_text, | ||||||
|  |                                           _(L("Invalid pinhead diameter")), | ||||||
|  |                                           wxICON_WARNING | wxOK); | ||||||
|  | 
 | ||||||
|         DynamicPrintConfig new_conf = *m_config; |         DynamicPrintConfig new_conf = *m_config; | ||||||
|         if (dialog->ShowModal() == wxID_OK) { |         if (dialog->ShowModal() == wxID_OK) { | ||||||
|              new_conf.set_key_value("support_head_front_diameter", new ConfigOptionFloat(pillar_d / 2.0)); |             new_conf.set_key_value("support_head_front_diameter", | ||||||
|  |                                    new ConfigOptionFloat(pillar_d / 2.0)); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         load_config(new_conf); |         load_config(new_conf); | ||||||
|     } |     } | ||||||
|      |      | ||||||
|  |     // if(m_config->opt_float("support_object_elevation") < EPSILON &&
 | ||||||
|  |     //    m_config->opt_bool("pad_enable")) {
 | ||||||
|  |     //     // TODO: disable editding of:
 | ||||||
|  |     //     // pad_object_connector_stride
 | ||||||
|  |     //     // pad_object_connector_width
 | ||||||
|  |     //     // pad_object_connector_penetration
 | ||||||
|  |     // }
 | ||||||
|  | 
 | ||||||
|     m_update_cnt--; |     m_update_cnt--; | ||||||
| 
 | 
 | ||||||
|      if (m_update_cnt == 0) |     if (m_update_cnt == 0) wxGetApp().mainframe->on_config_changed(m_config); | ||||||
|     wxGetApp().mainframe->on_config_changed(m_config); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| } // GUI
 | } // GUI
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Enrico Turri
						Enrico Turri